[Scummvm-git-logs] scummvm master -> 287902994ca5d38669225c8b80158a8c2b221674

athrxx athrxx at scummvm.org
Thu Jul 30 20:40:47 UTC 2020


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

Summary:
1dd482c762 KYRA: (EOB/SegaCD) - add detection entry
5be7a08c42 KYRA: (EOB/SegaCD) - add static resources
e4a33abfb5 KYRA: (EOB/SegaCD) - implement opening credits
7365c25ce1 KYRA: (EOB/SegaCD) - set screen height to 224 pixels
8f0399aaeb KYRA: (EOB/SegaCD) - recover changes lost during rebase
466a927738 KYRA: (EOB/SegaCD) - implement sequence player
71c8007be6 KYRA: (EOB) - move some graphics code between files
5e8691b726 KYRA: (EOB) - minor font code cleanup
91b18f8343 KYRA: (EOB/SegaCD) - more graphics and sequence player code
73650a0f54 KYRA: (EOB/SegaCD) - fix main menu
18ae001530 KYRA: (EOB/SegaCD) - add sprite converter
a26d08466c KYRA: (EOB/SegaCD) - implement default party creation
c2c7cfff9b KYRA: (EOB/SegaCD) - implement specific shape handling
78c6b8fb92 KYRA: (EOB/SegaCD) - cleanup
b46ab198f7 KYRA: (EOB/SegaCD) - fix warning
a657d02228 KYRA: (EOB/SegaCD) - implement ending credits and stats screen
ec2d4644f7 KYRA: (EOB/SegaCD) - fix item data init
08fea39d4a KYRA: (EOB2/Amiga) - fix minor color glitch
fbc75a6df5 KYRA: (EOB/PC98) - minor ending sequence tweak
4cb11adf9f KYRA: (EOB/LOL) - keymap init code reduction
5da41b3e11 KYRA: (EOB/SegaCD) - fix level loading
b59d201873 KYRA: (EOB/SegaCD) - fix decorations and monster animations
edc0c5dc91 AUDIO: (FM-TOWNS) - fix pcm rate
151e3e79ca KYRA: (EOB/SegaCD) - implement item shape handling
c803985605 KYRA: (EOB/SegaCD) - adjust timing
bd97aba171 KYRA: (EOB/SegaCD) - fix some gui elements
59df137cf4 KYRA: (EOB) - fix Amiga, PC98 and SegaCD GUIO flags
9d8c249015 KYRA: (EOB/SegaCD) - vertically shift screen output
8f41adc969 KYRA: - fix copyBlockToPage() glitch
0fc87659fb KYRA: (EOB/SegaCD) - more work on the gui
a904ba4f48 KYRA: (EOB/SegaCD) - fix text display, inventory and stats page
c078242486 KYRA: (EOB/SegaCD) - adjust buttons and implement compass animations
6c67807e17 KYRA: (EOB/SegaCD) - adjust savegame code and some cleanup
e2d01ae8e0 KYRA: (EOB/SegaCD) - fix door shapes
fcad95af09 KYRA: (EOB/SegaCD) - add level map function
5bffb5b9de KYRA: (EOB/SegaCD) - fix parchment display
b48a37dc2f KYRA: (EOB/SegaCD) - fix various sequences and gameover screen
5d18017509 KYRA: (EOB/SegaCD) - fix NPC portrait font glitch
1db312cd3b KYRA: (EOB/SegaCD) - fix spellbook and magic effects
db1fd2a546 KYRA: (EOB/PC-98) - fix detect magic spell
03a87dc6db KYRA: (EOB/SegaCD) - first part of camp menu
dc7de3b2a5 KYRA: (EOB/SegaCD) - finish camp menu
b5b9696383 KYRA: (EOB/SegaCD) - improve renderer
2ebf16cf13 KYRA: (EOB/SegaCD) - adapt code to updated renderer
87ec24fbfd KYRA: (EOB/SegaCD) - update character generator
d8bae9196c KYRA: (EOB/SegaCD) - fix save dialog glitch
e190feda07 KYRA: (EOB/SegaCD) - fix save slot naming glitch
9c1e8c99c6 KYRA: fix various warnings
1f11262c8a KYRA: (EOB/SegaCD) - fix scribe scroll dialog
1303dfc8b7 KYRA: (EOB/SegaCD) - fix text color glitch
fb9f6ba4a5 KYRA: (EOB) - allow return to launcher from gameover screen
f2b02a77ce KYRA: (EOB/SegaCD) - improve magic animations
5e276286ae AUDIO/GUI: add SegaCD sound settings
b379b52938 AUDIO: preparations for SegaCD sound driver
8e800d44b1 KYRA: (EOB/SegaCD) - add sound driver
dda1559f03 KYRA: (EOB/SegaCD) - SegaCD sound effect adjustments
ed5f695c04 KYRA: (EOB/SegaCD) - fix warnings
c8b042f0bc KYRA: (EOB) - fix level graphics glitch
2b2e90c8c6 KYRA: (EOB/SegaCD) - improve/cleanup sequence player
3f753e80a7 KYRA: (EOB/SegaCD) - fix playing timer and time stamps
fc0e1cfc34 KYRA: (EOB/SegaCD) - fix Wshadow warning
3773572ea0 KYRA: (EOB/SegaCD) - fix drow sequence gfx glitch
314eec3c45 KYRA: (EOB/SegaCD) - fix compass glitch
4e9a86c03e KYRA: (EOB/SegaCD) - fix king healing scene
287902994c NEWS: update with EOB/SegaCD


Commit: 1dd482c762190fb023bda7c3bb7aeb0228d1899f
    https://github.com/scummvm/scummvm/commit/1dd482c762190fb023bda7c3bb7aeb0228d1899f
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:08+02:00

Commit Message:
KYRA: (EOB/SegaCD) - 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 db8863b9b0..26427c1d0a 100644
--- a/engines/kyra/detection_tables.h
+++ b/engines/kyra/detection_tables.h
@@ -1829,6 +1829,22 @@ const KYRAGameDescription adGameDescs[] = {
 		EOB_PC98_FLAGS
 	},
 
+	{
+		{
+			"eob",
+			0,
+			{
+				{ "PLAYFLD", 0, "e9dbc6944e6c00801f3932808f98e443", -1 },
+				{ 0, 0, 0, 0 }
+			},
+			Common::EN_ANY,
+			Common::kPlatformSegaCD,
+			ADGF_NO_FLAGS,
+			GUIO3(GUIO_NOSPEECH, GUIO_NOMIDI, GAMEOPTION_EOB_HPGRAPHS)
+		},
+		EOB_FLAGS
+	},
+
 	{
 		{
 			"eob2",


Commit: 5be7a08c4249103a6c56d5e698faf4d87f450a31
    https://github.com/scummvm/scummvm/commit/5be7a08c4249103a6c56d5e698faf4d87f450a31
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:08+02:00

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

Changed paths:
  A devtools/create_kyradat/resources/eob1_segacd.h
  A devtools/create_kyradat/resources/eob1_segacd_english.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_amiga.h
    devtools/create_kyradat/resources/eob1_pc98.h
    devtools/create_kyradat/resources/eob1_pc98_japanese.h
    devtools/create_kyradat/resources/eob2_fmtowns_japanese.h
    dists/engine-data/kyra.dat
    engines/kyra/engine/chargen.cpp
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h
    engines/kyra/engine/sprites_eob.cpp
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/resource/resource.h
    engines/kyra/resource/staticres.cpp
    engines/kyra/resource/staticres_eob.cpp


diff --git a/devtools/create_kyradat/create_kyradat.cpp b/devtools/create_kyradat/create_kyradat.cpp
index 77a0700b4d..034ed6d1d0 100644
--- a/devtools/create_kyradat/create_kyradat.cpp
+++ b/devtools/create_kyradat/create_kyradat.cpp
@@ -45,7 +45,7 @@
 
 
 enum {
-	kKyraDatVersion = 101
+	kKyraDatVersion = 102
 };
 
 const ExtractFilename extractFilenames[] = {
@@ -370,6 +370,8 @@ const ExtractFilename extractFilenames[] = {
 	{ kEoBBaseBookNumbers, kStringList, true },
 	{ kEoBBaseMageSpellsList, kStringList, true },
 	{ kEoBBaseClericSpellsList, kStringList, true },
+	{ kEoBBaseMageSpellsList2, kStringList, true },
+	{ kEoBBaseClericSpellsList2, kStringList, true },
 	{ kEoBBaseSpellNames, kStringList, true },
 
 	{ kEoBBaseMagicStrings1, kStringList, true },
@@ -418,6 +420,8 @@ const ExtractFilename extractFilenames[] = {
 	{ kEoBBaseLevelSoundFiles2, kStringList, false },
 
 	// EYE OF THE BEHOLDER I
+	{ kEoB1DefaultPartyStats, kRawData, false },
+	{ kEoB1DefaultPartyNames, kStringList, true },
 	{ kEoB1MainMenuStrings, kStringList, true },
 	{ kEoB1BonusStrings, kStringList, true },
 
@@ -462,8 +466,10 @@ const ExtractFilename extractFilenames[] = {
 	{ kEoB1FinaleHandsAnim2, kRawDataBe16, false },
 	{ kEoB1FinaleHandsAnim3, kRawData, false },
 	{ kEoB1FinaleTextDuration, kRawData, false },
-	{ kEoB1CreditsStrings, kRawData, false },
+	{ kEoB1CreditsStrings, kRawData, true },
 	{ kEoB1CreditsCharWdth, kRawData, false },
+	{ kEoB1CreditsStrings2, kStringList, true },
+	{ kEoB1CreditsTileGrid, kRawData, false },
 	{ kEoB1DoorShapeDefs, kRawData, false },
 	{ kEoB1DoorSwitchShapeDefs, kRawData, false },
 	{ kEoB1DoorSwitchCoords, kRawData, false },
@@ -493,6 +499,15 @@ const ExtractFilename extractFilenames[] = {
 	{ kEoB1PalCycleData, kRawData, false },
 	{ kEoB1PalCycleStyle1, kRawDataBe16, false },
 	{ kEoB1PalCycleStyle2, kRawDataBe16, false },
+	{ kEoB1PalettesSega, kRawDataBe16, false },
+	{ kEoB1PatternTable0, kRawDataBe16, false },
+	{ kEoB1PatternTable1, kRawDataBe16, false },
+	{ kEoB1PatternTable2, kRawDataBe16, false },
+	{ kEoB1PatternTable3, kRawDataBe16, false },
+	{ kEoB1PatternTable4, kRawDataBe16, false },
+	{ kEoB1PatternTable5, kRawDataBe16, false },
+	{ kEoB1PatternAddTable1, kRawDataBe16, false },
+	{ kEoB1PatternAddTable2, kRawDataBe16, false },
 
 	{ kEoB1NpcShpData, kRawData, false },
 	{ kEoB1NpcSubShpIndex1, kRawData, false },
@@ -510,10 +525,24 @@ const ExtractFilename extractFilenames[] = {
 	{ kEoB1Npc6Strings, kStringList, true },
 	{ kEoB1Npc7Strings, kStringList, true },
 
+	{ kEoB1ParchmentStrings, kStringList, true },
 	{ kEoB1ItemNames, kStringList, true },
-	{ kEoB1Ascii2SjisTable1, kRawDataBe16, true },
-	{ kEoB1Ascii2SjisTable2, kRawDataBe16, true },
-	{ kEoB1FontLookupTable, kRawData, true },
+	{ kEoB1SpeechAnimData, kRawData, false },
+	{ kEoB1WdAnimSprites, kRawData, false },
+	{ kEoB1SequenceTrackMap, kRawData, false },
+
+	{ kEoB1MapStrings1, kStringList, true },
+	{ kEoB1MapStrings2, kStringList, true },
+	{ kEoB1MapStrings3, kStringList, true },
+	{ kEoB1MapLevelData, kRawData, false },
+
+	{ kEoB1Ascii2SjisTable1, kRawDataBe16, false },
+	{ kEoB1Ascii2SjisTable2, kRawDataBe16, false },
+	{ kEoB1FontLookupTable, kRawData, false },
+	{ kEoB1CharWidthTable1, kRawData, false },
+	{ kEoB1CharWidthTable2, kRawData, false },
+	{ kEoB1CharWidthTable3, kRawData, false },
+	{ kEoB1CharTilesTable, kRawData, false },
 
 	// EYE OF THE BEHOLDER II
 	{ kEoB2MainMenuStrings, kStringList, true },
@@ -657,6 +686,140 @@ const ExtractFilename extractFilenames[] = {
 	{ kEoBBaseMonsterDirChangeTable, kRawData, false },
 	{ kEoBBaseMonsterDistAttStrings, kStringList, true },
 	{ kEoBBaseEncodeMonsterDefs, kRawDataBe16, false },
+	{ kEoBBaseEncodeMonsterDefs00, kRawData, false },
+	{ kEoBBaseEncodeMonsterDefs01, kRawData, false },
+	{ kEoBBaseEncodeMonsterDefs02, kRawData, false },
+	{ kEoBBaseEncodeMonsterDefs03, kRawData, false },
+	{ kEoBBaseEncodeMonsterDefs04, kRawData, false },
+	{ kEoBBaseEncodeMonsterDefs05, kRawData, false },
+	{ kEoBBaseEncodeMonsterDefs06, kRawData, false },
+	{ kEoBBaseEncodeMonsterDefs07, kRawData, false },
+	{ kEoBBaseEncodeMonsterDefs08, kRawData, false },
+	{ kEoBBaseEncodeMonsterDefs09, kRawData, false },
+	{ kEoBBaseEncodeMonsterDefs10, kRawData, false },
+	{ kEoBBaseEncodeMonsterDefs11, kRawData, false },
+	{ kEoBBaseEncodeMonsterDefs12, kRawData, false },
+	{ kEoBBaseEncodeMonsterDefs13, kRawData, false },
+	{ kEoBBaseEncodeMonsterDefs14, kRawData, false },
+	{ kEoBBaseEncodeMonsterDefs15, kRawData, false },
+	{ kEoBBaseEncodeMonsterDefs16, kRawData, false },
+	{ kEoBBaseEncodeMonsterDefs17, kRawData, false },
+	{ kEoBBaseEncodeMonsterDefs18, kRawData, false },
+	{ kEoBBaseEncodeMonsterDefs19, kRawData, false },
+	{ kEoBBaseEncodeMonsterDefs20, kRawData, false },
+	{ kEoBBaseEncodeMonsterDefs21, kRawData, false },
+
+	{ kEoB1MonsterAnimFrames00, kRawData, false },
+	{ kEoB1MonsterAnimFrames01, kRawData, false },
+	{ kEoB1MonsterAnimFrames02, kRawData, false },
+	{ kEoB1MonsterAnimFrames03, kRawData, false },
+	{ kEoB1MonsterAnimFrames04, kRawData, false },
+	{ kEoB1MonsterAnimFrames05, kRawData, false },
+	{ kEoB1MonsterAnimFrames06, kRawData, false },
+	{ kEoB1MonsterAnimFrames07, kRawData, false },
+	{ kEoB1MonsterAnimFrames08, kRawData, false },
+	{ kEoB1MonsterAnimFrames09, kRawData, false },
+	{ kEoB1MonsterAnimFrames10, kRawData, false },
+	{ kEoB1MonsterAnimFrames11, kRawData, false },
+	{ kEoB1MonsterAnimFrames12, kRawData, false },
+	{ kEoB1MonsterAnimFrames13, kRawData, false },
+	{ kEoB1MonsterAnimFrames14, kRawData, false },
+	{ kEoB1MonsterAnimFrames15, kRawData, false },
+	{ kEoB1MonsterAnimFrames16, kRawData, false },
+	{ kEoB1MonsterAnimFrames17, kRawData, false },
+	{ kEoB1MonsterAnimFrames18, kRawData, false },
+	{ kEoB1MonsterAnimFrames19, kRawData, false },
+	{ kEoB1MonsterAnimFrames20, kRawData, false },
+	{ kEoB1MonsterAnimFrames21, kRawData, false },
+	{ kEoB1MonsterAnimFrames22, kRawData, false },
+	{ kEoB1MonsterAnimFrames23, kRawData, false },
+	{ kEoB1MonsterAnimFrames24, kRawData, false },
+	{ kEoB1MonsterAnimFrames25, kRawData, false },
+	{ kEoB1MonsterAnimFrames26, kRawData, false },
+	{ kEoB1MonsterAnimFrames27, kRawData, false },
+	{ kEoB1MonsterAnimFrames28, kRawData, false },
+	{ kEoB1MonsterAnimFrames29, kRawData, false },
+	{ kEoB1MonsterAnimFrames30, kRawData, false },
+	{ kEoB1MonsterAnimFrames31, kRawData, false },
+	{ kEoB1MonsterAnimFrames32, kRawData, false },
+	{ kEoB1MonsterAnimFrames33, kRawData, false },
+	{ kEoB1MonsterAnimFrames34, kRawData, false },
+	{ kEoB1MonsterAnimFrames35, kRawData, false },
+	{ kEoB1MonsterAnimFrames36, kRawData, false },
+	{ kEoB1MonsterAnimFrames37, kRawData, false },
+	{ kEoB1MonsterAnimFrames38, kRawData, false },
+	{ kEoB1MonsterAnimFrames39, kRawData, false },
+	{ kEoB1MonsterAnimFrames40, kRawData, false },
+	{ kEoB1MonsterAnimFrames41, kRawData, false },
+	{ kEoB1MonsterAnimFrames42, kRawData, false },
+	{ kEoB1MonsterAnimFrames43, kRawData, false },
+	{ kEoB1MonsterAnimFrames44, kRawData, false },
+	{ kEoB1MonsterAnimFrames45, kRawData, false },
+	{ kEoB1MonsterAnimFrames46, kRawData, false },
+	{ kEoB1MonsterAnimFrames47, kRawData, false },
+	{ kEoB1MonsterAnimFrames48, kRawData, false },
+	{ kEoB1MonsterAnimFrames49, kRawData, false },
+	{ kEoB1MonsterAnimFrames50, kRawData, false },
+	{ kEoB1MonsterAnimFrames51, kRawData, false },
+	{ kEoB1MonsterAnimFrames52, kRawData, false },
+	{ kEoB1MonsterAnimFrames53, kRawData, false },
+	{ kEoB1MonsterAnimFrames54, kRawData, false },
+	{ kEoB1MonsterAnimFrames55, kRawData, false },
+	{ kEoB1MonsterAnimFrames56, kRawData, false },
+	{ kEoB1MonsterAnimFrames57, kRawData, false },
+	{ kEoB1MonsterAnimFrames58, kRawData, false },
+	{ kEoB1MonsterAnimFrames59, kRawData, false },
+	{ kEoB1MonsterAnimFrames60, kRawData, false },
+	{ kEoB1MonsterAnimFrames61, kRawData, false },
+	{ kEoB1MonsterAnimFrames62, kRawData, false },
+	{ kEoB1MonsterAnimFrames63, kRawData, false },
+	{ kEoB1MonsterAnimFrames64, kRawData, false },
+	{ kEoB1MonsterAnimFrames65, kRawData, false },
+	{ kEoB1MonsterAnimFrames66, kRawData, false },
+	{ kEoB1MonsterAnimFrames67, kRawData, false },
+	{ kEoB1MonsterAnimFrames68, kRawData, false },
+	{ kEoB1MonsterAnimFrames69, kRawData, false },
+	{ kEoB1MonsterAnimFrames70, kRawData, false },
+	{ kEoB1MonsterAnimFrames71, kRawData, false },
+	{ kEoB1MonsterAnimFrames72, kRawData, false },
+	{ kEoB1MonsterAnimFrames73, kRawData, false },
+	{ kEoB1MonsterAnimFrames74, kRawData, false },
+	{ kEoB1MonsterAnimFrames75, kRawData, false },
+	{ kEoB1MonsterAnimFrames76, kRawData, false },
+	{ kEoB1MonsterAnimFrames77, kRawData, false },
+	{ kEoB1MonsterAnimFrames78, kRawData, false },
+	{ kEoB1MonsterAnimFrames79, kRawData, false },
+	{ kEoB1MonsterAnimFrames80, kRawData, false },
+	{ kEoB1MonsterAnimFrames81, kRawData, false },
+	{ kEoB1MonsterAnimFrames82, kRawData, false },
+	{ kEoB1MonsterAnimFrames83, kRawData, false },
+	{ kEoB1MonsterAnimFrames84, kRawData, false },
+	{ kEoB1MonsterAnimFrames85, kRawData, false },
+	{ kEoB1MonsterAnimFrames86, kRawData, false },
+	{ kEoB1MonsterAnimFrames87, kRawData, false },
+	{ kEoB1MonsterAnimFrames88, kRawData, false },
+	{ kEoB1MonsterAnimFrames89, kRawData, false },
+	{ kEoB1MonsterAnimFrames90, kRawData, false },
+	{ kEoB1MonsterAnimFrames91, kRawData, false },
+	{ kEoB1MonsterAnimFrames92, kRawData, false },
+	{ kEoB1MonsterAnimFrames93, kRawData, false },
+	{ kEoB1MonsterAnimFrames94, kRawData, false },
+	{ kEoB1MonsterAnimFrames95, kRawData, false },
+	{ kEoB1MonsterAnimFrames96, kRawData, false },
+	{ kEoB1MonsterAnimFrames97, kRawData, false },
+	{ kEoB1MonsterAnimFrames98, kRawData, false },
+	{ kEoB1MonsterAnimFrames99, kRawData, false },
+	{ kEoB1MonsterAnimFrames100, kRawData, false },
+	{ kEoB1MonsterAnimFrames101, kRawData, false },
+	{ kEoB1MonsterAnimFrames102, kRawData, false },
+	{ kEoB1MonsterAnimFrames103, kRawData, false },
+	{ kEoB1MonsterAnimFrames104, kRawData, false },
+	{ kEoB1MonsterAnimFrames105, kRawData, false },
+	{ kEoB1MonsterAnimFrames106, kRawData, false },
+	{ kEoB1MonsterAnimFrames107, kRawData, false },
+	{ kEoB1MonsterAnimFrames108, kRawData, false },
+	{ kEoB1MonsterAnimFrames109, kRawData, false },
+
 	{ kEoBBaseNpcPresets, kEoBNpcData, false },
 	{ kEoB2Npc1Strings, kStringList, true },
 	{ kEoB2Npc2Strings, kStringList, true },
@@ -884,8 +1047,8 @@ const ExtractFilename extractFilenames[] = {
 
 	{ kEoB2UtilMenuStrings, kStringList, true },
 	{ kEoB2Config2431Strings, kStringList, true },
-	{ kEoB2KatakanaLines, kStringList, true },
-	{ kEoB2KanaSelectStrings, kStringList, true },
+	{ kEoBBaseTextInputCharacterLines, kStringList, true },
+	{ kEoBBaseTextInputSelectStrings, kStringList, true },
 	{ kEoB2FontDmpSearchTbl, kRawDataBe16, false },
 	{ kEoB2Ascii2SjisTables, kStringList, false },
 	{ kEoB2Ascii2SjisTables2, kStringList, false },
@@ -1049,7 +1212,8 @@ const TypeTable platformTable[] = {
 	{ kPlatformAmiga, 1 },
 	{ kPlatformFMTowns, 2 },
 	{ kPlatformPC98, 3 },
-	{ kPlatformMacintosh, 4 },
+	{ kPlatformSegaCD, 4 },
+	{ kPlatformMacintosh, 5 },
 	{ -1, -1 }
 };
 
diff --git a/devtools/create_kyradat/create_kyradat.h b/devtools/create_kyradat/create_kyradat.h
index 783408e6ed..edc8009f2d 100644
--- a/devtools/create_kyradat/create_kyradat.h
+++ b/devtools/create_kyradat/create_kyradat.h
@@ -322,6 +322,29 @@ enum kExtractID {
 	kEoBBaseMonsterDistAttStrings,
 
 	kEoBBaseEncodeMonsterDefs,
+	kEoBBaseEncodeMonsterDefs00,
+	kEoBBaseEncodeMonsterDefs01,
+	kEoBBaseEncodeMonsterDefs02,
+	kEoBBaseEncodeMonsterDefs03,
+	kEoBBaseEncodeMonsterDefs04,
+	kEoBBaseEncodeMonsterDefs05,
+	kEoBBaseEncodeMonsterDefs06,
+	kEoBBaseEncodeMonsterDefs07,
+	kEoBBaseEncodeMonsterDefs08,
+	kEoBBaseEncodeMonsterDefs09,
+	kEoBBaseEncodeMonsterDefs10,
+	kEoBBaseEncodeMonsterDefs11,
+	kEoBBaseEncodeMonsterDefs12,
+	kEoBBaseEncodeMonsterDefs13,
+	kEoBBaseEncodeMonsterDefs14,
+	kEoBBaseEncodeMonsterDefs15,
+	kEoBBaseEncodeMonsterDefs16,
+	kEoBBaseEncodeMonsterDefs17,
+	kEoBBaseEncodeMonsterDefs18,
+	kEoBBaseEncodeMonsterDefs19,
+	kEoBBaseEncodeMonsterDefs20,
+	kEoBBaseEncodeMonsterDefs21,
+
 	kEoBBaseNpcPresets,
 
 	kEoBBaseWllFlagPreset,
@@ -374,6 +397,10 @@ enum kExtractID {
 	kEoBBaseBookNumbers,
 	kEoBBaseMageSpellsList,
 	kEoBBaseClericSpellsList,
+
+	kEoBBaseMageSpellsList2,
+	kEoBBaseClericSpellsList2,
+
 	kEoBBaseSpellNames,
 	kEoBBaseMagicStrings1,
 	kEoBBaseMagicStrings2,
@@ -421,6 +448,11 @@ enum kExtractID {
 	kEoBBaseLevelSoundFiles1,
 	kEoBBaseLevelSoundFiles2,
 
+	kEoBBaseTextInputCharacterLines,
+	kEoBBaseTextInputSelectStrings,
+
+	kEoB1DefaultPartyStats,
+	kEoB1DefaultPartyNames,
 	kEoB1MainMenuStrings,
 	kEoB1BonusStrings,
 
@@ -469,6 +501,9 @@ enum kExtractID {
 	kEoB1CreditsStrings,
 	kEoB1CreditsCharWdth,
 
+	kEoB1CreditsStrings2,
+	kEoB1CreditsTileGrid,
+
 	kEoB1DoorShapeDefs,
 	kEoB1DoorSwitchShapeDefs,
 	kEoB1DoorSwitchCoords,
@@ -499,6 +534,126 @@ enum kExtractID {
 	kEoB1PalCycleData,
 	kEoB1PalCycleStyle1,
 	kEoB1PalCycleStyle2,
+	kEoB1PalettesSega,
+	kEoB1PatternTable0,
+	kEoB1PatternTable1,
+	kEoB1PatternTable2,
+	kEoB1PatternTable3,
+	kEoB1PatternTable4,
+	kEoB1PatternTable5,
+	kEoB1PatternAddTable1,
+	kEoB1PatternAddTable2,
+
+	kEoB1MonsterAnimFrames00,
+	kEoB1MonsterAnimFrames01,
+	kEoB1MonsterAnimFrames02,
+	kEoB1MonsterAnimFrames03,
+	kEoB1MonsterAnimFrames04,
+	kEoB1MonsterAnimFrames05,
+	kEoB1MonsterAnimFrames06,
+	kEoB1MonsterAnimFrames07,
+	kEoB1MonsterAnimFrames08,
+	kEoB1MonsterAnimFrames09,
+	kEoB1MonsterAnimFrames10,
+	kEoB1MonsterAnimFrames11,
+	kEoB1MonsterAnimFrames12,
+	kEoB1MonsterAnimFrames13,
+	kEoB1MonsterAnimFrames14,
+	kEoB1MonsterAnimFrames15,
+	kEoB1MonsterAnimFrames16,
+	kEoB1MonsterAnimFrames17,
+	kEoB1MonsterAnimFrames18,
+	kEoB1MonsterAnimFrames19,
+	kEoB1MonsterAnimFrames20,
+	kEoB1MonsterAnimFrames21,
+	kEoB1MonsterAnimFrames22,
+	kEoB1MonsterAnimFrames23,
+	kEoB1MonsterAnimFrames24,
+	kEoB1MonsterAnimFrames25,
+	kEoB1MonsterAnimFrames26,
+	kEoB1MonsterAnimFrames27,
+	kEoB1MonsterAnimFrames28,
+	kEoB1MonsterAnimFrames29,
+	kEoB1MonsterAnimFrames30,
+	kEoB1MonsterAnimFrames31,
+	kEoB1MonsterAnimFrames32,
+	kEoB1MonsterAnimFrames33,
+	kEoB1MonsterAnimFrames34,
+	kEoB1MonsterAnimFrames35,
+	kEoB1MonsterAnimFrames36,
+	kEoB1MonsterAnimFrames37,
+	kEoB1MonsterAnimFrames38,
+	kEoB1MonsterAnimFrames39,
+	kEoB1MonsterAnimFrames40,
+	kEoB1MonsterAnimFrames41,
+	kEoB1MonsterAnimFrames42,
+	kEoB1MonsterAnimFrames43,
+	kEoB1MonsterAnimFrames44,
+	kEoB1MonsterAnimFrames45,
+	kEoB1MonsterAnimFrames46,
+	kEoB1MonsterAnimFrames47,
+	kEoB1MonsterAnimFrames48,
+	kEoB1MonsterAnimFrames49,
+	kEoB1MonsterAnimFrames50,
+	kEoB1MonsterAnimFrames51,
+	kEoB1MonsterAnimFrames52,
+	kEoB1MonsterAnimFrames53,
+	kEoB1MonsterAnimFrames54,
+	kEoB1MonsterAnimFrames55,
+	kEoB1MonsterAnimFrames56,
+	kEoB1MonsterAnimFrames57,
+	kEoB1MonsterAnimFrames58,
+	kEoB1MonsterAnimFrames59,
+	kEoB1MonsterAnimFrames60,
+	kEoB1MonsterAnimFrames61,
+	kEoB1MonsterAnimFrames62,
+	kEoB1MonsterAnimFrames63,
+	kEoB1MonsterAnimFrames64,
+	kEoB1MonsterAnimFrames65,
+	kEoB1MonsterAnimFrames66,
+	kEoB1MonsterAnimFrames67,
+	kEoB1MonsterAnimFrames68,
+	kEoB1MonsterAnimFrames69,
+	kEoB1MonsterAnimFrames70,
+	kEoB1MonsterAnimFrames71,
+	kEoB1MonsterAnimFrames72,
+	kEoB1MonsterAnimFrames73,
+	kEoB1MonsterAnimFrames74,
+	kEoB1MonsterAnimFrames75,
+	kEoB1MonsterAnimFrames76,
+	kEoB1MonsterAnimFrames77,
+	kEoB1MonsterAnimFrames78,
+	kEoB1MonsterAnimFrames79,
+	kEoB1MonsterAnimFrames80,
+	kEoB1MonsterAnimFrames81,
+	kEoB1MonsterAnimFrames82,
+	kEoB1MonsterAnimFrames83,
+	kEoB1MonsterAnimFrames84,
+	kEoB1MonsterAnimFrames85,
+	kEoB1MonsterAnimFrames86,
+	kEoB1MonsterAnimFrames87,
+	kEoB1MonsterAnimFrames88,
+	kEoB1MonsterAnimFrames89,
+	kEoB1MonsterAnimFrames90,
+	kEoB1MonsterAnimFrames91,
+	kEoB1MonsterAnimFrames92,
+	kEoB1MonsterAnimFrames93,
+	kEoB1MonsterAnimFrames94,
+	kEoB1MonsterAnimFrames95,
+	kEoB1MonsterAnimFrames96,
+	kEoB1MonsterAnimFrames97,
+	kEoB1MonsterAnimFrames98,
+	kEoB1MonsterAnimFrames99,
+	kEoB1MonsterAnimFrames100,
+	kEoB1MonsterAnimFrames101,
+	kEoB1MonsterAnimFrames102,
+	kEoB1MonsterAnimFrames103,
+	kEoB1MonsterAnimFrames104,
+	kEoB1MonsterAnimFrames105,
+	kEoB1MonsterAnimFrames106,
+	kEoB1MonsterAnimFrames107,
+	kEoB1MonsterAnimFrames108,
+	kEoB1MonsterAnimFrames109,
 
 	kEoB1NpcShpData,
 	kEoB1NpcSubShpIndex1,
@@ -516,10 +671,24 @@ enum kExtractID {
 	kEoB1Npc6Strings,
 	kEoB1Npc7Strings,
 
+	kEoB1ParchmentStrings,
 	kEoB1ItemNames,
+	kEoB1SpeechAnimData,
+	kEoB1WdAnimSprites,
+	kEoB1SequenceTrackMap,
+
+	kEoB1MapStrings1,
+	kEoB1MapStrings2,
+	kEoB1MapStrings3,
+	kEoB1MapLevelData,
+
 	kEoB1Ascii2SjisTable1,
 	kEoB1Ascii2SjisTable2,
 	kEoB1FontLookupTable,
+	kEoB1CharWidthTable1,
+	kEoB1CharWidthTable2,
+	kEoB1CharWidthTable3,
+	kEoB1CharTilesTable,
 
 	kEoB2MainMenuStrings,
 	kEoB2MainMenuUtilStrings,
@@ -875,8 +1044,6 @@ enum kExtractID {
 
 	kEoB2UtilMenuStrings,
 	kEoB2Config2431Strings,
-	kEoB2KatakanaLines,
-	kEoB2KanaSelectStrings,
 	kEoB2FontDmpSearchTbl,
 	kEoB2Ascii2SjisTables,
 	kEoB2Ascii2SjisTables2,
diff --git a/devtools/create_kyradat/games.cpp b/devtools/create_kyradat/games.cpp
index 61dddcc4b6..6d0eca29e6 100644
--- a/devtools/create_kyradat/games.cpp
+++ b/devtools/create_kyradat/games.cpp
@@ -110,6 +110,8 @@ const Game eob1Games[] = {
 
 	{ kEoB1, kPlatformPC98, kNoSpecial, JA_JPN },
 
+	{ kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY },
+
 	GAME_DUMMY_ENTRY
 };
 
@@ -2308,6 +2310,411 @@ const int eob1PC98Need[] = {
 	-1
 };
 
+const int eob1SegaCDNeed[] = {
+	kEoBBaseChargenStrings1,
+	kEoBBaseChargenStrings2,
+	kEoBBaseChargenStartLevels,
+	kEoBBaseChargenStatStrings,
+	kEoBBaseChargenRaceSexStrings,
+	kEoBBaseChargenClassStrings,
+	kEoBBaseChargenAlignmentStrings,
+	kEoBBaseChargenClassMinStats,
+	kEoBBaseChargenRaceMinStats,
+	kEoBBaseChargenRaceMaxStats,
+
+	kEoBBaseSaveThrowTable1,
+	kEoBBaseSaveThrowTable2,
+	kEoBBaseSaveThrowTable3,
+	kEoBBaseSaveThrowTable4,
+	kEoBBaseSaveThrwLvlIndex,
+	kEoBBaseSaveThrwModDiv,
+	kEoBBaseSaveThrwModExt,
+
+	kEoB1DefaultPartyStats,
+	kEoB1DefaultPartyNames,
+	kEoB1MainMenuStrings,
+	kEoB1BonusStrings,
+
+	kEoB1CreditsStrings2,
+	kEoB1CreditsTileGrid,
+	kEoB1IntroWdDsX,
+	kEoB1IntroWdDsY,
+
+	kEoB1DoorShapeDefs,
+	kEoB1DoorSwitchShapeDefs,
+	kEoB1DoorSwitchCoords,
+	kEoB1MonsterProperties,
+	kEoB1EnemyMageSpellList,
+	kEoB1EnemyMageSfx,
+	kEoB1BeholderSpellList,
+	kEoB1BeholderSfx,
+	kEoB1TurnUndeadString,
+
+	kEoB1PalettesSega,
+	kEoB1PatternTable0,
+	kEoB1PatternTable1,
+	kEoB1PatternTable2,
+	kEoB1PatternTable3,
+	kEoB1PatternTable4,
+	kEoB1PatternTable5,
+	kEoB1PatternAddTable1,
+	kEoB1PatternAddTable2,
+
+	kEoB1Npc0Strings,
+	kEoB1Npc11Strings,
+	kEoB1Npc12Strings,
+	kEoB1Npc21Strings,
+	kEoB1Npc22Strings,
+	kEoB1Npc31Strings,
+	kEoB1Npc32Strings,
+	kEoB1Npc4Strings,
+	kEoB1Npc5Strings,
+	kEoB1Npc6Strings,
+	kEoB1Npc7Strings,
+
+	kEoB1ParchmentStrings,
+	kEoB1ItemNames,
+	kEoB1SpeechAnimData,
+	kEoB1WdAnimSprites,
+	kEoB1SequenceTrackMap,
+
+	kEoB1MapStrings1,
+	kEoB1MapStrings2,
+	kEoB1MapStrings3,
+	kEoB1MapLevelData,
+
+	kEoB1Ascii2SjisTable1,
+	kEoB1Ascii2SjisTable2,
+	kEoB1CharWidthTable1,
+	kEoB1CharWidthTable2,
+	kEoB1CharWidthTable3,
+	kEoB1CharTilesTable,
+
+	kEoBBasePryDoorStrings,
+	kEoBBaseWarningStrings,
+
+	kEoBBaseItemSuffixStringsRings,
+	kEoBBaseItemSuffixStringsPotions,
+	kEoBBaseItemSuffixStringsWands,
+
+	kEoBBaseRipItemStrings,
+	kEoBBaseCursedString,
+	kEoBBaseEnchantedString,
+	kEoBBaseMagicObjectStrings,
+	kEoBBaseMagicObjectString5,
+	kEoBBasePatternSuffix,
+	kEoBBasePatternGrFix1,
+	kEoBBasePatternGrFix2,
+	kEoBBaseValidateArmorString,
+	kEoBBaseValidateNoDropString,
+	kEoBBasePotionStrings,
+	kEoBBaseWandStrings,
+	kEoBBaseItemMisuseStrings,
+
+	kEoBBaseTakenStrings,
+	kEoBBasePotionEffectStrings,
+
+	kEoBBaseYesNoStrings,
+	kRpgCommonMoreStrings,
+	kEoBBaseNpcMaxStrings,
+	kEoBBaseNpcJoinStrings,
+	kEoBBaseCancelStrings,
+
+	kEoBBaseMenuStringsSaveLoad,
+	kEoBBaseMenuStringsOnOff,
+	kEoBBaseMenuStringsSpells,
+	kEoBBaseMenuStringsRest,
+	kEoBBaseMenuStringsDrop,
+	kEoBBaseMenuStringsExit,
+	kEoBBaseMenuStringsStarve,
+	kEoBBaseMenuStringsScribe,
+	kEoBBaseMenuStringsDrop2,
+	kEoBBaseMenuStringsPoison,
+	kEoBBaseMenuStringsMgc,
+	kEoBBaseMenuStringsRest2,
+	kEoBBaseMenuStringsRest4,
+	kEoBBaseMenuStringsDefeat,
+	kEoBBaseMenuYesNoStrings,
+
+	kEoBBaseSpellLevelsMage,
+	kEoBBaseSpellLevelsCleric,
+	kEoBBaseNumSpellsCleric,
+	kEoBBaseNumSpellsWisAdj,
+	kEoBBaseNumSpellsPal,
+	kEoBBaseNumSpellsMage,
+
+	kEoBBaseCharGuiStringsIn,
+
+	kEoBBaseCharStatusStrings7,
+	kEoBBaseCharStatusStrings81,
+	kEoBBaseCharStatusStrings9,
+	kEoBBaseCharStatusStrings131,
+
+	kEoBBaseLevelGainStrings,
+	kEoBBaseExperienceTable0,
+	kEoBBaseExperienceTable1,
+	kEoBBaseExperienceTable2,
+	kEoBBaseExperienceTable3,
+	kEoBBaseExperienceTable4,
+
+	//kEoBBaseBookNumbers,
+	kEoBBaseMageSpellsList,
+	kEoBBaseClericSpellsList,
+	kEoBBaseMageSpellsList2,
+	kEoBBaseClericSpellsList2,
+	kEoBBaseSpellNames,
+	kEoBBaseMagicStrings1,
+	kEoBBaseMagicStrings2,
+	kEoBBaseMagicStrings3,
+	kEoBBaseMagicStrings4,
+	kEoBBaseMagicStrings6,
+	//kEoBBaseMagicStrings7,
+	kEoBBaseMagicStrings8,
+
+	kEoBBaseExpObjectTblIndex,
+	kEoBBaseExpObjectShpStart,
+	kEoBBaseExpObjectTbl1,
+	kEoBBaseExpObjectTbl2,
+	kEoBBaseExpObjectTbl3,
+	kEoBBaseExpObjectY,
+
+	kEoBBaseSparkDefSteps,
+	kEoBBaseSparkDefSubSteps,
+	kEoBBaseSparkDefShift,
+	kEoBBaseSparkDefAdd,
+	kEoBBaseSparkDefX,
+	kEoBBaseSparkDefY,
+	kEoBBaseSparkOfFlags1,
+	kEoBBaseSparkOfFlags2,
+	kEoBBaseSparkOfShift,
+	kEoBBaseSparkOfX,
+	kEoBBaseSparkOfY,
+
+	kEoBBaseSpellProperties,
+	kEoBBaseMagicFlightProps,
+	kEoBBaseTurnUndeadEffect,
+	kEoBBaseBurningHandsDest,
+	kEoBBaseConeOfColdDest1,
+	kEoBBaseConeOfColdDest2,
+	kEoBBaseConeOfColdDest3,
+	kEoBBaseConeOfColdDest4,
+	kEoBBaseConeOfColdGfxTbl,
+
+	kRpgCommonDscDoorShapeIndex,
+	kEoBBaseWllFlagPreset,
+	kEoBBaseDscShapeCoords,
+	kRpgCommonDscDoorScaleOffs,
+	kEoBBaseDscDoorScaleMult1,
+	kEoBBaseDscDoorScaleMult2,
+	kEoBBaseDscDoorScaleMult3,
+	kEoBBaseDscDoorScaleMult4,
+	kEoBBaseDscDoorScaleMult5,
+	kEoBBaseDscDoorScaleMult6,
+	kEoBBaseDscDoorXE,
+	kEoBBaseDscDoorY1,
+	kEoBBaseDscDoorY3,
+	kEoBBaseDscDoorY4,
+	kEoBBaseDscDoorY5,
+	kEoBBaseDscDoorY6,
+	kEoBBaseDscDoorY7,
+	kEoBBaseDscDoorCoordsExt,
+	kRpgCommonDscDoorFrameY1,
+	kRpgCommonDscDoorFrameY2,
+	kRpgCommonDscDoorFrameIndex1,
+	kRpgCommonDscDoorFrameIndex2,
+
+	kEoBBaseDscItemPosIndex,
+	kEoBBaseDscItemShpX,
+	kEoBBaseDscItemScaleIndex,
+	kEoBBaseDscItemTileIndex,
+	kEoBBaseDscItemShapeMap,
+	kEoBBaseDscTelptrShpCoords,
+
+	kEoBBasePortalSeqData,
+
+	kEoBBaseDscMonsterFrmOffsTbl1,
+	kEoBBaseDscMonsterFrmOffsTbl2,
+
+	kEoBBaseInvSlotX,
+	kEoBBaseInvSlotY,
+	kEoBBaseSlotValidationFlags,
+
+	kEoBBaseProjectileWeaponTypes,
+	kEoBBaseWandTypes,
+
+	kEoBBaseDrawObjPosIndex,
+	kEoBBaseFlightObjFlipIndex,
+	kEoBBaseFlightObjShpMap,
+	kEoBBaseFlightObjSclIndex,
+
+	kRpgCommonDscShapeIndex,
+	kRpgCommonDscX,
+	kRpgCommonDscTileIndex,
+	kRpgCommonDscDimData1,
+	kRpgCommonDscDimData2,
+	kRpgCommonDscBlockMap,
+	kRpgCommonDscDimMap,
+	kRpgCommonDscBlockIndex,
+
+	kEoBBaseClassModifierFlags,
+
+	kEoBBaseMonsterStepTable01,
+	kEoBBaseMonsterStepTable2,
+	kEoBBaseMonsterStepTable3,
+	kEoBBaseMonsterCloseAttPosTable1,
+	kEoBBaseMonsterCloseAttPosTable21,
+	kEoBBaseMonsterCloseAttChkTable1,
+	kEoBBaseMonsterCloseAttChkTable2,
+	kEoBBaseMonsterCloseAttDstTable1,
+	kEoBBaseMonsterCloseAttDstTable2,
+
+	kEoBBaseMonsterProximityTable,
+	kEoBBaseFindBlockMonstersTable,
+	kEoBBaseMonsterDirChangeTable,
+	kEoBBaseMonsterDistAttStrings,
+
+	kEoBBaseEncodeMonsterDefs00,
+	kEoBBaseEncodeMonsterDefs01,
+	kEoBBaseEncodeMonsterDefs02,
+	kEoBBaseEncodeMonsterDefs03,
+	kEoBBaseEncodeMonsterDefs04,
+	kEoBBaseEncodeMonsterDefs05,
+	kEoBBaseEncodeMonsterDefs06,
+	kEoBBaseEncodeMonsterDefs07,
+	kEoBBaseEncodeMonsterDefs08,
+	kEoBBaseEncodeMonsterDefs09,
+	kEoBBaseEncodeMonsterDefs10,
+	kEoBBaseEncodeMonsterDefs11,
+	kEoBBaseEncodeMonsterDefs12,
+	kEoBBaseEncodeMonsterDefs13,
+	kEoBBaseEncodeMonsterDefs14,
+	kEoBBaseEncodeMonsterDefs15,
+	kEoBBaseEncodeMonsterDefs16,
+	kEoBBaseEncodeMonsterDefs17,
+	kEoBBaseEncodeMonsterDefs18,
+	kEoBBaseEncodeMonsterDefs19,
+	kEoBBaseEncodeMonsterDefs20,
+	kEoBBaseEncodeMonsterDefs21,
+
+	kEoB1MonsterAnimFrames00,
+	kEoB1MonsterAnimFrames01,
+	kEoB1MonsterAnimFrames02,
+	kEoB1MonsterAnimFrames03,
+	kEoB1MonsterAnimFrames04,
+	kEoB1MonsterAnimFrames05,
+	kEoB1MonsterAnimFrames06,
+	kEoB1MonsterAnimFrames07,
+	kEoB1MonsterAnimFrames08,
+	kEoB1MonsterAnimFrames09,
+	kEoB1MonsterAnimFrames10,
+	kEoB1MonsterAnimFrames11,
+	kEoB1MonsterAnimFrames12,
+	kEoB1MonsterAnimFrames13,
+	kEoB1MonsterAnimFrames14,
+	kEoB1MonsterAnimFrames15,
+	kEoB1MonsterAnimFrames16,
+	kEoB1MonsterAnimFrames17,
+	kEoB1MonsterAnimFrames18,
+	kEoB1MonsterAnimFrames19,
+	kEoB1MonsterAnimFrames20,
+	kEoB1MonsterAnimFrames21,
+	kEoB1MonsterAnimFrames22,
+	kEoB1MonsterAnimFrames23,
+	kEoB1MonsterAnimFrames24,
+	kEoB1MonsterAnimFrames25,
+	kEoB1MonsterAnimFrames26,
+	kEoB1MonsterAnimFrames27,
+	kEoB1MonsterAnimFrames28,
+	kEoB1MonsterAnimFrames29,
+	kEoB1MonsterAnimFrames30,
+	kEoB1MonsterAnimFrames31,
+	kEoB1MonsterAnimFrames32,
+	kEoB1MonsterAnimFrames33,
+	kEoB1MonsterAnimFrames34,
+	kEoB1MonsterAnimFrames35,
+	kEoB1MonsterAnimFrames36,
+	kEoB1MonsterAnimFrames37,
+	kEoB1MonsterAnimFrames38,
+	kEoB1MonsterAnimFrames39,
+	kEoB1MonsterAnimFrames40,
+	kEoB1MonsterAnimFrames41,
+	kEoB1MonsterAnimFrames42,
+	kEoB1MonsterAnimFrames43,
+	kEoB1MonsterAnimFrames44,
+	kEoB1MonsterAnimFrames45,
+	kEoB1MonsterAnimFrames46,
+	kEoB1MonsterAnimFrames47,
+	kEoB1MonsterAnimFrames48,
+	kEoB1MonsterAnimFrames49,
+	kEoB1MonsterAnimFrames50,
+	kEoB1MonsterAnimFrames51,
+	kEoB1MonsterAnimFrames52,
+	kEoB1MonsterAnimFrames53,
+	kEoB1MonsterAnimFrames54,
+	kEoB1MonsterAnimFrames55,
+	kEoB1MonsterAnimFrames56,
+	kEoB1MonsterAnimFrames57,
+	kEoB1MonsterAnimFrames58,
+	kEoB1MonsterAnimFrames59,
+	kEoB1MonsterAnimFrames60,
+	kEoB1MonsterAnimFrames61,
+	kEoB1MonsterAnimFrames62,
+	kEoB1MonsterAnimFrames63,
+	kEoB1MonsterAnimFrames64,
+	kEoB1MonsterAnimFrames65,
+	kEoB1MonsterAnimFrames66,
+	kEoB1MonsterAnimFrames67,
+	kEoB1MonsterAnimFrames68,
+	kEoB1MonsterAnimFrames69,
+	kEoB1MonsterAnimFrames70,
+	kEoB1MonsterAnimFrames71,
+	kEoB1MonsterAnimFrames72,
+	kEoB1MonsterAnimFrames73,
+	kEoB1MonsterAnimFrames74,
+	kEoB1MonsterAnimFrames75,
+	kEoB1MonsterAnimFrames76,
+	kEoB1MonsterAnimFrames77,
+	kEoB1MonsterAnimFrames78,
+	kEoB1MonsterAnimFrames79,
+	kEoB1MonsterAnimFrames80,
+	kEoB1MonsterAnimFrames81,
+	kEoB1MonsterAnimFrames82,
+	kEoB1MonsterAnimFrames83,
+	kEoB1MonsterAnimFrames84,
+	kEoB1MonsterAnimFrames85,
+	kEoB1MonsterAnimFrames86,
+	kEoB1MonsterAnimFrames87,
+	kEoB1MonsterAnimFrames88,
+	kEoB1MonsterAnimFrames89,
+	kEoB1MonsterAnimFrames90,
+	kEoB1MonsterAnimFrames91,
+	kEoB1MonsterAnimFrames92,
+	kEoB1MonsterAnimFrames93,
+	kEoB1MonsterAnimFrames94,
+	kEoB1MonsterAnimFrames95,
+	kEoB1MonsterAnimFrames96,
+	kEoB1MonsterAnimFrames97,
+	kEoB1MonsterAnimFrames98,
+	kEoB1MonsterAnimFrames99,
+	kEoB1MonsterAnimFrames100,
+	kEoB1MonsterAnimFrames101,
+	kEoB1MonsterAnimFrames102,
+	kEoB1MonsterAnimFrames103,
+	kEoB1MonsterAnimFrames104,
+	kEoB1MonsterAnimFrames105,
+	kEoB1MonsterAnimFrames106,
+	kEoB1MonsterAnimFrames107,
+	kEoB1MonsterAnimFrames108,
+	kEoB1MonsterAnimFrames109,
+
+	kEoBBaseTextInputCharacterLines,
+	kEoBBaseTextInputSelectStrings,
+
+	kEoBBaseNpcPresets,
+
+	-1
+};
+
 const int eob2FloppyNeed[] = {
 	kEoBBaseChargenStrings1,
 	kEoBBaseChargenStrings2,
@@ -3846,8 +4253,8 @@ const int eob2FMTownsNeed[] = {
 
 	kEoB2UtilMenuStrings,
 	kEoB2Config2431Strings,
-	kEoB2KatakanaLines,
-	kEoB2KanaSelectStrings,
+	kEoBBaseTextInputCharacterLines,
+	kEoBBaseTextInputSelectStrings,
 	kEoB2FontDmpSearchTbl,
 	kEoB2Ascii2SjisTables,
 	kEoB2Ascii2SjisTables2,
@@ -3911,6 +4318,7 @@ const GameNeed gameNeedTable[] = {
 	{ kEoB1, kPlatformDOS, kOldFloppy, eob1FloppyOldNeed },
 	{ kEoB1, kPlatformAmiga, kNoSpecial, eob1AmigaNeed },
 	{ kEoB1, kPlatformPC98, kNoSpecial, eob1PC98Need },
+	{ kEoB1, kPlatformSegaCD, kNoSpecial, eob1SegaCDNeed },
 
 	{ kEoB2, kPlatformDOS, kNoSpecial, eob2FloppyNeed },
 	{ kEoB2, kPlatformAmiga, kNoSpecial, eob2AmigaNeed },
diff --git a/devtools/create_kyradat/resources.cpp b/devtools/create_kyradat/resources.cpp
index 542a389cac..38201ec899 100644
--- a/devtools/create_kyradat/resources.cpp
+++ b/devtools/create_kyradat/resources.cpp
@@ -103,6 +103,8 @@
 #include "resources/eob1_amiga_german.h"
 #include "resources/eob1_pc98.h"
 #include "resources/eob1_pc98_japanese.h"
+#include "resources/eob1_segacd.h"
+#include "resources/eob1_segacd_english.h"
 
 // Eye of the Beholder: The Legend of Darkmoon
 #include "resources/eob2_dos.h"
@@ -2034,7 +2036,7 @@ static const ResourceProvider resourceProviders[] = {
 	{ kEoB1FinaleHandsAnim, kEoB1, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB1FinaleHandsAnimPC98Provider },
 	{ kEoB1FinaleHandsAnim2, kEoB1, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB1FinaleHandsAnim2PC98Provider },
 	{ kEoB1FinaleHandsAnim3, kEoB1, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB1FinaleHandsAnim3PC98Provider },
-	{ kEoB1CreditsStrings, kEoB1, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB1CreditsStringsPC98Provider },
+	{ kEoB1CreditsStrings, kEoB1, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB1CreditsStringsPC98JapaneseProvider },
 	{ kEoB1CreditsCharWdth, kEoB1, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB1CreditsCharWdthPC98Provider },
 	{ kEoB1DoorShapeDefs, kEoB1, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB1DoorShapeDefsPC98Provider },
 	{ kEoB1DoorSwitchShapeDefs, kEoB1, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB1DoorSwitchShapeDefsPC98Provider },
@@ -2063,9 +2065,9 @@ static const ResourceProvider resourceProviders[] = {
 	{ kEoBBasePryDoorStrings, kEoB1, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB1PryDoorStringsPC98JapaneseProvider },
 	{ kEoBBaseWarningStrings, kEoB1, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB1WarningStringsPC98JapaneseProvider },
 	{ kEoB1ItemNames, kEoB1, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB1ItemNamesPC98JapaneseProvider },
-	{ kEoB1Ascii2SjisTable1, kEoB1, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB1Ascii2SjisTable1PC98JapaneseProvider },
-	{ kEoB1Ascii2SjisTable2, kEoB1, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB1Ascii2SjisTable2PC98JapaneseProvider },
-	{ kEoB1FontLookupTable, kEoB1, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB1FontLookupTablePC98JapaneseProvider },
+	{ kEoB1Ascii2SjisTable1, kEoB1, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB1Ascii2SjisTable1PC98Provider },
+	{ kEoB1Ascii2SjisTable2, kEoB1, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB1Ascii2SjisTable2PC98Provider },
+	{ kEoB1FontLookupTable, kEoB1, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB1FontLookupTablePC98Provider },
 	{ kEoBBaseItemSuffixStringsRings, kEoB1, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB1ItemSuffixStringsRingsPC98JapaneseProvider },
 	{ kEoBBaseItemSuffixStringsPotions, kEoB1, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB1ItemSuffixStringsPotionsPC98JapaneseProvider },
 	{ kEoBBaseItemSuffixStringsWands, kEoB1, kPlatformPC98, kNoSpecial, JA_JPN, &kEoB1ItemSuffixStringsWandsPC98JapaneseProvider },
@@ -2237,8 +2239,369 @@ static const ResourceProvider resourceProviders[] = {
 	{ kEoBBaseSoundFilesIntro, kEoB1, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB1SoundFilesIntroPC98Provider },
 	{ kEoBBaseSoundFilesIngame, kEoB1, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB1SoundFilesIngamePC98Provider },
 	{ kEoBBaseSoundFilesFinale, kEoB1, kPlatformPC98, kNoSpecial, UNK_LANG, &kEoB1SoundFilesFinalePC98Provider },
-
-
+	{ kEoBBaseChargenStrings1, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1ChargenStrings1SegaCDEnglishProvider },
+	{ kEoBBaseChargenStrings2, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1ChargenStrings2SegaCDEnglishProvider },
+	{ kEoBBaseChargenStartLevels, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1ChargenStartLevelsSegaCDProvider },
+	{ kEoBBaseChargenStatStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1ChargenStatStringsSegaCDEnglishProvider },
+	{ kEoBBaseChargenRaceSexStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1ChargenRaceSexStringsSegaCDEnglishProvider },
+	{ kEoBBaseChargenClassStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1ChargenClassStringsSegaCDEnglishProvider },
+	{ kEoBBaseChargenAlignmentStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1ChargenAlignmentStringsSegaCDEnglishProvider },
+	{ kEoBBaseChargenClassMinStats, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1ChargenClassMinStatsSegaCDProvider },
+	{ kEoBBaseChargenRaceMinStats, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1ChargenRaceMinStatsSegaCDProvider },
+	{ kEoBBaseChargenRaceMaxStats, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1ChargenRaceMaxStatsSegaCDProvider },
+	{ kEoBBaseSaveThrowTable1, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SaveThrowTable1SegaCDProvider },
+	{ kEoBBaseSaveThrowTable2, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SaveThrowTable2SegaCDProvider },
+	{ kEoBBaseSaveThrowTable3, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SaveThrowTable3SegaCDProvider },
+	{ kEoBBaseSaveThrowTable4, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SaveThrowTable4SegaCDProvider },
+	{ kEoBBaseSaveThrwLvlIndex, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SaveThrwLvlIndexSegaCDProvider },
+	{ kEoBBaseSaveThrwModDiv, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SaveThrwModDivSegaCDProvider },
+	{ kEoBBaseSaveThrwModExt, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SaveThrwModExtSegaCDProvider },
+	{ kEoB1DefaultPartyStats, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DefaultPartyStatsSegaCDProvider },
+	{ kEoB1DefaultPartyNames, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1DefaultPartyNamesSegaCDEnglishProvider },
+	{ kEoB1MainMenuStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MainMenuStringsSegaCDEnglishProvider },
+	{ kEoB1BonusStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1BonusStringsSegaCDEnglishProvider },
+	{ kEoB1CreditsStrings2, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1CreditsStrings2SegaCDEnglishProvider },
+	{ kEoB1CreditsTileGrid, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1CreditsTileGridSegaCDProvider },
+	{ kEoB1IntroWdDsX, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1IntroWdDsXSegaCDProvider },
+	{ kEoB1IntroWdDsY, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1IntroWdDsYSegaCDProvider },
+	{ kEoB1DoorShapeDefs, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DoorShapeDefsSegaCDProvider },
+	{ kEoB1DoorSwitchShapeDefs, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DoorSwitchShapeDefsSegaCDProvider },
+	{ kEoB1DoorSwitchCoords, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DoorSwitchCoordsSegaCDProvider },
+	{ kEoB1MonsterProperties, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterPropertiesSegaCDProvider },
+	{ kEoB1EnemyMageSpellList, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EnemyMageSpellListSegaCDProvider },
+	{ kEoB1EnemyMageSfx, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EnemyMageSfxSegaCDProvider },
+	{ kEoB1BeholderSpellList, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1BeholderSpellListSegaCDProvider },
+	{ kEoB1BeholderSfx, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1BeholderSfxSegaCDProvider },
+	{ kEoB1TurnUndeadString, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1TurnUndeadStringSegaCDEnglishProvider },
+	{ kEoB1PalettesSega, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1PalettesSegaCDProvider },
+	{ kEoB1PatternTable0, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1PatternTable0SegaCDProvider },
+	{ kEoB1PatternTable1, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1PatternTable1SegaCDProvider },
+	{ kEoB1PatternTable2, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1PatternTable2SegaCDProvider },
+	{ kEoB1PatternTable3, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1PatternTable3SegaCDProvider },
+	{ kEoB1PatternTable4, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1PatternTable4SegaCDProvider },
+	{ kEoB1PatternTable5, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1PatternTable5SegaCDProvider },
+	{ kEoB1PatternAddTable1, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1PatternAddTable1SegaCDProvider },
+	{ kEoB1PatternAddTable2, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1PatternAddTable2SegaCDProvider },
+	{ kEoB1Npc0Strings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1Npc0StringsSegaCDEnglishProvider },
+	{ kEoB1Npc11Strings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1Npc11StringsSegaCDEnglishProvider },
+	{ kEoB1Npc12Strings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1Npc12StringsSegaCDEnglishProvider },
+	{ kEoB1Npc21Strings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1Npc21StringsSegaCDEnglishProvider },
+	{ kEoB1Npc22Strings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1Npc22StringsSegaCDEnglishProvider },
+	{ kEoB1Npc31Strings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1Npc31StringsSegaCDEnglishProvider },
+	{ kEoB1Npc32Strings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1Npc32StringsSegaCDEnglishProvider },
+	{ kEoB1Npc4Strings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1Npc4StringsSegaCDEnglishProvider },
+	{ kEoB1Npc5Strings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1Npc5StringsSegaCDEnglishProvider },
+	{ kEoB1Npc6Strings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1Npc6StringsSegaCDEnglishProvider },
+	{ kEoB1Npc7Strings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1Npc7StringsSegaCDEnglishProvider },
+	{ kEoBBasePryDoorStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1PryDoorStringsSegaCDEnglishProvider },
+	{ kEoBBaseWarningStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1WarningStringsSegaCDEnglishProvider },
+	{ kEoB1ParchmentStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1ParchmentStringsSegaCDEnglishProvider },
+	{ kEoB1ItemNames, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1ItemNamesSegaCDEnglishProvider },
+	{ kEoB1SpeechAnimData, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SpeechAnimDataSegaCDProvider },
+	{ kEoB1WdAnimSprites, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1WdAnimSpritesSegaCDProvider },
+	{ kEoB1SequenceTrackMap, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SequenceTrackMapSegaCDProvider },
+	{ kEoB1MapStrings1, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MapStrings1SegaCDEnglishProvider },
+	{ kEoB1MapStrings2, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MapStrings2SegaCDEnglishProvider },
+	{ kEoB1MapStrings3, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MapStrings3SegaCDEnglishProvider },
+	{ kEoB1MapLevelData, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MapLevelDataSegaCDProvider },
+	{ kEoB1Ascii2SjisTable1, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1Ascii2SjisTable1SegaCDProvider },
+	{ kEoB1Ascii2SjisTable2, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1Ascii2SjisTable2SegaCDProvider },
+	{ kEoB1CharWidthTable1, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1CharWidthTable1SegaCDProvider },
+	{ kEoB1CharWidthTable2, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1CharWidthTable2SegaCDProvider },
+	{ kEoB1CharWidthTable3, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1CharWidthTable3SegaCDProvider },
+	{ kEoB1CharTilesTable, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1CharTilesTableSegaCDProvider },
+	{ kEoBBaseItemSuffixStringsRings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1ItemSuffixStringsRingsSegaCDEnglishProvider },
+	{ kEoBBaseItemSuffixStringsPotions, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1ItemSuffixStringsPotionsSegaCDEnglishProvider },
+	{ kEoBBaseItemSuffixStringsWands, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1ItemSuffixStringsWandsSegaCDEnglishProvider },
+	{ kEoBBaseRipItemStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1RipItemStringsSegaCDEnglishProvider },
+	{ kEoBBaseCursedString, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1CursedStringSegaCDEnglishProvider },
+	{ kEoBBaseEnchantedString, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EnchantedStringSegaCDProvider },
+	{ kEoBBaseMagicObjectStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MagicObjectStringsSegaCDEnglishProvider },
+	{ kEoBBaseMagicObjectString5, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MagicObjectString5SegaCDEnglishProvider },
+	{ kEoBBasePatternSuffix, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1PatternSuffixSegaCDEnglishProvider },
+	{ kEoBBasePatternGrFix1, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1PatternGrFix1SegaCDEnglishProvider },
+	{ kEoBBasePatternGrFix2, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1PatternGrFix2SegaCDEnglishProvider },
+	{ kEoBBaseValidateArmorString, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1ValidateArmorStringSegaCDEnglishProvider },
+	{ kEoBBaseValidateNoDropString, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1ValidateNoDropStringSegaCDEnglishProvider },
+	{ kEoBBasePotionStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1PotionStringsSegaCDEnglishProvider },
+	{ kEoBBaseWandStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1WandStringsSegaCDEnglishProvider },
+	{ kEoBBaseItemMisuseStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1ItemMisuseStringsSegaCDEnglishProvider },
+	{ kEoBBaseTakenStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1TakenStringsSegaCDEnglishProvider },
+	{ kEoBBasePotionEffectStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1PotionEffectStringsSegaCDEnglishProvider },
+	{ kEoBBaseYesNoStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1YesNoStringsSegaCDEnglishProvider },
+	{ kRpgCommonMoreStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MoreStringsSegaCDEnglishProvider },
+	{ kEoBBaseNpcMaxStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1NpcMaxStringsSegaCDEnglishProvider },
+	{ kEoBBaseNpcJoinStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1NpcJoinStringsSegaCDEnglishProvider },
+	{ kEoBBaseCancelStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1CancelStringsSegaCDEnglishProvider },
+	{ kEoBBaseMenuStringsSaveLoad, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MenuStringsSaveLoadSegaCDEnglishProvider },
+	{ kEoBBaseMenuStringsOnOff, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MenuStringsOnOffSegaCDEnglishProvider },
+	{ kEoBBaseMenuStringsSpells, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MenuStringsSpellsSegaCDEnglishProvider },
+	{ kEoBBaseMenuStringsRest, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MenuStringsRestSegaCDEnglishProvider },
+	{ kEoBBaseMenuStringsDrop, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MenuStringsDropSegaCDEnglishProvider },
+	{ kEoBBaseMenuStringsExit, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MenuStringsExitSegaCDEnglishProvider },
+	{ kEoBBaseMenuStringsStarve, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MenuStringsStarveSegaCDEnglishProvider },
+	{ kEoBBaseMenuStringsScribe, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MenuStringsScribeSegaCDEnglishProvider },
+	{ kEoBBaseMenuStringsDrop2, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MenuStringsDrop2SegaCDEnglishProvider },
+	{ kEoBBaseMenuStringsPoison, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MenuStringsPoisonSegaCDEnglishProvider },
+	{ kEoBBaseMenuStringsMgc, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MenuStringsMgcSegaCDEnglishProvider },
+	{ kEoBBaseMenuStringsRest2, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MenuStringsRest2SegaCDEnglishProvider },
+	{ kEoBBaseMenuStringsRest4, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MenuStringsRest4SegaCDEnglishProvider },
+	{ kEoBBaseMenuStringsDefeat, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MenuStringsDefeatSegaCDEnglishProvider },
+	{ kEoBBaseMenuYesNoStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MenuYesNoStringsSegaCDEnglishProvider },
+	{ kEoBBaseSpellLevelsMage, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SpellLevelsMageSegaCDProvider },
+	{ kEoBBaseSpellLevelsCleric, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SpellLevelsClericSegaCDProvider },
+	{ kEoBBaseNumSpellsCleric, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1NumSpellsClericSegaCDProvider },
+	{ kEoBBaseNumSpellsWisAdj, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1NumSpellsWisAdjSegaCDProvider },
+	{ kEoBBaseNumSpellsPal, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1NumSpellsPalSegaCDProvider },
+	{ kEoBBaseNumSpellsMage, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1NumSpellsMageSegaCDProvider },
+	{ kEoBBaseCharGuiStringsIn, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1CharGuiStringsInSegaCDEnglishProvider },
+	{ kEoBBaseCharStatusStrings7, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1CharStatusStrings7SegaCDEnglishProvider },
+	{ kEoBBaseCharStatusStrings81, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1CharStatusStrings81SegaCDEnglishProvider },
+	{ kEoBBaseCharStatusStrings9, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1CharStatusStrings9SegaCDEnglishProvider },
+	{ kEoBBaseCharStatusStrings131, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1CharStatusStrings131SegaCDEnglishProvider },
+	{ kEoBBaseLevelGainStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1LevelGainStringsSegaCDEnglishProvider },
+	{ kEoBBaseExperienceTable0, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1ExperienceTable0SegaCDProvider },
+	{ kEoBBaseExperienceTable1, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1ExperienceTable1SegaCDProvider },
+	{ kEoBBaseExperienceTable2, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1ExperienceTable2SegaCDProvider },
+	{ kEoBBaseExperienceTable3, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1ExperienceTable3SegaCDProvider },
+	{ kEoBBaseExperienceTable4, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1ExperienceTable4SegaCDProvider },
+	//{ kEoBBaseBookNumbers, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1BookNumbersSegaCDEnglishProvider },
+	{ kEoBBaseMageSpellsList, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MageSpellsListSegaCDEnglishProvider },
+	{ kEoBBaseClericSpellsList, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1ClericSpellsListSegaCDEnglishProvider },
+	{ kEoBBaseMageSpellsList2, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MageSpellsList2SegaCDEnglishProvider },
+	{ kEoBBaseClericSpellsList2, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1ClericSpellsList2SegaCDEnglishProvider },
+	{ kEoBBaseSpellNames, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1SpellNamesSegaCDEnglishProvider },
+	{ kEoBBaseMagicStrings1, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MagicStrings1SegaCDEnglishProvider },
+	{ kEoBBaseMagicStrings2, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MagicStrings2SegaCDEnglishProvider },
+	{ kEoBBaseMagicStrings3, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MagicStrings3SegaCDEnglishProvider },
+	{ kEoBBaseMagicStrings4, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MagicStrings4SegaCDEnglishProvider },
+	{ kEoBBaseMagicStrings6, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MagicStrings6SegaCDEnglishProvider },
+	//{ kEoBBaseMagicStrings7, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MagicStrings7SegaCDEnglishProvider },
+	{ kEoBBaseMagicStrings8, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MagicStrings8SegaCDEnglishProvider },
+	//{ kEoBBaseMagicStrings9, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MagicStrings9SegaCDEnglishProvider },
+	{ kEoBBaseExpObjectTblIndex, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1ExpObjectTblIndexSegaCDProvider },
+	{ kEoBBaseExpObjectShpStart, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1ExpObjectShpStartSegaCDProvider },
+	{ kEoBBaseExpObjectTbl1, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1ExpObjectTbl1SegaCDProvider },
+	{ kEoBBaseExpObjectTbl2, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1ExpObjectTbl2SegaCDProvider },
+	{ kEoBBaseExpObjectTbl3, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1ExpObjectTbl3SegaCDProvider },
+	{ kEoBBaseExpObjectY, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1ExpObjectYSegaCDProvider },
+	{ kEoBBaseSparkDefSteps, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SparkDefStepsSegaCDProvider },
+	{ kEoBBaseSparkDefSubSteps, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SparkDefSubStepsSegaCDProvider },
+	{ kEoBBaseSparkDefShift, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SparkDefShiftSegaCDProvider },
+	{ kEoBBaseSparkDefAdd, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SparkDefAddSegaCDProvider },
+	{ kEoBBaseSparkDefX, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SparkDefXSegaCDProvider },
+	{ kEoBBaseSparkDefY, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SparkDefYSegaCDProvider },
+	{ kEoBBaseSparkOfFlags1, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SparkOfFlags1SegaCDProvider },
+	{ kEoBBaseSparkOfFlags2, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SparkOfFlags2SegaCDProvider },
+	{ kEoBBaseSparkOfShift, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SparkOfShiftSegaCDProvider },
+	{ kEoBBaseSparkOfX, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SparkOfXSegaCDProvider },
+	{ kEoBBaseSparkOfY, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SparkOfYSegaCDProvider },
+	{ kEoBBaseSpellProperties, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SpellPropertiesSegaCDProvider },
+	{ kEoBBaseMagicFlightProps, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MagicFlightPropsSegaCDProvider },
+	{ kEoBBaseTurnUndeadEffect, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1TurnUndeadEffectSegaCDProvider },
+	{ kEoBBaseBurningHandsDest, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1BurningHandsDestSegaCDProvider },
+	{ kEoBBaseConeOfColdDest1, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1ConeOfColdDest1SegaCDProvider },
+	{ kEoBBaseConeOfColdDest2, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1ConeOfColdDest2SegaCDProvider },
+	{ kEoBBaseConeOfColdDest3, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1ConeOfColdDest3SegaCDProvider },
+	{ kEoBBaseConeOfColdDest4, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1ConeOfColdDest4SegaCDProvider },
+	{ kEoBBaseConeOfColdGfxTbl, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1ConeOfColdGfxTblSegaCDProvider },
+	{ kRpgCommonDscDoorShapeIndex, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscDoorShapeIndexSegaCDProvider },
+	{ kEoBBaseWllFlagPreset, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1WllFlagPresetSegaCDProvider },
+	{ kEoBBaseDscShapeCoords, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscShapeCoordsSegaCDProvider },
+	{ kRpgCommonDscDoorScaleOffs, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscDoorScaleOffsSegaCDProvider },
+	{ kEoBBaseDscDoorScaleMult1, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscDoorScaleMult1SegaCDProvider },
+	{ kEoBBaseDscDoorScaleMult2, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscDoorScaleMult2SegaCDProvider },
+	{ kEoBBaseDscDoorScaleMult3, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscDoorScaleMult3SegaCDProvider },
+	{ kEoBBaseDscDoorScaleMult4, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscDoorScaleMult4SegaCDProvider },
+	{ kEoBBaseDscDoorScaleMult5, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscDoorScaleMult5SegaCDProvider },
+	{ kEoBBaseDscDoorScaleMult6, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscDoorScaleMult6SegaCDProvider },
+	{ kEoBBaseDscDoorXE, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscDoorXESegaCDProvider },
+	{ kEoBBaseDscDoorY1, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscDoorY1SegaCDProvider },
+	{ kEoBBaseDscDoorY3, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscDoorY3SegaCDProvider },
+	{ kEoBBaseDscDoorY4, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscDoorY4SegaCDProvider },
+	{ kEoBBaseDscDoorY5, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscDoorY5SegaCDProvider },
+	{ kEoBBaseDscDoorY6, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscDoorY6SegaCDProvider },
+	{ kEoBBaseDscDoorY7, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscDoorY7SegaCDProvider },
+	{ kEoBBaseDscDoorCoordsExt, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscDoorCoordsExtSegaCDProvider },
+	{ kRpgCommonDscDoorFrameY1, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscDoorFrameY1SegaCDProvider },
+	{ kRpgCommonDscDoorFrameY2, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscDoorFrameY2SegaCDProvider },
+	{ kRpgCommonDscDoorFrameIndex1, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscDoorFrameIndex1SegaCDProvider },
+	{ kRpgCommonDscDoorFrameIndex2, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscDoorFrameIndex2SegaCDProvider },
+	{ kEoBBaseDscItemPosIndex, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscItemPosIndexSegaCDProvider },
+	{ kEoBBaseDscItemShpX, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscItemShpXSegaCDProvider },
+	{ kEoBBaseDscItemScaleIndex, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscItemScaleIndexSegaCDProvider },
+	{ kEoBBaseDscItemTileIndex, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscItemTileIndexSegaCDProvider },
+	{ kEoBBaseDscItemShapeMap, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscItemShapeMapSegaCDProvider },
+	{ kEoBBaseDscTelptrShpCoords, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscTelptrShpCoordsSegaCDProvider },
+	{ kEoBBasePortalSeqData, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1PortalSeqDataSegaCDProvider },
+	{ kEoBBaseDscMonsterFrmOffsTbl1, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscMonsterFrmOffsTbl1SegaCDProvider },
+	{ kEoBBaseDscMonsterFrmOffsTbl2, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscMonsterFrmOffsTbl2SegaCDProvider },
+	{ kEoBBaseInvSlotX, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1InvSlotXSegaCDProvider },
+	{ kEoBBaseInvSlotY, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1InvSlotYSegaCDProvider },
+	{ kEoBBaseSlotValidationFlags, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1SlotValidationFlagsSegaCDProvider },
+	{ kEoBBaseProjectileWeaponTypes, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1ProjectileWeaponTypesSegaCDProvider },
+	{ kEoBBaseWandTypes, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1WandTypesSegaCDProvider },
+	{ kEoBBaseDrawObjPosIndex, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DrawObjPosIndexSegaCDProvider },
+	{ kEoBBaseFlightObjFlipIndex, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1FlightObjFlipIndexSegaCDProvider },
+	{ kEoBBaseFlightObjShpMap, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1FlightObjShpMapSegaCDProvider },
+	{ kEoBBaseFlightObjSclIndex, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1FlightObjSclIndexSegaCDProvider },
+	{ kRpgCommonDscShapeIndex, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscShapeIndexSegaCDProvider },
+	{ kRpgCommonDscX, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscXSegaCDProvider },
+	{ kRpgCommonDscTileIndex, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscTileIndexSegaCDProvider },
+	{ kRpgCommonDscDimData1, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscDimData1SegaCDProvider },
+	{ kRpgCommonDscDimData2, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscDimData2SegaCDProvider },
+	{ kRpgCommonDscBlockMap, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscBlockMapSegaCDProvider },
+	{ kRpgCommonDscDimMap, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscDimMapSegaCDProvider },
+	{ kRpgCommonDscBlockIndex, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1DscBlockIndexSegaCDProvider },
+	{ kEoBBaseClassModifierFlags, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1ClassModifierFlagsSegaCDProvider },
+	{ kEoBBaseMonsterStepTable01, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterStepTable01SegaCDProvider },
+	{ kEoBBaseMonsterStepTable2, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterStepTable2SegaCDProvider },
+	{ kEoBBaseMonsterStepTable3, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterStepTable3SegaCDProvider },
+	{ kEoBBaseMonsterCloseAttPosTable1, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterCloseAttPosTable1SegaCDProvider },
+	{ kEoBBaseMonsterCloseAttPosTable21, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterCloseAttPosTable21SegaCDProvider },
+	{ kEoBBaseMonsterCloseAttChkTable1, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterCloseAttChkTable1SegaCDProvider },
+	{ kEoBBaseMonsterCloseAttChkTable2, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterCloseAttChkTable2SegaCDProvider },
+	{ kEoBBaseMonsterCloseAttDstTable1, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterCloseAttDstTable1SegaCDProvider },
+	{ kEoBBaseMonsterCloseAttDstTable2, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterCloseAttDstTable2SegaCDProvider },
+	{ kEoBBaseMonsterProximityTable, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterProximityTableSegaCDProvider },
+	{ kEoBBaseFindBlockMonstersTable, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1FindBlockMonstersTableSegaCDProvider },
+	{ kEoBBaseMonsterDirChangeTable, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterDirChangeTableSegaCDProvider },
+	{ kEoBBaseMonsterDistAttStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1MonsterDistAttStringsSegaCDEnglishProvider },
+	{ kEoBBaseEncodeMonsterDefs00, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EncodeMonsterDefs00SegaCDProvider },
+	{ kEoBBaseEncodeMonsterDefs01, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EncodeMonsterDefs01SegaCDProvider },
+	{ kEoBBaseEncodeMonsterDefs02, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EncodeMonsterDefs02SegaCDProvider },
+	{ kEoBBaseEncodeMonsterDefs03, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EncodeMonsterDefs03SegaCDProvider },
+	{ kEoBBaseEncodeMonsterDefs04, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EncodeMonsterDefs04SegaCDProvider },
+	{ kEoBBaseEncodeMonsterDefs05, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EncodeMonsterDefs05SegaCDProvider },
+	{ kEoBBaseEncodeMonsterDefs06, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EncodeMonsterDefs06SegaCDProvider },
+	{ kEoBBaseEncodeMonsterDefs07, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EncodeMonsterDefs07SegaCDProvider },
+	{ kEoBBaseEncodeMonsterDefs08, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EncodeMonsterDefs08SegaCDProvider },
+	{ kEoBBaseEncodeMonsterDefs09, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EncodeMonsterDefs09SegaCDProvider },
+	{ kEoBBaseEncodeMonsterDefs10, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EncodeMonsterDefs10SegaCDProvider },
+	{ kEoBBaseEncodeMonsterDefs11, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EncodeMonsterDefs11SegaCDProvider },
+	{ kEoBBaseEncodeMonsterDefs12, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EncodeMonsterDefs12SegaCDProvider },
+	{ kEoBBaseEncodeMonsterDefs13, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EncodeMonsterDefs13SegaCDProvider },
+	{ kEoBBaseEncodeMonsterDefs14, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EncodeMonsterDefs14SegaCDProvider },
+	{ kEoBBaseEncodeMonsterDefs15, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EncodeMonsterDefs15SegaCDProvider },
+	{ kEoBBaseEncodeMonsterDefs16, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EncodeMonsterDefs16SegaCDProvider },
+	{ kEoBBaseEncodeMonsterDefs17, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EncodeMonsterDefs17SegaCDProvider },
+	{ kEoBBaseEncodeMonsterDefs18, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EncodeMonsterDefs18SegaCDProvider },
+	{ kEoBBaseEncodeMonsterDefs19, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EncodeMonsterDefs19SegaCDProvider },
+	{ kEoBBaseEncodeMonsterDefs20, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EncodeMonsterDefs20SegaCDProvider },
+	{ kEoBBaseEncodeMonsterDefs21, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1EncodeMonsterDefs21SegaCDProvider },
+	{ kEoB1MonsterAnimFrames00, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames00SegaCDProvider },
+	{ kEoB1MonsterAnimFrames01, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames01SegaCDProvider },
+	{ kEoB1MonsterAnimFrames02, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames02SegaCDProvider },
+	{ kEoB1MonsterAnimFrames03, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames03SegaCDProvider },
+	{ kEoB1MonsterAnimFrames04, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames04SegaCDProvider },
+	{ kEoB1MonsterAnimFrames05, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames05SegaCDProvider },
+	{ kEoB1MonsterAnimFrames06, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames06SegaCDProvider },
+	{ kEoB1MonsterAnimFrames07, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames07SegaCDProvider },
+	{ kEoB1MonsterAnimFrames08, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames08SegaCDProvider },
+	{ kEoB1MonsterAnimFrames09, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames09SegaCDProvider },
+	{ kEoB1MonsterAnimFrames10, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames10SegaCDProvider },
+	{ kEoB1MonsterAnimFrames11, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames11SegaCDProvider },
+	{ kEoB1MonsterAnimFrames12, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames12SegaCDProvider },
+	{ kEoB1MonsterAnimFrames13, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames13SegaCDProvider },
+	{ kEoB1MonsterAnimFrames14, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames14SegaCDProvider },
+	{ kEoB1MonsterAnimFrames15, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames15SegaCDProvider },
+	{ kEoB1MonsterAnimFrames16, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames16SegaCDProvider },
+	{ kEoB1MonsterAnimFrames17, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames17SegaCDProvider },
+	{ kEoB1MonsterAnimFrames18, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames18SegaCDProvider },
+	{ kEoB1MonsterAnimFrames19, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames19SegaCDProvider },
+	{ kEoB1MonsterAnimFrames20, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames20SegaCDProvider },
+	{ kEoB1MonsterAnimFrames21, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames21SegaCDProvider },
+	{ kEoB1MonsterAnimFrames22, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames22SegaCDProvider },
+	{ kEoB1MonsterAnimFrames23, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames23SegaCDProvider },
+	{ kEoB1MonsterAnimFrames24, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames24SegaCDProvider },
+	{ kEoB1MonsterAnimFrames25, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames25SegaCDProvider },
+	{ kEoB1MonsterAnimFrames26, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames26SegaCDProvider },
+	{ kEoB1MonsterAnimFrames27, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames27SegaCDProvider },
+	{ kEoB1MonsterAnimFrames28, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames28SegaCDProvider },
+	{ kEoB1MonsterAnimFrames29, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames29SegaCDProvider },
+	{ kEoB1MonsterAnimFrames30, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames30SegaCDProvider },
+	{ kEoB1MonsterAnimFrames31, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames31SegaCDProvider },
+	{ kEoB1MonsterAnimFrames32, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames32SegaCDProvider },
+	{ kEoB1MonsterAnimFrames33, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames33SegaCDProvider },
+	{ kEoB1MonsterAnimFrames34, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames34SegaCDProvider },
+	{ kEoB1MonsterAnimFrames35, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames35SegaCDProvider },
+	{ kEoB1MonsterAnimFrames36, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames36SegaCDProvider },
+	{ kEoB1MonsterAnimFrames37, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames37SegaCDProvider },
+	{ kEoB1MonsterAnimFrames38, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames38SegaCDProvider },
+	{ kEoB1MonsterAnimFrames39, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames39SegaCDProvider },
+	{ kEoB1MonsterAnimFrames40, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames40SegaCDProvider },
+	{ kEoB1MonsterAnimFrames41, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames41SegaCDProvider },
+	{ kEoB1MonsterAnimFrames42, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames42SegaCDProvider },
+	{ kEoB1MonsterAnimFrames43, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames43SegaCDProvider },
+	{ kEoB1MonsterAnimFrames44, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames44SegaCDProvider },
+	{ kEoB1MonsterAnimFrames45, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames45SegaCDProvider },
+	{ kEoB1MonsterAnimFrames46, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames46SegaCDProvider },
+	{ kEoB1MonsterAnimFrames47, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames47SegaCDProvider },
+	{ kEoB1MonsterAnimFrames48, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames48SegaCDProvider },
+	{ kEoB1MonsterAnimFrames49, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames49SegaCDProvider },
+	{ kEoB1MonsterAnimFrames50, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames50SegaCDProvider },
+	{ kEoB1MonsterAnimFrames51, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames51SegaCDProvider },
+	{ kEoB1MonsterAnimFrames52, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames52SegaCDProvider },
+	{ kEoB1MonsterAnimFrames53, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames53SegaCDProvider },
+	{ kEoB1MonsterAnimFrames54, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames54SegaCDProvider },
+	{ kEoB1MonsterAnimFrames55, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames55SegaCDProvider },
+	{ kEoB1MonsterAnimFrames56, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames56SegaCDProvider },
+	{ kEoB1MonsterAnimFrames57, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames57SegaCDProvider },
+	{ kEoB1MonsterAnimFrames58, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames58SegaCDProvider },
+	{ kEoB1MonsterAnimFrames59, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames59SegaCDProvider },
+	{ kEoB1MonsterAnimFrames60, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames60SegaCDProvider },
+	{ kEoB1MonsterAnimFrames61, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames61SegaCDProvider },
+	{ kEoB1MonsterAnimFrames62, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames62SegaCDProvider },
+	{ kEoB1MonsterAnimFrames63, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames63SegaCDProvider },
+	{ kEoB1MonsterAnimFrames64, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames64SegaCDProvider },
+	{ kEoB1MonsterAnimFrames65, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames65SegaCDProvider },
+	{ kEoB1MonsterAnimFrames66, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames66SegaCDProvider },
+	{ kEoB1MonsterAnimFrames67, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames67SegaCDProvider },
+	{ kEoB1MonsterAnimFrames68, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames68SegaCDProvider },
+	{ kEoB1MonsterAnimFrames69, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames69SegaCDProvider },
+	{ kEoB1MonsterAnimFrames70, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames70SegaCDProvider },
+	{ kEoB1MonsterAnimFrames71, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames71SegaCDProvider },
+	{ kEoB1MonsterAnimFrames72, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames72SegaCDProvider },
+	{ kEoB1MonsterAnimFrames73, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames73SegaCDProvider },
+	{ kEoB1MonsterAnimFrames74, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames74SegaCDProvider },
+	{ kEoB1MonsterAnimFrames75, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames75SegaCDProvider },
+	{ kEoB1MonsterAnimFrames76, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames76SegaCDProvider },
+	{ kEoB1MonsterAnimFrames77, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames77SegaCDProvider },
+	{ kEoB1MonsterAnimFrames78, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames78SegaCDProvider },
+	{ kEoB1MonsterAnimFrames79, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames79SegaCDProvider },
+	{ kEoB1MonsterAnimFrames80, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames80SegaCDProvider },
+	{ kEoB1MonsterAnimFrames81, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames81SegaCDProvider },
+	{ kEoB1MonsterAnimFrames82, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames82SegaCDProvider },
+	{ kEoB1MonsterAnimFrames83, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames83SegaCDProvider },
+	{ kEoB1MonsterAnimFrames84, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames84SegaCDProvider },
+	{ kEoB1MonsterAnimFrames85, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames85SegaCDProvider },
+	{ kEoB1MonsterAnimFrames86, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames86SegaCDProvider },
+	{ kEoB1MonsterAnimFrames87, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames87SegaCDProvider },
+	{ kEoB1MonsterAnimFrames88, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames88SegaCDProvider },
+	{ kEoB1MonsterAnimFrames89, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames89SegaCDProvider },
+	{ kEoB1MonsterAnimFrames90, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames90SegaCDProvider },
+	{ kEoB1MonsterAnimFrames91, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames91SegaCDProvider },
+	{ kEoB1MonsterAnimFrames92, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames92SegaCDProvider },
+	{ kEoB1MonsterAnimFrames93, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames93SegaCDProvider },
+	{ kEoB1MonsterAnimFrames94, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames94SegaCDProvider },
+	{ kEoB1MonsterAnimFrames95, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames95SegaCDProvider },
+	{ kEoB1MonsterAnimFrames96, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames96SegaCDProvider },
+	{ kEoB1MonsterAnimFrames97, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames97SegaCDProvider },
+	{ kEoB1MonsterAnimFrames98, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames98SegaCDProvider },
+	{ kEoB1MonsterAnimFrames99, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames99SegaCDProvider },
+	{ kEoB1MonsterAnimFrames100, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames100SegaCDProvider },
+	{ kEoB1MonsterAnimFrames101, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames101SegaCDProvider },
+	{ kEoB1MonsterAnimFrames102, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames102SegaCDProvider },
+	{ kEoB1MonsterAnimFrames103, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames103SegaCDProvider },
+	{ kEoB1MonsterAnimFrames104, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames104SegaCDProvider },
+	{ kEoB1MonsterAnimFrames105, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames105SegaCDProvider },
+	{ kEoB1MonsterAnimFrames106, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames106SegaCDProvider },
+	{ kEoB1MonsterAnimFrames107, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames107SegaCDProvider },
+	{ kEoB1MonsterAnimFrames108, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames108SegaCDProvider },
+	{ kEoB1MonsterAnimFrames109, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1MonsterAnimFrames109SegaCDProvider },
+	{ kEoBBaseNpcPresets, kEoB1, kPlatformSegaCD, kNoSpecial, UNK_LANG, &kEoB1NpcPresetsSegaCDProvider },
+	{ kEoBBaseTextInputCharacterLines, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1TextInputCharacterLinesSegaCDProvider },
+	{ kEoBBaseTextInputSelectStrings, kEoB1, kPlatformSegaCD, kNoSpecial, EN_ANY, &kEoB1TextInputSelectStringsSegaCDProvider },
 	{ kEoBBaseChargenStrings1, kEoB2, kPlatformDOS, kNoSpecial, EN_ANY, &kEoB2ChargenStrings1DOSEnglishProvider },
 	{ kEoBBaseChargenStrings2, kEoB2, kPlatformDOS, kNoSpecial, EN_ANY, &kEoB2ChargenStrings2DOSEnglishProvider },
 	{ kEoBBaseChargenStartLevels, kEoB2, kPlatformDOS, kNoSpecial, UNK_LANG, &kEoB2ChargenStartLevelsDOSProvider },
@@ -3627,8 +3990,8 @@ static const ResourceProvider resourceProviders[] = {
 	{ kEoB2WallOfForceShapeData05, kEoB2, kPlatformFMTowns, kNoSpecial, UNK_LANG, &kEoB2WallOfForceShapeData05FMTownsProvider },
 	{ kEoB2UtilMenuStrings, kEoB2, kPlatformFMTowns, kNoSpecial, JA_JPN, &kEoB2UtilMenuStringsFMTownsProvider },
 	{ kEoB2Config2431Strings, kEoB2, kPlatformFMTowns, kNoSpecial, JA_JPN, &kEoB2Config2431StringsFMTownsProvider },
-	{ kEoB2KatakanaLines, kEoB2, kPlatformFMTowns, kNoSpecial, JA_JPN, &kEoB2KatakanaLinesFMTownsProvider },
-	{ kEoB2KanaSelectStrings, kEoB2, kPlatformFMTowns, kNoSpecial, JA_JPN, &kEoB2KanaSelectStringsFMTownsProvider },
+	{ kEoBBaseTextInputCharacterLines, kEoB2, kPlatformFMTowns, kNoSpecial, JA_JPN, &kEoB2TextInputCharacterLinesFMTownsProvider },
+	{ kEoBBaseTextInputSelectStrings, kEoB2, kPlatformFMTowns, kNoSpecial, JA_JPN, &kEoB2TextInputSelectStringsFMTownsProvider },
 	{ kEoB2FontDmpSearchTbl, kEoB2, kPlatformFMTowns, kNoSpecial, UNK_LANG, &kEoB2FontDmpSearchTblFMTownsProvider },
 	{ kEoB2Ascii2SjisTables, kEoB2, kPlatformFMTowns, kNoSpecial, UNK_LANG, &kEoB2Ascii2SjisTablesFMTownsProvider },
 	{ kEoB2Ascii2SjisTables2, kEoB2, kPlatformFMTowns, kNoSpecial, UNK_LANG, &kEoB2Ascii2SjisTables2FMTownsProvider },
diff --git a/devtools/create_kyradat/resources/eob1_amiga.h b/devtools/create_kyradat/resources/eob1_amiga.h
index b0ca527760..3d5a7607cc 100644
--- a/devtools/create_kyradat/resources/eob1_amiga.h
+++ b/devtools/create_kyradat/resources/eob1_amiga.h
@@ -1537,7 +1537,7 @@ static const byte kEoB1FindBlockMonstersTableAmiga[64] = {
 
 static const ByteProvider kEoB1FindBlockMonstersTableAmigaProvider = { ARRAYSIZE(kEoB1FindBlockMonstersTableAmiga), kEoB1FindBlockMonstersTableAmiga };
 
-static const byte kEoB1MonsterDirChangeTableAmiga[48] = {
+static const byte kEoB1MonsterDirChangeTableAmiga[16] = {
 	0xff, 0x06, 0x02, 0xff, 0x00, 0x07, 0x01, 0xff,
 	0x04, 0x05, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff
 };
diff --git a/devtools/create_kyradat/resources/eob1_pc98.h b/devtools/create_kyradat/resources/eob1_pc98.h
index cf9c7dd729..98d205a757 100644
--- a/devtools/create_kyradat/resources/eob1_pc98.h
+++ b/devtools/create_kyradat/resources/eob1_pc98.h
@@ -173,7 +173,7 @@ static const char *const kEoB1IntroFilesTunnelPC98[2] = {
 static const StringListProvider kEoB1IntroFilesTunnelPC98Provider = { ARRAYSIZE(kEoB1IntroFilesTunnelPC98), kEoB1IntroFilesTunnelPC98 };
 
 static const byte kEoB1IntroOpeningFrmDelayPC98[7] = {
-	0x8C, 0x64, 0x32, 0x64, 0x32, 0x8C, 0x64
+	0x46, 0x32, 0x19, 0x32, 0x19, 0x46, 0x32
 };
 
 static const ByteProvider kEoB1IntroOpeningFrmDelayPC98Provider = { ARRAYSIZE(kEoB1IntroOpeningFrmDelayPC98), kEoB1IntroOpeningFrmDelayPC98 };
@@ -1787,40 +1787,6 @@ static const byte kEoB1FinaleHandsAnim3PC98[50] = {
 
 static const ByteProvider kEoB1FinaleHandsAnim3PC98Provider = { ARRAYSIZE(kEoB1FinaleHandsAnim3PC98), kEoB1FinaleHandsAnim3PC98 };
 
-static const byte kEoB1CreditsStringsPC98[459] = {
-	0x0d, 0x08, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x53, 0x74, 0x61, 0x66, 0x66, 0x0d, 0x0c, 0x50,
-	0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x0d, 0x08, 0x20,
-	0x20, 0x20, 0x20, 0x59, 0x61, 0x73, 0x75, 0x74, 0x61, 0x6b, 0x61, 0x20, 0x55, 0x6b, 0x61, 0x69,
-	0x0d, 0x0c, 0x43, 0x68, 0x69, 0x65, 0x66, 0x20, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d,
-	0x65, 0x72, 0x0d, 0x08, 0x20, 0x20, 0x20, 0x20, 0x4d, 0x61, 0x6b, 0x6f, 0x74, 0x6f, 0x20, 0x49,
-	0x63, 0x68, 0x69, 0x6e, 0x6f, 0x73, 0x65, 0x6b, 0x69, 0x0d, 0x0c, 0x53, 0x75, 0x62, 0x20, 0x50,
-	0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x0d, 0x08, 0x20, 0x20, 0x20, 0x20, 0x48,
-	0x69, 0x72, 0x6f, 0x79, 0x75, 0x6b, 0x69, 0x20, 0x46, 0x75, 0x6a, 0x69, 0x77, 0x61, 0x72, 0x61,
-	0x0d, 0x20, 0x20, 0x20, 0x20, 0x48, 0x69, 0x64, 0x65, 0x66, 0x75, 0x6d, 0x69, 0x20, 0x4f, 0x68,
-	0x61, 0x72, 0x61, 0x0d, 0x0c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x20, 0x44, 0x65, 0x73,
-	0x69, 0x67, 0x6e, 0x0d, 0x08, 0x20, 0x20, 0x20, 0x20, 0x41, 0x74, 0x73, 0x75, 0x68, 0x69, 0x72,
-	0x6f, 0x20, 0x47, 0x75, 0x6e, 0x6a, 0x69, 0x0d, 0x20, 0x20, 0x20, 0x20, 0x52, 0x69, 0x65, 0x6b,
-	0x6f, 0x20, 0x59, 0x6f, 0x73, 0x68, 0x69, 0x64, 0x61, 0x0d, 0x0c, 0x4d, 0x75, 0x73, 0x69, 0x63,
-	0x0d, 0x08, 0x20, 0x20, 0x20, 0x20, 0x59, 0x75, 0x7a, 0x6f, 0x20, 0x4b, 0x6f, 0x73, 0x68, 0x69,
-	0x72, 0x6f, 0x0d, 0x20, 0x20, 0x20, 0x20, 0x59, 0x75, 0x6a, 0x69, 0x20, 0x59, 0x61, 0x6d, 0x61,
-	0x64, 0x61, 0x0d, 0x0c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
-	0x26, 0x20, 0x53, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x0d, 0x08, 0x20,
-	0x20, 0x20, 0x20, 0x48, 0x69, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x20, 0x59, 0x61, 0x73, 0x75, 0x64,
-	0x61, 0x0d, 0x20, 0x20, 0x20, 0x20, 0x4d, 0x69, 0x79, 0x75, 0x6b, 0x69, 0x20, 0x4b, 0x69, 0x79,
-	0x6f, 0x6d, 0x61, 0x74, 0x73, 0x75, 0x0d, 0x20, 0x20, 0x20, 0x20, 0x4d, 0x65, 0x67, 0x75, 0x6d,
-	0x69, 0x20, 0x54, 0x73, 0x75, 0x67, 0x65, 0x0d, 0x0c, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69,
-	0x6f, 0x6e, 0x0d, 0x08, 0x20, 0x20, 0x20, 0x20, 0x59, 0x6f, 0x73, 0x68, 0x69, 0x61, 0x6b, 0x69,
-	0x20, 0x4d, 0x61, 0x74, 0x73, 0x75, 0x6d, 0x6f, 0x74, 0x6f, 0x0d, 0x20, 0x20, 0x20, 0x20, 0x4e,
-	0x6f, 0x62, 0x75, 0x61, 0x6b, 0x69, 0x20, 0x53, 0x75, 0x7a, 0x75, 0x6b, 0x69, 0x0d, 0x0c, 0x44,
-	0x65, 0x62, 0x75, 0x67, 0x0d, 0x08, 0x20, 0x20, 0x20, 0x20, 0x4d, 0x61, 0x73, 0x61, 0x61, 0x6b,
-	0x69, 0x20, 0x46, 0x75, 0x72, 0x75, 0x79, 0x61, 0x0d, 0x20, 0x20, 0x20, 0x20, 0x4b, 0x69, 0x79,
-	0x6f, 0x74, 0x6f, 0x20, 0x59, 0x6f, 0x73, 0x68, 0x69, 0x6d, 0x75, 0x72, 0x61, 0x0d, 0x0c, 0x40,
-	0x0d, 0x08, 0x20, 0x54, 0x68, 0x75, 0x73, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x73, 0x20, 0x61,
-	0x20, 0x6c, 0x65, 0x67, 0x65, 0x6e, 0x64, 0x5b, 0x0d, 0x0c, 0x00
-};
-
-static const ByteProvider kEoB1CreditsStringsPC98Provider = { ARRAYSIZE(kEoB1CreditsStringsPC98), kEoB1CreditsStringsPC98 };
-
 static const byte kEoB1CreditsCharWdthPC98[128] = {
 	0x0b, 0x0a, 0x09, 0x0b, 0x0a, 0x09, 0x0a, 0x0b,
 	0x06, 0x08, 0x0c, 0x09, 0x0d, 0x0c, 0x09, 0x0a,
@@ -1834,6 +1800,80 @@ static const byte kEoB1CreditsCharWdthPC98[128] = {
 
 static const ByteProvider kEoB1CreditsCharWdthPC98Provider = { ARRAYSIZE(kEoB1CreditsCharWdthPC98), kEoB1CreditsCharWdthPC98 };
 
+static const uint16 kEoB1Ascii2SjisTable1PC98[30] = {
+	0x4b83, 0x4d83, 0x4f83, 0x5183, 0x5383, 0x5583, 0x5783, 0x5983,
+	0x5b83, 0x5d83, 0x5f83, 0x6183, 0x6483, 0x6683, 0x6883, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x6f83, 0x7283, 0x7583, 0x7883,
+	0x7b83, 0x7083, 0x7383, 0x7683, 0x7983, 0x7c83
+};
+
+static const Uint16Provider kEoB1Ascii2SjisTable1PC98Provider = { ARRAYSIZE(kEoB1Ascii2SjisTable1PC98), kEoB1Ascii2SjisTable1PC98 };
+
+static const uint16 kEoB1Ascii2SjisTable2PC98[160] = {
+	0x4081, 0x3981, 0x3a81, 0x9481, 0x9081, 0x9381, 0x9581, 0x8c81,
+	0x6981, 0x6a81, 0x9681, 0x7b81, 0x4381, 0x7c81, 0x4481, 0x5e81,
+	0x4f82, 0x5082, 0x5182, 0x5282, 0x5382, 0x5482, 0x5582, 0x5682,
+	0x5782, 0x5882, 0x4681, 0x4781, 0x7181, 0x8181, 0x7281, 0x4881,
+	0x9781, 0x6082, 0x6182, 0x6282, 0x6382, 0x6482, 0x6582, 0x6682,
+	0x6782, 0x6882, 0x6982, 0x6a82, 0x6b82, 0x6c82, 0x6d82, 0x6e82,
+	0x6f82, 0x7082, 0x7182, 0x7282, 0x7382, 0x7482, 0x7582, 0x7682,
+	0x7782, 0x7882, 0x7982, 0x6d81, 0x8f81, 0x6e81, 0x4f81, 0x5181,
+	0x4081, 0x8182, 0x8282, 0x8382, 0x8482, 0x8582, 0x8682, 0x8782,
+	0x8882, 0x8982, 0x8a82, 0x8b82, 0x8c82, 0x8d82, 0x8e82, 0x8f82,
+	0x9082, 0x9182, 0x9282, 0x9382, 0x9482, 0x9582, 0x9682, 0x9782,
+	0x9882, 0x9982, 0x9a82, 0x6f81, 0x6281, 0x7081, 0x6081, 0x4081,
+	0x4081, 0x4281, 0x7581, 0x7681, 0x4181, 0x4581, 0x9283, 0x4083,
+	0x4283, 0x4483, 0x4683, 0x4883, 0x8383, 0x8583, 0x8783, 0x6283,
+	0x5b81, 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,
+	0x8a83, 0x8b83, 0x8c83, 0x8d83, 0x8f83, 0x9383, 0x4a81, 0x4b81
+};
+
+static const Uint16Provider kEoB1Ascii2SjisTable2PC98Provider = { ARRAYSIZE(kEoB1Ascii2SjisTable2PC98), kEoB1Ascii2SjisTable2PC98 };
+
+static const uint8 kEoB1FontLookupTablePC98[275] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+	0x08, 0x09, 0x0a, 0x0b, 0x01, 0x01, 0x01, 0x01,
+	0x0c, 0x0d, 0x0e, 0x01, 0x01, 0x01, 0x01, 0x01,
+	0x01, 0x01, 0x01, 0x01, 0x0f, 0x10, 0x01, 0x11,
+	0x01, 0x01, 0x01, 0x12, 0x01, 0x01, 0x01, 0x01,
+	0x01, 0x01, 0x13, 0x14, 0x01, 0x01, 0x15, 0x16,
+	0x17, 0x18, 0x01, 0x01, 0x01, 0x01, 0x19, 0x1a,
+	0x01, 0x01, 0x01, 0x01, 0x1b, 0x1c, 0x01, 0x01,
+	0x01, 0x01, 0x1d, 0x01, 0x1e, 0x1f, 0x01, 0x01,
+	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+	0x20, 0x21, 0x01, 0x01, 0x22, 0x23, 0x24, 0x25,
+	0x26, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+	0x01, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
+	0x2e, 0x2f, 0x30, 0x01, 0x01, 0x01, 0x01, 0x01,
+	0x01, 0x01, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
+	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
+	0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
+	0x47, 0x48, 0x49, 0x4a, 0x01, 0x01, 0x01, 0x01,
+	0x01, 0x01, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
+	0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+	0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
+	0x61, 0x62, 0x63, 0x64, 0x65, 0x01, 0x01, 0x01,
+	0x01, 0x01, 0x01, 0x66, 0x67, 0x68, 0x69, 0x6a,
+	0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
+	0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
+	0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82,
+	0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
+	0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92,
+	0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a,
+	0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2,
+	0xa3, 0xa4, 0x01, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
+	0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1,
+	0xb2, 0xb3, 0xb4, 0x01, 0x01, 0xb5, 0xb6, 0x01,
+	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+	0x01, 0x01, 0x01
+};
+
+static const ByteProvider kEoB1FontLookupTablePC98Provider = { ARRAYSIZE(kEoB1FontLookupTablePC98), kEoB1FontLookupTablePC98 };
+
 static const char *const kEoB1SoundFilesIntroPC98[2] = {
 	"DUM",
 	"EOBOP"
diff --git a/devtools/create_kyradat/resources/eob1_pc98_japanese.h b/devtools/create_kyradat/resources/eob1_pc98_japanese.h
index 0446ebcae0..3cbb42aab6 100644
--- a/devtools/create_kyradat/resources/eob1_pc98_japanese.h
+++ b/devtools/create_kyradat/resources/eob1_pc98_japanese.h
@@ -334,80 +334,6 @@ static const char *const kEoB1ItemNamesPC98Japanese[96] = {
 
 static const StringListProvider kEoB1ItemNamesPC98JapaneseProvider = { ARRAYSIZE(kEoB1ItemNamesPC98Japanese), kEoB1ItemNamesPC98Japanese };
 
-static const uint16 kEoB1Ascii2SjisTable1PC98Japanese[30] = {
-	0x4b83, 0x4d83, 0x4f83, 0x5183, 0x5383, 0x5583, 0x5783, 0x5983,
-	0x5b83, 0x5d83, 0x5f83, 0x6183, 0x6483, 0x6683, 0x6883, 0x0000,
-	0x0000, 0x0000, 0x0000, 0x0000, 0x6f83, 0x7283, 0x7583, 0x7883,
-	0x7b83, 0x7083, 0x7383, 0x7683, 0x7983, 0x7c83
-};
-
-static const Uint16Provider kEoB1Ascii2SjisTable1PC98JapaneseProvider = { ARRAYSIZE(kEoB1Ascii2SjisTable1PC98Japanese), kEoB1Ascii2SjisTable1PC98Japanese };
-
-static const uint16 kEoB1Ascii2SjisTable2PC98Japanese[160] = {
-	0x4081, 0x3981, 0x3a81, 0x9481, 0x9081, 0x9381, 0x9581, 0x8c81,
-	0x6981, 0x6a81, 0x9681, 0x7b81, 0x4381, 0x7c81, 0x4481, 0x5e81,
-	0x4f82, 0x5082, 0x5182, 0x5282, 0x5382, 0x5482, 0x5582, 0x5682,
-	0x5782, 0x5882, 0x4681, 0x4781, 0x7181, 0x8181, 0x7281, 0x4881,
-	0x9781, 0x6082, 0x6182, 0x6282, 0x6382, 0x6482, 0x6582, 0x6682,
-	0x6782, 0x6882, 0x6982, 0x6a82, 0x6b82, 0x6c82, 0x6d82, 0x6e82,
-	0x6f82, 0x7082, 0x7182, 0x7282, 0x7382, 0x7482, 0x7582, 0x7682,
-	0x7782, 0x7882, 0x7982, 0x6d81, 0x8f81, 0x6e81, 0x4f81, 0x5181,
-	0x4081, 0x8182, 0x8282, 0x8382, 0x8482, 0x8582, 0x8682, 0x8782,
-	0x8882, 0x8982, 0x8a82, 0x8b82, 0x8c82, 0x8d82, 0x8e82, 0x8f82,
-	0x9082, 0x9182, 0x9282, 0x9382, 0x9482, 0x9582, 0x9682, 0x9782,
-	0x9882, 0x9982, 0x9a82, 0x6f81, 0x6281, 0x7081, 0x6081, 0x4081,
-	0x4081, 0x4281, 0x7581, 0x7681, 0x4181, 0x4581, 0x9283, 0x4083,
-	0x4283, 0x4483, 0x4683, 0x4883, 0x8383, 0x8583, 0x8783, 0x6283,
-	0x5b81, 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,
-	0x8a83, 0x8b83, 0x8c83, 0x8d83, 0x8f83, 0x9383, 0x4a81, 0x4b81
-};
-
-static const Uint16Provider kEoB1Ascii2SjisTable2PC98JapaneseProvider = { ARRAYSIZE(kEoB1Ascii2SjisTable2PC98Japanese), kEoB1Ascii2SjisTable2PC98Japanese };
-
-static const uint8 kEoB1FontLookupTablePC98Japanese[275] = {
-	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-	0x08, 0x09, 0x0a, 0x0b, 0x01, 0x01, 0x01, 0x01,
-	0x0c, 0x0d, 0x0e, 0x01, 0x01, 0x01, 0x01, 0x01,
-	0x01, 0x01, 0x01, 0x01, 0x0f, 0x10, 0x01, 0x11,
-	0x01, 0x01, 0x01, 0x12, 0x01, 0x01, 0x01, 0x01,
-	0x01, 0x01, 0x13, 0x14, 0x01, 0x01, 0x15, 0x16,
-	0x17, 0x18, 0x01, 0x01, 0x01, 0x01, 0x19, 0x1a,
-	0x01, 0x01, 0x01, 0x01, 0x1b, 0x1c, 0x01, 0x01,
-	0x01, 0x01, 0x1d, 0x01, 0x1e, 0x1f, 0x01, 0x01,
-	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-	0x20, 0x21, 0x01, 0x01, 0x22, 0x23, 0x24, 0x25,
-	0x26, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-	0x01, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
-	0x2e, 0x2f, 0x30, 0x01, 0x01, 0x01, 0x01, 0x01,
-	0x01, 0x01, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
-	0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
-	0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
-	0x47, 0x48, 0x49, 0x4a, 0x01, 0x01, 0x01, 0x01,
-	0x01, 0x01, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
-	0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
-	0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
-	0x61, 0x62, 0x63, 0x64, 0x65, 0x01, 0x01, 0x01,
-	0x01, 0x01, 0x01, 0x66, 0x67, 0x68, 0x69, 0x6a,
-	0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
-	0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
-	0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82,
-	0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
-	0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92,
-	0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a,
-	0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2,
-	0xa3, 0xa4, 0x01, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
-	0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1,
-	0xb2, 0xb3, 0xb4, 0x01, 0x01, 0xb5, 0xb6, 0x01,
-	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
-	0x01, 0x01, 0x01
-};
-
-static const ByteProvider kEoB1FontLookupTablePC98JapaneseProvider = { ARRAYSIZE(kEoB1FontLookupTablePC98Japanese), kEoB1FontLookupTablePC98Japanese };
-
 static const char *const kEoB1ItemSuffixStringsRingsPC98Japanese[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\x83""B",
@@ -1069,3 +995,37 @@ static const char *const kEoB1FinaleStringsPC98Japanese[17] = {
 };
 
 static const StringListProvider kEoB1FinaleStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB1FinaleStringsPC98Japanese), kEoB1FinaleStringsPC98Japanese };
+
+static const byte kEoB1CreditsStringsPC98Japanese[459] = {
+	0x0d, 0x08, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x53, 0x74, 0x61, 0x66, 0x66, 0x0d, 0x0c, 0x50,
+	0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x0d, 0x08, 0x20,
+	0x20, 0x20, 0x20, 0x59, 0x61, 0x73, 0x75, 0x74, 0x61, 0x6b, 0x61, 0x20, 0x55, 0x6b, 0x61, 0x69,
+	0x0d, 0x0c, 0x43, 0x68, 0x69, 0x65, 0x66, 0x20, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d,
+	0x65, 0x72, 0x0d, 0x08, 0x20, 0x20, 0x20, 0x20, 0x4d, 0x61, 0x6b, 0x6f, 0x74, 0x6f, 0x20, 0x49,
+	0x63, 0x68, 0x69, 0x6e, 0x6f, 0x73, 0x65, 0x6b, 0x69, 0x0d, 0x0c, 0x53, 0x75, 0x62, 0x20, 0x50,
+	0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x65, 0x72, 0x0d, 0x08, 0x20, 0x20, 0x20, 0x20, 0x48,
+	0x69, 0x72, 0x6f, 0x79, 0x75, 0x6b, 0x69, 0x20, 0x46, 0x75, 0x6a, 0x69, 0x77, 0x61, 0x72, 0x61,
+	0x0d, 0x20, 0x20, 0x20, 0x20, 0x48, 0x69, 0x64, 0x65, 0x66, 0x75, 0x6d, 0x69, 0x20, 0x4f, 0x68,
+	0x61, 0x72, 0x61, 0x0d, 0x0c, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x20, 0x44, 0x65, 0x73,
+	0x69, 0x67, 0x6e, 0x0d, 0x08, 0x20, 0x20, 0x20, 0x20, 0x41, 0x74, 0x73, 0x75, 0x68, 0x69, 0x72,
+	0x6f, 0x20, 0x47, 0x75, 0x6e, 0x6a, 0x69, 0x0d, 0x20, 0x20, 0x20, 0x20, 0x52, 0x69, 0x65, 0x6b,
+	0x6f, 0x20, 0x59, 0x6f, 0x73, 0x68, 0x69, 0x64, 0x61, 0x0d, 0x0c, 0x4d, 0x75, 0x73, 0x69, 0x63,
+	0x0d, 0x08, 0x20, 0x20, 0x20, 0x20, 0x59, 0x75, 0x7a, 0x6f, 0x20, 0x4b, 0x6f, 0x73, 0x68, 0x69,
+	0x72, 0x6f, 0x0d, 0x20, 0x20, 0x20, 0x20, 0x59, 0x75, 0x6a, 0x69, 0x20, 0x59, 0x61, 0x6d, 0x61,
+	0x64, 0x61, 0x0d, 0x0c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+	0x26, 0x20, 0x53, 0x75, 0x70, 0x65, 0x72, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x0d, 0x08, 0x20,
+	0x20, 0x20, 0x20, 0x48, 0x69, 0x74, 0x6f, 0x73, 0x68, 0x69, 0x20, 0x59, 0x61, 0x73, 0x75, 0x64,
+	0x61, 0x0d, 0x20, 0x20, 0x20, 0x20, 0x4d, 0x69, 0x79, 0x75, 0x6b, 0x69, 0x20, 0x4b, 0x69, 0x79,
+	0x6f, 0x6d, 0x61, 0x74, 0x73, 0x75, 0x0d, 0x20, 0x20, 0x20, 0x20, 0x4d, 0x65, 0x67, 0x75, 0x6d,
+	0x69, 0x20, 0x54, 0x73, 0x75, 0x67, 0x65, 0x0d, 0x0c, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69,
+	0x6f, 0x6e, 0x0d, 0x08, 0x20, 0x20, 0x20, 0x20, 0x59, 0x6f, 0x73, 0x68, 0x69, 0x61, 0x6b, 0x69,
+	0x20, 0x4d, 0x61, 0x74, 0x73, 0x75, 0x6d, 0x6f, 0x74, 0x6f, 0x0d, 0x20, 0x20, 0x20, 0x20, 0x4e,
+	0x6f, 0x62, 0x75, 0x61, 0x6b, 0x69, 0x20, 0x53, 0x75, 0x7a, 0x75, 0x6b, 0x69, 0x0d, 0x0c, 0x44,
+	0x65, 0x62, 0x75, 0x67, 0x0d, 0x08, 0x20, 0x20, 0x20, 0x20, 0x4d, 0x61, 0x73, 0x61, 0x61, 0x6b,
+	0x69, 0x20, 0x46, 0x75, 0x72, 0x75, 0x79, 0x61, 0x0d, 0x20, 0x20, 0x20, 0x20, 0x4b, 0x69, 0x79,
+	0x6f, 0x74, 0x6f, 0x20, 0x59, 0x6f, 0x73, 0x68, 0x69, 0x6d, 0x75, 0x72, 0x61, 0x0d, 0x0c, 0x40,
+	0x0d, 0x08, 0x20, 0x54, 0x68, 0x75, 0x73, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x73, 0x20, 0x61,
+	0x20, 0x6c, 0x65, 0x67, 0x65, 0x6e, 0x64, 0x5b, 0x0d, 0x0c, 0x00
+};
+
+static const ByteProvider kEoB1CreditsStringsPC98JapaneseProvider = { ARRAYSIZE(kEoB1CreditsStringsPC98Japanese), kEoB1CreditsStringsPC98Japanese };
diff --git a/devtools/create_kyradat/resources/eob1_segacd.h b/devtools/create_kyradat/resources/eob1_segacd.h
new file mode 100644
index 0000000000..91837736f8
--- /dev/null
+++ b/devtools/create_kyradat/resources/eob1_segacd.h
@@ -0,0 +1,3065 @@
+static const byte kEoB1ChargenStartLevelsSegaCD[60] = {
+	0x03, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x01,
+	0x03, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x01,
+	0x03, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x01,
+	0x02, 0x02, 0x00, 0x02, 0x02, 0x03, 0x00, 0x02,
+	0x02, 0x02, 0x00, 0x02, 0x01, 0x01, 0x02, 0x03,
+	0x03, 0x02, 0x00, 0x02, 0x02, 0x03, 0x00, 0x02,
+	0x01, 0x02, 0x01, 0x03, 0x02, 0x02, 0x00, 0x02,
+	0x02, 0x02, 0x00, 0x02
+};
+
+static const ByteProvider kEoB1ChargenStartLevelsSegaCDProvider = { ARRAYSIZE(kEoB1ChargenStartLevelsSegaCD), kEoB1ChargenStartLevelsSegaCD };
+
+static const byte kEoB1ChargenClassMinStatsSegaCD[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 kEoB1ChargenClassMinStatsSegaCDProvider = { ARRAYSIZE(kEoB1ChargenClassMinStatsSegaCD), kEoB1ChargenClassMinStatsSegaCD };
+
+static const byte kEoB1ChargenRaceMinStatsSegaCD[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 kEoB1ChargenRaceMinStatsSegaCDProvider = { ARRAYSIZE(kEoB1ChargenRaceMinStatsSegaCD), kEoB1ChargenRaceMinStatsSegaCD };
+
+static const uint16 kEoB1ChargenRaceMaxStatsSegaCD[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 kEoB1ChargenRaceMaxStatsSegaCDProvider = { ARRAYSIZE(kEoB1ChargenRaceMaxStatsSegaCD), kEoB1ChargenRaceMaxStatsSegaCD };
+
+static const byte kEoB1SaveThrowTable1SegaCD[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 kEoB1SaveThrowTable1SegaCDProvider = { ARRAYSIZE(kEoB1SaveThrowTable1SegaCD), kEoB1SaveThrowTable1SegaCD };
+
+static const byte kEoB1SaveThrowTable2SegaCD[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 kEoB1SaveThrowTable2SegaCDProvider = { ARRAYSIZE(kEoB1SaveThrowTable2SegaCD), kEoB1SaveThrowTable2SegaCD };
+
+static const byte kEoB1SaveThrowTable3SegaCD[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 kEoB1SaveThrowTable3SegaCDProvider = { ARRAYSIZE(kEoB1SaveThrowTable3SegaCD), kEoB1SaveThrowTable3SegaCD };
+
+static const byte kEoB1SaveThrowTable4SegaCD[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 kEoB1SaveThrowTable4SegaCDProvider = { ARRAYSIZE(kEoB1SaveThrowTable4SegaCD), kEoB1SaveThrowTable4SegaCD };
+
+static const byte kEoB1SaveThrwLvlIndexSegaCD[6] = {
+	0x11, 0x15, 0x13, 0x15, 0x11, 0x11
+};
+
+static const ByteProvider kEoB1SaveThrwLvlIndexSegaCDProvider = { ARRAYSIZE(kEoB1SaveThrwLvlIndexSegaCD), kEoB1SaveThrwLvlIndexSegaCD };
+
+static const byte kEoB1SaveThrwModDivSegaCD[6] = {
+	0x02, 0x05, 0x03, 0x04, 0x02, 0x02
+};
+
+static const ByteProvider kEoB1SaveThrwModDivSegaCDProvider = { ARRAYSIZE(kEoB1SaveThrwModDivSegaCD), kEoB1SaveThrwModDivSegaCD };
+
+static const byte kEoB1SaveThrwModExtSegaCD[6] = {
+	0x0A, 0x05, 0x07, 0x06, 0x0A, 0x0A
+};
+
+static const ByteProvider kEoB1SaveThrwModExtSegaCDProvider = { ARRAYSIZE(kEoB1SaveThrwModExtSegaCD), kEoB1SaveThrwModExtSegaCD };
+
+static const uint16 kEoB1IntroWdDsXSegaCD[14] = {
+	0x0120, 0x0130, 0x00b2, 0x00bc, 0x00ae, 0x0125, 0x0101,
+	0x00e9, 0x00ab, 0x00cc, 0x00f0, 0x010c, 0x00e0, 0x00ba
+};
+
+static const Uint16Provider kEoB1IntroWdDsXSegaCDProvider = { ARRAYSIZE(kEoB1IntroWdDsXSegaCD), kEoB1IntroWdDsXSegaCD };
+
+static const byte kEoB1IntroWdDsYSegaCD[14] = {
+	0x28, 0x38, 0x26, 0x1d, 0x34, 0x50, 0x50,
+	0x0d, 0x0d, 0x2a, 0x66, 0x67, 0x1c, 0x44
+};
+
+static const ByteProvider kEoB1IntroWdDsYSegaCDProvider = { ARRAYSIZE(kEoB1IntroWdDsYSegaCD), kEoB1IntroWdDsYSegaCD };
+
+static const byte kEoB1DefaultPartyStatsSegaCD[48] = {
+	0x00, 0x00, 0x01, 0x00, 0x12, 0x0e, 0x0f, 0x12,
+	0x0e, 0x10, 0x06, 0x1e, 0x09, 0x07, 0x04, 0x26,
+	0x10, 0x10, 0x0e, 0x10, 0x0f, 0x0a, 0x08, 0x10,
+	0x02, 0x03, 0x06, 0x06, 0x0d, 0x11, 0x0d, 0x0e,
+	0x0f, 0x0c, 0x0a, 0x0f, 0x05, 0x04, 0x01, 0x2b,
+	0x0e, 0x12, 0x11, 0x0d, 0x0e, 0x10, 0x0a, 0x16
+};
+
+static const ByteProvider kEoB1DefaultPartyStatsSegaCDProvider = { ARRAYSIZE(kEoB1DefaultPartyStatsSegaCD), kEoB1DefaultPartyStatsSegaCD };
+
+static const byte kEoB1CreditsTileGridSegaCD[64] = {
+	0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x0c, 0xcc, 0xcc,
+	0x0c, 0xcc, 0xc0, 0xcc, 0xc0, 0xcc, 0x0c, 0xc0,
+	0xcc, 0xc0, 0xc0, 0xc0, 0x0c, 0x0c, 0x0c, 0x0c,
+	0xc0, 0x00, 0xc0, 0xc0, 0x00, 0xc0, 0x0c, 0x00,
+	0x00, 0xc0, 0x0c, 0x00, 0xc0, 0x00, 0xc0, 0xc0,
+	0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xc0, 0xc0, 0xc0,
+	0xc0, 0xcc, 0x0c, 0xc0, 0x0c, 0xcc, 0xc0, 0xcc,
+	0xcc, 0x0c, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc
+};
+
+static const ByteProvider kEoB1CreditsTileGridSegaCDProvider = { ARRAYSIZE(kEoB1CreditsTileGridSegaCD), kEoB1CreditsTileGridSegaCD };
+
+static const byte kEoB1DoorShapeDefsSegaCD[120] = {
+	0x0a, 0x09, 0x00, 0x00,	0x07, 0x06, 0x01, 0x02,
+	0x04, 0x04, 0x00, 0x03,	0x0a, 0x09, 0x00, 0x00,
+	0x07, 0x06, 0x01, 0x02,	0x04, 0x04, 0x00, 0x03,
+	0x06, 0x09, 0x00, 0x00,	0x04, 0x06, 0x00, 0x00,
+	0x03, 0x04, 0x00, 0x00,	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,	0x00, 0x00, 0x00, 0x00,
+	0x0c, 0x09, 0x00, 0x00,	0x07, 0x05, 0x02, 0x00,
+	0x04, 0x04, 0x00, 0x00,	0x0a, 0x02, 0x04, 0x00,
+	0x04, 0x02, 0x04, 0x02,	0x02, 0x02, 0x04, 0x04,
+	0x09, 0x07, 0x02, 0x00,	0x07, 0x05, 0x02, 0x00,
+	0x05, 0x03, 0x01, 0x00,	0x09, 0x04, 0x00, 0x00,
+	0x07, 0x03, 0x00, 0x00,	0x05, 0x02, 0x00, 0x00,
+	0x09, 0x0a, 0x00, 0x00,	0x07, 0x06, 0x00, 0x00,
+	0x04, 0x04, 0x00, 0x00,	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,	0x00, 0x00, 0x00, 0x00
+};
+
+static const ByteProvider kEoB1DoorShapeDefsSegaCDProvider = { ARRAYSIZE(kEoB1DoorShapeDefsSegaCD), kEoB1DoorShapeDefsSegaCD };
+
+static const byte kEoB1DoorSwitchShapeDefsSegaCD[60] = {
+	0x01, 0x01, 0x00, 0x01,	0x01, 0x01, 0x00, 0x05,
+	0x01, 0x01, 0x00, 0x07,	0x01, 0x02, 0x00, 0x05,
+	0x01, 0x01, 0x00, 0x02,	0x01, 0x01, 0x00, 0x04,
+	0x01, 0x02, 0x00, 0x02,	0x01, 0x02, 0x00, 0x06,
+	0x00, 0x00, 0x00, 0x00,	0x01, 0x02, 0x00, 0x05,
+	0x01, 0x01, 0x00, 0x02,	0x01, 0x01, 0x00, 0x04,
+	0x01, 0x02, 0x00, 0x06,	0x01, 0x01, 0x00, 0x04,
+	0x00, 0x00, 0x00, 0x00
+};
+
+static const ByteProvider kEoB1DoorSwitchShapeDefsSegaCDProvider = { ARRAYSIZE(kEoB1DoorSwitchShapeDefsSegaCD), kEoB1DoorSwitchShapeDefsSegaCD };
+
+static const byte kEoB1DoorSwitchCoordsSegaCD[48] = {
+	0x82, 0x24, 0x74, 0x27, 0x00, 0x00, 0x82, 0x24,
+	0x74, 0x27, 0x00, 0x00, 0x5b, 0x36, 0x5a, 0x33,
+	0x5a, 0x2d, 0x88, 0x26, 0x74, 0x29, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x24,
+	0x73, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x80, 0x2b, 0x74, 0x2b, 0x00, 0x00
+};
+
+static const ByteProvider kEoB1DoorSwitchCoordsSegaCDProvider = { ARRAYSIZE(kEoB1DoorSwitchCoordsSegaCD), kEoB1DoorSwitchCoordsSegaCD };
+
+static const byte kEoB1MonsterPropertiesSegaCD[616] = {
+	0x07, 0x12, 0xff, 0x01, 0x01, 0x04, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x02,
+	0x4b, 0x02, 0xff, 0x00, 0x07, 0x11, 0x02, 0x01,
+	0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02,
+	0x00, 0x78, 0x01, 0x38, 0x39, 0x00, 0xff, 0x00,
+	0x05, 0x11, 0x01, 0x01, 0x01, 0x06, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0c, 0x00,
+	0x00, 0x00, 0x00, 0x04, 0x00, 0x41, 0x00, 0x42,
+	0x43, 0x00, 0x02, 0x00, 0x08, 0x0f, 0x02, 0x01,
+	0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
+	0x00, 0x41, 0x00, 0x49, 0x4a, 0x00, 0x03, 0x00,
+	0x04, 0x10, 0x02, 0x02, 0x01, 0x04, 0x01, 0x01,
+	0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xaf, 0x02, 0x36,
+	0x37, 0x04, 0xff, 0x14, 0x05, 0x11, 0x03, 0x01,
+	0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x03,
+	0x00, 0x41, 0x00, 0x2f, 0x30, 0x00, 0xff, 0x00,
+	0x06, 0x13, 0x02, 0x01, 0x01, 0x08, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00,
+	0x00, 0x00, 0x00, 0x02, 0x02, 0x8a, 0x02, 0x31,
+	0x26, 0x00, 0xff, 0x00, 0x03, 0x0c, 0x07, 0x01,
+	0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+	0x02, 0xda, 0x00, 0x2d, 0x2e, 0x00, 0xff, 0x00,
+	0x05, 0x07, 0x0a, 0x01, 0x01, 0x08, 0x06, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00,
+	0x00, 0x00, 0x00, 0x04, 0x05, 0xdc, 0x00, 0x40,
+	0x41, 0x00, 0x07, 0x00, 0x04, 0x0f, 0x06, 0x02,
+	0x01, 0x08, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03,
+	0x03, 0xcf, 0x01, 0x27, 0x28, 0x00, 0xff, 0x00,
+	0x05, 0x0d, 0x06, 0x03, 0x01, 0x08, 0x00, 0x01,
+	0x04, 0x00, 0x01, 0x04, 0x00, 0x00, 0x28, 0x00,
+	0x00, 0x00, 0x00, 0x03, 0x05, 0x78, 0x02, 0x25,
+	0x24, 0x00, 0xff, 0x00, 0x04, 0x0d, 0x05, 0x02,
+	0x01, 0x04, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+	0x04, 0xe2, 0x00, 0x3e, 0x3f, 0x00, 0xff, 0x00,
+	0x04, 0x0f, 0x04, 0x01, 0x01, 0x08, 0x04, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
+	0x00, 0x00, 0x00, 0x01, 0x02, 0x8a, 0x00, 0x2b,
+	0x2c, 0x00, 0xff, 0x3c, 0xff, 0x07, 0x0a, 0x01,
+	0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02,
+	0x27, 0x10, 0x02, 0x44, 0x45, 0x00, 0xff, 0x64,
+	0xff, 0x07, 0x09, 0x01, 0x03, 0x04, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00,
+	0x00, 0x00, 0x00, 0x03, 0x07, 0xd0, 0x00, 0x1f,
+	0xff, 0x08, 0xff, 0x00, 0x03, 0x0d, 0x07, 0x01,
+	0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x02,
+	0x13, 0x88, 0x02, 0x29, 0x2a, 0x02, 0xff, 0x3c,
+	0x05, 0x0d, 0x07, 0x01, 0x01, 0x06, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
+	0x00, 0x00, 0x00, 0x02, 0x13, 0x88, 0x00, 0x34,
+	0x35, 0x02, 0xff, 0x00, 0x05, 0x0b, 0x08, 0x01,
+	0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x1f, 0x40, 0x00, 0x3a, 0x3b, 0xff, 0xff, 0x5a,
+	0x02, 0x0f, 0x05, 0x01, 0x01, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00,
+	0x00, 0x00, 0x00, 0x02, 0x01, 0x0e, 0x02, 0x3c,
+	0x3d, 0x00, 0xff, 0x00, 0xfe, 0x0d, 0x08, 0x01,
+	0x06, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02,
+	0x0f, 0xa0, 0x02, 0x48, 0x47, 0x00, 0xff, 0x32,
+	0x04, 0x0d, 0x07, 0x01, 0x01, 0x0a, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+	0x00, 0x00, 0x00, 0x02, 0x03, 0xcf, 0x01, 0x33,
+	0x32, 0x00, 0xff, 0x28, 0x00, 0x05, 0x23, 0x01,
+	0x01, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x36, 0xb0, 0x02, 0xff, 0xff, 0xff, 0xff, 0x64
+};
+
+static const ByteProvider kEoB1MonsterPropertiesSegaCDProvider = { ARRAYSIZE(kEoB1MonsterPropertiesSegaCD), kEoB1MonsterPropertiesSegaCD };
+
+static const byte kEoB1EnemyMageSpellListSegaCD[10] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x05,
+	0x06, 0x00
+};
+
+static const ByteProvider kEoB1EnemyMageSpellListSegaCDProvider = { ARRAYSIZE(kEoB1EnemyMageSpellListSegaCD), kEoB1EnemyMageSpellListSegaCD };
+
+static const byte kEoB1EnemyMageSfxSegaCD[10] = {
+	0x00, 0x55, 0x55, 0x55, 0x55, 0x62, 0x62, 0x1F,
+	0x62, 0x00
+};
+
+static const ByteProvider kEoB1EnemyMageSfxSegaCDProvider = { ARRAYSIZE(kEoB1EnemyMageSfxSegaCD), kEoB1EnemyMageSfxSegaCD };
+
+static const byte kEoB1BeholderSpellListSegaCD[4] = {
+	0x02, 0x0A, 0x0B, 0x00
+};
+
+static const ByteProvider kEoB1BeholderSpellListSegaCDProvider = { ARRAYSIZE(kEoB1BeholderSpellListSegaCD), kEoB1BeholderSpellListSegaCD };
+
+static const byte kEoB1BeholderSfxSegaCD[4] = {
+	0x62, 0x53, 0x40, 0x00
+};
+
+static const ByteProvider kEoB1BeholderSfxSegaCDProvider = { ARRAYSIZE(kEoB1BeholderSfxSegaCD), kEoB1BeholderSfxSegaCD };
+
+static const char *const kEoB1EnchantedStringSegaCD[1] = {
+	"%s +%d"
+};
+
+static const StringListProvider kEoB1EnchantedStringSegaCDProvider = { ARRAYSIZE(kEoB1EnchantedStringSegaCD), kEoB1EnchantedStringSegaCD };
+
+static const byte kEoB1SpellLevelsMageSegaCD[26] = {
+	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+	0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03,
+	0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05,
+	0x05, 0x00
+};
+
+static const ByteProvider kEoB1SpellLevelsMageSegaCDProvider = { ARRAYSIZE(kEoB1SpellLevelsMageSegaCD), kEoB1SpellLevelsMageSegaCD };
+
+static const byte kEoB1SpellLevelsClericSegaCD[25] = {
+	0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02,
+	0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04,
+	0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05,
+	0x00
+};
+
+static const ByteProvider kEoB1SpellLevelsClericSegaCDProvider = { ARRAYSIZE(kEoB1SpellLevelsClericSegaCD), kEoB1SpellLevelsClericSegaCD };
+
+static const byte kEoB1NumSpellsClericSegaCD[50] = {
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x03,
+	0x02, 0x00, 0x00, 0x00, 0x03, 0x03, 0x01, 0x00,
+	0x00, 0x03, 0x03, 0x02, 0x00, 0x00, 0x03, 0x03,
+	0x02, 0x01, 0x00, 0x03, 0x03, 0x03, 0x02, 0x00,
+	0x04, 0x04, 0x03, 0x02, 0x01, 0x04, 0x04, 0x03,
+	0x03, 0x02
+};
+
+static const ByteProvider kEoB1NumSpellsClericSegaCDProvider = { ARRAYSIZE(kEoB1NumSpellsClericSegaCD), kEoB1NumSpellsClericSegaCD };
+
+static const byte kEoB1NumSpellsWisAdjSegaCD[40] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02,
+	0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00,
+	0x00, 0x02, 0x02, 0x01, 0x00, 0x00, 0x02, 0x02,
+	0x01, 0x01, 0x00, 0x03, 0x02, 0x01, 0x02, 0x00
+};
+
+static const ByteProvider kEoB1NumSpellsWisAdjSegaCDProvider = { ARRAYSIZE(kEoB1NumSpellsWisAdjSegaCD), kEoB1NumSpellsWisAdjSegaCD };
+
+static const byte kEoB1NumSpellsPalSegaCD[60] = {
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
+	0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+	0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x02,
+	0x02, 0x00, 0x00, 0x00
+};
+
+static const ByteProvider kEoB1NumSpellsPalSegaCDProvider = { ARRAYSIZE(kEoB1NumSpellsPalSegaCD), kEoB1NumSpellsPalSegaCD };
+
+static const byte kEoB1NumSpellsMageSegaCD[55] = {
+	0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x03,
+	0x02, 0x00, 0x00, 0x00, 0x04, 0x02, 0x01, 0x00,
+	0x00, 0x04, 0x02, 0x02, 0x00, 0x00, 0x04, 0x03,
+	0x02, 0x01, 0x00, 0x04, 0x03, 0x03, 0x02, 0x00,
+	0x04, 0x03, 0x03, 0x02, 0x01, 0x04, 0x04, 0x03,
+	0x02, 0x02, 0x04, 0x04, 0x04, 0x03, 0x03
+};
+
+static const ByteProvider kEoB1NumSpellsMageSegaCDProvider = { ARRAYSIZE(kEoB1NumSpellsMageSegaCD), kEoB1NumSpellsMageSegaCD };
+
+static const uint32 kEoB1ExperienceTable0SegaCD[12] = {
+	0x00000000, 0x000007D0, 0x00000FA0, 0x00001F40, 0x00003E80, 0x00007D00, 0x0000FA00, 0x0001E848,
+	0x0003D090, 0x0007A120, 0x000B71B0, 0xFFFFFFFF
+};
+
+static const Uint32Provider kEoB1ExperienceTable0SegaCDProvider = { ARRAYSIZE(kEoB1ExperienceTable0SegaCD), kEoB1ExperienceTable0SegaCD };
+
+static const uint32 kEoB1ExperienceTable1SegaCD[12] = {
+	0x00000000, 0x000009C4, 0x00001388, 0x00002710, 0x00004E20, 0x00009C40, 0x0000EA60, 0x00015F90,
+	0x00020F58, 0x0003D090, 0x0005B8D8, 0xFFFFFFFF
+};
+
+static const Uint32Provider kEoB1ExperienceTable1SegaCDProvider = { ARRAYSIZE(kEoB1ExperienceTable1SegaCD), kEoB1ExperienceTable1SegaCD };
+
+static const uint32 kEoB1ExperienceTable2SegaCD[11] = {
+	0x00000000, 0x000005DC, 0x00000BB8, 0x00001770, 0x000032C8, 0x00006B6C, 0x0000D6D8, 0x0001ADB0,
+	0x00036EE8, 0x0006DDD0, 0xFFFFFFFF
+};
+
+static const Uint32Provider kEoB1ExperienceTable2SegaCDProvider = { ARRAYSIZE(kEoB1ExperienceTable2SegaCD), kEoB1ExperienceTable2SegaCD };
+
+static const uint32 kEoB1ExperienceTable3SegaCD[12] = {
+	0x00000000, 0x000004E2, 0x000009C4, 0x00001388, 0x00002710, 0x00004E20, 0x00009C40, 0x00011170,
+	0x0001ADB0, 0x00027100, 0x00035B60, 0xFFFFFFFF
+};
+
+static const Uint32Provider kEoB1ExperienceTable3SegaCDProvider = { ARRAYSIZE(kEoB1ExperienceTable3SegaCD), kEoB1ExperienceTable3SegaCD };
+
+static const uint32 kEoB1ExperienceTable4SegaCD[12] = {
+	0x00000000, 0x000008CA, 0x00001194, 0x00002328, 0x00004650, 0x00008CA0, 0x000124F8, 0x000249F0,
+	0x000493E0, 0x000927C0, 0x000DBBA0, 0xFFFFFFFF
+};
+
+static const Uint32Provider kEoB1ExperienceTable4SegaCDProvider = { ARRAYSIZE(kEoB1ExperienceTable4SegaCD), kEoB1ExperienceTable4SegaCD };
+
+static const byte kEoB1ExpObjectTblIndexSegaCD[9] = {
+	0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01,
+	0x00
+};
+
+static const ByteProvider kEoB1ExpObjectTblIndexSegaCDProvider = { ARRAYSIZE(kEoB1ExpObjectTblIndexSegaCD), kEoB1ExpObjectTblIndexSegaCD };
+
+static const byte kEoB1ExpObjectShpStartSegaCD[4] = {
+	0x07, 0x0C, 0x0F, 0x12
+};
+
+static const ByteProvider kEoB1ExpObjectShpStartSegaCDProvider = { ARRAYSIZE(kEoB1ExpObjectShpStartSegaCD), kEoB1ExpObjectShpStartSegaCD };
+
+static const byte kEoB1ExpObjectTbl1SegaCD[13] = {
+	0x3f, 0x35, 0x3f, 0x35, 0x31, 0x35, 0x31, 0x38,
+	0x31, 0x38, 0x31, 0x38, 0x00
+};
+
+static const ByteProvider kEoB1ExpObjectTbl1SegaCDProvider = { ARRAYSIZE(kEoB1ExpObjectTbl1SegaCD), kEoB1ExpObjectTbl1SegaCD };
+
+static const byte kEoB1ExpObjectTbl2SegaCD[10] = {
+	0x3f, 0x39, 0x3f, 0x39, 0x3a, 0x3b, 0x3b, 0x3b,
+	0x3b, 0x00
+};
+
+static const ByteProvider kEoB1ExpObjectTbl2SegaCDProvider = { ARRAYSIZE(kEoB1ExpObjectTbl2SegaCD), kEoB1ExpObjectTbl2SegaCD };
+
+static const byte kEoB1ExpObjectTbl3SegaCD[11] = {
+	0x35, 0x33, 0x35, 0x33, 0x34, 0x33, 0x34, 0x3b,
+	0x34, 0x3b, 0x00
+};
+
+static const ByteProvider kEoB1ExpObjectTbl3SegaCDProvider = { ARRAYSIZE(kEoB1ExpObjectTbl3SegaCD), kEoB1ExpObjectTbl3SegaCD };
+
+static const byte kEoB1ExpObjectYSegaCD[4] = {
+	0x77, 0x67, 0x4F, 0x3F
+};
+
+static const ByteProvider kEoB1ExpObjectYSegaCDProvider = { ARRAYSIZE(kEoB1ExpObjectYSegaCD), kEoB1ExpObjectYSegaCD };
+
+static const byte kEoB1SparkDefStepsSegaCD[8] = {
+	0x40, 0x90, 0xE4, 0xB9, 0x6E, 0x1B, 0x06, 0x01
+};
+
+static const ByteProvider kEoB1SparkDefStepsSegaCDProvider = { ARRAYSIZE(kEoB1SparkDefStepsSegaCD), kEoB1SparkDefStepsSegaCD };
+
+static const byte kEoB1SparkDefSubStepsSegaCD[4] = {
+	0xC0, 0x30, 0x0C, 0x03
+};
+
+static const ByteProvider kEoB1SparkDefSubStepsSegaCDProvider = { ARRAYSIZE(kEoB1SparkDefSubStepsSegaCD), kEoB1SparkDefSubStepsSegaCD };
+
+static const byte kEoB1SparkDefShiftSegaCD[4] = {
+	0x06, 0x04, 0x02, 0x00
+};
+
+static const ByteProvider kEoB1SparkDefShiftSegaCDProvider = { ARRAYSIZE(kEoB1SparkDefShiftSegaCD), kEoB1SparkDefShiftSegaCD };
+
+static const byte kEoB1SparkDefAddSegaCD[8] = {
+	0x08, 0x04, 0x18, 0x0C, 0x0C, 0x10, 0x18, 0x00
+};
+
+static const ByteProvider kEoB1SparkDefAddSegaCDProvider = { ARRAYSIZE(kEoB1SparkDefAddSegaCD), kEoB1SparkDefAddSegaCD };
+
+static const byte kEoB1SparkDefXSegaCD[6] = {
+	0x17, 0x20, 0x17, 0x20, 0x17, 0x20
+};
+
+static const ByteProvider kEoB1SparkDefXSegaCDProvider = { ARRAYSIZE(kEoB1SparkDefXSegaCD), kEoB1SparkDefXSegaCD };
+
+static const byte kEoB1SparkDefYSegaCD[6] = {
+	0x10, 0x10, 0x48, 0x48, 0x80, 0x80
+};
+
+static const ByteProvider kEoB1SparkDefYSegaCDProvider = { ARRAYSIZE(kEoB1SparkDefYSegaCD), kEoB1SparkDefYSegaCD };
+
+static const uint32 kEoB1SparkOfFlags1SegaCD[11] = {
+	0x40000000, 0x95000000, 0xEA550000, 0xBFAA5400, 0x6AFFA954, 0x15AAFEA9, 0x0055ABFE, 0x000056AB,
+	0x00000156, 0x00000001, 0x00000000
+};
+
+static const Uint32Provider kEoB1SparkOfFlags1SegaCDProvider = { ARRAYSIZE(kEoB1SparkOfFlags1SegaCD), kEoB1SparkOfFlags1SegaCD };
+
+static const uint32 kEoB1SparkOfFlags2SegaCD[16] = {
+	0xC0000000, 0x30000000, 0x0C000000, 0x03000000, 0x00C00000, 0x00300000, 0x000C0000, 0x00030000,
+	0x0000C000, 0x00003000, 0x00000C00, 0x00000300, 0x000000C0, 0x00000030, 0x0000000C, 0x00000003
+};
+
+static const Uint32Provider kEoB1SparkOfFlags2SegaCDProvider = { ARRAYSIZE(kEoB1SparkOfFlags2SegaCD), kEoB1SparkOfFlags2SegaCD };
+
+static const byte kEoB1SparkOfShiftSegaCD[16] = {
+	0x1E, 0x1C, 0x1A, 0x18, 0x16, 0x14, 0x12, 0x10,
+	0x0E, 0x0C, 0x0A, 0x08, 0x06, 0x04, 0x02, 0x00
+};
+
+static const ByteProvider kEoB1SparkOfShiftSegaCDProvider = { ARRAYSIZE(kEoB1SparkOfShiftSegaCD), kEoB1SparkOfShiftSegaCD };
+
+static const byte kEoB1SparkOfXSegaCD[16] = {
+	0x50, 0x70, 0x30, 0x68, 0x20, 0x60, 0x38, 0x78,
+	0x80, 0x48, 0x58, 0x28, 0x60, 0x40, 0x70, 0x48
+};
+
+static const ByteProvider kEoB1SparkOfXSegaCDProvider = { ARRAYSIZE(kEoB1SparkOfXSegaCD), kEoB1SparkOfXSegaCD };
+
+static const byte kEoB1SparkOfYSegaCD[16] = {
+	0x31, 0x2B, 0x48, 0x17, 0x16, 0x48, 0x35, 0x1B,
+	0x43, 0x2E, 0x24, 0x28, 0x38, 0x1C, 0x16, 0x44
+};
+
+static const ByteProvider kEoB1SparkOfYSegaCDProvider = { ARRAYSIZE(kEoB1SparkOfYSegaCD), kEoB1SparkOfYSegaCD };
+
+static const byte kEoB1SpellPropertiesSegaCD[1060] = {
+	0x00, 0x04, 0x7d, 0x52, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x7d, 0x54,
+	0x01, 0x00, 0x00, 0x02, 0x07, 0x0c, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x02, 0x07, 0xa4, 0x5c, 0x00,
+	0x00, 0x04, 0x7d, 0x5a, 0x00, 0x00, 0x00, 0x02,
+	0x07, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x57, 0x01, 0x00, 0x04, 0x7d, 0x68,
+	0x08, 0x00, 0x00, 0x02, 0x08, 0xd8, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x02, 0x09, 0x24, 0x5f, 0x00,
+	0x00, 0x04, 0x7d, 0x76, 0x00, 0x00, 0x00, 0x02,
+	0x09, 0x4a, 0x00, 0x02, 0x09, 0x84, 0x00, 0x00,
+	0x00, 0x00, 0x55, 0x01, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x04, 0x7d, 0x84, 0x04, 0x00, 0x00, 0x02,
+	0x09, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+	0x0a, 0x2c, 0x5c, 0x00, 0x00, 0x04, 0x7d, 0x8c,
+	0x02, 0x00, 0x00, 0x02, 0x0a, 0x5a, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x02, 0x0b, 0x1e, 0x58, 0x00,
+	0x00, 0x04, 0x7d, 0x9c, 0x01, 0x00, 0x00, 0x02,
+	0x0b, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+	0x0b, 0x9e, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x04, 0x7d, 0xaa, 0x00, 0x00, 0x00, 0x02,
+	0x0b, 0xbe, 0x00, 0x02, 0x0b, 0xfa, 0x00, 0x00,
+	0x00, 0x00, 0x60, 0x01, 0x00, 0x04, 0x7d, 0xbc,
+	0x00, 0x00, 0x00, 0x02, 0x0c, 0x3e, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+	0x00, 0x04, 0x7d, 0xcc, 0x01, 0x00, 0x00, 0x02,
+	0x0c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x61, 0x00, 0x00, 0x04, 0x7d, 0xda,
+	0x00, 0x00, 0x00, 0x02, 0x0d, 0x3c, 0x00, 0x02,
+	0x0d, 0x78, 0x00, 0x00, 0x00, 0x00, 0x62, 0x01,
+	0x00, 0x04, 0x7d, 0xe4, 0x00, 0x00, 0x00, 0x02,
+	0x0d, 0xb2, 0x00, 0x02, 0x0d, 0xee, 0x00, 0x00,
+	0x00, 0x00, 0x63, 0x01, 0x00, 0x04, 0x7d, 0xf0,
+	0x08, 0x00, 0x00, 0x02, 0x0e, 0x28, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x02, 0x0e, 0x9e, 0x64, 0x00,
+	0x00, 0x04, 0x7d, 0xf6, 0x00, 0x00, 0x00, 0x02,
+	0x0e, 0xaa, 0x00, 0x02, 0x0e, 0xe6, 0x00, 0x00,
+	0x00, 0x00, 0x65, 0x01, 0x00, 0x04, 0x7e, 0x02,
+	0x08, 0x00, 0x00, 0x02, 0x0f, 0x92, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x02, 0x0b, 0x9e, 0x5e, 0x00,
+	0x00, 0x04, 0x7e, 0x1a, 0x00, 0x00, 0x00, 0x02,
+	0x0f, 0xb4, 0x00, 0x02, 0x0f, 0xf0, 0x00, 0x00,
+	0x00, 0x00, 0x1f, 0x01, 0x00, 0x04, 0x7e, 0x2a,
+	0x02, 0x00, 0x00, 0x02, 0x10, 0x2a, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x02, 0x10, 0xd6, 0x66, 0x00,
+	0x00, 0x04, 0x7e, 0x3a, 0x00, 0x00, 0x00, 0x02,
+	0x10, 0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x67, 0x01, 0x00, 0x04, 0x7e, 0x40,
+	0x00, 0x00, 0x00, 0x02, 0x11, 0xc4, 0x00, 0x02,
+	0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x01,
+	0x00, 0x04, 0x7e, 0x4a, 0x01, 0x00, 0x00, 0x02,
+	0x12, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x5d, 0x00, 0x00, 0x04, 0x7e, 0x54,
+	0x00, 0x00, 0x00, 0x02, 0x13, 0x08, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+	0x00, 0x04, 0x7e, 0x5e, 0x00, 0x00, 0x00, 0x02,
+	0x13, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x76, 0x01, 0x00, 0x04, 0x7e, 0x6c,
+	0x00, 0x00, 0x00, 0x02, 0x14, 0x0e, 0x00, 0x02,
+	0x14, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x65, 0x01,
+	0x00, 0x04, 0x7e, 0x7a, 0x08, 0x00, 0x00, 0x02,
+	0x14, 0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+	0x15, 0x44, 0x5b, 0x00, 0x00, 0x04, 0x7e, 0x80,
+	0x01, 0x00, 0x00, 0x02, 0x15, 0x54, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
+	0x00, 0x04, 0x7e, 0x92, 0x00, 0x00, 0x00, 0x02,
+	0x15, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x6b, 0x01, 0x00, 0x04, 0x7e, 0xa6,
+	0x08, 0x00, 0x00, 0x02, 0x08, 0xd8, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x02, 0x09, 0x24, 0x5f, 0x00,
+	0x00, 0x04, 0x7e, 0xb4, 0x01, 0x00, 0x00, 0x02,
+	0x15, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+	0x15, 0xde, 0x6e, 0x00, 0x00, 0x04, 0x7e, 0xca,
+	0x01, 0x00, 0x00, 0x02, 0x15, 0xfc, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x02, 0x16, 0xaa, 0x5b, 0x00,
+	0x00, 0x04, 0x7e, 0xce, 0x02, 0x00, 0x00, 0x02,
+	0x17, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+	0x0b, 0x1e, 0x63, 0x00, 0x00, 0x04, 0x7e, 0xda,
+	0x00, 0x00, 0x00, 0x02, 0x0e, 0xaa, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x01,
+	0x00, 0x04, 0x7e, 0xe6, 0x01, 0x00, 0x00, 0x02,
+	0x16, 0xde, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+	0x17, 0x68, 0x6f, 0x00, 0x00, 0x04, 0x7e, 0xf2,
+	0x08, 0x00, 0x00, 0x02, 0x18, 0x98, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00,
+	0x00, 0x04, 0x7e, 0xfe, 0x01, 0x00, 0x00, 0x02,
+	0x0c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x61, 0x00, 0x00, 0x04, 0x7f, 0x0c,
+	0x04, 0x00, 0x00, 0x02, 0x18, 0xc0, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x02, 0x19, 0x18, 0x71, 0x00,
+	0x00, 0x04, 0x7f, 0x1e, 0x08, 0x00, 0x00, 0x02,
+	0x19, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+	0x19, 0x80, 0x5b, 0x00, 0x00, 0x04, 0x7f, 0x26,
+	0x08, 0x00, 0x00, 0x02, 0x19, 0x90, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00,
+	0x00, 0x04, 0x7f, 0x38, 0x01, 0x00, 0x00, 0x02,
+	0x19, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x69, 0x00, 0x00, 0x04, 0x7f, 0x4c,
+	0x00, 0x00, 0x00, 0x02, 0x1a, 0x14, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x01,
+	0x00, 0x04, 0x7f, 0x62, 0x01, 0x00, 0x00, 0x02,
+	0x1a, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x73, 0x00, 0x00, 0x04, 0x7f, 0x74,
+	0x08, 0x00, 0x00, 0x02, 0x1a, 0x5c, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x02, 0x1a, 0xb8, 0x6e, 0x00,
+	0x00, 0x04, 0x7f, 0x94, 0x04, 0x00, 0x00, 0x02,
+	0x1a, 0xde, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x74, 0x00, 0x00, 0x04, 0x7f, 0xae,
+	0x01, 0x00, 0x00, 0x02, 0x1a, 0xe6, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x00,
+	0x00, 0x04, 0x7f, 0xc4, 0x00, 0x00, 0x00, 0x02,
+	0x1b, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x6c, 0x01, 0x00, 0x04, 0x7f, 0xda,
+	0x00, 0x00, 0x00, 0x02, 0x1b, 0x2e, 0x00, 0x02,
+	0x1b, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x62, 0x01,
+	0x00, 0x04, 0x7f, 0xe8, 0x01, 0x00, 0x00, 0x02,
+	0x1b, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x75, 0x00, 0x00, 0x04, 0x7f, 0xf4,
+	0x01, 0x00, 0x00, 0x02, 0x1d, 0x98, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
+	0x00, 0x04, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x02, 0x1b, 0xe4, 0x00, 0x00,
+	0x00, 0x00, 0x75, 0x00, 0x00, 0x04, 0x80, 0x04,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+	0x1c, 0x08, 0x00, 0x00, 0x00, 0x00, 0x75, 0x00,
+	0x00, 0x04, 0x80, 0x06, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x02, 0x1c, 0xe8, 0x00, 0x00,
+	0x00, 0x00, 0x75, 0x00
+};
+
+static const ByteProvider kEoB1SpellPropertiesSegaCDProvider = { ARRAYSIZE(kEoB1SpellPropertiesSegaCD), kEoB1SpellPropertiesSegaCD };
+
+static const byte kEoB1MagicFlightPropsSegaCD[48] = {
+	0x04, 0xff, 0x41, 0x09, 0x0a, 0xff, 0x04, 0x05,
+	0x0d, 0xff, 0x7a, 0x06, 0x0e, 0xff, 0x0c, 0x05,
+	0x10, 0xff, 0x4a, 0x0a, 0x12, 0x06, 0x38, 0x07,
+	0x15, 0x06, 0x7a, 0x08, 0x19, 0xff, 0x4a, 0x0a,
+	0x2f, 0xff, 0x4b, 0x06, 0x32, 0xff, 0x09, 0x07,
+	0x33, 0xff, 0x09, 0x0a, 0x34, 0xff, 0x09, 0x0a
+};
+
+static const ByteProvider kEoB1MagicFlightPropsSegaCDProvider = { ARRAYSIZE(kEoB1MagicFlightPropsSegaCD), kEoB1MagicFlightPropsSegaCD };
+
+static const byte kEoB1TurnUndeadEffectSegaCD[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 kEoB1TurnUndeadEffectSegaCDProvider = { ARRAYSIZE(kEoB1TurnUndeadEffectSegaCD), kEoB1TurnUndeadEffectSegaCD };
+
+static const byte kEoB1BurningHandsDestSegaCD[8] = {
+	0x02, 0x03, 0x00, 0x02, 0x00, 0x01, 0x01, 0x03
+};
+
+static const ByteProvider kEoB1BurningHandsDestSegaCDProvider = { ARRAYSIZE(kEoB1BurningHandsDestSegaCD), kEoB1BurningHandsDestSegaCD };
+
+static const byte kEoB1ConeOfColdDest1SegaCD[7] = {
+	0xE0, 0xC0, 0xC1, 0xBF, 0xA0, 0x9F, 0xA1
+};
+
+static const ByteProvider kEoB1ConeOfColdDest1SegaCDProvider = { ARRAYSIZE(kEoB1ConeOfColdDest1SegaCD), kEoB1ConeOfColdDest1SegaCD };
+
+static const byte kEoB1ConeOfColdDest2SegaCD[7] = {
+	0x01, 0x02, 0xE2, 0x22, 0x03, 0xE3, 0x23
+};
+
+static const ByteProvider kEoB1ConeOfColdDest2SegaCDProvider = { ARRAYSIZE(kEoB1ConeOfColdDest2SegaCD), kEoB1ConeOfColdDest2SegaCD };
+
+static const byte kEoB1ConeOfColdDest3SegaCD[7] = {
+	0x20, 0x40, 0x3F, 0x41, 0x60, 0x5F, 0x61
+};
+
+static const ByteProvider kEoB1ConeOfColdDest3SegaCDProvider = { ARRAYSIZE(kEoB1ConeOfColdDest3SegaCD), kEoB1ConeOfColdDest3SegaCD };
+
+static const byte kEoB1ConeOfColdDest4SegaCD[7] = {
+	0xFF, 0xFE, 0x1E, 0xDE, 0xFD, 0x1D, 0xDD
+};
+
+static const ByteProvider kEoB1ConeOfColdDest4SegaCDProvider = { ARRAYSIZE(kEoB1ConeOfColdDest4SegaCD), kEoB1ConeOfColdDest4SegaCD };
+
+static const byte kEoB1ConeOfColdGfxTblSegaCD[8] = {
+	0x3F, 0x39, 0x3F, 0x39, 0x3A, 0x3B, 0x3A, 0x00
+};
+
+static const ByteProvider kEoB1ConeOfColdGfxTblSegaCDProvider = { ARRAYSIZE(kEoB1ConeOfColdGfxTblSegaCD), kEoB1ConeOfColdGfxTblSegaCD };
+
+static const byte kEoB1DscDoorShapeIndexSegaCD[32] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03,
+	0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03
+};
+
+static const ByteProvider kEoB1DscDoorShapeIndexSegaCDProvider = { ARRAYSIZE(kEoB1DscDoorShapeIndexSegaCD), kEoB1DscDoorShapeIndexSegaCD };
+
+static const byte kEoB1WllFlagPresetSegaCD[25] = {
+	0x07, 0x00, 0x40, 0xa8, 0x88, 0x88, 0x88, 0x9f,
+	0xa8, 0x88, 0x88, 0x88, 0x9f, 0xaa, 0x8a, 0x8a,
+	0x8a, 0x9f, 0xaa, 0x8a, 0x8a, 0x8a, 0x9f, 0x03,
+	0x03
+};
+
+static const ByteProvider kEoB1WllFlagPresetSegaCDProvider = { ARRAYSIZE(kEoB1WllFlagPresetSegaCD), kEoB1WllFlagPresetSegaCD };
+
+static const uint16 kEoB1DscShapeCoordsSegaCD[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 kEoB1DscShapeCoordsSegaCDProvider = { ARRAYSIZE(kEoB1DscShapeCoordsSegaCD), kEoB1DscShapeCoordsSegaCD };
+
+static const byte kEoB1DscDoorScaleOffsSegaCD[32] = {
+	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, 0x1E, 0x1F
+};
+
+static const ByteProvider kEoB1DscDoorScaleOffsSegaCDProvider = { ARRAYSIZE(kEoB1DscDoorScaleOffsSegaCD), kEoB1DscDoorScaleOffsSegaCD };
+
+static const byte kEoB1DscDoorScaleMult1SegaCD[4] = {
+	0x08, 0x0C, 0x12, 0x00
+};
+
+static const ByteProvider kEoB1DscDoorScaleMult1SegaCDProvider = { ARRAYSIZE(kEoB1DscDoorScaleMult1SegaCD), kEoB1DscDoorScaleMult1SegaCD };
+
+static const byte kEoB1DscDoorScaleMult2SegaCD[4] = {
+	0x00, 0x02, 0x04, 0x00
+};
+
+static const ByteProvider kEoB1DscDoorScaleMult2SegaCDProvider = { ARRAYSIZE(kEoB1DscDoorScaleMult2SegaCD), kEoB1DscDoorScaleMult2SegaCD };
+
+static const byte kEoB1DscDoorScaleMult3SegaCD[4] = {
+	0x04, 0x06, 0x09, 0x00
+};
+
+static const ByteProvider kEoB1DscDoorScaleMult3SegaCDProvider = { ARRAYSIZE(kEoB1DscDoorScaleMult3SegaCD), kEoB1DscDoorScaleMult3SegaCD };
+
+static const byte kEoB1DscDoorScaleMult4SegaCD[4] = {
+	0x00, 0x02, 0x04, 0x00
+};
+
+static const ByteProvider kEoB1DscDoorScaleMult4SegaCDProvider = { ARRAYSIZE(kEoB1DscDoorScaleMult4SegaCD), kEoB1DscDoorScaleMult4SegaCD };
+
+static const byte kEoB1DscDoorScaleMult5SegaCD[4] = {
+	0x07, 0x0B, 0x0E, 0x00
+};
+
+static const ByteProvider kEoB1DscDoorScaleMult5SegaCDProvider = { ARRAYSIZE(kEoB1DscDoorScaleMult5SegaCD), kEoB1DscDoorScaleMult5SegaCD };
+
+static const byte kEoB1DscDoorScaleMult6SegaCD[4] = {
+	0x00, 0x02, 0x04, 0x00
+};
+
+static const ByteProvider kEoB1DscDoorScaleMult6SegaCDProvider = { ARRAYSIZE(kEoB1DscDoorScaleMult6SegaCD), kEoB1DscDoorScaleMult6SegaCD };
+
+static const byte kEoB1DscDoorXESegaCD[32] = {
+	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, 0x1E, 0x1F
+};
+
+static const ByteProvider kEoB1DscDoorXESegaCDProvider = { ARRAYSIZE(kEoB1DscDoorXESegaCD), kEoB1DscDoorXESegaCD };
+
+static const byte kEoB1DscDoorY1SegaCD[4] = {
+	0x3A, 0x47, 0x56, 0x00
+};
+
+static const ByteProvider kEoB1DscDoorY1SegaCDProvider = { ARRAYSIZE(kEoB1DscDoorY1SegaCD), kEoB1DscDoorY1SegaCD };
+
+static const byte kEoB1DscDoorY3SegaCD[4] = {
+	0x22, 0x20, 0x18, 0x00
+};
+
+static const ByteProvider kEoB1DscDoorY3SegaCDProvider = { ARRAYSIZE(kEoB1DscDoorY3SegaCD), kEoB1DscDoorY3SegaCD };
+
+static const byte kEoB1DscDoorY4SegaCD[4] = {
+	0x1E, 0x18, 0x10, 0x00
+};
+
+static const ByteProvider kEoB1DscDoorY4SegaCDProvider = { ARRAYSIZE(kEoB1DscDoorY4SegaCD), kEoB1DscDoorY4SegaCD };
+
+static const byte kEoB1DscDoorY5SegaCD[4] = {
+	0x30, 0x34, 0x3B, 0x00
+};
+
+static const ByteProvider kEoB1DscDoorY5SegaCDProvider = { ARRAYSIZE(kEoB1DscDoorY5SegaCD), kEoB1DscDoorY5SegaCD };
+
+static const byte kEoB1DscDoorY6SegaCD[4] = {
+	0x3e, 0x47, 0x5f, 0x00
+};
+
+static const ByteProvider kEoB1DscDoorY6SegaCDProvider = { ARRAYSIZE(kEoB1DscDoorY6SegaCD), kEoB1DscDoorY6SegaCD };
+
+static const byte kEoB1DscDoorY7SegaCD[4] = {
+	0x3B, 0x47, 0x59, 0x00
+};
+
+static const ByteProvider kEoB1DscDoorY7SegaCDProvider = { ARRAYSIZE(kEoB1DscDoorY7SegaCD), kEoB1DscDoorY7SegaCD };
+
+static const uint16 kEoB1DscDoorCoordsExtSegaCD[36] = {
+	0x0000, 0x0000, 0xffe8, 0x0010, 0x0020, 0x0040, 0x0048, 0x0068,
+	0x0070, 0x0090, 0x00a0, 0x00c8, 0x0000, 0x0000, 0x0000, 0x0000,
+	0xffe8, 0x0030, 0x0038, 0x0078, 0x0080, 0x00c8, 0x0000, 0x0000,
+	0xffa8, 0x0020, 0x0028, 0x0088, 0x0090, 0x0108, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000
+};
+
+static const Uint16Provider kEoB1DscDoorCoordsExtSegaCDProvider = { ARRAYSIZE(kEoB1DscDoorCoordsExtSegaCD), kEoB1DscDoorCoordsExtSegaCD };
+
+static const byte kEoB1DscDoorFrameY1SegaCD[32] = {
+	0x20, 0x18, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x20, 0x20, 0x18, 0x00, 0x1E, 0x18, 0x10, 0x00,
+	0x3A, 0x48, 0x60, 0x78, 0x3A, 0x46, 0x56, 0x00,
+	0x78, 0x78, 0x78, 0x78, 0x1F, 0x18, 0x0F, 0x00
+};
+
+static const ByteProvider kEoB1DscDoorFrameY1SegaCDProvider = { ARRAYSIZE(kEoB1DscDoorFrameY1SegaCD), kEoB1DscDoorFrameY1SegaCD };
+
+static const byte kEoB1DscDoorFrameY2SegaCD[32] = {
+	0x20, 0x18, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x20, 0x20, 0x18, 0x00, 0x1E, 0x18, 0x10, 0x00,
+	0x3A, 0x48, 0x60, 0x78, 0x3A, 0x46, 0x56, 0x00,
+	0x78, 0x78, 0x78, 0x78, 0x1F, 0x18, 0x0F, 0x00
+};
+
+static const ByteProvider kEoB1DscDoorFrameY2SegaCDProvider = { ARRAYSIZE(kEoB1DscDoorFrameY2SegaCD), kEoB1DscDoorFrameY2SegaCD };
+
+static const byte kEoB1DscDoorFrameIndex1SegaCD[12] = {
+	0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x08, 0x08,
+	0x08, 0x0C, 0x0C, 0x1C
+};
+
+static const ByteProvider kEoB1DscDoorFrameIndex1SegaCDProvider = { ARRAYSIZE(kEoB1DscDoorFrameIndex1SegaCD), kEoB1DscDoorFrameIndex1SegaCD };
+
+static const byte kEoB1DscDoorFrameIndex2SegaCD[12] = {
+	0x10, 0x10, 0x10, 0x18, 0x18, 0x18, 0x10, 0x10,
+	0x10, 0x14, 0x14, 0x18
+};
+
+static const ByteProvider kEoB1DscDoorFrameIndex2SegaCDProvider = { ARRAYSIZE(kEoB1DscDoorFrameIndex2SegaCD), kEoB1DscDoorFrameIndex2SegaCD };
+
+static const byte kEoB1DscItemPosIndexSegaCD[16] = {
+	0x00, 0x01, 0x02, 0x03, 0x02, 0x00, 0x03, 0x01,
+	0x03, 0x02, 0x01, 0x00, 0x01, 0x03, 0x00, 0x02
+};
+
+static const ByteProvider kEoB1DscItemPosIndexSegaCDProvider = { ARRAYSIZE(kEoB1DscItemPosIndexSegaCD), kEoB1DscItemPosIndexSegaCD };
+
+static const uint16 kEoB1DscItemShpXSegaCD[18] = {
+	0xffc8, 0xfff8, 0x0028, 0x0058, 0x0088, 0x00b8, 0x00e8, 0xffb8,
+	0x0008, 0x0058, 0x00a8, 0x00f8, 0xffd8, 0x0058, 0x00d8, 0xffa8,
+	0x0058, 0x0108
+	/*0xffc8, 0xfff8, 0x0028, 0x0058, 0x0088, 0x00b8, 0x00e8, 0xffb8,
+	0x001c, 0x0058, 0x0092, 0x00f8, 0xffd8, 0x0058, 0x00d8, 0xffa8,
+	0x0058, 0x0108*/
+};
+
+static const Uint16Provider kEoB1DscItemShpXSegaCDProvider = { ARRAYSIZE(kEoB1DscItemShpXSegaCD), kEoB1DscItemShpXSegaCD };
+
+static const byte kEoB1DscItemScaleIndexSegaCD[18] = {
+	0xFF, 0xFF, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02,
+	0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xFF, 0xFF,
+	0x25, 0x00
+};
+
+static const ByteProvider kEoB1DscItemScaleIndexSegaCDProvider = { ARRAYSIZE(kEoB1DscItemScaleIndexSegaCD), kEoB1DscItemScaleIndexSegaCD };
+
+static const byte kEoB1DscItemTileIndexSegaCD[18] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0x09, 0xFF, 0x0B,
+	0x0C, 0x0D
+};
+
+static const ByteProvider kEoB1DscItemTileIndexSegaCDProvider = { ARRAYSIZE(kEoB1DscItemTileIndexSegaCD), kEoB1DscItemTileIndexSegaCD };
+
+static const byte kEoB1DscItemShapeMapSegaCD[90] = {
+	0x00, 0x00, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04,
+	0x05, 0x06, 0x07, 0x07, 0x12, 0x06, 0x0f, 0x12,
+	0x14, 0x17, 0x13, 0x11, 0x15, 0x0d, 0x09, 0x09,
+	0x16, 0x1f, 0x0c, 0x23, 0x0c, 0x0c, 0x0c, 0x0b,
+	0x0a, 0x1c, 0x1c, 0x1a, 0x1b, 0x21, 0x1d, 0x1d,
+	0x22, 0x22, 0x22, 0x00, 0x08, 0x00, 0x25, 0x18,
+	0x16, 0x17, 0x16, 0x17, 0x16, 0x17, 0x19, 0x23,
+	0x10, 0x1e, 0x24, 0x18, 0x1c, 0x20, 0x12, 0x21,
+	0x1e, 0x23, 0x1c, 0x00, 0x20, 0x11, 0x11, 0x0d,
+	0x1f, 0x09, 0x09, 0x15, 0x15, 0x0f, 0x05, 0x0c,
+	0x1e, 0x1e, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1b,
+	0x1b, 0x00
+};
+
+static const ByteProvider kEoB1DscItemShapeMapSegaCDProvider = { ARRAYSIZE(kEoB1DscItemShapeMapSegaCD), kEoB1DscItemShapeMapSegaCD };
+
+static const byte kEoB1DscTelptrShpCoordsSegaCD[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 kEoB1DscTelptrShpCoordsSegaCDProvider = { ARRAYSIZE(kEoB1DscTelptrShpCoordsSegaCD), kEoB1DscTelptrShpCoordsSegaCD };
+
+static const byte kEoB1PortalSeqDataSegaCD[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 kEoB1PortalSeqDataSegaCDProvider = { ARRAYSIZE(kEoB1PortalSeqDataSegaCD), kEoB1PortalSeqDataSegaCD };
+
+static const byte kEoB1DscMonsterFrmOffsTbl1SegaCD[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 kEoB1DscMonsterFrmOffsTbl1SegaCDProvider = { ARRAYSIZE(kEoB1DscMonsterFrmOffsTbl1SegaCD), kEoB1DscMonsterFrmOffsTbl1SegaCD };
+
+static const byte kEoB1DscMonsterFrmOffsTbl2SegaCD[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 kEoB1DscMonsterFrmOffsTbl2SegaCDProvider = { ARRAYSIZE(kEoB1DscMonsterFrmOffsTbl2SegaCD), kEoB1DscMonsterFrmOffsTbl2SegaCD };
+
+static const uint16 kEoB1InvSlotXSegaCD[28] = {
+	0x00E0, 0x0110, 0x00B8, 0x00C8, 0x00B8, 0x00C8, 0x00B8, 0x00C8,
+	0x00B8, 0x00C8, 0x00B8, 0x00C8, 0x00B8, 0x00C8, 0x00B8, 0x00C8,
+	0x00E0, 0x00D8, 0x00D8, 0x0110, 0x0118, 0x0110, 0x0128, 0x0128,
+	0x0128, 0x00E0, 0x00E8, 0x0128
+};
+
+static const Uint16Provider kEoB1InvSlotXSegaCDProvider = { ARRAYSIZE(kEoB1InvSlotXSegaCD), kEoB1InvSlotXSegaCD };
+
+static const byte kEoB1InvSlotYSegaCD[28] = {
+	0x78, 0x70, 0x30, 0x30, 0x40, 0x40, 0x50, 0x50,
+	0x60, 0x60, 0x70, 0x70, 0x80, 0x80, 0x90, 0x90,
+	0x38, 0x50, 0x60, 0x38, 0x50, 0x88, 0x60, 0x70,
+	0x80, 0x90, 0x90, 0x38
+};
+
+static const ByteProvider kEoB1InvSlotYSegaCDProvider = { ARRAYSIZE(kEoB1InvSlotYSegaCD), kEoB1InvSlotYSegaCD };
+
+static const uint16 kEoB1SlotValidationFlagsSegaCD[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 kEoB1SlotValidationFlagsSegaCDProvider = { ARRAYSIZE(kEoB1SlotValidationFlagsSegaCD), kEoB1SlotValidationFlagsSegaCD };
+
+static const byte kEoB1ProjectileWeaponTypesSegaCD[8] = {
+	0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x12
+};
+
+static const ByteProvider kEoB1ProjectileWeaponTypesSegaCDProvider = { ARRAYSIZE(kEoB1ProjectileWeaponTypesSegaCD), kEoB1ProjectileWeaponTypesSegaCD };
+
+static const byte kEoB1WandTypesSegaCD[7] = {
+	0x00, 0x12, 0x18, 0x28, 0x0D, 0xFF, 0x04
+};
+
+static const ByteProvider kEoB1WandTypesSegaCDProvider = { ARRAYSIZE(kEoB1WandTypesSegaCD), kEoB1WandTypesSegaCD };
+
+static const byte kEoB1DrawObjPosIndexSegaCD[20] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x02, 0x00, 0x03,
+	0x01, 0x04, 0x02, 0x03, 0x00, 0x01, 0x04, 0x01,
+	0x03, 0x00, 0x02, 0x04
+};
+
+static const ByteProvider kEoB1DrawObjPosIndexSegaCDProvider = { ARRAYSIZE(kEoB1DrawObjPosIndexSegaCD), kEoB1DrawObjPosIndexSegaCD };
+
+static const byte kEoB1FlightObjFlipIndexSegaCD[16] = {
+	0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01,
+	0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00
+};
+
+static const ByteProvider kEoB1FlightObjFlipIndexSegaCDProvider = { ARRAYSIZE(kEoB1FlightObjFlipIndexSegaCD), kEoB1FlightObjFlipIndexSegaCD };
+
+static const byte kEoB1FlightObjShpMapSegaCD[90] = {
+	0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01,
+	0xff, 0xff, 0x03, 0xff, 0x05, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0x04, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0x02, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff
+};
+
+static const ByteProvider kEoB1FlightObjShpMapSegaCDProvider = { ARRAYSIZE(kEoB1FlightObjShpMapSegaCD), kEoB1FlightObjShpMapSegaCD };
+
+static const byte kEoB1FlightObjSclIndexSegaCD[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 kEoB1FlightObjSclIndexSegaCDProvider = { ARRAYSIZE(kEoB1FlightObjSclIndexSegaCD), kEoB1FlightObjSclIndexSegaCD };
+
+static const byte kEoB1DscShapeIndexSegaCD[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 kEoB1DscShapeIndexSegaCDProvider = { ARRAYSIZE(kEoB1DscShapeIndexSegaCD), kEoB1DscShapeIndexSegaCD };
+
+static const uint16 kEoB1DscXSegaCD[18] = {
+	0xFF70, 0xFFA0, 0xFFD0, 0x0000, 0x0030, 0x0060, 0x0090, 0xFF60,
+	0xFFB0, 0x0000, 0x0050, 0x00A0, 0xFF80, 0x0000, 0x0080, 0x0000,
+	0x0000, 0x0000
+};
+
+static const Uint16Provider kEoB1DscXSegaCDProvider = { ARRAYSIZE(kEoB1DscXSegaCD), kEoB1DscXSegaCD };
+
+static const byte kEoB1DscTileIndexSegaCD[18] = {
+	0x00, 0x06, 0x01, 0x05, 0x02, 0x04, 0x03, 0x07,
+	0x0B, 0x08, 0x0A, 0x09, 0x0C, 0x0E, 0x0D, 0x0F,
+	0x11, 0x10
+};
+
+static const ByteProvider kEoB1DscTileIndexSegaCDProvider = { ARRAYSIZE(kEoB1DscTileIndexSegaCD), kEoB1DscTileIndexSegaCD };
+
+static const byte kEoB1DscDimData1SegaCD[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 kEoB1DscDimData1SegaCDProvider = { ARRAYSIZE(kEoB1DscDimData1SegaCD), kEoB1DscDimData1SegaCD };
+
+static const byte kEoB1DscDimData2SegaCD[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 kEoB1DscDimData2SegaCDProvider = { ARRAYSIZE(kEoB1DscDimData2SegaCD), kEoB1DscDimData2SegaCD };
+
+static const byte kEoB1DscBlockMapSegaCD[12] = {
+	0x02, 0x03, 0x00, 0x01, 0x01, 0x02, 0x03, 0x00,
+	0x03, 0x00, 0x01, 0x02
+};
+
+static const ByteProvider kEoB1DscBlockMapSegaCDProvider = { ARRAYSIZE(kEoB1DscBlockMapSegaCD), kEoB1DscBlockMapSegaCD };
+
+static const byte kEoB1DscDimMapSegaCD[18] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+	0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03,
+	0x03, 0x03
+};
+
+static const ByteProvider kEoB1DscDimMapSegaCDProvider = { ARRAYSIZE(kEoB1DscDimMapSegaCD), kEoB1DscDimMapSegaCD };
+
+static const byte kEoB1DscBlockIndexSegaCD[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 kEoB1DscBlockIndexSegaCDProvider = { ARRAYSIZE(kEoB1DscBlockIndexSegaCD), kEoB1DscBlockIndexSegaCD };
+
+static const byte kEoB1ClassModifierFlagsSegaCD[15] = {
+	0x01, 0x01, 0x05, 0x02, 0x04, 0x08, 0x05, 0x09,
+	0x03, 0x0B, 0x0A, 0x0C, 0x07, 0x05, 0x06
+	//0x0a, 0x0a, 0x0a, 0x04, 0x08, 0x06, 0x0a, 0x0a,
+	//0x0a, 0x0a, 0x04, 0x08, 0x0a, 0x0a, 0x08
+};
+
+static const ByteProvider kEoB1ClassModifierFlagsSegaCDProvider = { ARRAYSIZE(kEoB1ClassModifierFlagsSegaCD), kEoB1ClassModifierFlagsSegaCD };
+
+static const byte kEoB1MonsterStepTable01SegaCD[4] = {
+	0xE0, 0x01, 0x20, 0xFF
+};
+
+static const ByteProvider kEoB1MonsterStepTable01SegaCDProvider = { ARRAYSIZE(kEoB1MonsterStepTable01SegaCD), kEoB1MonsterStepTable01SegaCD };
+
+static const byte kEoB1MonsterStepTable2SegaCD[8] = {
+	0x07, 0xfa, 0x05, 0xfc, 0x03, 0xfe, 0x01, 0x00
+};
+
+static const ByteProvider kEoB1MonsterStepTable2SegaCDProvider = { ARRAYSIZE(kEoB1MonsterStepTable2SegaCD), kEoB1MonsterStepTable2SegaCD };
+
+static const byte kEoB1MonsterStepTable3SegaCD[8] = {
+	0xf9, 0x06, 0xfb, 0x04, 0xfd, 0x02, 0xff, 0x00
+};
+
+static const ByteProvider kEoB1MonsterStepTable3SegaCDProvider = { ARRAYSIZE(kEoB1MonsterStepTable3SegaCD), kEoB1MonsterStepTable3SegaCD };
+
+static const byte kEoB1MonsterCloseAttPosTable1SegaCD[4] = {
+	0x00, 0x01, 0x03, 0x02
+};
+
+static const ByteProvider kEoB1MonsterCloseAttPosTable1SegaCDProvider = { ARRAYSIZE(kEoB1MonsterCloseAttPosTable1SegaCD), kEoB1MonsterCloseAttPosTable1SegaCD };
+
+static const byte kEoB1MonsterCloseAttPosTable21SegaCD[8] = {
+	0x00, 0x01, 0x02, 0x03, 0x00, 0x02, 0x01, 0x03
+};
+
+static const ByteProvider kEoB1MonsterCloseAttPosTable21SegaCDProvider = { ARRAYSIZE(kEoB1MonsterCloseAttPosTable21SegaCD), kEoB1MonsterCloseAttPosTable21SegaCD };
+
+static const byte kEoB1MonsterCloseAttChkTable1SegaCD[16] = {
+	0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
+	0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00
+};
+
+static const ByteProvider kEoB1MonsterCloseAttChkTable1SegaCDProvider = { ARRAYSIZE(kEoB1MonsterCloseAttChkTable1SegaCD), kEoB1MonsterCloseAttChkTable1SegaCD };
+
+static const byte kEoB1MonsterCloseAttChkTable2SegaCD[16] = {
+	0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01,
+	0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00
+};
+
+static const ByteProvider kEoB1MonsterCloseAttChkTable2SegaCDProvider = { ARRAYSIZE(kEoB1MonsterCloseAttChkTable2SegaCD), kEoB1MonsterCloseAttChkTable2SegaCD };
+
+static const byte kEoB1MonsterCloseAttDstTable1SegaCD[16] = {
+	0x02, 0x03, 0x00, 0x01, 0x01, 0x02, 0x03, 0x00,
+	0x00, 0x01, 0x02, 0x03, 0x03, 0x00, 0x01, 0x02
+};
+
+static const ByteProvider kEoB1MonsterCloseAttDstTable1SegaCDProvider = { ARRAYSIZE(kEoB1MonsterCloseAttDstTable1SegaCD), kEoB1MonsterCloseAttDstTable1SegaCD };
+
+static const byte kEoB1MonsterCloseAttDstTable2SegaCD[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 kEoB1MonsterCloseAttDstTable2SegaCDProvider = { ARRAYSIZE(kEoB1MonsterCloseAttDstTable2SegaCD), kEoB1MonsterCloseAttDstTable2SegaCD };
+
+static const byte kEoB1MonsterProximityTableSegaCD[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 kEoB1MonsterProximityTableSegaCDProvider = { ARRAYSIZE(kEoB1MonsterProximityTableSegaCD), kEoB1MonsterProximityTableSegaCD };
+
+static const byte kEoB1FindBlockMonstersTableSegaCD[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 kEoB1FindBlockMonstersTableSegaCDProvider = { ARRAYSIZE(kEoB1FindBlockMonstersTableSegaCD), kEoB1FindBlockMonstersTableSegaCD };
+
+static const byte kEoB1MonsterDirChangeTableSegaCD[16] = {
+	0xff, 0x06, 0x02, 0xff, 0x04, 0x05, 0x03, 0xff,
+	0x00, 0x07, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
+
+static const ByteProvider kEoB1MonsterDirChangeTableSegaCDProvider = { ARRAYSIZE(kEoB1MonsterDirChangeTableSegaCD), kEoB1MonsterDirChangeTableSegaCD };
+
+static const byte kEoB1EncodeMonsterDefs00SegaCD[28] = {
+	0x28, 0x30, 0x28, 0x30, 0x28, 0x30, 0x28, 0x30, 0x28, 0x30, 0x30, 0x30, 0x28, 0x30, 0x28, 0x30, 0x30, 0x40, 0x30, 0x40, 0x28, 0x30, 0x28, 0x38, 0x20, 0x10, 0x20, 0x10
+};
+
+static const ByteProvider kEoB1EncodeMonsterDefs00SegaCDProvider = { ARRAYSIZE(kEoB1EncodeMonsterDefs00SegaCD), kEoB1EncodeMonsterDefs00SegaCD };
+
+static const byte kEoB1EncodeMonsterDefs01SegaCD[16] = {
+	0x60, 0x20, 0x60, 0x20, 0x40, 0x30, 0x40, 0x30, 0x38, 0x28, 0x38, 0x28, 0x38, 0x38, 0x38, 0x38
+};
+
+static const ByteProvider kEoB1EncodeMonsterDefs01SegaCDProvider = { ARRAYSIZE(kEoB1EncodeMonsterDefs01SegaCD), kEoB1EncodeMonsterDefs01SegaCD };
+
+static const byte kEoB1EncodeMonsterDefs02SegaCD[18] = {
+	0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x40, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60
+};
+
+static const ByteProvider kEoB1EncodeMonsterDefs02SegaCDProvider = { ARRAYSIZE(kEoB1EncodeMonsterDefs02SegaCD), kEoB1EncodeMonsterDefs02SegaCD };
+
+static const byte kEoB1EncodeMonsterDefs03SegaCD[16] = {
+	0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x30, 0x60, 0x30, 0x60, 0x38, 0x60, 0x38, 0x60
+};
+
+static const ByteProvider kEoB1EncodeMonsterDefs03SegaCDProvider = { ARRAYSIZE(kEoB1EncodeMonsterDefs03SegaCD), kEoB1EncodeMonsterDefs03SegaCD };
+
+static const byte kEoB1EncodeMonsterDefs04SegaCD[24] = {
+	0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60
+};
+
+static const ByteProvider kEoB1EncodeMonsterDefs04SegaCDProvider = { ARRAYSIZE(kEoB1EncodeMonsterDefs04SegaCD), kEoB1EncodeMonsterDefs04SegaCD };
+
+static const byte kEoB1EncodeMonsterDefs05SegaCD[16] = {
+	0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60
+};
+
+static const ByteProvider kEoB1EncodeMonsterDefs05SegaCDProvider = { ARRAYSIZE(kEoB1EncodeMonsterDefs05SegaCD), kEoB1EncodeMonsterDefs05SegaCD };
+
+static const byte kEoB1EncodeMonsterDefs06SegaCD[16] = {
+	0x50, 0x48, 0x50, 0x48, 0x50, 0x48, 0x50, 0x48, 0x50, 0x48, 0x50, 0x48, 0x50, 0x48, 0x50, 0x48
+};
+
+static const ByteProvider kEoB1EncodeMonsterDefs06SegaCDProvider = { ARRAYSIZE(kEoB1EncodeMonsterDefs06SegaCD), kEoB1EncodeMonsterDefs06SegaCD };
+
+static const byte kEoB1EncodeMonsterDefs07SegaCD[14] = {
+	0x38, 0x40, 0x38, 0x40, 0x38, 0x40, 0x38, 0x40, 0x38, 0x40, 0x38, 0x40, 0x38, 0x40
+};
+
+static const ByteProvider kEoB1EncodeMonsterDefs07SegaCDProvider = { ARRAYSIZE(kEoB1EncodeMonsterDefs07SegaCD), kEoB1EncodeMonsterDefs07SegaCD };
+
+static const byte kEoB1EncodeMonsterDefs08SegaCD[14] = {
+	0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60
+};
+
+static const ByteProvider kEoB1EncodeMonsterDefs08SegaCDProvider = { ARRAYSIZE(kEoB1EncodeMonsterDefs08SegaCD), kEoB1EncodeMonsterDefs08SegaCD };
+
+static const byte kEoB1EncodeMonsterDefs09SegaCD[26] = {
+	0x68, 0x38, 0x68, 0x38, 0x68, 0x38, 0x68, 0x38, 0x38, 0x48, 0x38, 0x48, 0x38, 0x48, 0x38, 0x48, 0x38, 0x50, 0x38, 0x50, 0x38, 0x50, 0x38, 0x50, 0x38, 0x50
+};
+
+static const ByteProvider kEoB1EncodeMonsterDefs09SegaCDProvider = { ARRAYSIZE(kEoB1EncodeMonsterDefs09SegaCD), kEoB1EncodeMonsterDefs09SegaCD };
+
+static const byte kEoB1EncodeMonsterDefs10SegaCD[16] = {
+	0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58
+};
+
+static const ByteProvider kEoB1EncodeMonsterDefs10SegaCDProvider = { ARRAYSIZE(kEoB1EncodeMonsterDefs10SegaCD), kEoB1EncodeMonsterDefs10SegaCD };
+
+static const byte kEoB1EncodeMonsterDefs11SegaCD[24] = {
+	0x38, 0x58, 0x38, 0x58, 0x38, 0x58, 0x38, 0x58, 0x38, 0x58, 0x28, 0x58, 0x30, 0x58, 0x28, 0x58, 0x28, 0x58, 0x28, 0x10, 0x40, 0x10, 0x10, 0x10
+};
+
+static const ByteProvider kEoB1EncodeMonsterDefs11SegaCDProvider = { ARRAYSIZE(kEoB1EncodeMonsterDefs11SegaCD), kEoB1EncodeMonsterDefs11SegaCD };
+
+static const byte kEoB1EncodeMonsterDefs12SegaCD[20] = {
+	0x60, 0x50, 0x60, 0x50, 0x60, 0x50, 0x60, 0x50, 0x60, 0x50, 0x28, 0x48, 0x38, 0x48, 0x30, 0x48, 0x28, 0x50, 0x28, 0x50
+};
+
+static const ByteProvider kEoB1EncodeMonsterDefs12SegaCDProvider = { ARRAYSIZE(kEoB1EncodeMonsterDefs12SegaCD), kEoB1EncodeMonsterDefs12SegaCD };
+
+static const byte kEoB1EncodeMonsterDefs13SegaCD[14] = {
+	0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60
+};
+
+static const ByteProvider kEoB1EncodeMonsterDefs13SegaCDProvider = { ARRAYSIZE(kEoB1EncodeMonsterDefs13SegaCD), kEoB1EncodeMonsterDefs13SegaCD };
+
+static const byte kEoB1EncodeMonsterDefs14SegaCD[14] = {
+	0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60
+};
+
+static const ByteProvider kEoB1EncodeMonsterDefs14SegaCDProvider = { ARRAYSIZE(kEoB1EncodeMonsterDefs14SegaCD), kEoB1EncodeMonsterDefs14SegaCD };
+
+static const byte kEoB1EncodeMonsterDefs15SegaCD[18] = {
+	0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58
+};
+
+static const ByteProvider kEoB1EncodeMonsterDefs15SegaCDProvider = { ARRAYSIZE(kEoB1EncodeMonsterDefs15SegaCD), kEoB1EncodeMonsterDefs15SegaCD };
+
+static const byte kEoB1EncodeMonsterDefs16SegaCD[20] = {
+	0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x48, 0x60, 0x40, 0x60, 0x38, 0x60, 0x40, 0x60
+};
+
+static const ByteProvider kEoB1EncodeMonsterDefs16SegaCDProvider = { ARRAYSIZE(kEoB1EncodeMonsterDefs16SegaCD), kEoB1EncodeMonsterDefs16SegaCD };
+
+static const byte kEoB1EncodeMonsterDefs17SegaCD[16] = {
+	0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60, 0x38, 0x60
+};
+
+static const ByteProvider kEoB1EncodeMonsterDefs17SegaCDProvider = { ARRAYSIZE(kEoB1EncodeMonsterDefs17SegaCD), kEoB1EncodeMonsterDefs17SegaCD };
+
+static const byte kEoB1EncodeMonsterDefs18SegaCD[16] = {
+	0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58
+};
+
+static const ByteProvider kEoB1EncodeMonsterDefs18SegaCDProvider = { ARRAYSIZE(kEoB1EncodeMonsterDefs18SegaCD), kEoB1EncodeMonsterDefs18SegaCD };
+
+static const byte kEoB1EncodeMonsterDefs19SegaCD[18] = {
+	0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58
+};
+
+static const ByteProvider kEoB1EncodeMonsterDefs19SegaCDProvider = { ARRAYSIZE(kEoB1EncodeMonsterDefs19SegaCD), kEoB1EncodeMonsterDefs19SegaCD };
+
+static const byte kEoB1EncodeMonsterDefs20SegaCD[20] = {
+	0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x30, 0x38, 0x30, 0x38, 0x30, 0x48, 0x38, 0x38, 0x30, 0x38, 0x30, 0x48
+};
+
+static const ByteProvider kEoB1EncodeMonsterDefs20SegaCDProvider = { ARRAYSIZE(kEoB1EncodeMonsterDefs20SegaCD), kEoB1EncodeMonsterDefs20SegaCD };
+
+static const byte kEoB1EncodeMonsterDefs21SegaCD[24] = {
+	0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x50, 0x58, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x20, 0x28, 0x20
+};
+
+static const ByteProvider kEoB1EncodeMonsterDefs21SegaCDProvider = { ARRAYSIZE(kEoB1EncodeMonsterDefs21SegaCD), kEoB1EncodeMonsterDefs21SegaCD };
+
+static const byte kEoB1MonsterAnimFrames00SegaCD[5] = {
+	0x00, 0x00, 0x00, 0x05, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames00SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames00SegaCD), kEoB1MonsterAnimFrames00SegaCD };
+
+static const byte kEoB1MonsterAnimFrames01SegaCD[13] = {
+	0x03, 0x00, 0x00, 0x05, 0x04, 0x00, 0x00, 0x05, 0x05, 0x00, 0x00, 0x05, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames01SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames01SegaCD), kEoB1MonsterAnimFrames01SegaCD };
+
+static const byte kEoB1MonsterAnimFrames02SegaCD[5] = {
+	0x01, 0x00, 0x00, 0x05, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames02SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames02SegaCD), kEoB1MonsterAnimFrames02SegaCD };
+
+static const byte kEoB1MonsterAnimFrames03SegaCD[13] = {
+	0x83, 0x00, 0x00, 0x06, 0x84, 0x00, 0x00, 0x06, 0x85, 0x00, 0x00, 0x05, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames03SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames03SegaCD), kEoB1MonsterAnimFrames03SegaCD };
+
+static const byte kEoB1MonsterAnimFrames04SegaCD[41] = {
+	0x06, 0x00, 0x00, 0x05, 0x47, 0x00, 0x00, 0x05, 0x0c, 0x00, 0x00, 0x00, 0x4b, 0x00, 0xe0, 0x05, 0x0d, 0x00, 0x00, 0x00, 0x08, 0x00, 0xe2, 0x05, 0x49, 0x00, 0xe8, 0x05, 0x0c, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x05, 0x0d, 0x00, 0x00, 0x00, 0xff
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames04SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames04SegaCD), kEoB1MonsterAnimFrames04SegaCD };
+
+static const byte kEoB1MonsterAnimFrames05SegaCD[5] = {
+	0x02, 0x00, 0x00, 0x05, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames05SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames05SegaCD), kEoB1MonsterAnimFrames05SegaCD };
+
+static const byte kEoB1MonsterAnimFrames06SegaCD[9] = {
+	0x00, 0x00, 0xf7, 0x05, 0x01, 0x00, 0xf7, 0x05, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames06SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames06SegaCD), kEoB1MonsterAnimFrames06SegaCD };
+
+static const byte kEoB1MonsterAnimFrames07SegaCD[5] = {
+	0x03, 0x00, 0x00, 0x05, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames07SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames07SegaCD), kEoB1MonsterAnimFrames07SegaCD };
+
+static const byte kEoB1MonsterAnimFrames08SegaCD[9] = {
+	0x80, 0x00, 0xf7, 0x06, 0x81, 0x00, 0xf7, 0x06, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames08SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames08SegaCD), kEoB1MonsterAnimFrames08SegaCD };
+
+static const byte kEoB1MonsterAnimFrames09SegaCD[29] = {
+	0x04, 0x00, 0xf0, 0x05, 0x05, 0x00, 0xf0, 0x05, 0x06, 0x00, 0xf0, 0x05, 0x07, 0x00, 0xf0, 0x05, 0x06, 0x00, 0xf0, 0x05, 0x05, 0x00, 0xf0, 0x05, 0x04, 0x00, 0xf0, 0x05, 0xff
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames09SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames09SegaCD), kEoB1MonsterAnimFrames09SegaCD };
+
+static const byte kEoB1MonsterAnimFrames10SegaCD[13] = {
+	0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames10SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames10SegaCD), kEoB1MonsterAnimFrames10SegaCD };
+
+static const byte kEoB1MonsterAnimFrames11SegaCD[13] = {
+	0x01, 0x00, 0x00, 0x05, 0x02, 0x00, 0x00, 0x05, 0x08, 0x00, 0x00, 0x05, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames11SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames11SegaCD), kEoB1MonsterAnimFrames11SegaCD };
+
+static const byte kEoB1MonsterAnimFrames12SegaCD[5] = {
+	0x03, 0x00, 0x00, 0x05, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames12SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames12SegaCD), kEoB1MonsterAnimFrames12SegaCD };
+
+static const byte kEoB1MonsterAnimFrames13SegaCD[13] = {
+	0x81, 0x00, 0x00, 0x06, 0x82, 0x00, 0x00, 0x06, 0x88, 0x00, 0x00, 0x05, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames13SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames13SegaCD), kEoB1MonsterAnimFrames13SegaCD };
+
+static const byte kEoB1MonsterAnimFrames14SegaCD[17] = {
+	0x04, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00, 0x05, 0x07, 0x00, 0x00, 0x05, 0x05, 0x00, 0x00, 0x05, 0xff
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames14SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames14SegaCD), kEoB1MonsterAnimFrames14SegaCD };
+
+static const byte kEoB1MonsterAnimFrames15SegaCD[5] = {
+	0x00, 0x00, 0x00, 0x05, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames15SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames15SegaCD), kEoB1MonsterAnimFrames15SegaCD };
+
+static const byte kEoB1MonsterAnimFrames16SegaCD[13] = {
+	0x01, 0x00, 0x00, 0x05, 0x02, 0x00, 0x00, 0x05, 0x07, 0x00, 0x00, 0x05, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames16SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames16SegaCD), kEoB1MonsterAnimFrames16SegaCD };
+
+static const byte kEoB1MonsterAnimFrames17SegaCD[5] = {
+	0x03, 0x00, 0x00, 0x05, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames17SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames17SegaCD), kEoB1MonsterAnimFrames17SegaCD };
+
+static const byte kEoB1MonsterAnimFrames18SegaCD[13] = {
+	0x81, 0x00, 0x00, 0x06, 0x82, 0x00, 0x00, 0x06, 0x87, 0x00, 0x00, 0x05, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames18SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames18SegaCD), kEoB1MonsterAnimFrames18SegaCD };
+
+static const byte kEoB1MonsterAnimFrames19SegaCD[13] = {
+	0x04, 0x00, 0x00, 0x05, 0x05, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00, 0x05, 0xff
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames19SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames19SegaCD), kEoB1MonsterAnimFrames19SegaCD };
+
+static const byte kEoB1MonsterAnimFrames20SegaCD[5] = {
+	0x00, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames20SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames20SegaCD), kEoB1MonsterAnimFrames20SegaCD };
+
+static const byte kEoB1MonsterAnimFrames21SegaCD[17] = {
+	0x01, 0x00, 0x00, 0x02, 0x0b, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0x0b, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames21SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames21SegaCD), kEoB1MonsterAnimFrames21SegaCD };
+
+static const byte kEoB1MonsterAnimFrames22SegaCD[5] = {
+	0x03, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames22SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames22SegaCD), kEoB1MonsterAnimFrames22SegaCD };
+
+static const byte kEoB1MonsterAnimFrames23SegaCD[17] = {
+	0x81, 0x00, 0x00, 0x02, 0x8b, 0x00, 0x00, 0x02, 0x82, 0x00, 0x00, 0x02, 0x8b, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames23SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames23SegaCD), kEoB1MonsterAnimFrames23SegaCD };
+
+static const byte kEoB1MonsterAnimFrames24SegaCD[29] = {
+	0x05, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x02, 0x09, 0x00, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x02, 0xff
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames24SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames24SegaCD), kEoB1MonsterAnimFrames24SegaCD };
+
+static const byte kEoB1MonsterAnimFrames25SegaCD[5] = {
+	0x00, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames25SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames25SegaCD), kEoB1MonsterAnimFrames25SegaCD };
+
+static const byte kEoB1MonsterAnimFrames26SegaCD[17] = {
+	0x01, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames26SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames26SegaCD), kEoB1MonsterAnimFrames26SegaCD };
+
+static const byte kEoB1MonsterAnimFrames27SegaCD[5] = {
+	0x03, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames27SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames27SegaCD), kEoB1MonsterAnimFrames27SegaCD };
+
+static const byte kEoB1MonsterAnimFrames28SegaCD[17] = {
+	0x81, 0x00, 0x00, 0x02, 0x86, 0x00, 0x00, 0x02, 0x82, 0x00, 0x00, 0x02, 0x86, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames28SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames28SegaCD), kEoB1MonsterAnimFrames28SegaCD };
+
+static const byte kEoB1MonsterAnimFrames29SegaCD[13] = {
+	0x04, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x02, 0xff
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames29SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames29SegaCD), kEoB1MonsterAnimFrames29SegaCD };
+
+static const byte kEoB1MonsterAnimFrames30SegaCD[5] = {
+	0x00, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames30SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames30SegaCD), kEoB1MonsterAnimFrames30SegaCD };
+
+static const byte kEoB1MonsterAnimFrames31SegaCD[17] = {
+	0x04, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames31SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames31SegaCD), kEoB1MonsterAnimFrames31SegaCD };
+
+static const byte kEoB1MonsterAnimFrames32SegaCD[5] = {
+	0x01, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames32SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames32SegaCD), kEoB1MonsterAnimFrames32SegaCD };
+
+static const byte kEoB1MonsterAnimFrames33SegaCD[17] = {
+	0x84, 0x00, 0x00, 0x02, 0x85, 0x00, 0x00, 0x02, 0x86, 0x00, 0x00, 0x02, 0x87, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames33SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames33SegaCD), kEoB1MonsterAnimFrames33SegaCD };
+
+static const byte kEoB1MonsterAnimFrames34SegaCD[17] = {
+	0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0xff
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames34SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames34SegaCD), kEoB1MonsterAnimFrames34SegaCD };
+
+static const byte kEoB1MonsterAnimFrames35SegaCD[5] = {
+	0x00, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames35SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames35SegaCD), kEoB1MonsterAnimFrames35SegaCD };
+
+static const byte kEoB1MonsterAnimFrames36SegaCD[13] = {
+	0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames36SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames36SegaCD), kEoB1MonsterAnimFrames36SegaCD };
+
+static const byte kEoB1MonsterAnimFrames37SegaCD[5] = {
+	0x04, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames37SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames37SegaCD), kEoB1MonsterAnimFrames37SegaCD };
+
+static const byte kEoB1MonsterAnimFrames38SegaCD[13] = {
+	0x81, 0x00, 0x00, 0x02, 0x82, 0x00, 0x00, 0x02, 0x83, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames38SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames38SegaCD), kEoB1MonsterAnimFrames38SegaCD };
+
+static const byte kEoB1MonsterAnimFrames39SegaCD[9] = {
+	0x05, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x02, 0xff
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames39SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames39SegaCD), kEoB1MonsterAnimFrames39SegaCD };
+
+static const byte kEoB1MonsterAnimFrames40SegaCD[5] = {
+	0x00, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames40SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames40SegaCD), kEoB1MonsterAnimFrames40SegaCD };
+
+static const byte kEoB1MonsterAnimFrames41SegaCD[9] = {
+	0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames41SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames41SegaCD), kEoB1MonsterAnimFrames41SegaCD };
+
+static const byte kEoB1MonsterAnimFrames42SegaCD[5] = {
+	0x03, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames42SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames42SegaCD), kEoB1MonsterAnimFrames42SegaCD };
+
+static const byte kEoB1MonsterAnimFrames43SegaCD[9] = {
+	0x81, 0x00, 0x00, 0x02, 0x82, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames43SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames43SegaCD), kEoB1MonsterAnimFrames43SegaCD };
+
+static const byte kEoB1MonsterAnimFrames44SegaCD[13] = {
+	0x04, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x02, 0xff
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames44SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames44SegaCD), kEoB1MonsterAnimFrames44SegaCD };
+
+static const byte kEoB1MonsterAnimFrames45SegaCD[9] = {
+	0x04, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames45SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames45SegaCD), kEoB1MonsterAnimFrames45SegaCD };
+
+static const byte kEoB1MonsterAnimFrames46SegaCD[17] = {
+	0x00, 0x00, 0xf7, 0x02, 0x01, 0x00, 0xf7, 0x02, 0x02, 0x00, 0xf7, 0x02, 0x03, 0x00, 0xf7, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames46SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames46SegaCD), kEoB1MonsterAnimFrames46SegaCD };
+
+static const byte kEoB1MonsterAnimFrames47SegaCD[9] = {
+	0x06, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames47SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames47SegaCD), kEoB1MonsterAnimFrames47SegaCD };
+
+static const byte kEoB1MonsterAnimFrames48SegaCD[17] = {
+	0x80, 0x00, 0xf7, 0x02, 0x81, 0x00, 0xf7, 0x02, 0x82, 0x00, 0xf7, 0x02, 0x83, 0x00, 0xf7, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames48SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames48SegaCD), kEoB1MonsterAnimFrames48SegaCD };
+
+static const byte kEoB1MonsterAnimFrames49SegaCD[25] = {
+	0x05, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x02, 0x09, 0x00, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x02, 0x0b, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x02, 0xff
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames49SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames49SegaCD), kEoB1MonsterAnimFrames49SegaCD };
+
+static const byte kEoB1MonsterAnimFrames50SegaCD[5] = {
+	0x00, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames50SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames50SegaCD), kEoB1MonsterAnimFrames50SegaCD };
+
+static const byte kEoB1MonsterAnimFrames51SegaCD[13] = {
+	0x04, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames51SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames51SegaCD), kEoB1MonsterAnimFrames51SegaCD };
+
+static const byte kEoB1MonsterAnimFrames52SegaCD[5] = {
+	0x07, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames52SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames52SegaCD), kEoB1MonsterAnimFrames52SegaCD };
+
+static const byte kEoB1MonsterAnimFrames53SegaCD[13] = {
+	0x84, 0x00, 0x00, 0x02, 0x85, 0x00, 0x00, 0x02, 0x86, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames53SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames53SegaCD), kEoB1MonsterAnimFrames53SegaCD };
+
+static const byte kEoB1MonsterAnimFrames54SegaCD[13] = {
+	0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0x03, 0xf5, 0x00, 0x02, 0xff
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames54SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames54SegaCD), kEoB1MonsterAnimFrames54SegaCD };
+
+static const byte kEoB1MonsterAnimFrames55SegaCD[5] = {
+	0x00, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames55SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames55SegaCD), kEoB1MonsterAnimFrames55SegaCD };
+
+static const byte kEoB1MonsterAnimFrames56SegaCD[17] = {
+	0x06, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames56SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames56SegaCD), kEoB1MonsterAnimFrames56SegaCD };
+
+static const byte kEoB1MonsterAnimFrames57SegaCD[5] = {
+	0x05, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames57SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames57SegaCD), kEoB1MonsterAnimFrames57SegaCD };
+
+static const byte kEoB1MonsterAnimFrames58SegaCD[17] = {
+	0x86, 0x00, 0x00, 0x02, 0x87, 0x00, 0x00, 0x02, 0x86, 0x00, 0x00, 0x02, 0x87, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames58SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames58SegaCD), kEoB1MonsterAnimFrames58SegaCD };
+
+static const byte kEoB1MonsterAnimFrames59SegaCD[45] = {
+	0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x02, 0x42, 0x00, 0x00, 0x02, 0x09, 0xf6, 0xd2, 0x02, 0x02, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x02, 0x03, 0xf4, 0x00, 0x02, 0x43, 0xf4, 0x00, 0x02, 0x0a, 0xf4, 0xd8, 0x02, 0x03, 0xf4, 0x00, 0x02, 0x04, 0x00, 0x00, 0x02, 0xff
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames59SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames59SegaCD), kEoB1MonsterAnimFrames59SegaCD };
+
+static const byte kEoB1MonsterAnimFrames60SegaCD[5] = {
+	0x09, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames60SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames60SegaCD), kEoB1MonsterAnimFrames60SegaCD };
+
+static const byte kEoB1MonsterAnimFrames61SegaCD[17] = {
+	0x05, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames61SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames61SegaCD), kEoB1MonsterAnimFrames61SegaCD };
+
+static const byte kEoB1MonsterAnimFrames62SegaCD[5] = {
+	0x08, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames62SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames62SegaCD), kEoB1MonsterAnimFrames62SegaCD };
+
+static const byte kEoB1MonsterAnimFrames63SegaCD[17] = {
+	0x85, 0x00, 0x00, 0x02, 0x86, 0x00, 0x00, 0x02, 0x86, 0x00, 0x00, 0x02, 0x86, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames63SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames63SegaCD), kEoB1MonsterAnimFrames63SegaCD };
+
+static const byte kEoB1MonsterAnimFrames64SegaCD[21] = {
+	0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x02, 0xff
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames64SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames64SegaCD), kEoB1MonsterAnimFrames64SegaCD };
+
+static const byte kEoB1MonsterAnimFrames65SegaCD[5] = {
+	0x00, 0x00, 0x03, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames65SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames65SegaCD), kEoB1MonsterAnimFrames65SegaCD };
+
+static const byte kEoB1MonsterAnimFrames66SegaCD[9] = {
+	0x01, 0x00, 0x03, 0x02, 0x02, 0x00, 0x03, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames66SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames66SegaCD), kEoB1MonsterAnimFrames66SegaCD };
+
+static const byte kEoB1MonsterAnimFrames67SegaCD[5] = {
+	0x03, 0x00, 0x03, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames67SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames67SegaCD), kEoB1MonsterAnimFrames67SegaCD };
+
+static const byte kEoB1MonsterAnimFrames68SegaCD[9] = {
+	0x81, 0x00, 0x03, 0x02, 0x82, 0x00, 0x03, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames68SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames68SegaCD), kEoB1MonsterAnimFrames68SegaCD };
+
+static const byte kEoB1MonsterAnimFrames69SegaCD[13] = {
+	0x04, 0x00, 0x03, 0x02, 0x05, 0x00, 0x03, 0x02, 0x06, 0x00, 0x03, 0x02, 0xff
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames69SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames69SegaCD), kEoB1MonsterAnimFrames69SegaCD };
+
+static const byte kEoB1MonsterAnimFrames70SegaCD[5] = {
+	0x00, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames70SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames70SegaCD), kEoB1MonsterAnimFrames70SegaCD };
+
+static const byte kEoB1MonsterAnimFrames71SegaCD[9] = {
+	0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames71SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames71SegaCD), kEoB1MonsterAnimFrames71SegaCD };
+
+static const byte kEoB1MonsterAnimFrames72SegaCD[5] = {
+	0x03, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames72SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames72SegaCD), kEoB1MonsterAnimFrames72SegaCD };
+
+static const byte kEoB1MonsterAnimFrames73SegaCD[9] = {
+	0x81, 0x00, 0x00, 0x02, 0x82, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames73SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames73SegaCD), kEoB1MonsterAnimFrames73SegaCD };
+
+static const byte kEoB1MonsterAnimFrames74SegaCD[13] = {
+	0x04, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x06, 0xff
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames74SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames74SegaCD), kEoB1MonsterAnimFrames74SegaCD };
+
+static const byte kEoB1MonsterAnimFrames75SegaCD[5] = {
+	0x00, 0x00, 0x08, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames75SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames75SegaCD), kEoB1MonsterAnimFrames75SegaCD };
+
+static const byte kEoB1MonsterAnimFrames76SegaCD[17] = {
+	0x01, 0x00, 0x08, 0x02, 0x02, 0x00, 0x08, 0x02, 0x06, 0x00, 0x08, 0x02, 0x07, 0x00, 0x08, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames76SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames76SegaCD), kEoB1MonsterAnimFrames76SegaCD };
+
+static const byte kEoB1MonsterAnimFrames77SegaCD[5] = {
+	0x03, 0x00, 0x08, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames77SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames77SegaCD), kEoB1MonsterAnimFrames77SegaCD };
+
+static const byte kEoB1MonsterAnimFrames78SegaCD[17] = {
+	0x81, 0x00, 0x08, 0x02, 0x82, 0x00, 0x08, 0x02, 0x86, 0x00, 0x08, 0x02, 0x87, 0x00, 0x08, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames78SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames78SegaCD), kEoB1MonsterAnimFrames78SegaCD };
+
+static const byte kEoB1MonsterAnimFrames79SegaCD[13] = {
+	0x04, 0x00, 0x08, 0x02, 0x05, 0x00, 0x08, 0x02, 0x08, 0x00, 0x08, 0x02, 0xff
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames79SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames79SegaCD), kEoB1MonsterAnimFrames79SegaCD };
+
+static const byte kEoB1MonsterAnimFrames80SegaCD[5] = {
+	0x00, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames80SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames80SegaCD), kEoB1MonsterAnimFrames80SegaCD };
+
+static const byte kEoB1MonsterAnimFrames81SegaCD[13] = {
+	0x01, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames81SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames81SegaCD), kEoB1MonsterAnimFrames81SegaCD };
+
+static const byte kEoB1MonsterAnimFrames82SegaCD[5] = {
+	0x08, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames82SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames82SegaCD), kEoB1MonsterAnimFrames82SegaCD };
+
+static const byte kEoB1MonsterAnimFrames83SegaCD[13] = {
+	0x81, 0x00, 0x00, 0x02, 0x83, 0x00, 0x00, 0x02, 0x82, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames83SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames83SegaCD), kEoB1MonsterAnimFrames83SegaCD };
+
+static const byte kEoB1MonsterAnimFrames84SegaCD[13] = {
+	0x06, 0x08, 0x00, 0x02, 0x07, 0x00, 0x00, 0x02, 0x09, 0xf6, 0x00, 0x02, 0xff
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames84SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames84SegaCD), kEoB1MonsterAnimFrames84SegaCD };
+
+static const byte kEoB1MonsterAnimFrames85SegaCD[5] = {
+	0x01, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames85SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames85SegaCD), kEoB1MonsterAnimFrames85SegaCD };
+
+static const byte kEoB1MonsterAnimFrames86SegaCD[9] = {
+	0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames86SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames86SegaCD), kEoB1MonsterAnimFrames86SegaCD };
+
+static const byte kEoB1MonsterAnimFrames87SegaCD[5] = {
+	0x03, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames87SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames87SegaCD), kEoB1MonsterAnimFrames87SegaCD };
+
+static const byte kEoB1MonsterAnimFrames88SegaCD[9] = {
+	0x80, 0x00, 0x00, 0x02, 0x82, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames88SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames88SegaCD), kEoB1MonsterAnimFrames88SegaCD };
+
+static const byte kEoB1MonsterAnimFrames89SegaCD[13] = {
+	0x04, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x02, 0xff
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames89SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames89SegaCD), kEoB1MonsterAnimFrames89SegaCD };
+
+static const byte kEoB1MonsterAnimFrames90SegaCD[5] = {
+	0x00, 0x00, 0x09, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames90SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames90SegaCD), kEoB1MonsterAnimFrames90SegaCD };
+
+static const byte kEoB1MonsterAnimFrames91SegaCD[17] = {
+	0x04, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames91SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames91SegaCD), kEoB1MonsterAnimFrames91SegaCD };
+
+static const byte kEoB1MonsterAnimFrames92SegaCD[5] = {
+	0x03, 0x00, 0x09, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames92SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames92SegaCD), kEoB1MonsterAnimFrames92SegaCD };
+
+static const byte kEoB1MonsterAnimFrames93SegaCD[17] = {
+	0x84, 0x00, 0x00, 0x02, 0x85, 0x00, 0x00, 0x02, 0x86, 0x00, 0x00, 0x02, 0x87, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames93SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames93SegaCD), kEoB1MonsterAnimFrames93SegaCD };
+
+static const byte kEoB1MonsterAnimFrames94SegaCD[13] = {
+	0x00, 0x00, 0x09, 0x02, 0x01, 0x00, 0x09, 0x02, 0x02, 0x00, 0x09, 0x02, 0xff
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames94SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames94SegaCD), kEoB1MonsterAnimFrames94SegaCD };
+
+static const byte kEoB1MonsterAnimFrames95SegaCD[5] = {
+	0x00, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames95SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames95SegaCD), kEoB1MonsterAnimFrames95SegaCD };
+
+static const byte kEoB1MonsterAnimFrames96SegaCD[9] = {
+	0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames96SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames96SegaCD), kEoB1MonsterAnimFrames96SegaCD };
+
+static const byte kEoB1MonsterAnimFrames97SegaCD[5] = {
+	0x03, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames97SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames97SegaCD), kEoB1MonsterAnimFrames97SegaCD };
+
+static const byte kEoB1MonsterAnimFrames98SegaCD[9] = {
+	0x81, 0x00, 0x00, 0x02, 0x82, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames98SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames98SegaCD), kEoB1MonsterAnimFrames98SegaCD };
+
+static const byte kEoB1MonsterAnimFrames99SegaCD[17] = {
+	0x04, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x02, 0x07, 0x00, 0x00, 0x02, 0xff
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames99SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames99SegaCD), kEoB1MonsterAnimFrames99SegaCD };
+
+static const byte kEoB1MonsterAnimFrames100SegaCD[5] = {
+	0x04, 0x00, 0xf9, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames100SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames100SegaCD), kEoB1MonsterAnimFrames100SegaCD };
+
+static const byte kEoB1MonsterAnimFrames101SegaCD[17] = {
+	0x00, 0x00, 0xf7, 0x02, 0x01, 0x00, 0xf7, 0x02, 0x02, 0x00, 0xf7, 0x02, 0x03, 0x00, 0xf7, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames101SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames101SegaCD), kEoB1MonsterAnimFrames101SegaCD };
+
+static const byte kEoB1MonsterAnimFrames102SegaCD[5] = {
+	0x05, 0x00, 0xf9, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames102SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames102SegaCD), kEoB1MonsterAnimFrames102SegaCD };
+
+static const byte kEoB1MonsterAnimFrames103SegaCD[17] = {
+	0x80, 0x00, 0xf7, 0x02, 0x81, 0x00, 0xf7, 0x02, 0x82, 0x00, 0xf7, 0x02, 0x83, 0x00, 0xf7, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames103SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames103SegaCD), kEoB1MonsterAnimFrames103SegaCD };
+
+static const byte kEoB1MonsterAnimFrames104SegaCD[21] = {
+	0x04, 0x00, 0xf9, 0x02, 0x07, 0x00, 0xf9, 0x02, 0x06, 0x00, 0xf9, 0x02, 0x09, 0x00, 0xf9, 0x02, 0x08, 0x00, 0xf9, 0x02, 0xff
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames104SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames104SegaCD), kEoB1MonsterAnimFrames104SegaCD };
+
+static const byte kEoB1MonsterAnimFrames105SegaCD[5] = {
+	0x00, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames105SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames105SegaCD), kEoB1MonsterAnimFrames105SegaCD };
+
+static const byte kEoB1MonsterAnimFrames106SegaCD[9] = {
+	0x03, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames106SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames106SegaCD), kEoB1MonsterAnimFrames106SegaCD };
+
+static const byte kEoB1MonsterAnimFrames107SegaCD[5] = {
+	0x05, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames107SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames107SegaCD), kEoB1MonsterAnimFrames107SegaCD };
+
+static const byte kEoB1MonsterAnimFrames108SegaCD[9] = {
+	0x83, 0x00, 0x00, 0x02, 0x84, 0x00, 0x00, 0x02, 0xfe
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames108SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames108SegaCD), kEoB1MonsterAnimFrames108SegaCD };
+
+static const byte kEoB1MonsterAnimFrames109SegaCD[9] = {
+	0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0xff
+};
+
+static const ByteProvider kEoB1MonsterAnimFrames109SegaCDProvider = { ARRAYSIZE(kEoB1MonsterAnimFrames109SegaCD), kEoB1MonsterAnimFrames109SegaCD };
+
+static const EoBCharacter kEoB1NpcPresetsSegaCD[9] = {
+	{ 0x00, 0x01, "Anya",
+	  18, 18, 59, 59,  5,  5, 11, 11, 14, 14, 16, 16,  9,  9,
+	    45,   45, 10, 0, 1, 0, 2, -1, 100, { 4, 0, 0 },
+	  { 0x00001F40, 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, "Beohram",
+	  17, 17,  0,  0,  9,  9, 15, 15, 13, 13, 18, 18, 17, 17,
+	    55,   55, 10, 0, 0, 0, 0, -2, 100, { 7, 0, 0 },
+	  { 0x000130B0, 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, "Kirath",
+	  11, 11,  0,  0, 17, 17, 13, 13, 18, 18,  8,  8, 12, 12,
+	    20,   20, 10, 0, 4, 3, 4, -3, 100, { 7, 0, 0 },
+	  { 0x00011170, 0x00000000, 0x00000000 }, 0x00B3126B,
+	  { 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, "Ileria",
+	  10, 10,  0,  0, 12, 12,  9,  9, 15, 15, 17, 17, 17, 17,
+	    52,   52, 10, 0, 4, 4, 0, -4, 100, { 6, 0, 0 },
+	  { 0x00004E20, 0x00000000, 0x00000000 }, 0xFFFFFFFF,
+	  { 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, "Tyrra",
+	  16, 16,  0,  0, 14, 14, 16, 16, 18, 18, 17, 17,  7,  7,
+	    45,   45, 10, 0, 1, 1, 2, -5, 100, { 6, 0, 0 },
+	  { 0x0000CF08, 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, "Tod Uphill",
+	  17, 17,  0,  0, 11, 11, 14, 14, 19, 19, 18, 18, 16, 16,
+	    32,   32, 10, 0, 10, 5, 5, -6, 100, { 5, 0, 0 },
+	  { 0x00002D3F, 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, "Taghor",
+	  17, 17,  0,  0, 11, 11, 15, 15, 15, 15, 19, 19,  9,  9,
+	     3,   45,  3, 0, 6, 0, 1, -7, 25, { 5, 0, 0 },
+	  { 0x00003F6A, 0x00000000, 0x00000000 }, 0x00000000,
+	  { 0x0024, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0029, 0x0000, 0x002B, 0x0000, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0000, 0x0000 } },
+	{ 0x00, 0x01, "Dohrum",
+	  18, 18, 29, 29, 13, 13, 11, 11, 16, 16, 17, 17, 14, 14,
+	    28,   28, 10, 0, 6, 0, 0, -8, 100, { 3, 0, 0 },
+	  { 0x000013A0, 0x00000000, 0x00000000 }, 0x00000000,
+	  { 0x0024, 0x0000, 0x0030, 0x0037, 0x0037, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0029, 0x0000, 0x002B, 0x0000, 0x0000, 0x0000, 0x0000,
+	    0x0000, 0x0000, 0x0000 } },
+	{ 0x00, 0x01, "Keirgar",
+	  18, 18, 92, 92, 15, 15, 15, 15, 12, 12, 19, 19, 17, 17,
+	     3,   45,  3, 0, 6, 0, 1, -9, 25, { 5, 0, 0 },
+	  { 0x00001F40, 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 } }
+};
+
+static const EoBCharacterProvider kEoB1NpcPresetsSegaCDProvider = { ARRAYSIZE(kEoB1NpcPresetsSegaCD), kEoB1NpcPresetsSegaCD };
+
+static const byte kEoB1SpeechAnimDataSegaCD[84] = {
+	0x02, 0x05, 0x01, 0x05, 0x00, 0x05, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x01, 0x05, 0x00, 0x05,	0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x02, 0x05, 0x00, 0x05, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x05, 0x01, 0x05,	0x02, 0x05, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x05, 0x01, 0x05, 0xff, 0x00, 0x00, 0x00,	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x09, 0x01, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x02, 0x01, 0x05, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const ByteProvider kEoB1SpeechAnimDataSegaCDProvider = { ARRAYSIZE(kEoB1SpeechAnimDataSegaCD), kEoB1SpeechAnimDataSegaCD };
+
+static const byte kEoB1WdAnimSpritesSegaCD[31] = {
+	0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03,	0x03, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06,
+	0x07, 0x07, 0x07, 0x08, 0x08, 0x09, 0x09, 0x0a,	0x0a, 0x0b, 0x0b, 0x0c, 0x0c, 0x0d, 0x0d
+};
+
+static const ByteProvider kEoB1WdAnimSpritesSegaCDProvider = { ARRAYSIZE(kEoB1WdAnimSpritesSegaCD), kEoB1WdAnimSpritesSegaCD };
+
+static const byte kEoB1SequenceTrackMapSegaCD[60] = {
+	0x00, 0x0d, 0x0f, 0x11, 0x12, 0x13, 0x14, 0x15,
+	0x00, 0x1d, 0x1e, 0x00, 0x0e, 0x16, 0x37, 0x38,
+	0x22, 0x00, 0x23, 0x24, 0x25, 0x1c, 0x18, 0x10,
+	0x1f, 0x17, 0x00, 0x1b, 0x21, 0x00, 0x00, 0x00,
+	0x30, 0x31, 0x26, 0x27, 0x28, 0x29, 0x35, 0x36,
+	0x33, 0x34, 0x2c, 0x2b, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00
+};
+
+static const ByteProvider kEoB1SequenceTrackMapSegaCDProvider = { ARRAYSIZE(kEoB1SequenceTrackMapSegaCD), kEoB1SequenceTrackMapSegaCD };
+
+static const byte kEoB1MapLevelDataSegaCD[224] = {
+	0x61, 0x01, 0x00, 0x24, 0x24, 0x01, 0x03, 0x47,
+	0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x61, 0x01,
+	0x00, 0x24, 0x24, 0x03, 0x02, 0xec, 0x00, 0x00,
+	0x00, 0x00, 0x02, 0x02, 0x61, 0x01, 0x00, 0x24,
+	0x24, 0x03, 0x03, 0x81, 0x00, 0x00, 0x00, 0x00,
+	0x03, 0x03, 0x61, 0x01, 0x00, 0x24, 0x24, 0x03,
+	0x02, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04,
+	0x61, 0x01, 0x00, 0x24, 0x24, 0x03, 0x00, 0x2a,
+	0x00, 0x00, 0x00, 0x00, 0x05, 0x05, 0x61, 0x01,
+	0x00, 0x24, 0x24, 0x03, 0x01, 0xfb, 0x00, 0x00,
+	0x00, 0x00, 0x06, 0x06, 0x61, 0x01, 0x00, 0x24,
+	0x24, 0x03, 0x02, 0x59, 0x00, 0x00, 0x00, 0x00,
+	0x07, 0x07, 0x61, 0x01, 0x00, 0x24, 0x24, 0x02,
+	0x01, 0xc9, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08,
+	0x61, 0x01, 0x00, 0x24, 0x24, 0x03, 0x01, 0x25,
+	0x00, 0x00, 0x00, 0x00, 0x09, 0x09, 0x61, 0x01,
+	0x00, 0x24, 0x24, 0x03, 0x01, 0xe1, 0x00, 0x00,
+	0x00, 0x00, 0x0a, 0x0a, 0x61, 0x01, 0x00, 0x24,
+	0x24, 0x03, 0x03, 0x1b, 0x00, 0x00, 0x00, 0x00,
+	0x0b, 0x0b, 0x61, 0x01, 0x00, 0x24, 0x24, 0x03,
+	0x02, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c,
+	0x5c, 0x01, 0x40, 0x29, 0x27, 0x01, 0x00, 0xb6,
+	0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x5c, 0x01,
+	0x40, 0x29, 0x27, 0x01, 0x00, 0xb6, 0x00, 0x00,
+	0x00, 0x00, 0x04, 0x08, 0x5c, 0x01, 0x40, 0x29,
+	0x27, 0x02, 0x00, 0xe9, 0x00, 0x00, 0x00, 0x00,
+	0x04, 0x08, 0x5c, 0x01, 0x40, 0x29, 0x27, 0x03,
+	0x03, 0xac, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08
+};
+
+static const ByteProvider kEoB1MapLevelDataSegaCDProvider = { ARRAYSIZE(kEoB1MapLevelDataSegaCD), kEoB1MapLevelDataSegaCD };
+
+static const uint16 kEoB1Ascii2SjisTable1SegaCD[48] = {
+	0x2021, 0x2121, 0x2221, 0x22c3, 0x2365, 0x2407, 0x2621, 0x2721,
+	0x2821, 0x2921, 0x2a21, 0x2b21, 0x2c21, 0x2d21, 0x2e21, 0x2f21,
+	0x2ea9, 0x2f4b, 0x2fed, 0x308f, 0x3131, 0x31d3, 0x3275, 0x3317,
+	0x33b9, 0x345b, 0x34fd, 0x359f, 0x3641, 0x36e3, 0x3785, 0x3827,
+	0x38c9, 0x396b, 0x3a0d, 0x3aaf, 0x3b51, 0x3bf3, 0x3c95, 0x3d37,
+	0x3dd9, 0x3e7b, 0x3f1d, 0x3fbf, 0x4061, 0x4103, 0x41a5, 0x4247
+};
+
+static const Uint16Provider kEoB1Ascii2SjisTable1SegaCDProvider = { ARRAYSIZE(kEoB1Ascii2SjisTable1SegaCD), kEoB1Ascii2SjisTable1SegaCD };
+
+static const uint16 kEoB1Ascii2SjisTable2SegaCD[224] = {
+	0x8140, 0x8149, 0x818d, 0x8194, 0x8190, 0x8193, 0x8195, 0x818c,
+	0x8169, 0x816a, 0x8196, 0x817b, 0x8143, 0x817c, 0x8144, 0x815e,
+	0x824f, 0x8250, 0x8251, 0x8252, 0x8253, 0x8254, 0x8255, 0x8256,
+	0x8257, 0x8258, 0x8146, 0x8147, 0x8183, 0x8181, 0x8184, 0x8148,
+	0x8197, 0x8260, 0x8261, 0x8262, 0x8263, 0x8264, 0x8265, 0x8266,
+	0x8267, 0x8268, 0x8269, 0x826a, 0x826b, 0x826c, 0x826d, 0x826e,
+	0x826f, 0x8270, 0x8271, 0x8272, 0x8273, 0x8274, 0x8275, 0x8276,
+	0x8277, 0x8278, 0x8279, 0x816d, 0x818f, 0x816e, 0x814f, 0x8151,
+	0x814d, 0x8281, 0x8282, 0x8283, 0x8284, 0x8285, 0x8286, 0x8287,
+	0x8288, 0x8289, 0x828a, 0x828b, 0x828c, 0x828d, 0x828e, 0x828f,
+	0x8290, 0x8291, 0x8292, 0x8293, 0x8294, 0x8295, 0x8296, 0x8297,
+	0x8298, 0x8299, 0x829a, 0x816f, 0x8162, 0x8170, 0x8150, 0x8140,
+	0x8140, 0x8142, 0x8175, 0x8176, 0x8141, 0x8145, 0x82f0, 0x829f,
+	0x82a1, 0x82a3, 0x82a5, 0x82a7, 0x82e1, 0x82e3, 0x82e5, 0x82c1,
+	0x815b, 0x82a0, 0x82a2, 0x82a4, 0x82a6, 0x82a8, 0x82a9, 0x82ab,
+	0x82ad, 0x82af, 0x82b1, 0x82b3, 0x82b5, 0x82b7, 0x82b9, 0x82bb,
+	0x8140, 0x8142, 0x8175, 0x8176, 0x8141, 0x8145, 0x8392, 0x8340,
+	0x8342, 0x8344, 0x8346, 0x8348, 0x8383, 0x8385, 0x8387, 0x8362,
+	0x815b, 0x8341, 0x8343, 0x8345, 0x8347, 0x8349, 0x834a, 0x834c,
+	0x834e, 0x8350, 0x8352, 0x8354, 0x8356, 0x8358, 0x835a, 0x835c,
+	0x835e, 0x8360, 0x8363, 0x8365, 0x8367, 0x8369, 0x836a, 0x836b,
+	0x836c, 0x836d, 0x836e, 0x8371, 0x8374, 0x8377, 0x837a, 0x837d,
+	0x837e, 0x8380, 0x8381, 0x8382, 0x8384, 0x8386, 0x8388, 0x8389,
+	0x838a, 0x838b, 0x838c, 0x838d, 0x838f, 0x8393, 0x814a, 0x814b,
+	0x82bd, 0x82bf, 0x82c2, 0x82c4, 0x82c6, 0x82c8, 0x82c9, 0x82ca,
+	0x82cb, 0x82cc, 0x82cd, 0x82d0, 0x82d3, 0x82d6, 0x82d9, 0x82dc,
+	0x82dd, 0x82de, 0x82df, 0x82e0, 0x82e2, 0x82e4, 0x82e6, 0x82e7,
+	0x82e8, 0x82e9, 0x82ea, 0x82eb, 0x82ed, 0x82f1, 0x814a, 0x814b
+};
+
+static const Uint16Provider kEoB1Ascii2SjisTable2SegaCDProvider = { ARRAYSIZE(kEoB1Ascii2SjisTable2SegaCD), kEoB1Ascii2SjisTable2SegaCD };
+
+static const byte kEoB1CharWidthTable1SegaCD[188] = {
+	0x06, 0x04, 0x05, 0x04, 0x03, 0x03, 0x04, 0x04,
+	0x07, 0x06, 0x05, 0x04, 0x06, 0x06, 0x07, 0x06,
+	0x09, 0x09, 0x05, 0x08, 0x06, 0x08, 0x06, 0x0a,
+	0x09, 0x07, 0x0a, 0x0a, 0x09, 0x05, 0x0b, 0x0b,
+	0x0b, 0x04, 0x02, 0x0b, 0x07, 0x04, 0x04, 0x06,
+	0x06, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05,
+	0x05, 0x06, 0x06, 0x09, 0x09, 0x05, 0x05, 0x05,
+	0x05, 0x05, 0x05, 0x08, 0x08, 0x08, 0x09, 0x08,
+	0x08, 0x09, 0x06, 0x06, 0x09, 0x09, 0x0c, 0x0c,
+	0x0c, 0x0c, 0x0c, 0x05, 0x0c, 0x0c, 0x0c, 0x0c,
+	0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+	0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+	0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+	0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x08, 0x05, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x08, 0x07, 0x07, 0x0c,
+	0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0a,
+	0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x05, 0x09,
+	0x0b, 0x0a, 0x0c, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b,
+	0x0a, 0x0b, 0x0a, 0x0a, 0x0c, 0x0b, 0x0a, 0x0a,
+	0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x08, 0x08,
+	0x07, 0x08, 0x08, 0x07, 0x08, 0x09, 0x04, 0x06,
+	0x09, 0x04, 0x0b, 0x09, 0x08, 0x08, 0x08, 0x08,
+	0x08, 0x07, 0x0a, 0x09, 0x0c, 0x09, 0x09, 0x08,
+	0x0c, 0x0c, 0x0c, 0x0c
+};
+
+static const ByteProvider kEoB1CharWidthTable1SegaCDProvider = { ARRAYSIZE(kEoB1CharWidthTable1SegaCD), kEoB1CharWidthTable1SegaCD };
+
+static const byte kEoB1CharWidthTable2SegaCD[188] = {
+	0x06, 0x04, 0x05, 0x04, 0x03, 0x03, 0x04, 0x04,
+	0x07, 0x04, 0x05, 0x04, 0x06, 0x06, 0x07, 0x06,
+	0x09, 0x09, 0x05, 0x08, 0x06, 0x08, 0x06, 0x0a,
+	0x09, 0x07, 0x0a, 0x0a, 0x09, 0x05, 0x0b, 0x0b,
+	0x0b, 0x04, 0x02, 0x0b,	0x07, 0x04, 0x04, 0x06,
+	0x06, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05,
+	0x05, 0x06, 0x06, 0x09,	0x09, 0x05, 0x05, 0x05,
+	0x05, 0x05, 0x05, 0x08, 0x08, 0x08, 0x09, 0x08,
+	0x08, 0x09, 0x06, 0x06,	0x09, 0x09, 0x0c, 0x0c,
+	0x0c, 0x0c, 0x0c, 0x05, 0x0c, 0x0c, 0x0c, 0x0c,
+	0x0c, 0x0c, 0x0c, 0x0c,	0x0c, 0x0c, 0x0c, 0x0c,
+	0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+	0x0c, 0x0c, 0x0c, 0x0c,	0x0c, 0x0c, 0x0c, 0x0c,
+	0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x06, 0x04, 0x06,
+	0x06, 0x06, 0x06, 0x06,	0x06, 0x06, 0x06, 0x0c,
+	0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x06, 0x06,
+	0x06, 0x06, 0x06, 0x06,	0x06, 0x06, 0x02, 0x06,
+	0x06, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+	0x06, 0x07, 0x06, 0x06,	0x06, 0x06, 0x06, 0x06,
+	0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x05, 0x05,
+	0x05, 0x05, 0x05, 0x05,	0x05, 0x05, 0x03, 0x04,
+	0x05, 0x03, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05,
+	0x05, 0x05, 0x05, 0x06,	0x06, 0x06, 0x06, 0x05,
+	0x0c, 0x0c, 0x0c, 0x0c
+};
+
+static const ByteProvider kEoB1CharWidthTable2SegaCDProvider = { ARRAYSIZE(kEoB1CharWidthTable2SegaCD), kEoB1CharWidthTable2SegaCD };
+
+static const byte kEoB1CharWidthTable3SegaCD[188] = {
+	0x06, 0x04, 0x05, 0x04, 0x03, 0x03, 0x04, 0x04,
+	0x07, 0x04, 0x05, 0x04, 0x06, 0x06, 0x07, 0x06,
+	0x09, 0x09, 0x05, 0x08, 0x06, 0x08, 0x06, 0x0a,
+	0x09, 0x07, 0x0a, 0x0a, 0x09, 0x05, 0x0b, 0x0b,
+	0x0b, 0x04, 0x02, 0x0b, 0x07, 0x04, 0x04, 0x06,
+	0x06, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05,
+	0x05, 0x06, 0x06, 0x09, 0x09, 0x05, 0x05, 0x05,
+	0x05, 0x05, 0x05, 0x08, 0x08, 0x08, 0x09, 0x08,
+	0x08, 0x09, 0x06, 0x06, 0x09, 0x09, 0x0c, 0x0c,
+	0x0c, 0x0c, 0x0c, 0x05, 0x0c, 0x0c, 0x0c, 0x0c,
+	0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+	0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+	0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+	0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x06, 0x04, 0x06,
+	0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x0c,
+	0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x07, 0x07,
+	0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x04, 0x07,
+	0x08, 0x07, 0x08, 0x07, 0x07, 0x07, 0x07, 0x07,
+	0x07, 0x08, 0x07, 0x07, 0x07, 0x07, 0x08, 0x07,
+	0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x06, 0x06,
+	0x06, 0x06, 0x06, 0x06, 0x07, 0x06, 0x04, 0x05,
+	0x06, 0x03, 0x09, 0x07, 0x06, 0x06, 0x06, 0x06,
+	0x06, 0x07, 0x06, 0x06, 0x07, 0x07, 0x08, 0x06,
+	0x0c, 0x0c, 0x0c, 0x0c
+};
+
+static const ByteProvider kEoB1CharWidthTable3SegaCDProvider = { ARRAYSIZE(kEoB1CharWidthTable3SegaCD), kEoB1CharWidthTable3SegaCD };
+
+static const byte kEoB1CharTilesTableSegaCD[256] = {
+	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, 0x2d,	0x00, 0x00, 0x00, 0x21, 0x00, 0x20, 0x1f, 0x22,
+	0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,	0x2b, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,	0x18, 0x19, 0x1a, 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, 0x11,	0x19, 0x00, 0x00, 0x00, 0x16, 0x02, 0x00, 0x37,
+	0x03, 0x55, 0x12, 0x00, 0x06, 0x1f, 0x00, 0x00,	0x10, 0x1e, 0x00, 0x00, 0x15, 0x56, 0x00, 0x00,
+	0x13, 0x21, 0x00, 0x18, 0x0a, 0x00, 0x1c, 0x00,	0x00, 0x0d, 0x09, 0x01, 0x08, 0x20, 0x00, 0x04,
+	0x00, 0x0e, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x17,	0x0f, 0x07, 0x14, 0x1b, 0x0c, 0x05, 0x0b, 0x31,
+	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 kEoB1CharTilesTableSegaCDProvider = { ARRAYSIZE(kEoB1CharTilesTableSegaCD), kEoB1CharTilesTableSegaCD };
+
+static const uint16 kEoB1PalettesSegaCD[960] = {
+	0x0000, 0x0a22, 0x0a44, 0x000a, 0x0002, 0x04ce, 0x0a2e, 0x0024,	0x0004, 0x0602, 0x0e86, 0x0e6e, 0x0000, 0x0400, 0x008e, 0x0eee,
+	0x0000, 0x040a, 0x0a86, 0x0482, 0x0264, 0x06ce, 0x08ce, 0x046a,	0x0448, 0x0caa, 0x0a44, 0x0622, 0x0222, 0x0644, 0x0888, 0x0eee,
+	0x0000, 0x0caa, 0x0400, 0x0802, 0x0622, 0x00ec, 0x0088, 0x0046,	0x0024, 0x0200, 0x0422, 0x0644, 0x0866, 0x0a88, 0x0ecc, 0x0eee,
+	0x0000, 0x042e, 0x0ce8, 0x00a0, 0x0040, 0x00ee, 0x08ce, 0x046a,	0x0226, 0x0e88, 0x0a24, 0x0602, 0x0000, 0x0222, 0x0888, 0x0eee,
+	0x0000, 0x020e, 0x0eca, 0x02a0, 0x0040, 0x0224, 0x066c, 0x0448,	0x0226, 0x0e88, 0x0a44, 0x0622, 0x0000, 0x0222, 0x0666, 0x0eee,
+	0x0000, 0x0222, 0x0820, 0x0400, 0x0200, 0x00ee, 0x0266, 0x0206,	0x0800, 0x0caa, 0x0a66, 0x0444, 0x0644, 0x0a88, 0x004c, 0x0eee,
+	0x0000, 0x0222, 0x0820, 0x0400, 0x0200, 0x00ee, 0x0266, 0x0206,	0x0800, 0x0caa, 0x0a66, 0x0444, 0x0644, 0x0a88, 0x004c, 0x0eee,
+	0x0000, 0x042e, 0x0ce8, 0x00a0, 0x0040, 0x00ee, 0x08ce, 0x046a,	0x0226, 0x0e88, 0x0a24, 0x0602, 0x0000, 0x0222, 0x0888, 0x0eee,
+	0x0000, 0x022c, 0x044e, 0x08ae, 0x0ccc, 0x04ec, 0x06a8, 0x0468,	0x0224, 0x0642, 0x0a66, 0x0e8a, 0x0002, 0x0446, 0x0888, 0x0eee,
+	0x0000, 0x0ecc, 0x0caa, 0x0a88, 0x0866, 0x0644, 0x0422, 0x0400,	0x0000, 0x0422, 0x0644, 0x0866, 0x0a88, 0x0caa, 0x0ecc, 0x0eee,
+	0x0000, 0x0402, 0x0268, 0x06ee, 0x02aa, 0x0c8c, 0x0c6a, 0x0646,	0x0424, 0x0848, 0x0626, 0x0cce, 0x0000, 0x0026, 0x00e0, 0x0eee,
+	0x0004, 0x0eec, 0x0cea, 0x0ac8, 0x08a6, 0x0684, 0x0442, 0x0220,	0x06ea, 0x04c8, 0x0284, 0x0064, 0x0022, 0x0000, 0x0000, 0x0eee,
+	0x0000, 0x0206, 0x0e8a, 0x0808, 0x000e, 0x000e, 0x000e, 0x0402,	0x0e86, 0x0ea8, 0x0eca, 0x0a24, 0x0200, 0x0c46, 0x0c68, 0x0eee,
+	0x0000, 0x0caa, 0x0400, 0x0802, 0x0622, 0x00ec, 0x0088, 0x0046,	0x0024, 0x0200, 0x0422, 0x0644, 0x0866, 0x0a88, 0x0ecc, 0x0eee,
+	0x0000, 0x0206, 0x0ea2, 0x0e20, 0x0e60, 0x00ee, 0x0088, 0x0064,	0x0e8e, 0x0ee8, 0x0c00, 0x0600, 0x0200, 0x084e, 0x060a, 0x0eee,
+	0x0000, 0x042e, 0x0ce8, 0x00a0, 0x0040, 0x00ee, 0x08ce, 0x046a,	0x0226, 0x0e88, 0x0a24, 0x0602, 0x0000, 0x0222, 0x0888, 0x0eee,
+	0x0000, 0x020e, 0x08ca, 0x0264, 0x0042, 0x0224, 0x068a, 0x0448,	0x0226, 0x06a8, 0x0486, 0x0244, 0x0000, 0x0222, 0x0666, 0x0ccc,
+	0x0000, 0x020e, 0x08aa, 0x08ca, 0x0066, 0x0224, 0x068c, 0x046a,	0x0246, 0x0688, 0x0466, 0x0244, 0x0000, 0x0222, 0x0666, 0x0acc,
+	0x0000, 0x020e, 0x0eca, 0x04a0, 0x0240, 0x0224, 0x06ac, 0x046a,	0x0246, 0x0e88, 0x0a44, 0x0622, 0x0000, 0x0222, 0x0666, 0x0eec,
+	0x0000, 0x0200, 0x0446, 0x0ace, 0x0222, 0x00ee, 0x088c, 0x026a,	0x0224, 0x0488, 0x020a, 0x0ecc, 0x0444, 0x0666, 0x0aaa, 0x0eee,
+	0x0000, 0x0000, 0x0a64, 0x0482, 0x0022, 0x00ee, 0x068e, 0x0248,	0x0026, 0x0eaa, 0x0822, 0x0600, 0x000e, 0x0062, 0x06e4, 0x0eee,
+	0x0000, 0x00e0, 0x00e0, 0x00e0, 0x0844, 0x0008, 0x0208, 0x082e,	0x0844, 0x0eaa, 0x0200, 0x0400, 0x0000, 0x0422, 0x0e66, 0x0eee,
+	0x0000, 0x0000, 0x0c42, 0x0246, 0x0644, 0x00ee, 0x068c, 0x0248,	0x0024, 0x0e88, 0x0002, 0x0600, 0x0200, 0x0422, 0x0866, 0x0eee,
+	0x0000, 0x020e, 0x08ce, 0x0064, 0x0e86, 0x00ae, 0x068c, 0x0248,	0x0026, 0x08ea, 0x0e42, 0x0622, 0x0200, 0x0040, 0x04a6, 0x0eee,
+	0x0000, 0x0000, 0x0eaa, 0x020a, 0x0004, 0x06ee, 0x048a, 0x0046,	0x0a66, 0x0844, 0x0422, 0x0222, 0x0200, 0x0444, 0x0888, 0x0eee,
+	0x0000, 0x0002, 0x0ea4, 0x020a, 0x0000, 0x00ae, 0x026c, 0x0048,	0x0026, 0x0822, 0x0e60, 0x0400, 0x0000, 0x0004, 0x0eca, 0x0eee,
+	0x0000, 0x0000, 0x0e04, 0x0200, 0x0400, 0x00ce, 0x0468, 0x0046,	0x0224, 0x0e24, 0x0a02, 0x0600, 0x0002, 0x0022, 0x0888, 0x0eee,
+	0x0000, 0x0000, 0x0222, 0x0888, 0x0ce8, 0x0666, 0x06ce, 0x0068,	0x0024, 0x0ecc, 0x040c, 0x0004, 0x0444, 0x0220, 0x0666, 0x0eee,
+	0x0000, 0x040e, 0x0208, 0x0824, 0x0222, 0x00ce, 0x084e, 0x0046,	0x0204, 0x0eac, 0x0e66, 0x0446, 0x0002, 0x0888, 0x0cca, 0x0eee,
+	0x0000, 0x0000, 0x0220, 0x0422, 0x0644, 0x0664, 0x0886, 0x0aa6,	0x0cca, 0x02a4, 0x0002, 0x0224, 0x0448, 0x066c, 0x0aae, 0x0eee,
+	0x0000, 0x042e, 0x0ce8, 0x00a0, 0x0040, 0x00ee, 0x08ce, 0x046a,	0x0226, 0x0e88, 0x0a24, 0x0602, 0x0000, 0x0222, 0x0000, 0x0eee,
+	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, 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, 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, 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, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0e46, 0x0262, 0x00e0, 0x000e, 0x08ce, 0x048a,	0x0448, 0x0ae4, 0x0800, 0x0400, 0x0222, 0x0224, 0x0888, 0x0eee,
+	0x0000, 0x0caa, 0x0440, 0x0684, 0x0ce8, 0x00ec, 0x0088, 0x0046,	0x0024, 0x0200, 0x0422, 0x0644, 0x0866, 0x0a88, 0x0ecc, 0x0eee,
+	0x0000, 0x0cee, 0x0acc, 0x08aa, 0x0688, 0x0466, 0x0244, 0x0022,	0x0006, 0x062e, 0x0222, 0x0000, 0x0044, 0x008a, 0x08ee, 0x0eee,
+	0x0000, 0x0ecc, 0x0ca8, 0x0a86, 0x0864, 0x0642, 0x0420, 0x0200,	0x0006, 0x062e, 0x0220, 0x0000, 0x0224, 0x044a, 0x0aac, 0x0eee,
+	0x0000, 0x0eec, 0x0cca, 0x0aa8, 0x0886, 0x0664, 0x0442, 0x0200,	0x0006, 0x062e, 0x0222, 0x0000, 0x0046, 0x028a, 0x06cc, 0x0eee,
+	0x0000, 0x0202, 0x0248, 0x06ee, 0x028a, 0x0c8c, 0x086a, 0x0646,	0x0424, 0x0848, 0x0826, 0x0cce, 0x0000, 0x0402, 0x0468, 0x0eee,
+	0x0000, 0x0002, 0x0248, 0x06ee, 0x026a, 0x08ac, 0x046a, 0x0226,	0x0024, 0x0448, 0x0224, 0x0aaa, 0x0000, 0x0022, 0x0468, 0x0eee,
+	0x0000, 0x0020, 0x0228, 0x0e6e, 0x0e4e, 0x0eea, 0x0e84, 0x0842,	0x0220, 0x0844, 0x0640, 0x0aaa, 0x0000, 0x0020, 0x0468, 0x0eee,
+	0x0000, 0x0ace, 0x08ac, 0x068a, 0x0468, 0x0446, 0x0224, 0x0000,	0x06ac, 0x028a, 0x0248, 0x0024, 0x0002, 0x0444, 0x0aaa, 0x0eee,
+	0x0000, 0x0eec, 0x0cea, 0x0ac8, 0x08a6, 0x0684, 0x0442, 0x0000,	0x06ea, 0x04c8, 0x0284, 0x0064, 0x0022, 0x0444, 0x0aaa, 0x0eee,
+	0x0000, 0x0206, 0x0e8c, 0x0808, 0x082e, 0x02ee, 0x006c, 0x0402,	0x0e86, 0x0ea8, 0x0eca, 0x0a24, 0x0000, 0x0c46, 0x0c68, 0x0eee,
+	0x0000, 0x0caa, 0x0008, 0x000c, 0x0002, 0x04ee, 0x0e0e, 0x0022,	0x0004, 0x0e84, 0x0622, 0x0422, 0x0866, 0x0a88, 0x008e, 0x0eee,
+	0x0000, 0x0000, 0x0002, 0x0024, 0x046a, 0x068c, 0x08ae, 0x0ace,	0x000c, 0x0446, 0x0220, 0x0242, 0x0664, 0x0886, 0x0ca8, 0x0eee,
+	0x0000, 0x0000, 0x0002, 0x0024, 0x0026, 0x0248, 0x046c, 0x068c,	0x0422, 0x0620, 0x0400, 0x0ccc, 0x0420, 0x0666, 0x0222, 0x0eee,
+	0x0000, 0x0000, 0x004e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0eee,
+	0x0000, 0x0022, 0x0000, 0x00e0, 0x00e0, 0x0026, 0x026c, 0x026a,	0x00e0, 0x00e0, 0x0046, 0x006a, 0x02ac, 0x04ce, 0x06ee, 0x0eee,
+	0x0000, 0x0248, 0x026c, 0x048c, 0x0224, 0x04ae, 0x00e0, 0x04ce,	0x06ce, 0x0022, 0x06cc, 0x000e, 0x08cc, 0x0000, 0x0acc, 0x0ccc,
+	0x0000, 0x0026, 0x026c, 0x048c, 0x0224, 0x04ae, 0x0e00, 0x04ce,	0x06ce, 0x00a0, 0x06ee, 0x000e, 0x00ee, 0x0402, 0x0cee, 0x0eee,
+	0x0000, 0x0000, 0x0220, 0x0422, 0x0644, 0x0664, 0x0886, 0x0aa6,	0x0cca, 0x0282, 0x0002, 0x0224, 0x0448, 0x066c, 0x0aae, 0x0eee,
+	0x0000, 0x0000, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e,	0x0ca8, 0x0eea, 0x0c42, 0x0a02, 0x0200, 0x000e, 0x000e, 0x0eee,
+	0x0000, 0x0622, 0x0a44, 0x0c66, 0x0e88, 0x0eca, 0x00cc, 0x00ee,	0x0e40, 0x0000, 0x0000, 0x0422, 0x0000, 0x0000, 0x0000, 0x0eee
+};
+
+static const Uint16Provider kEoB1PalettesSegaCDProvider = { ARRAYSIZE(kEoB1PalettesSegaCD), kEoB1PalettesSegaCD };
+
+static const uint16 kEoB1PatternTable0SegaCD[1040] = {
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0079, 0x00f0,
+	0x00f1, 0x00f1, 0x00f1, 0x00f1, 0x00f1, 0x00f1, 0x00f2, 0x0076,
+	0x00f0, 0x00f1, 0x00f1, 0x00f1, 0x00f1, 0x00f1, 0x00f1, 0x00f2,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0089, 0x00f3,
+	0x00f4, 0x00f4, 0x00f4, 0x00f4, 0x00f4, 0x00f4, 0x00f5, 0x0086,
+	0x00f3, 0x00f4, 0x00f4, 0x00f4, 0x00f4, 0x00f4, 0x00f4, 0x00f5,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0099, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x00f7, 0x00f8, 0x00f8, 0x00f9, 0x0096,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x00f7, 0x00f8, 0x00f8, 0x00f9,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00a9, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x00f3, 0x00f4, 0x00f4, 0x00f5, 0x00a6,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x00f3, 0x00f4, 0x00f4, 0x00f5,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00b9, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x00f7, 0x00f8, 0x00f8, 0x00f9, 0x00b6,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x00f7, 0x00f8, 0x00f8, 0x00f9,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00c9, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x00f3, 0x00f4, 0x00f4, 0x00f5, 0x00c6,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x00f3, 0x00f4, 0x00f4, 0x00f5,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00d9, 0x00fa,
+	0x00fb, 0x00fc, 0x00fc, 0x00fc, 0x00fc, 0x00fc, 0x00f6, 0x00d6,
+	0x00fa, 0x00fb, 0x00fc, 0x00fc, 0x00fc, 0x00fc, 0x00fc, 0x00f6,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00c0, 0x00f0,
+	0x00f1, 0x00f1, 0x00f1, 0x00f1, 0x00f1, 0x00f1, 0x00f2, 0x007f,
+	0x00f0, 0x00f1, 0x00f1, 0x00f1, 0x00f1, 0x00f1, 0x00f1, 0x00f2,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0081, 0x00f3,
+	0x00f4, 0x00f4, 0x00f4, 0x00f4, 0x00f4, 0x00f4, 0x00f5, 0x008f,
+	0x00f3, 0x00f4, 0x00f4, 0x00f4, 0x00f4, 0x00f4, 0x00f4, 0x00f5,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0091, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x00f7, 0x00f8, 0x00f8, 0x00f9, 0x009f,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x00f7, 0x00f8, 0x00f8, 0x00f9,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00a1, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x00f3, 0x00f4, 0x00f4, 0x00f5, 0x00af,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x00f3, 0x00f4, 0x00f4, 0x00f5,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00b1, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x00f7, 0x00f8, 0x00f8, 0x00f9, 0x00bf,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x00f7, 0x00f8, 0x00f8, 0x00f9,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00c1, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x00f3, 0x00f4, 0x00f4, 0x00f5, 0x00cf,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x00f3, 0x00f4, 0x00f4, 0x00f5,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00d1, 0x00fa,
+	0x00fb, 0x00fc, 0x00fc, 0x00fc, 0x00fc, 0x00fc, 0x00f6, 0x00df,
+	0x00fa, 0x00fb, 0x00fc, 0x00fc, 0x00fc, 0x00fc, 0x00fc, 0x00f6,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0070, 0x0071,
+	0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079,
+	0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x00ac, 0x00ad,
+	0x00c6, 0x00c7, 0x0081, 0x00c9, 0x00c5, 0x00c6, 0x00cc, 0x00af,
+	0x009b, 0x008a, 0x00d0, 0x009c, 0x00af, 0x000b, 0x000c, 0x000d,
+	0x000e, 0x000f, 0x0034, 0x0035, 0x00bc, 0x00bd, 0x0080, 0x0081,
+	0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089,
+	0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x00bc, 0x00bd,
+	0x0086, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e,
+	0x004f, 0x009a, 0x009b, 0x00ac, 0x0010, 0x0011, 0x0012, 0x0013,
+	0x0014, 0x0015, 0x0016, 0x0017, 0x0036, 0x0090, 0x0090, 0x0091,
+	0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099,
+	0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00cc, 0x00cd,
+	0x0096, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056,
+	0x0057, 0x00aa, 0x00ab, 0x0037, 0x0018, 0x0019, 0x001a, 0x001b,
+	0x001c, 0x001d, 0x001e, 0x001f, 0x0038, 0x00a0, 0x00a0, 0x00a1,
+	0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9,
+	0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00dc, 0x00dd,
+	0x00a6, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e,
+	0x005f, 0x00ba, 0x00bb, 0x0020, 0x0021, 0x0022, 0x0023, 0x0039,
+	0x003a, 0x0024, 0x0025, 0x0026, 0x0027, 0x003b, 0x00b0, 0x00b1,
+	0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9,
+	0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00ec, 0x00ed,
+	0x0089, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066,
+	0x0067, 0x00c5, 0x003c, 0x0028, 0x0029, 0x002a, 0x003d, 0x003e,
+	0x003f, 0x0040, 0x002b, 0x002c, 0x002d, 0x0041, 0x00c0, 0x00c1,
+	0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9,
+	0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x008d, 0x008e,
+	0x0099, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e,
+	0x006f, 0x00d5, 0x0042, 0x002e, 0x002f, 0x0030, 0x0043, 0x0044,
+	0x0045, 0x0046, 0x0031, 0x0032, 0x0033, 0x0047, 0x00d0, 0x00d1,
+	0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9,
+	0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x009d, 0x009e,
+	0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x00a0,
+	0x0087, 0x0088, 0x0089, 0x00a4, 0x009f, 0x00ba, 0x00a4, 0x00a5,
+	0x00a6, 0x0086, 0x0087, 0x0096, 0x00ab, 0x00c9, 0x00e0, 0x00e1,
+	0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9,
+	0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00ad, 0x00ae,
+	0x0093, 0x0094, 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, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00bd, 0x00be,
+	0x00a3, 0x00a4, 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, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00c8, 0x00c9,
+	0x00b3, 0x00b4, 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, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00d8, 0x00d9,
+	0x00b0, 0x00b1, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
+	0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
+	0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
+	0x0098, 0x0099, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5,
+	0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00e8, 0x00e9
+};
+
+static const Uint16Provider kEoB1PatternTable0SegaCDProvider = { ARRAYSIZE(kEoB1PatternTable0SegaCD), kEoB1PatternTable0SegaCD };
+
+static const uint16 kEoB1PatternTable1SegaCD[36] = {
+	0x0000, 0x04da, 0x04da, 0x04da, 0x04da, 0x04db, 0x0000, 0x04dd,
+	0x0000, 0x0000, 0x0000, 0x04dd, 0x0000, 0x04dd, 0x0000, 0x0000,
+	0x0000, 0x04dd, 0x0000, 0x04dd, 0x0000, 0x0000, 0x0000, 0x04dd,
+	0x0000, 0x04dd, 0x0000, 0x0000, 0x0000, 0x04dd, 0x0000, 0x04df,
+	0x04df, 0x04df, 0x04df, 0x04e0
+};
+
+static const Uint16Provider kEoB1PatternTable1SegaCDProvider = { ARRAYSIZE(kEoB1PatternTable1SegaCD), kEoB1PatternTable1SegaCD };
+
+static const uint16 kEoB1PatternTable2SegaCD[1280] = {
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0060, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0060, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0060, 0x0060, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0060, 0x0060, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0031, 0x0060, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0061, 0x0001,
+	0x0060, 0x0060, 0x0060, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0060, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0061, 0x0001, 0x0061, 0x0061, 0x0001, 0x0001, 0x0010,
+	0x0011, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0020,
+	0x0021, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0060,
+	0x0060, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0010,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0061, 0x0001, 0x0061, 0x0001, 0x0060, 0x0001, 0x0030,
+	0x0031, 0x0001, 0x0001, 0x0001, 0x0001, 0x0061, 0x0001, 0x0060,
+	0x0060, 0x0060, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0020,
+	0x0001, 0x0061, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0011, 0x0001, 0x0011, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0061, 0x0061, 0x0001, 0x0001, 0x0001, 0x0060,
+	0x0060, 0x0001, 0x0060, 0x0060, 0x0001, 0x0001, 0x0060, 0x0030,
+	0x0031, 0x0061, 0x0001, 0x0001, 0x0001, 0x0061, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0021, 0x0001, 0x0021, 0x0001, 0x0001, 0x0001, 0x0001, 0x0030,
+	0x0031, 0x0061, 0x0001, 0x0001, 0x0001, 0x0061, 0x0001, 0x0060,
+	0x0060, 0x0060, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0061, 0x0061, 0x0061, 0x0001, 0x0001, 0x0001, 0x0060,
+	0x0060, 0x0001, 0x0060, 0x0060, 0x0060, 0x0060, 0x0001, 0x0001,
+	0x0031, 0x0001, 0x0031, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0061, 0x0061, 0x0061, 0x0001, 0x0001, 0x0001, 0x0060,
+	0x0060, 0x0001, 0x0060, 0x0060, 0x0001, 0x0001, 0x0060, 0x0060,
+	0x0001, 0x0061, 0x0001, 0x0011, 0x0001, 0x0001, 0x0001, 0x0061,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0061, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0010, 0x0011, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0061, 0x0001, 0x0021, 0x0001, 0x0001, 0x0001, 0x0061,
+	0x0001, 0x0001, 0x0060, 0x0060, 0x0060, 0x0060, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0060, 0x0061, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0020, 0x0021, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0061, 0x0061, 0x0031, 0x0001, 0x0001, 0x0061, 0x0001,
+	0x0061, 0x0001, 0x0060, 0x0060, 0x0060, 0x0060, 0x0060, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0061, 0x0001,
+	0x0060, 0x0061, 0x0060, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0030, 0x0031, 0x0001, 0x0001, 0x0001, 0x0001, 0x0061, 0x0001,
+	0x0061, 0x0001, 0x0061, 0x0061, 0x0001, 0x0061, 0x0061, 0x0001,
+	0x0001, 0x0001, 0x0060, 0x0060, 0x0060, 0x0060, 0x0001, 0x0060,
+	0x0001, 0x0061, 0x0001, 0x0061, 0x0061, 0x0001, 0x0001, 0x0001,
+	0x0060, 0x0061, 0x0001, 0x0060, 0x0061, 0x0001, 0x0001, 0x0060,
+	0x0001, 0x0001, 0x0001, 0x0061, 0x0061, 0x0001, 0x0001, 0x0001,
+	0x0061, 0x0001, 0x0061, 0x0060, 0x0060, 0x0001, 0x0001, 0x0060,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0061, 0x0010, 0x0011, 0x0001,
+	0x0001, 0x0002, 0x0001, 0x0001, 0x0061, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0061, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0061, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0020, 0x0061, 0x0001,
+	0x0002, 0x0002, 0x0001, 0x0061, 0x0051, 0x0060, 0x0060, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0060, 0x0060, 0x0061,
+	0x0061, 0x0001, 0x0001, 0x0001, 0x0001, 0x0061, 0x0001, 0x0060,
+	0x0001, 0x0001, 0x0051, 0x0051, 0x0051, 0x0001, 0x0001, 0x0001,
+	0x0051, 0x0051, 0x0051, 0x0001, 0x0001, 0x0030, 0x0061, 0x0002,
+	0x0068, 0x0068, 0x0001, 0x0061, 0x0048, 0x0051, 0x0060, 0x0060,
+	0x0001, 0x0001, 0x0051, 0x0051, 0x0051, 0x0051, 0x0051, 0x0051,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0051, 0x0051, 0x0001, 0x0060,
+	0x0001, 0x0051, 0x0049, 0x0051, 0x0051, 0x0051, 0x0001, 0x0001,
+	0x0058, 0x0048, 0x0048, 0x0051, 0x0051, 0x0051, 0x0002, 0x0002,
+	0x0068, 0x0068, 0x0001, 0x0051, 0x0058, 0x0051, 0x0051, 0x0001,
+	0x0051, 0x0048, 0x0051, 0x0059, 0x0059, 0x0059, 0x0051, 0x0051,
+	0x0051, 0x0001, 0x0001, 0x0001, 0x0051, 0x0049, 0x0049, 0x0060,
+	0x0060, 0x0051, 0x0051, 0x0051, 0x0048, 0x0051, 0x0001, 0x0051,
+	0x0068, 0x0058, 0x0058, 0x0048, 0x0048, 0x0058, 0x0068, 0x0002,
+	0x0068, 0x0068, 0x0068, 0x0058, 0x0051, 0x0048, 0x0051, 0x0049,
+	0x0051, 0x0051, 0x0049, 0x0069, 0x0069, 0x0069, 0x0069, 0x0051,
+	0x0059, 0x0051, 0x0051, 0x0051, 0x0049, 0x0049, 0x0059, 0x0061,
+	0x0048, 0x0049, 0x0069, 0x0051, 0x0048, 0x0049, 0x0001, 0x0001,
+	0x0068, 0x0068, 0x0068, 0x0058, 0x0058, 0x0068, 0x0058, 0x0068,
+	0x0068, 0x0068, 0x0068, 0x0068, 0x0069, 0x0058, 0x0058, 0x0051,
+	0x0051, 0x0058, 0x0059, 0x0068, 0x0048, 0x0069, 0x0068, 0x0069,
+	0x0069, 0x0051, 0x0051, 0x0059, 0x0049, 0x0059, 0x0069, 0x0049,
+	0x0049, 0x0059, 0x0059, 0x0069, 0x0058, 0x0048, 0x0049, 0x0001,
+	0x0048, 0x0048, 0x0048, 0x0068, 0x0068, 0x0069, 0x0068, 0x0068,
+	0x0068, 0x0068, 0x0068, 0x0048, 0x0048, 0x0048, 0x0048, 0x0058,
+	0x0059, 0x004b, 0x004b, 0x004b, 0x004b, 0x004b, 0x004b, 0x004b,
+	0x004b, 0x004b, 0x0051, 0x004b, 0x0059, 0x0069, 0x004b, 0x0059,
+	0x0059, 0x0069, 0x0048, 0x0068, 0x0048, 0x0048, 0x0049, 0x0001,
+	0x004b, 0x004b, 0x004b, 0x004b, 0x004b, 0x004b, 0x004b, 0x004b,
+	0x004b, 0x004b, 0x004b, 0x004b, 0x004b, 0x004b, 0x004b, 0x004b,
+	0x004b, 0x004b, 0x004b, 0x004b, 0x004b, 0x0069, 0x0059, 0x0048,
+	0x0048, 0x0048, 0x0048, 0x0048, 0x004b, 0x004b, 0x004b, 0x004b,
+	0x004b, 0x004b, 0x004b, 0x004b, 0x004b, 0x0069, 0x0069, 0x0069,
+	0x0073, 0x0073, 0x0073, 0x0073, 0x0073, 0x0073, 0x0073, 0x0073,
+	0x0073, 0x0073, 0x0073, 0x0073, 0x0073, 0x0073, 0x0073, 0x0073,
+	0x0073, 0x0062, 0x0062, 0x0062, 0x0062, 0x0062, 0x0062, 0x0062,
+	0x0062, 0x0062, 0x0062, 0x0062, 0x0062, 0x0062, 0x0062, 0x0062,
+	0x0062, 0x0062, 0x0062, 0x0062, 0x0062, 0x0062, 0x0062, 0x0073,
+	0x0072, 0x0073, 0x0062, 0x0063, 0x0072, 0x0072, 0x0072, 0x0062,
+	0x0062, 0x0072, 0x0072, 0x0062, 0x0063, 0x0062, 0x0004, 0x0004,
+	0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004,
+	0x0004, 0x0004, 0x0004, 0x0073, 0x0073, 0x0073, 0x0073, 0x0062,
+	0x0062, 0x0062, 0x0062, 0x0062, 0x0073, 0x0073, 0x0073, 0x0073,
+	0x0072, 0x0073, 0x0072, 0x0073, 0x0072, 0x0072, 0x0073, 0x0072,
+	0x0072, 0x0073, 0x0004, 0x0004, 0x0004, 0x0035, 0x0035, 0x0034,
+	0x0035, 0x0035, 0x0035, 0x0034, 0x0035, 0x0034, 0x0034, 0x0034,
+	0x0034, 0x0034, 0x0034, 0x0004, 0x0004, 0x0004, 0x0004, 0x0072,
+	0x0072, 0x0072, 0x0072, 0x0072, 0x0072, 0x0072, 0x0072, 0x0072,
+	0x0072, 0x0072, 0x0072, 0x0072, 0x0072, 0x0072, 0x0073, 0x0004,
+	0x0004, 0x0035, 0x0035, 0x0055, 0x0055, 0x0055, 0x0055, 0x0055,
+	0x0055, 0x0055, 0x0055, 0x0055, 0x0055, 0x0055, 0x0055, 0x0055,
+	0x0055, 0x0055, 0x0055, 0x0035, 0x0035, 0x0034, 0x0034, 0x0004,
+	0x0004, 0x0004, 0x0004, 0x0072, 0x0004, 0x0072, 0x0072, 0x0072,
+	0x0072, 0x0072, 0x0072, 0x0004, 0x0004, 0x0004, 0x0004, 0x0035,
+	0x0035, 0x0035, 0x0055, 0x0055, 0x0055, 0x00a1, 0x00a2, 0x00a3,
+	0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab,
+	0x00ac, 0x00ad, 0x0055, 0x0055, 0x0055, 0x0035, 0x0035, 0x0035,
+	0x0035, 0x0014, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0072,
+	0x0072, 0x0072, 0x0004, 0x0004, 0x0035, 0x0035, 0x0035, 0x0055,
+	0x0055, 0x0055, 0x0055, 0x0055, 0x00b0, 0x00b1, 0x00b2, 0x00b3,
+	0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb,
+	0x00bc, 0x00bd, 0x00be, 0x00bf, 0x0055, 0x0055, 0x0055, 0x0035,
+	0x0035, 0x0035, 0x0014, 0x0004, 0x0014, 0x0004, 0x0004, 0x0072,
+	0x0072, 0x0072, 0x0072, 0x0004, 0x0004, 0x0035, 0x0035, 0x0035,
+	0x0055, 0x0055, 0x0055, 0x0055, 0x00c0, 0x00c1, 0x00c2, 0x00c3,
+	0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb,
+	0x00cc, 0x00cd, 0x00ce, 0x0055, 0x0055, 0x0055, 0x0035, 0x0035,
+	0x0014, 0x0004, 0x0014, 0x0004, 0x0014, 0x0004, 0x0072, 0x0072,
+	0x0072, 0x0072, 0x0072, 0x0072, 0x0004, 0x0004, 0x0004, 0x0004,
+	0x0035, 0x0055, 0x0055, 0x0055, 0x0055, 0x0055, 0x00d2, 0x00d3,
+	0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db,
+	0x00dc, 0x00dd, 0x0055, 0x0055, 0x0055, 0x0055, 0x0035, 0x0035,
+	0x0004, 0x0004, 0x0004, 0x0072, 0x0004, 0x0072, 0x0072, 0x0072,
+	0x0072, 0x0072, 0x0072, 0x0072, 0x0072, 0x0072, 0x0004, 0x0015,
+	0x0004, 0x0035, 0x0055, 0x0055, 0x0055, 0x0055, 0x0055, 0x0055,
+	0x0055, 0x0055, 0x0055, 0x0055, 0x0055, 0x0055, 0x0055, 0x00ff,
+	0x0055, 0x0055, 0x0055, 0x0055, 0x0055, 0x0034, 0x0014, 0x0014,
+	0x0004, 0x0072, 0x0072, 0x0072, 0x0072, 0x0072, 0x0072, 0x0072,
+	0x0072, 0x0072, 0x0072, 0x0072, 0x0072, 0x0072, 0x0072, 0x0072,
+	0x0072, 0x0004, 0x0004, 0x0004, 0x0035, 0x0035, 0x0035, 0x0035,
+	0x00ff, 0x0055, 0x0055, 0x0055, 0x0055, 0x0055, 0x0055, 0x0055,
+	0x0055, 0x0055, 0x0055, 0x00fd, 0x0055, 0x0055, 0x0034, 0x0014,
+	0x0004, 0x0004, 0x0004, 0x0072, 0x0072, 0x0072, 0x0072, 0x0072
+};
+
+static const Uint16Provider kEoB1PatternTable2SegaCDProvider = { ARRAYSIZE(kEoB1PatternTable2SegaCD), kEoB1PatternTable2SegaCD };
+
+static const uint16 kEoB1PatternTable3SegaCD[378] = {
+	0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0005, 0x0006, 0x0006,
+	0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x0006,
+	0x0006, 0x000f, 0x0010, 0x0056, 0x0056, 0x0056, 0x0056, 0x0015,
+	0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016,
+	0x0016, 0x0016, 0x0016, 0x001f, 0x0010, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x0025, 0x0026, 0x0016, 0x000c, 0x0029, 0x0029, 0x0029,
+	0x0029, 0x0029, 0x0029, 0x0032, 0x0016, 0x001f, 0x0010, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x0035, 0x0036, 0x0037, 0x003d, 0x0029,
+	0x0029, 0x0029, 0x0029, 0x0029, 0x0029, 0x0032, 0x0016, 0x001f,
+	0x0010, 0x0056, 0x0056, 0x0056, 0x0056, 0x0045, 0x0046, 0x0047,
+	0x0048, 0x0048, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+	0x0003, 0x0004, 0x0050, 0x0051, 0x0052, 0x0051, 0x0051, 0x0055,
+	0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d,
+	0x005e, 0x005f, 0x0013, 0x0014, 0x0060, 0x0016, 0x0062, 0x0016,
+	0x0016, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b,
+	0x006c, 0x006d, 0x006e, 0x006f, 0x0023, 0x0024, 0x0060, 0x0071,
+	0x0072, 0x0071, 0x0071, 0x0049, 0x0076, 0x0077, 0x00af, 0x0079,
+	0x007a, 0x007b, 0x0016, 0x0016, 0x0022, 0x007f, 0x0033, 0x0044,
+	0x0060, 0x0016, 0x0062, 0x0016, 0x0016, 0x0075, 0x0086, 0x0087,
+	0x0088, 0x0089, 0x008a, 0x008b, 0x0016, 0x0016, 0x0022, 0x008f,
+	0x0043, 0x0044, 0x0060, 0x0071, 0x0072, 0x0071, 0x0071, 0x0095,
+	0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d,
+	0x009e, 0x009f, 0x0053, 0x0054, 0x0060, 0x0016, 0x0062, 0x0016,
+	0x0062, 0x00a5, 0x0016, 0x00a7, 0x00a8, 0x00a9, 0x0056, 0x00ab,
+	0x00ac, 0x0016, 0x0016, 0x00af, 0x0056, 0x00c4, 0x0060, 0x0071,
+	0x0072, 0x0071, 0x0072, 0x00b5, 0x0071, 0x00b7, 0x00b8, 0x0056,
+	0x00ba, 0x00bb, 0x007b, 0x0016, 0x0016, 0x00bf, 0x0066, 0x0021,
+	0x0060, 0x0016, 0x0062, 0x0016, 0x0062, 0x00a5, 0x0016, 0x00c7,
+	0x00c8, 0x00c9, 0x00a6, 0x00b6, 0x00bd, 0x00be, 0x0002, 0x0016,
+	0x0016, 0x0031, 0x0060, 0x0071, 0x0072, 0x0071, 0x0072, 0x00a5,
+	0x0016, 0x00ad, 0x00ae, 0x0056, 0x0094, 0x00a4, 0x00b4, 0x0042,
+	0x0012, 0x0016, 0x0016, 0x0031, 0x0060, 0x0016, 0x0062, 0x0016,
+	0x0016, 0x0039, 0x0063, 0x0064, 0x0073, 0x0056, 0x007c, 0x007d,
+	0x0016, 0x0016, 0x0022, 0x0061, 0x0061, 0x0041, 0x0060, 0x0071,
+	0x0072, 0x0071, 0x0071, 0x0049, 0x0016, 0x0016, 0x002d, 0x002e,
+	0x008c, 0x0093, 0x0016, 0x0016, 0x0022, 0x0016, 0x0016, 0x0031,
+	0x0060, 0x0016, 0x0062, 0x0016, 0x0016, 0x0022, 0x0016, 0x0016,
+	0x0080, 0x0056, 0x0082, 0x0083, 0x000b, 0x000b, 0x0070, 0x0061,
+	0x0061, 0x0041, 0x0060, 0x0071, 0x0072, 0x0071, 0x0071, 0x0007,
+	0x0008, 0x0009, 0x0090, 0x0091, 0x0092, 0x0093, 0x001b, 0x001b,
+	0x001d, 0x0016, 0x0016, 0x0031, 0x0060, 0x0016, 0x0062, 0x0016,
+	0x0016, 0x0017, 0x0018, 0x0019, 0x00a0, 0x00a1, 0x00a2, 0x002c,
+	0x0016, 0x0016, 0x002f, 0x0020, 0x00c5, 0x00c6, 0x0060, 0x0071,
+	0x0072, 0x0071, 0x0071, 0x0027, 0x0028, 0x000b, 0x00b0, 0x00b1,
+	0x00b2, 0x003c, 0x003e, 0x003e, 0x003f, 0x0030, 0x002a, 0x002b,
+	0x00c0, 0x00c1, 0x00c1, 0x00c1, 0x00c1, 0x00c2, 0x00c2, 0x00c2,
+	0x00c2, 0x00c2, 0x00c2, 0x00c2, 0x00c2, 0x00c2, 0x00c2, 0x0040,
+	0x003a, 0x003b
+};
+
+static const Uint16Provider kEoB1PatternTable3SegaCDProvider = { ARRAYSIZE(kEoB1PatternTable3SegaCD), kEoB1PatternTable3SegaCD };
+
+static const uint16 kEoB1PatternTable4SegaCD[378] = {
+	0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0005, 0x0006, 0x0006,
+	0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x0006,
+	0x0006, 0x000f, 0x0010, 0x0056, 0x0056, 0x0056, 0x0056, 0x0015,
+	0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016,
+	0x0016, 0x0016, 0x0016, 0x001f, 0x0010, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x0025, 0x0026, 0x0016, 0x000c, 0x0029, 0x0029, 0x0029,
+	0x0029, 0x0029, 0x0029, 0x0032, 0x0016, 0x001f, 0x0010, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x0035, 0x0036, 0x0037, 0x003d, 0x0029,
+	0x0029, 0x0029, 0x0029, 0x0029, 0x0029, 0x0032, 0x0016, 0x001f,
+	0x0010, 0x0056, 0x0056, 0x0056, 0x0056, 0x0045, 0x0046, 0x0011,
+	0x0011, 0x0011, 0x0011, 0x0011, 0x004c, 0x004d, 0x004e, 0x004f,
+	0x0003, 0x0004, 0x00bc, 0x0085, 0x0085, 0x0085, 0x0085, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x001e, 0x005d,
+	0x005e, 0x005f, 0x0013, 0x0014, 0x008d, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056,
+	0x0034, 0x0074, 0x0078, 0x007e, 0x0081, 0x0084, 0x008d, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x00c4,
+	0x008d, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x00c4, 0x008d, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x00c4, 0x008d, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x00c4, 0x008d, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x00c4,
+	0x008d, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x00c4, 0x008d, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x00c4, 0x008d, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x00c4, 0x008d, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x00c4,
+	0x008d, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x00c4, 0x008d, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x00c4, 0x008d, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x008e, 0x00aa, 0x00b9, 0x008d, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056,
+	0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0030, 0x002a, 0x002b,
+	0x00c3, 0x00c2, 0x00c2, 0x00c2, 0x00c2, 0x00c2, 0x00c2, 0x00c2,
+	0x00c2, 0x00c2, 0x00c2, 0x00c2, 0x00c2, 0x00c2, 0x00c2, 0x0040,
+	0x003a, 0x003b
+};
+
+static const Uint16Provider kEoB1PatternTable4SegaCDProvider = { ARRAYSIZE(kEoB1PatternTable4SegaCD), kEoB1PatternTable4SegaCD };
+
+static const uint16 kEoB1PatternTable5SegaCD[378] = {
+	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, 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, 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, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034,
+	0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0040, 0x0041, 0x0042,
+	0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a,
+	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, 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, 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, 0x0050,
+	0x0051, 0x004f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x002f,
+	0x003c, 0x0054, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x003e, 0x004c, 0x0051, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x002e, 0x004d, 0x004c, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0053, 0x003e, 0x0050, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x002e, 0x003d, 0x002d, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0055, 0x0003, 0x0004,
+	0x0003, 0x0010, 0x0017, 0x0056, 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, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x003c, 0x0054,
+	0x004e, 0x0000, 0x0000, 0x0000, 0x003f, 0x0052, 0x003f, 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, 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, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000
+};
+
+static const Uint16Provider kEoB1PatternTable5SegaCDProvider = { ARRAYSIZE(kEoB1PatternTable5SegaCD), kEoB1PatternTable5SegaCD };
+
+static const uint16 kEoB1PatternAddTable1SegaCD[256] = {
+	0x0603, 0x0770, 0x0771, 0x0772, 0x0773, 0x0774, 0x0775, 0x0776,
+	0x0777, 0x0778, 0x0779, 0x077a, 0x077b, 0x077c, 0x077d, 0x077e,
+	0x077f, 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0786,
+	0x0787, 0x0788, 0x0789, 0x078a, 0x078b, 0x078c, 0x078d, 0x078e,
+	0x078f, 0x0790, 0x0791, 0x0792, 0x0793, 0x0794, 0x0795, 0x0796,
+	0x0797, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, 0x079e,
+	0x079f, 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a6,
+	0x07a7, 0x07a8, 0x07a9, 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07ae,
+	0x07af, 0x07b0, 0x07b1, 0x07b2, 0x07b3, 0x07b4, 0x07b5, 0x07b6,
+	0x07b7, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bc, 0x0607, 0x060b,
+	0x060f, 0x0613, 0x0617, 0x061b, 0x061f, 0x0623, 0x0627, 0x062b,
+	0x062f, 0x0633, 0x0637, 0x063b, 0x063f, 0x0643, 0x0647, 0x064b,
+	0x064f, 0x0653, 0x0657, 0x065b, 0x065f, 0x0663, 0x0667, 0x066b,
+	0x066f, 0x0670, 0x0671, 0x0672, 0x0673, 0x0674, 0x0675, 0x0676,
+	0x0677, 0x0678, 0x0679, 0x067a, 0x067b, 0x067c, 0x067d, 0x067e,
+	0x067f, 0x06c1, 0x06c2, 0x06c3, 0x06c4, 0x06c5, 0x06c6, 0x06c7,
+	0x06c8, 0x06c9, 0x06ca, 0x06cb, 0x06cc, 0x06cd, 0x06ce, 0x06cf,
+	0x06d0, 0x06d1, 0x06d2, 0x06d3, 0x06d4, 0x06d5, 0x06d6, 0x06d7,
+	0x06d8, 0x06d9, 0x06da, 0x06db, 0x06dc, 0x06dd, 0x06de, 0x06df,
+	0x06f4, 0x06f5, 0x06f6, 0x06f7, 0x06f8, 0x06f9, 0x06fa, 0x06fb,
+	0x06fc, 0x06fd, 0x06fe, 0x06ff, 0x0703, 0x0707, 0x070b, 0x070f,
+	0x0713, 0x0717, 0x071b, 0x071f, 0x0723, 0x0727, 0x072b, 0x072f,
+	0x0733, 0x0737, 0x073b, 0x073f, 0x0743, 0x0747, 0x074b, 0x074f,
+	0x0753, 0x0757, 0x075b, 0x075f, 0x0763, 0x0767, 0x076b, 0x076f,
+	0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0686, 0x0687,
+	0x0688, 0x0689, 0x068a, 0x068b, 0x068c, 0x068d, 0x068e, 0x068f,
+	0x0690, 0x0691, 0x0692, 0x0693, 0x0694, 0x0695, 0x0696, 0x0697,
+	0x0698, 0x0699, 0x069a, 0x069b, 0x069c, 0x069d, 0x069e, 0x069f,
+	0x06a0, 0x06a1, 0x06a2, 0x06a3, 0x06a4, 0x06a5, 0x06a6, 0x06a7,
+	0x06a8, 0x06a9, 0x06aa, 0x06ab, 0x06ac, 0x06ad, 0x06ae, 0x06af,
+	0x06b0, 0x06b1, 0x06b2, 0x06b3, 0x06b4, 0x06b5, 0x06b6, 0x06b7,
+	0x06b8, 0x06b9, 0x06ba, 0x06bb, 0x06bc, 0x06bd, 0x06be, 0x06bf
+};
+
+static const Uint16Provider kEoB1PatternAddTable1SegaCDProvider = { ARRAYSIZE(kEoB1PatternAddTable1SegaCD), kEoB1PatternAddTable1SegaCD };
+
+static const uint16 kEoB1PatternAddTable2SegaCD[200] = {
+	0x06bc, 0x06bd, 0x06bd, 0x06bd, 0x06bd, 0x06bd, 0x06bd, 0x06bd,
+	0x06bd, 0x06bd, 0x06bd, 0x06bd, 0x06bd, 0x06bd, 0x06bd, 0x06bd,
+	0x06bd, 0x06bd, 0x06bd, 0x06bd, 0x06bd, 0x06bd, 0x06bd, 0x06bd,
+	0x06bd, 0x06bd, 0x06bd, 0x06bd, 0x06bd, 0x06bd, 0x06bd, 0x06bd,
+	0x06bd, 0x06bd, 0x06bd, 0x06bd, 0x06bd, 0x06bd, 0x06bd, 0x06bd,
+	0x06be, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2,
+	0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2,
+	0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2,
+	0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2,
+	0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b3, 0x06b3, 0x06b4,
+	0x06be, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2,
+	0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2,
+	0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2,
+	0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2,
+	0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b5, 0x06b6, 0x06b7, 0x06b8,
+	0x06be, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2,
+	0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2,
+	0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2,
+	0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b2,
+	0x06b2, 0x06b2, 0x06b2, 0x06b2, 0x06b9, 0x06ba, 0x06bb, 0x06bf,
+	0x16bc, 0x16bd, 0x16bd, 0x16bd, 0x16bd, 0x16bd, 0x16bd, 0x16bd,
+	0x16bd, 0x16bd, 0x16bd, 0x16bd, 0x16bd, 0x16bd, 0x16bd, 0x16bd,
+	0x16bd, 0x16bd, 0x16bd, 0x16bd, 0x16bd, 0x16bd, 0x16bd, 0x16bd,
+	0x16bd, 0x16bd, 0x16bd, 0x16bd, 0x16bd, 0x16bd, 0x16bd, 0x16bd,
+	0x16bd, 0x16bd, 0x16bd, 0x16bd, 0x16bd, 0x16bd, 0x16bd, 0x16bd
+};
+
+static const Uint16Provider kEoB1PatternAddTable2SegaCDProvider = { ARRAYSIZE(kEoB1PatternAddTable2SegaCD), kEoB1PatternAddTable2SegaCD };
diff --git a/devtools/create_kyradat/resources/eob1_segacd_english.h b/devtools/create_kyradat/resources/eob1_segacd_english.h
new file mode 100644
index 0000000000..cec22fe703
--- /dev/null
+++ b/devtools/create_kyradat/resources/eob1_segacd_english.h
@@ -0,0 +1,1149 @@
+static const char *const kEoB1ChargenStrings1SegaCDEnglish[9] = {
+	"\r Your party is complete.\r Select the PLAY button to start the game.\r",
+	"          ",
+	"STR        AC\rINT        HP\rWIS        LVL\rDEX\rCON\rCHA\r",
+	"%s\r%02d\r%02d\r%02d\r%02d\r%02d",
+	"%02d\r%02d",
+	"%d",
+	"%d/%d",
+	"%d/%d/%d",
+	" Select the box of the character you wish to create or view.\r"
+};
+
+static const StringListProvider kEoB1ChargenStrings1SegaCDEnglishProvider = { ARRAYSIZE(kEoB1ChargenStrings1SegaCDEnglish), kEoB1ChargenStrings1SegaCDEnglish };
+
+static const char *const kEoB1ChargenStrings2SegaCDEnglish[12] = {
+	"",
+	"",
+	"",
+	"",
+	"",
+	"",
+	"",
+	"",
+	"SELECT RACE:\r",
+	"SELECT CLASS:\r",
+	"SELECT ALIGNMENT:\r",
+	"NAME:"
+};
+
+static const StringListProvider kEoB1ChargenStrings2SegaCDEnglishProvider = { ARRAYSIZE(kEoB1ChargenStrings2SegaCDEnglish), kEoB1ChargenStrings2SegaCDEnglish };
+
+static const char *const kEoB1ChargenStatStringsSegaCDEnglish[12] = {
+	"STR        AC\rINT        HP\rWIS        LVL\rDEX\rCON\rCHA\r",
+	"",
+	"",
+	"",
+	"",
+	"",
+	"STR     DEX",
+	"INT     CON",
+	"WIS     CHA",
+	"AC",
+	"EXP   LVL",
+	"/"
+};
+
+static const StringListProvider kEoB1ChargenStatStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1ChargenStatStringsSegaCDEnglish), kEoB1ChargenStatStringsSegaCDEnglish };
+
+static const char *const kEoB1ChargenRaceSexStringsSegaCDEnglish[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 kEoB1ChargenRaceSexStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1ChargenRaceSexStringsSegaCDEnglish), kEoB1ChargenRaceSexStringsSegaCDEnglish };
+
+static const char *const kEoB1ChargenClassStringsSegaCDEnglish[29] = {
+	"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",
+	"FTR.",
+	"MAGE",
+	"CLERIC",
+	"THIEF",
+	"PAL.",
+	"RANGER",
+	"FTR/MU/THF.",
+	"FTR/CL/MU."
+};
+
+static const StringListProvider kEoB1ChargenClassStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1ChargenClassStringsSegaCDEnglish), kEoB1ChargenClassStringsSegaCDEnglish };
+
+static const char *const kEoB1ChargenAlignmentStringsSegaCDEnglish[9] = {
+	"LAWFUL GOOD",
+	"NEUTRAL GOOD",
+	"CHAOTIC GOOD",
+	"LAWFUL NEUTRAL",
+	"TRUE NEUTRAL",
+	"CHAOTIC NEUTRAL",
+	"LAWFUL EVIL",
+	"NEUTRAL EVIL",
+	"CHAOTIC EVIL"
+};
+
+static const StringListProvider kEoB1ChargenAlignmentStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1ChargenAlignmentStringsSegaCDEnglish), kEoB1ChargenAlignmentStringsSegaCDEnglish };
+
+static const char *const kEoB1MainMenuStringsSegaCDEnglish[3] = {
+	"Load game in progress",
+	"Start a new party",
+	"Start a default party"
+};
+
+static const StringListProvider kEoB1MainMenuStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1MainMenuStringsSegaCDEnglish), kEoB1MainMenuStringsSegaCDEnglish };
+
+static const char *const kEoB1BonusStringsSegaCDEnglish[9] = {
+	"Congratulations on completing all 12 Beholder Bonuses.",
+	"The names of the character in your winning Beholder Bonus party are:",
+	"Search results",
+	"Search time",
+	"Enemies",
+	"Steps",
+	"Arrows",
+	"Maps",
+	"Special searches"
+};
+
+static const StringListProvider kEoB1BonusStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1BonusStringsSegaCDEnglish), kEoB1BonusStringsSegaCDEnglish };
+
+static const char *const kEoB1TurnUndeadStringSegaCDEnglish[1] = {
+	"%s uses the power to turn undead!\r"
+};
+
+static const StringListProvider kEoB1TurnUndeadStringSegaCDEnglishProvider = { ARRAYSIZE(kEoB1TurnUndeadStringSegaCDEnglish), kEoB1TurnUndeadStringSegaCDEnglish };
+
+static const char *const kEoB1Npc0StringsSegaCDEnglish[2] = {
+	"",
+	"\rWhich should be resurrected?"
+};
+
+static const StringListProvider kEoB1Npc0StringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1Npc0StringsSegaCDEnglish), kEoB1Npc0StringsSegaCDEnglish };
+
+static const char *const kEoB1Npc11StringsSegaCDEnglish[3] = {
+	"Tend his wounds",
+	"Talk",
+	"Leave"
+};
+
+static const StringListProvider kEoB1Npc11StringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1Npc11StringsSegaCDEnglish), kEoB1Npc11StringsSegaCDEnglish };
+
+static const char *const kEoB1Npc12StringsSegaCDEnglish[2] = {
+	"Tend his wounds",
+	"Leave"
+};
+
+static const StringListProvider kEoB1Npc12StringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1Npc12StringsSegaCDEnglish), kEoB1Npc12StringsSegaCDEnglish };
+
+static const char *const kEoB1Npc21StringsSegaCDEnglish[3] = {
+	"Hear proposal",
+	"Leave",
+	"Once more"
+};
+
+static const StringListProvider kEoB1Npc21StringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1Npc21StringsSegaCDEnglish), kEoB1Npc21StringsSegaCDEnglish };
+
+static const char *const kEoB1Npc22StringsSegaCDEnglish[3] = {
+	"Help him",
+	"Leave",
+	"Once more"
+};
+
+static const StringListProvider kEoB1Npc22StringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1Npc22StringsSegaCDEnglish), kEoB1Npc22StringsSegaCDEnglish };
+
+static const char *const kEoB1Npc31StringsSegaCDEnglish[2] = {
+	"Heal Party",
+	"Leave"
+};
+
+static const StringListProvider kEoB1Npc31StringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1Npc31StringsSegaCDEnglish), kEoB1Npc31StringsSegaCDEnglish };
+
+static const char *const kEoB1Npc32StringsSegaCDEnglish[3] = {
+	"Heal Party",
+	"Resurrect Dead",
+	"Leave"
+};
+
+static const StringListProvider kEoB1Npc32StringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1Npc32StringsSegaCDEnglish), kEoB1Npc32StringsSegaCDEnglish };
+
+static const char *const kEoB1Npc4StringsSegaCDEnglish[2] = {
+	"Attack",
+	"Bribe"
+};
+
+static const StringListProvider kEoB1Npc4StringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1Npc4StringsSegaCDEnglish), kEoB1Npc4StringsSegaCDEnglish };
+
+static const char *const kEoB1Npc5StringsSegaCDEnglish[4] = {
+	"Kill her",
+	"Hear her out",
+	"Let her go",
+	"Once More"
+};
+
+static const StringListProvider kEoB1Npc5StringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1Npc5StringsSegaCDEnglish), kEoB1Npc5StringsSegaCDEnglish };
+
+static const char *const kEoB1Npc6StringsSegaCDEnglish[3] = {
+	"Surrender",
+	"Attack",
+	"Once more"
+};
+
+static const StringListProvider kEoB1Npc6StringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1Npc6StringsSegaCDEnglish), kEoB1Npc6StringsSegaCDEnglish };
+
+static const char *const kEoB1Npc7StringsSegaCDEnglish[4] = {
+	"Free Him",
+	"Kill Him",
+	"Leave",
+	"Once more"
+};
+
+static const StringListProvider kEoB1Npc7StringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1Npc7StringsSegaCDEnglish), kEoB1Npc7StringsSegaCDEnglish };
+
+static const char *const kEoB1PryDoorStringsSegaCDEnglish[7] = {
+	"Nobody is able to force the door.\r",
+	"The party forces the door.\r",
+	"",
+	"The party tries to force the door and fails.\r",
+	"",
+	"You can't put that item there.\r",
+	"No one is able to pry this door open.\r"
+};
+
+static const StringListProvider kEoB1PryDoorStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1PryDoorStringsSegaCDEnglish), kEoB1PryDoorStringsSegaCDEnglish };
+
+static const char *const kEoB1WarningStringsSegaCDEnglish[4] = {
+	"You can't go that way.\r",
+	"%s isn't capable of eating food!\r",
+	"You may only eat food!\r",
+	"Obtained map of this level!\r"
+};
+
+static const StringListProvider kEoB1WarningStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1WarningStringsSegaCDEnglish), kEoB1WarningStringsSegaCDEnglish };
+
+static const char *const kEoB1ParchmentStringsSegaCDEnglish[7] = {
+	"\rThe light of the stars sparkles in the gem.\rFollow one to see the other.",
+	"\rAround the neck\rmade of gold\rthe sign of Dwarves\ryou've been told",
+	"\rThe orb leads to great evil.",
+	"\rThe greatest weakness of the most feared creature is that, although it lurks in shadows and sees all, it cannot make itself invisible.",
+	"Commission and Letter of Marque:\r\r This document is a binding commission of service to the Lords and sovereign city of Waterdeep. The bearers of this document are agents of the Lords of Waterdeep and are granted full rights of passage beneath the city of Waterdeep. Any who would dare interfere risk the full penalty of our wrath.",
+	"\r Information has been presented to us that there is a plot afoot in our city. Evidence points to the sewers that run beneath Waterdeep. We have no information about the exact nature of the threat, but we feel the urgency is grave. We commission you to find the nature of the danger, and to destroy it if you are able.",
+	"\r You are granted full rights of marque. All treasures, artifacts, or other valuables are yours by right of conquest.\r This writ is made legal and binding by our mark on this fifth day of Marpenoth in the year of Shadows. "
+};
+
+static const StringListProvider kEoB1ParchmentStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1ParchmentStringsSegaCDEnglish), kEoB1ParchmentStringsSegaCDEnglish };
+
+static const char *const kEoB1ItemNamesSegaCDEnglish[98] = {
+	"Mouse Pointer",
+	"",
+	"Leather armor",
+	"Robe",
+	"Staff",
+	"Dagger",
+	"Short sword",
+	"Lock picks",
+	"Spellbook",
+	"Cleric Holy symbol",
+	"Leather boots",
+	"Iron Rations",
+	"NULL",
+	"Jeweled Key",
+	"Potion",
+	"Gem",
+	"Skull Key",
+	"Wand",
+	"Scroll",
+	"Ring",
+	"Ring of Protection +2",
+	"Adamantite Dart",
+	"Paladin Holy Symbol",
+	"Wand of Slivias",
+	"Dwarf Bones",
+	"Key",
+	"Commission and Letter of Marque",
+	"Axe",
+	"Dart",
+	"Halberd",
+	"Chainmail",
+	"Helmet",
+	"Dwarf Helmet",
+	"Silver Key",
+	"Adamantite Long Sword",
+	"Mace",
+	"Longsword",
+	"'Guinsoo'",
+	"Orb of Power",
+	"Dwarven Healing Potion",
+	"Rock",
+	"Rations",
+	"Fancy Robe",
+	"Igneous Rock",
+	"Spear",
+	"Stone Medallion",
+	"Halfling Bones",
+	"Arrow",
+	"Shield",
+	"Gold Key",
+	"Bow",
+	"Stone dagger",
+	"Sling",
+	"'Backstabber'",
+	"Long Sword",
+	"Dwarven Key",
+	"Medallion",
+	"Medallion of Adornment",
+	"'Drow Cleaver'",
+	"Stone Scepter",
+	"Dwarven Helmet",
+	"Dwarven Shield",
+	"Stone Necklace",
+	"Plate Mail",
+	"Scale Mail",
+	"Boots",
+	"Kenku Egg",
+	"Stone Ring",
+	"Bracers",
+	"Chieftain Halberd",
+	"Necklace",
+	"Necklace of Adornment",
+	"Luck Stone Medallion",
+	"'Slicer'",
+	"Banded Armor",
+	"Drow Key",
+	"Ruby Key",
+	"'Night Stalker'",
+	"Drow Bow",
+	"Drow Boots",
+	"Plate Mail of Great Beauty",
+	"Flail",
+	"Scepter of Kingly Might",
+	"Drow Shield",
+	"Stone Holy Symbol",
+	"Stone Orb",
+	"'Slasher'",
+	"Robe of Defense",
+	"'Flicka'",
+	"Human Bones",
+	"'Severious'",
+	"Wand of Fireballs",
+	"Cure Poison Potion",
+	"Holy Symbol",
+	"Spell Book",
+	"",
+	"Letter",
+	"Map"
+};
+
+static const StringListProvider kEoB1ItemNamesSegaCDEnglishProvider = { ARRAYSIZE(kEoB1ItemNamesSegaCDEnglish), kEoB1ItemNamesSegaCDEnglish };
+
+static const char *const kEoB1MapStrings1SegaCDEnglish[3] = {
+	"Upper",
+	"Lower",
+	"Exit"
+};
+
+static const StringListProvider kEoB1MapStrings1SegaCDEnglishProvider = { ARRAYSIZE(kEoB1MapStrings1SegaCDEnglish), kEoB1MapStrings1SegaCDEnglish };
+
+static const char *const kEoB1MapStrings2SegaCDEnglish[12] = {
+	"Floor1",
+	"Floor2",
+	"Floor3",
+	"Floor4",
+	"Floor5",
+	"Floor6",
+	"Floor7",
+	"Floor8",
+	"Floor9",
+	"Floor10",
+	"Floor11",
+	"Floor12"
+};
+
+static const StringListProvider kEoB1MapStrings2SegaCDEnglishProvider = { ARRAYSIZE(kEoB1MapStrings2SegaCDEnglish), kEoB1MapStrings2SegaCDEnglish };
+
+static const char *const kEoB1MapStrings3SegaCDEnglish[12] = {
+	"\x82""k""\x82""P",
+	"\x82""k""\x82""Q",
+	"\x82""k""\x82""R",
+	"\x82""k""\x82""S",
+	"\x82""k""\x82""T",
+	"\x82""k""\x82""U",
+	"\x82""k""\x82""V",
+	"\x82""k""\x82""W",
+	"\x82""k""\x82""X",
+	"\x82""k""\x82""P""\x82""O",
+	"\x82""k""\x82""P""\x82""P",
+	"\x82""k""\x82""P""\x82""Q"
+};
+
+static const StringListProvider kEoB1MapStrings3SegaCDEnglishProvider = { ARRAYSIZE(kEoB1MapStrings3SegaCDEnglish), kEoB1MapStrings3SegaCDEnglish };
+
+static const char *const kEoB1ItemSuffixStringsRingsSegaCDEnglish[4] = {
+	"Adornment",
+	"Wizardry",
+	"Sustenance",
+	"Feather Fall"
+};
+
+static const StringListProvider kEoB1ItemSuffixStringsRingsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1ItemSuffixStringsRingsSegaCDEnglish), kEoB1ItemSuffixStringsRingsSegaCDEnglish };
+
+static const char *const kEoB1ItemSuffixStringsPotionsSegaCDEnglish[8] = {
+	"Giant Strength",
+	"Healing",
+	"Extra Healing",
+	"Poison",
+	"Vitality",
+	"Speed",
+	"Invisibility",
+	"Cure Poison"
+};
+
+static const StringListProvider kEoB1ItemSuffixStringsPotionsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1ItemSuffixStringsPotionsSegaCDEnglish), kEoB1ItemSuffixStringsPotionsSegaCDEnglish };
+
+static const char *const kEoB1ItemSuffixStringsWandsSegaCDEnglish[7] = {
+	"Stick",
+	"Lightning",
+	"Frost",
+	"Curing",
+	"Fireball",
+	"Silvias",
+	"Magic Missile"
+};
+
+static const StringListProvider kEoB1ItemSuffixStringsWandsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1ItemSuffixStringsWandsSegaCDEnglish), kEoB1ItemSuffixStringsWandsSegaCDEnglish };
+
+static const char *const kEoB1RipItemStringsSegaCDEnglish[3] = {
+	"%s has lost her ",
+	"%s has lost his ",
+	".\r"
+};
+
+static const StringListProvider kEoB1RipItemStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1RipItemStringsSegaCDEnglish), kEoB1RipItemStringsSegaCDEnglish };
+
+static const char *const kEoB1CursedStringSegaCDEnglish[1] = {
+	"Cursed %s %d"
+};
+
+static const StringListProvider kEoB1CursedStringSegaCDEnglishProvider = { ARRAYSIZE(kEoB1CursedStringSegaCDEnglish), kEoB1CursedStringSegaCDEnglish };
+
+static const char *const kEoB1MagicObjectStringsSegaCDEnglish[5] = {
+	"Mage Scroll",
+	"Cleric Scroll",
+	"Ring",
+	"Potion",
+	"Wand"
+};
+
+static const StringListProvider kEoB1MagicObjectStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1MagicObjectStringsSegaCDEnglish), kEoB1MagicObjectStringsSegaCDEnglish };
+
+static const char *const kEoB1MagicObjectString5SegaCDEnglish[1] = {
+	"Stick"
+};
+
+static const StringListProvider kEoB1MagicObjectString5SegaCDEnglishProvider = { ARRAYSIZE(kEoB1MagicObjectString5SegaCDEnglish), kEoB1MagicObjectString5SegaCDEnglish };
+
+static const char *const kEoB1PatternSuffixSegaCDEnglish[1] = {
+	"%s of %s"
+};
+
+static const StringListProvider kEoB1PatternSuffixSegaCDEnglishProvider = { ARRAYSIZE(kEoB1PatternSuffixSegaCDEnglish), kEoB1PatternSuffixSegaCDEnglish };
+
+static const char *const kEoB1PatternGrFix1SegaCDEnglish[1] = {
+	"%s of %s"
+};
+
+static const StringListProvider kEoB1PatternGrFix1SegaCDEnglishProvider = { ARRAYSIZE(kEoB1PatternGrFix1SegaCDEnglish), kEoB1PatternGrFix1SegaCDEnglish };
+
+static const char *const kEoB1PatternGrFix2SegaCDEnglish[1] = {
+	"%s of %s"
+};
+
+static const StringListProvider kEoB1PatternGrFix2SegaCDEnglishProvider = { ARRAYSIZE(kEoB1PatternGrFix2SegaCDEnglish), kEoB1PatternGrFix2SegaCDEnglish };
+
+static const char *const kEoB1ValidateArmorStringSegaCDEnglish[1] = {
+	"%s can't wear that type of armor.\r"
+};
+
+static const StringListProvider kEoB1ValidateArmorStringSegaCDEnglishProvider = { ARRAYSIZE(kEoB1ValidateArmorStringSegaCDEnglish), kEoB1ValidateArmorStringSegaCDEnglish };
+
+static const char *const kEoB1ValidateNoDropStringSegaCDEnglish[1] = {
+	"You cant put that item there.\r"
+};
+
+static const StringListProvider kEoB1ValidateNoDropStringSegaCDEnglishProvider = { ARRAYSIZE(kEoB1ValidateNoDropStringSegaCDEnglish), kEoB1ValidateNoDropStringSegaCDEnglish };
+
+static const char *const kEoB1PotionStringsSegaCDEnglish[2] = {
+	"poisoned",
+	"%s feels %s!\r"
+};
+
+static const StringListProvider kEoB1PotionStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1PotionStringsSegaCDEnglish), kEoB1PotionStringsSegaCDEnglish };
+
+static const char *const kEoB1WandStringsSegaCDEnglish[2] = {
+	"The wand has no apparent magical effect.\r",
+	"no effect.\r"
+};
+
+static const StringListProvider kEoB1WandStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1WandStringsSegaCDEnglish), kEoB1WandStringsSegaCDEnglish };
+
+static const char *const kEoB1ItemMisuseStringsSegaCDEnglish[3] = {
+	"%s can not use this item.\r",
+	"This item automatically used when worn.\r",
+	"This item is not used in this way.\r"
+};
+
+static const StringListProvider kEoB1ItemMisuseStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1ItemMisuseStringsSegaCDEnglish), kEoB1ItemMisuseStringsSegaCDEnglish };
+
+static const char *const kEoB1TakenStringsSegaCDEnglish[1] = {
+	" taken.\r"
+};
+
+static const StringListProvider kEoB1TakenStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1TakenStringsSegaCDEnglish), kEoB1TakenStringsSegaCDEnglish };
+
+static const char *const kEoB1PotionEffectStringsSegaCDEnglish[8] = {
+	"much stronger",
+	"better",
+	"much better",
+	"ill for a moment",
+	"no longer hungry",
+	"fast and agile",
+	"transparent",
+	"better"
+};
+
+static const StringListProvider kEoB1PotionEffectStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1PotionEffectStringsSegaCDEnglish), kEoB1PotionEffectStringsSegaCDEnglish };
+
+static const char *const kEoB1YesNoStringsSegaCDEnglish[3] = {
+	"Yes",
+	"No",
+	"Once more"
+};
+
+static const StringListProvider kEoB1YesNoStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1YesNoStringsSegaCDEnglish), kEoB1YesNoStringsSegaCDEnglish };
+
+static const char *const kEoB1MoreStringsSegaCDEnglish[2] = {
+	"Next",
+	"Once more"
+};
+
+static const StringListProvider kEoB1MoreStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1MoreStringsSegaCDEnglish), kEoB1MoreStringsSegaCDEnglish };
+
+static const char *const kEoB1NpcMaxStringsSegaCDEnglish[1] = {
+	"You may only have six characters in your party.\rSelect the one you wish to drop."
+};
+
+static const StringListProvider kEoB1NpcMaxStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1NpcMaxStringsSegaCDEnglish), kEoB1NpcMaxStringsSegaCDEnglish };
+
+static const char *const kEoB1NpcJoinStringsSegaCDEnglish[1] = {
+	"%s joins the party.\r"
+};
+
+static const StringListProvider kEoB1NpcJoinStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1NpcJoinStringsSegaCDEnglish), kEoB1NpcJoinStringsSegaCDEnglish };
+
+static const char *const kEoB1CancelStringsSegaCDEnglish[1] = {
+	"Cancel"
+};
+
+static const StringListProvider kEoB1CancelStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1CancelStringsSegaCDEnglish), kEoB1CancelStringsSegaCDEnglish };
+
+static const char *const kEoB1MenuStringsSaveLoadSegaCDEnglish[8] = {
+	"",
+	"",
+	"",
+	"",
+	"\x82""r""\x82""`""\x82""u""\x82""d""\x81""@""\x82""c""\x82""`""\x82""s""\x82""`""\x81""@""\x82""d""\x82""q""\x82""q""\x82""n""\x82""q""\x81""I\r",
+	"\r Saved.\r",
+	"Wrong save data version.\r",
+	""
+};
+
+static const StringListProvider kEoB1MenuStringsSaveLoadSegaCDEnglishProvider = { ARRAYSIZE(kEoB1MenuStringsSaveLoadSegaCDEnglish), kEoB1MenuStringsSaveLoadSegaCDEnglish };
+
+static const char *const kEoB1MenuStringsOnOffSegaCDEnglish[2] = {
+	"ON",
+	"OFF"
+};
+
+static const StringListProvider kEoB1MenuStringsOnOffSegaCDEnglishProvider = { ARRAYSIZE(kEoB1MenuStringsOnOffSegaCDEnglish), kEoB1MenuStringsOnOffSegaCDEnglish };
+
+static const char *const kEoB1MenuStringsSpellsSegaCDEnglish[17] = {
+	"Select a character from your party who would like to learn spells.",
+	"Your Paladin is too low a level for spells.\r",
+	"The Mage has no Spell Book!\r",
+	"Select a character from your party who would like to pray for spells.",
+	"You don't have any Cleric able to pray in your party.\r",
+	"You don't have any Mage able to learn spells.\r",
+	"An unconscious or dead Mage cannot memorize spells.\r",
+	"An unconscious or dead Cleric cannot pray for spells.\r",
+	"1",
+	"2",
+	"3",
+	"4",
+	"5",
+	"Clear",
+	"\x82""r""\x82\x90\x82\x85\x82\x8c\x82\x8c\x82\x93\x81""@""\x82""`""\x82\x96\x82\x81\x82\x89\x82\x8c\x82\x81\x82\x82\x82\x8c\x82\x85\x81""F",
+	"Yes",
+	"No"
+};
+
+static const StringListProvider kEoB1MenuStringsSpellsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1MenuStringsSpellsSegaCDEnglish), kEoB1MenuStringsSpellsSegaCDEnglish };
+
+static const char *const kEoB1MenuStringsRestSegaCDEnglish[5] = {
+	"Will your healers heal the party?",
+	" Someone is still injured.\rRest until healed?",
+	"\x82""q""\x82\x85\x82\x93\x82\x94\x82\x89\x82\x8e\x82\x87\x81""@""\x82\x90\x82\x81\x82\x92\x82\x94\x82\x99",// Resting Party
+	" All characters are fully rested.",
+	" Your party needs to rest to gain spells."
+};
+
+static const StringListProvider kEoB1MenuStringsRestSegaCDEnglishProvider = { ARRAYSIZE(kEoB1MenuStringsRestSegaCDEnglish), kEoB1MenuStringsRestSegaCDEnglish };
+
+static const char *const kEoB1MenuStringsDropSegaCDEnglish[1] = {
+	" You cannot have less than four characters."
+};
+
+static const StringListProvider kEoB1MenuStringsDropSegaCDEnglishProvider = { ARRAYSIZE(kEoB1MenuStringsDropSegaCDEnglish), kEoB1MenuStringsDropSegaCDEnglish };
+
+static const char *const kEoB1MenuStringsExitSegaCDEnglish[1] = {
+	" Are you sure you\r wish to exit the\r game?"
+};
+
+static const StringListProvider kEoB1MenuStringsExitSegaCDEnglishProvider = { ARRAYSIZE(kEoB1MenuStringsExitSegaCDEnglish), kEoB1MenuStringsExitSegaCDEnglish };
+
+static const char *const kEoB1MenuStringsStarveSegaCDEnglish[1] = {
+	"Your party is starving. Do you wish to continue resting?"
+};
+
+static const StringListProvider kEoB1MenuStringsStarveSegaCDEnglishProvider = { ARRAYSIZE(kEoB1MenuStringsStarveSegaCDEnglish), kEoB1MenuStringsStarveSegaCDEnglish };
+
+static const char *const kEoB1MenuStringsScribeSegaCDEnglish[5] = {
+	"\x82""r""\x82\x85\x82\x8c\x82\x85\x82\x83\x82\x94\x81""@""\x82\x94\x82\x88\x82\x85\x81""@""\x82\x93\x82\x83\x82\x92\x82\x8f\x82\x8c\x82\x8c\x81""i""\x82\x93\x81""j\r""\x82\x99\x82\x8f\x82\x95\x81""@""\x82\x97\x82\x89\x82\x93\x82\x88\x81""@""\x82\x94\x82\x8f\x81""@""\x82\x93\x82\x83\x82\x92\x82\x89\x82\x82\x82\x85\x81""D",
+	"Select a Mage from your party who would like to scribe spells.",
+	"You don't have any scolls to be scribed.",
+	"You don't have\r any scrolls that\r this Mage needs.",
+	"You don't have any Mage able to scribe scrolls.\r" // FIXED (original: "An unconscious or dead Mage cannot memorize spells")
+};
+
+static const StringListProvider kEoB1MenuStringsScribeSegaCDEnglishProvider = { ARRAYSIZE(kEoB1MenuStringsScribeSegaCDEnglish), kEoB1MenuStringsScribeSegaCDEnglish };
+
+static const char *const kEoB1MenuStringsDrop2SegaCDEnglish[3] = {
+	"Select the character you wish to drop.",
+	"", // Are you sure you\r wish to SAVE the\r game?",
+	"", // Are you sure you\r wish to LOAD a\r saved game?"
+};
+
+static const StringListProvider kEoB1MenuStringsDrop2SegaCDEnglishProvider = { ARRAYSIZE(kEoB1MenuStringsDrop2SegaCDEnglish), kEoB1MenuStringsDrop2SegaCDEnglish };
+
+static const char *const kEoB1MenuStringsPoisonSegaCDEnglish[1] = {
+	"Poisoned party members will die!\rRest anyway?"
+};
+
+static const StringListProvider kEoB1MenuStringsPoisonSegaCDEnglishProvider = { ARRAYSIZE(kEoB1MenuStringsPoisonSegaCDEnglish), kEoB1MenuStringsPoisonSegaCDEnglish };
+
+static const char *const kEoB1MenuStringsMgcSegaCDEnglish[2] = {
+	"%-18s%1d",
+	"\x82""%c""\x81""@""\x82\x8f\x82\x86\x81""@""\x82""%c""\x81""@""\x82""q""\x82\x85\x82\x8d\x82\x81\x82\x89\x82\x8e\x82\x89\x82\x8e\x82\x87\x81""D"
+};
+
+static const StringListProvider kEoB1MenuStringsMgcSegaCDEnglishProvider = { ARRAYSIZE(kEoB1MenuStringsMgcSegaCDEnglish), kEoB1MenuStringsMgcSegaCDEnglish };
+
+static const char *const kEoB1MenuStringsRest2SegaCDEnglish[4] = {
+	"%s gained %s.\r",
+	"%s memorized %s.\r",
+	"%s casts healing on %s.\r",
+	"\x82""g""\x82\x8f\x82\x95\x82\x92\x82\x93\x81""@""\x82\x92\x82\x85\x82\x93\x82\x94\x82\x85\x82\x84\x81""F"//Hours rested:
+};
+
+static const StringListProvider kEoB1MenuStringsRest2SegaCDEnglishProvider = { ARRAYSIZE(kEoB1MenuStringsRest2SegaCDEnglish), kEoB1MenuStringsRest2SegaCDEnglish };
+
+static const char *const kEoB1MenuStringsRest4SegaCDEnglish[1] = {
+	"\rYou can't rest here, monsters are near.\r"
+};
+
+static const StringListProvider kEoB1MenuStringsRest4SegaCDEnglishProvider = { ARRAYSIZE(kEoB1MenuStringsRest4SegaCDEnglish), kEoB1MenuStringsRest4SegaCDEnglish };
+
+static const char *const kEoB1MenuStringsDefeatSegaCDEnglish[3] = {
+	"All of your party has been defeated.",
+	"The minions of evil will be able to",
+	"carry out their plans unhindered!"
+};
+
+static const StringListProvider kEoB1MenuStringsDefeatSegaCDEnglishProvider = { ARRAYSIZE(kEoB1MenuStringsDefeatSegaCDEnglish), kEoB1MenuStringsDefeatSegaCDEnglish };
+
+static const char *const kEoB1MenuYesNoStringsSegaCDEnglish[2] = {
+	"Yes",
+	"No"
+};
+
+static const StringListProvider kEoB1MenuYesNoStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1MenuYesNoStringsSegaCDEnglish), kEoB1MenuYesNoStringsSegaCDEnglish };
+
+static const char *const kEoB1CharGuiStringsInSegaCDEnglish[4] = {
+	"CHARACTER INFO",
+	"AC",
+	"EXP",
+	"LVL"
+};
+
+static const StringListProvider kEoB1CharGuiStringsInSegaCDEnglishProvider = { ARRAYSIZE(kEoB1CharGuiStringsInSegaCDEnglish), kEoB1CharGuiStringsInSegaCDEnglish };
+
+static const char *const kEoB1CharStatusStrings7SegaCDEnglish[1] = {
+	"%s no longer has giant strength.\r"
+};
+
+static const StringListProvider kEoB1CharStatusStrings7SegaCDEnglishProvider = { ARRAYSIZE(kEoB1CharStatusStrings7SegaCDEnglish), kEoB1CharStatusStrings7SegaCDEnglish };
+
+static const char *const kEoB1CharStatusStrings81SegaCDEnglish[1] = {
+	"%s feels the effects of poison!\r"
+};
+
+static const StringListProvider kEoB1CharStatusStrings81SegaCDEnglishProvider = { ARRAYSIZE(kEoB1CharStatusStrings81SegaCDEnglish), kEoB1CharStatusStrings81SegaCDEnglish };
+
+static const char *const kEoB1CharStatusStrings9SegaCDEnglish[1] = {
+	"%s is no longer paralyzed!\r"
+};
+
+static const StringListProvider kEoB1CharStatusStrings9SegaCDEnglishProvider = { ARRAYSIZE(kEoB1CharStatusStrings9SegaCDEnglish), kEoB1CharStatusStrings9SegaCDEnglish };
+
+static const char *const kEoB1CharStatusStrings131SegaCDEnglish[1] = {
+	"%s is %s!\r"
+};
+
+static const StringListProvider kEoB1CharStatusStrings131SegaCDEnglishProvider = { ARRAYSIZE(kEoB1CharStatusStrings131SegaCDEnglish), kEoB1CharStatusStrings131SegaCDEnglish };
+
+static const char *const kEoB1LevelGainStringsSegaCDEnglish[1] = {
+	"\x06\x55""%s has gained a level of experience.""\x06\xFF""\r"
+};
+
+static const StringListProvider kEoB1LevelGainStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1LevelGainStringsSegaCDEnglish), kEoB1LevelGainStringsSegaCDEnglish };
+
+/*static const char *const kEoB1BookNumbersSegaCDEnglish[5] = {
+	"First",
+	"Second",
+	"Third",
+	"Fourth",
+	"Fifth"
+};
+
+static const StringListProvider kEoB1BookNumbersSegaCDEnglishProvider = { ARRAYSIZE(kEoB1BookNumbersSegaCDEnglish), kEoB1BookNumbersSegaCDEnglish };
+*/
+static const char *const kEoB1MageSpellsListSegaCDEnglish[26] = {
+	"",
+	"Armor",
+	"Burning Hands",
+	"Detect Magic",
+	"Magic Missile",
+	"Read Magic",
+	"Shield",
+	"Shocking Grasp",
+	"Invisibility",
+	"Knock",
+	"M's Acid Arrow",
+	"Stinking Cloud",
+	"Dispel Magic",
+	"Fireball",
+	"Flame Arrow",
+	"Haste",
+	"Hold Person",
+	"Invisibility 10'",
+	"Lightning Bolt",
+	"Vampiric Touch",
+	"Fear",
+	"Ice Storm",
+	"Stoneskin",
+	"Cloudkill",
+	"Cone of Cold",
+	"Hold Monster"
+};
+
+static const StringListProvider kEoB1MageSpellsListSegaCDEnglishProvider = { ARRAYSIZE(kEoB1MageSpellsListSegaCDEnglish), kEoB1MageSpellsListSegaCDEnglish };
+
+static const char *const kEoB1ClericSpellsListSegaCDEnglish[25] = {
+	"",
+	"Bless",
+	"Cure Light Wnds",
+	"Cause Light Wnds",
+	"Detect Magic",
+	"Protect-Evil",
+	"Aid",
+	"Flame Blade",
+	"Hold Person",
+	"Slow Poison",
+	"Create Food",
+	"Dispel Magic",
+	"Magical Vestment",
+	"Prayer",
+	"Remove Paralysis",
+	"Cure Serious",
+	"Cause Serious",
+	"Neutral-Poison",
+	"Protect-Evil 10'",
+	"Protect-Lightning",
+	"Cure Critical",
+	"Cause Critical",
+	"Flame Strike",
+	"Raise Dead",
+	"Lay on Hands"
+};
+
+static const StringListProvider kEoB1ClericSpellsListSegaCDEnglishProvider = { ARRAYSIZE(kEoB1ClericSpellsListSegaCDEnglish), kEoB1ClericSpellsListSegaCDEnglish };
+
+static const char *const kEoB1MageSpellsList2SegaCDEnglish[26] = {
+	"",
+	"Armor",
+	"Burning Hand",
+	"Detect Magic",
+	"M. Missile",
+	"Read Magic",
+	"Shield",
+	"Shock. Grasp",
+	"Invisibility",
+	"Knock",
+	"Acid Arrow",
+	"Stink. Cloud",
+	"Dispel Magic",
+	"Fireball",
+	"Flame Arrow",
+	"Haste",
+	"Hold Person",
+	"Invis. 10'",
+	"Lightning",
+	"Vamp. Touch",
+	"Fear",
+	"Ice Storm",
+	"Stoneskin",
+	"Cloudkill",
+	"Cone of Cold",
+	"Hold Monster"
+};
+
+static const StringListProvider kEoB1MageSpellsList2SegaCDEnglishProvider = { ARRAYSIZE(kEoB1MageSpellsList2SegaCDEnglish), kEoB1MageSpellsList2SegaCDEnglish };
+
+static const char *const kEoB1ClericSpellsList2SegaCDEnglish[25] = {
+	"",
+	"Bless",
+	"Cure Light",
+	"Cause Light",
+	"Detect Magic",
+	"Protect Evil",
+	"Aid",
+	"Flame Blade",
+	"Hold Person",
+	"Slow Poison",
+	"Create Food",
+	"Dispel Magic",
+	"M. Vestment",
+	"Prayer",
+	"R. Paralysis",
+	"Cure Serious",
+	"Cs. Serious",
+	"Neut. Poison",
+	"Pro.Evil 10'",
+	"P. Lightning",
+	"Cr. Critical",		// FIXED (Original: "\r. Critical" )
+	"Cs. Critical",
+	"Flame Strike",
+	"Raise Dead",
+	"Lay on Hands"
+};
+
+static const StringListProvider kEoB1ClericSpellsList2SegaCDEnglishProvider = { ARRAYSIZE(kEoB1ClericSpellsList2SegaCDEnglish), kEoB1ClericSpellsList2SegaCDEnglish };
+
+static const char *const kEoB1SpellNamesSegaCDEnglish[51] = {
+	"",
+	"armor",
+	"burning hands",
+	"detect magic",
+	"magic missile",
+	"shield",
+	"shocking grasp",
+	"invisibility",
+	"melf's acid arrow",
+	"stinking cloud",
+	"dispel magic",
+	"fireball",
+	"flame arrow",
+	"haste",
+	"hold person",
+	"invisibility 10' radius",
+	"lightning bolt",
+	"vampiric touch",
+	"fear",
+	"ice storm",
+	"stoneskin",
+	"cloudkill",
+	"cone of cold",
+	"hold monster",
+	"bless",
+	"cure light wounds",
+	"cause light wounds",
+	"detect magic",
+	"protection from evil",
+	"aid",
+	"flame blade",
+	"hold person",
+	"slow poison",
+	"create food",
+	"dispel magic",
+	"magical vestment",
+	"prayer",
+	"remove paralysis",
+	"cure serious wounds",
+	"cause serious wounds",
+	"neutralize poison",
+	"protection from evil 10' radius",
+	"protection from lightning",
+	"cure critical wounds",
+	"cause critical wounds",
+	"flame strike",
+	"raise dead",
+	"lay on hands",
+	"",
+	"",
+	""
+};
+
+static const StringListProvider kEoB1SpellNamesSegaCDEnglishProvider = { ARRAYSIZE(kEoB1SpellNamesSegaCDEnglish), kEoB1SpellNamesSegaCDEnglish };
+
+static const char *const kEoB1MagicStrings1SegaCDEnglish[6] = {
+	"",
+	"",
+	"You must have a free hand for this spell.\r",
+	"You can't have two of this spell type active.\r",
+	"%s casts %s.\r",
+	""
+};
+
+static const StringListProvider kEoB1MagicStrings1SegaCDEnglishProvider = { ARRAYSIZE(kEoB1MagicStrings1SegaCDEnglish), kEoB1MagicStrings1SegaCDEnglish };
+
+static const char *const kEoB1MagicStrings2SegaCDEnglish[3] = {
+	"no effect\r",
+	"%s has been disintegrated!\r\r",
+	"The party has been hit by a death spell!\r"
+};
+
+static const StringListProvider kEoB1MagicStrings2SegaCDEnglishProvider = { ARRAYSIZE(kEoB1MagicStrings2SegaCDEnglish), kEoB1MagicStrings2SegaCDEnglish };
+
+static const char *const kEoB1MagicStrings3SegaCDEnglish[6] = {
+	"Cast spell on which character?",
+	"\r",
+	"\rSpell aborted.\r",
+	"%s's %s spell expires.\r",
+	"%s missed the monster.\r",
+	"%s must be in the front ranks to hit!\r"
+};
+
+static const StringListProvider kEoB1MagicStrings3SegaCDEnglishProvider = { ARRAYSIZE(kEoB1MagicStrings3SegaCDEnglish), kEoB1MagicStrings3SegaCDEnglish };
+
+static const char *const kEoB1MagicStrings4SegaCDEnglish[1] = {
+	"no effect.\r"
+};
+
+static const StringListProvider kEoB1MagicStrings4SegaCDEnglishProvider = { ARRAYSIZE(kEoB1MagicStrings4SegaCDEnglish), kEoB1MagicStrings4SegaCDEnglish };
+
+static const char *const kEoB1MagicStrings6SegaCDEnglish[1] = {
+	"%s already has a high base armor class.\r"
+};
+
+static const StringListProvider kEoB1MagicStrings6SegaCDEnglishProvider = { ARRAYSIZE(kEoB1MagicStrings6SegaCDEnglish), kEoB1MagicStrings6SegaCDEnglish };
+
+/*static const char *const kEoB1MagicStrings7SegaCDEnglish[5] = {
+	"1ST",
+	"2ND",
+	"3RD",
+	"4TH",
+	"5TH"
+};
+
+static const StringListProvider kEoB1MagicStrings7SegaCDEnglishProvider = { ARRAYSIZE(kEoB1MagicStrings7SegaCDEnglish), kEoB1MagicStrings7SegaCDEnglish };
+*/
+static const char *const kEoB1MagicStrings8SegaCDEnglish[3] = {
+	"All spells on %s are dispelled.\r",
+	"The party is already blessed!\r",
+	"The aid spell fails!\r",
+};
+
+static const StringListProvider kEoB1MagicStrings8SegaCDEnglishProvider = { ARRAYSIZE(kEoB1MagicStrings8SegaCDEnglish), kEoB1MagicStrings8SegaCDEnglish };
+
+static const char *const kEoB1MonsterDistAttStringsSegaCDEnglish[5] = {
+	"%s is hit by a cause serious wounds spell!\r",
+	"The party is hit with a psychic mind blast!\r",
+	"paralyzed",
+	"poisoned",
+	"paralyzed"
+};
+
+static const StringListProvider kEoB1MonsterDistAttStringsSegaCDEnglishProvider = { ARRAYSIZE(kEoB1MonsterDistAttStringsSegaCDEnglish), kEoB1MonsterDistAttStringsSegaCDEnglish };
+
+static const char *const kEoB1CreditsStrings2SegaCDEnglish[120] = {
+	";Eye of the Beholder",
+	"",
+	"for SEGA-CD",
+	"/1",
+	"STAFF",
+	"/8",
+	"PRODUCER",
+	"",
+	"Yasutaka Ukai",
+	"/8",
+	"CHIEF PROGRAMMER",
+	"",
+	"Makoto Ichinoseki",
+	"/9",
+	"TECHNICAL SUPPORT",
+	"",
+	"Hiroyuki Fujiwara",
+	"Satoru Miki",
+	"/8",
+	"GRAPHICS",
+	"",
+	"Atsuhiro Gunji",
+	"Hiroshi Akagi",
+	"Ken Takagi",
+	"Yasuaki Mizutani",
+	"Saeko Satou",
+	"Kiyoto Yoshimura",
+	"/8",
+	"GAME DESIGN",
+	"",
+	"Atsuhiro Gunji",
+	"Masaaki Furuya",
+	"/8",
+	"MUSIC",
+	"",
+	"Yuzo Koshiro",
+	"Motohiro Kawashima",
+	"/9",
+	"SOUND EFFECTS",
+	"",
+	"YmoH.S",
+	"/9",
+	"VISUAL",
+	"",
+	"",
+	"",
+	"PROGRAM",
+	"",
+	"Makoto Ichinoseki",
+	"Kiyoto Yoshimura",
+	"Yoko Ogasawara",
+	"/9",
+	"GRAPHICS",
+	"",
+	"Atsuhiro Gunji",
+	"Ken Takagi",
+	"/7",
+	"SOUND EDIT",
+	"",
+	"Hidefumi Ohara",
+	"Keiichi Yoshida",
+	"Kiyoto Yoshimura",
+	"/9",
+	"VOICES",
+	"",
+	"Martha Carlucci",
+	"Teacey Steinmetz",
+	"Margot Blattmann",
+	"Charlens Landrum",
+	"Tim Brown",
+	"Harold Johnson",
+	"James Ward",
+	"Nicholas Beliacff",
+	"/9",
+	"TRASLATION SUPERVISE",
+	"",
+	"GROUP SNE",
+	"Hitoshi Yasuda",
+	"Miyuki Kiyomatsu",
+	"Megumi Tsuge",
+	"/8",
+	"PROMOTION",
+	"",
+	"Yoshiaki Matsumoto",
+	"Masayoshi Kanagawa",
+	"/8",
+	"Testing",
+	"",
+	"Jeff Gregg",
+	"/8",
+	"THANKS TO",
+	"",
+	"Darren",
+	"Ross",
+	"Eric",
+	"Erik",
+	"Rich",
+	"Charlene",
+	"Robert",
+	"Albert",
+	"Michael",
+	"Mr. Fukuda",
+	"Elaine",
+	"Cynthia",
+	"Po",
+	"Kuniaki Ikariya(SEGA)",
+	"Yuji Ikeuchi(SEGA)",
+	"/9",
+	"",
+	"SPECIAL THANKS TO",
+	"",
+	"James Ward(TSR)",
+	"Nicholas Beliaeff",
+	"Kaz Saito(FCI-NY)",
+	"Doreen Do(FCI-NY)",
+	"Eiko Nagata(FCI-NY)",
+	"James MacLean(FCI-NY)",
+	"/9",
+	"/4",
+	"/E"
+};
+
+static const StringListProvider kEoB1CreditsStrings2SegaCDEnglishProvider = { ARRAYSIZE(kEoB1CreditsStrings2SegaCDEnglish), kEoB1CreditsStrings2SegaCDEnglish };
+
+static const char *const kEoB1DefaultPartyNamesSegaCDEnglish[4] = {
+	"FIGHTER",
+	"THIEF",
+	"MAGE",
+	"CLERIC"
+};
+
+static const StringListProvider kEoB1DefaultPartyNamesSegaCDEnglishProvider = { ARRAYSIZE(kEoB1DefaultPartyNamesSegaCDEnglish), kEoB1DefaultPartyNamesSegaCDEnglish };
+
+static const char *const kEoB1TextInputCharacterLinesSegaCD[2] = {
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZ          0123456789  ,./'&-_()   ",
+	"abcdefghijklmnopqrstuvwxyz          0123456789  ,./'&-_()   "
+};
+
+static const StringListProvider kEoB1TextInputCharacterLinesSegaCDProvider = { ARRAYSIZE(kEoB1TextInputCharacterLinesSegaCD), kEoB1TextInputCharacterLinesSegaCD };
+
+static const char *const kEoB1TextInputSelectStringsSegaCD[3] = {
+	"NEXT",
+	"BS",
+	"END"
+};
+
+static const StringListProvider kEoB1TextInputSelectStringsSegaCDProvider = { ARRAYSIZE(kEoB1TextInputSelectStringsSegaCD), kEoB1TextInputSelectStringsSegaCD };
diff --git a/devtools/create_kyradat/resources/eob2_fmtowns_japanese.h b/devtools/create_kyradat/resources/eob2_fmtowns_japanese.h
index 85d75705ed..be67079de5 100644
--- a/devtools/create_kyradat/resources/eob2_fmtowns_japanese.h
+++ b/devtools/create_kyradat/resources/eob2_fmtowns_japanese.h
@@ -923,7 +923,7 @@ static const char *const kEoB2Config2431StringsFMTowns[2] = {
 
 static const StringListProvider kEoB2Config2431StringsFMTownsProvider = { ARRAYSIZE(kEoB2Config2431StringsFMTowns), kEoB2Config2431StringsFMTowns };
 
-static const char *const kEoB2KatakanaLinesFMTowns[12] = {
+static const char *const kEoB2TextInputCharacterLinesFMTowns[12] = {
 	"\x83""A""\x83""C""\x83""E""\x83""G""\x83""I""\x81""@""\x83""J""\x83""L""\x83""N""\x83""P""\x83""R""\x81""@""\x83""T""\x83""V""\x83""X""\x83""Z""\x83""\\",
 	"\x83""^""\x83""`""\x83""c""\x83""e""\x83""g""\x81""@""\x83""i""\x83""j""\x83""k""\x83""l""\x83""m""\x81""@""\x83""n""\x83""q""\x83""t""\x83""w""\x83""z",
 	"\x83""}""\x83""~""\x83\x80\x83\x81\x83\x82\x81""@""\x83\x84\x81""@""\x83\x86\x81""@""\x83\x88\x81""@""\x83\x89\x83\x8a\x83\x8b\x83\x8c\x83\x8d",
@@ -938,12 +938,12 @@ static const char *const kEoB2KatakanaLinesFMTowns[12] = {
 	"\x81""@""\x81""@""\x81""@""\x81""@""\x81""@""\x81""@""\x81""@""\x81""@""\x81""@""\x81""@""\x81""@""\x81""@""\x81""@""\x81""@""\x81""@""\x81""@""\x81""@"
 };
 
-static const StringListProvider kEoB2KatakanaLinesFMTownsProvider = { ARRAYSIZE(kEoB2KatakanaLinesFMTowns), kEoB2KatakanaLinesFMTowns };
+static const StringListProvider kEoB2TextInputCharacterLinesFMTownsProvider = { ARRAYSIZE(kEoB2TextInputCharacterLinesFMTowns), kEoB2TextInputCharacterLinesFMTowns };
 
-static const char *const kEoB2KanaSelectStringsFMTowns[3] = {
+static const char *const kEoB2TextInputSelectStringsFMTowns[3] = {
 	"NEXT",
 	"END",
 	"DEL"
 };
 
-static const StringListProvider kEoB2KanaSelectStringsFMTownsProvider = { ARRAYSIZE(kEoB2KanaSelectStringsFMTowns), kEoB2KanaSelectStringsFMTowns };
+static const StringListProvider kEoB2TextInputSelectStringsFMTownsProvider = { ARRAYSIZE(kEoB2TextInputSelectStringsFMTowns), kEoB2TextInputSelectStringsFMTowns };
diff --git a/dists/engine-data/kyra.dat b/dists/engine-data/kyra.dat
index ded2c46f9c..f6c48aa678 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 d2afad312e..a41e5738f9 100644
--- a/engines/kyra/engine/chargen.cpp
+++ b/engines/kyra/engine/chargen.cpp
@@ -1180,9 +1180,11 @@ int CharacterGenerator::getMinHp(int cclass, int constitution, int level1, int l
 
 void CharacterGenerator::finish() {
 	_screen->copyRegion(0, 0, 160, 0, 160, 128, 2, 2, Screen::CR_NO_P_CHECK);
-	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);
-	_screen->setCurPage(cp);
+	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);
+		_screen->setCurPage(cp);
+	}
 	_screen->copyRegion(160, 0, 144, 64, 160, 128, 2, 0, Screen::CR_NO_P_CHECK);
 	_screen->updateScreen();
 
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 576e3eb7d0..64e52a192e 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -218,7 +218,7 @@ EoBCoreEngine::EoBCoreEngine(OSystem *system, const GameFlags &flags) : KyraRpgE
 	_menuStringsScribe = _menuStringsDrop2 = _menuStringsHead = _menuStringsPoison = 0;
 	_menuStringsMgc = _menuStringsPrefs = _menuStringsRest2 = _menuStringsRest3 = 0;
 	_menuStringsRest4 = _menuStringsDefeat = _menuStringsTransfer = _menuStringsSpec = 0;
-	_menuStringsSpellNo = _menuYesNoStrings = _2431Strings = _katakanaLines = _katakanaSelectStrings = 0;
+	_menuStringsSpellNo = _menuYesNoStrings = _2431Strings = _textInputCharacterLines = _textInputSelectStrings = 0;
 	_errorSlotEmptyString = _errorSlotNoNameString = _menuOkString = 0;
 	_spellLevelsMage = _spellLevelsCleric = _numSpellsCleric = _numSpellsWisAdj = _numSpellsPal = _numSpellsMage = 0;
 	_mnNumWord = _numSpells = _mageSpellListSize = _spellLevelsMageSize = _spellLevelsClericSize = 0;
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index 51099eaf02..b3464df850 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -1169,8 +1169,8 @@ protected:
 	const char *_errorSlotNoNameString;
 	const char *_menuOkString;
 	const char *const *_2431Strings;
-	const char *const *_katakanaLines;
-	const char *const *_katakanaSelectStrings;
+	const char *const *_textInputCharacterLines;
+	const char *const *_textInputSelectStrings;
 	const char *const *_menuStringsTransfer;
 	const char *const *_transferStringsScummVM;
 	const char *const *_menuStringsSpec;
diff --git a/engines/kyra/engine/sprites_eob.cpp b/engines/kyra/engine/sprites_eob.cpp
index b02d1ccc43..845d9d233f 100644
--- a/engines/kyra/engine/sprites_eob.cpp
+++ b/engines/kyra/engine/sprites_eob.cpp
@@ -447,6 +447,7 @@ void EoBCoreEngine::drawDoor(int index) {
 
 void EoBCoreEngine::drawMonsters(int index) {
 	static const uint8 distMap[] = { 2, 1, 0, 4 };
+	// SEGA: 2, 1, 0, 3 ??
 	static const uint8 yAdd[] = { 20, 12, 4, 4, 2, 0, 0 };
 
 	int blockDistance = distMap[_dscDimMap[index]];
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 11000c0f40..2e4cdb2998 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -2781,8 +2781,8 @@ int GUI_EoB::checkKatakanaSelection() {
 			int lineOffs = (y - 112) >> 4;
 			int column = (x - 152) >> 2;
 
-			_csjis[0] = _vm->_katakanaLines[_currentKanaPage * 4 + lineOffs][column];
-			_csjis[1] = _vm->_katakanaLines[_currentKanaPage * 4 + lineOffs][column + 1];
+			_csjis[0] = _vm->_textInputCharacterLines[_currentKanaPage * 4 + lineOffs][column];
+			_csjis[1] = _vm->_textInputCharacterLines[_currentKanaPage * 4 + lineOffs][column + 1];
 
 			if (_csjis[0] != '\x81' || _csjis[1] != '\x40') {
 				highlight = lineOffs << 8 | column;
@@ -2795,11 +2795,11 @@ int GUI_EoB::checkKatakanaSelection() {
 
 	if (highlight == -1) {
 		for (int i = 0; i < 3; i++) {
-			if (!_vm->posWithinRect(mousePos.x, mousePos.y, kanaSelXCrds[i], 176, kanaSelXCrds[i] + _screen->getTextWidth(_vm->_katakanaSelectStrings[i]), 184))
+			if (!_vm->posWithinRect(mousePos.x, mousePos.y, kanaSelXCrds[i], 176, kanaSelXCrds[i] + _screen->getTextWidth(_vm->_textInputSelectStrings[i]), 184))
 				continue;
 
 			highlight = 0x400 | i;
-			_screen->printShadedText(_vm->_katakanaSelectStrings[i], kanaSelXCrds[i], 176, _vm->guiSettings()->colors.guiColorLightRed, 0, _vm->guiSettings()->colors.guiColorBlack);
+			_screen->printShadedText(_vm->_textInputSelectStrings[i], kanaSelXCrds[i], 176, _vm->guiSettings()->colors.guiColorLightRed, 0, _vm->guiSettings()->colors.guiColorBlack);
 			i = 3;
 		}
 	}
@@ -2815,11 +2815,11 @@ int GUI_EoB::checkKatakanaSelection() {
 
 	if (_menuCur != -1) {
 		if (_menuCur & 0x400) {
-			_screen->printShadedText(_vm->_katakanaSelectStrings[_menuCur & 3], kanaSelXCrds[_menuCur & 3], 176, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+			_screen->printShadedText(_vm->_textInputSelectStrings[_menuCur & 3], kanaSelXCrds[_menuCur & 3], 176, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
 		} else {
 			char osjis[3];
-			osjis[0] = _vm->_katakanaLines[_currentKanaPage * 4 + (_menuCur >> 8)][_menuCur & 0xFF];
-			osjis[1] = _vm->_katakanaLines[_currentKanaPage * 4 + (_menuCur >> 8)][(_menuCur & 0xFF) + 1];
+			osjis[0] = _vm->_textInputCharacterLines[_currentKanaPage * 4 + (_menuCur >> 8)][_menuCur & 0xFF];
+			osjis[1] = _vm->_textInputCharacterLines[_currentKanaPage * 4 + (_menuCur >> 8)][(_menuCur & 0xFF) + 1];
 			osjis[2] = 0;
 			_screen->printShadedText(osjis, 152 + ((_menuCur & 0xFF) << 2), 112 + ((_menuCur >> 4) & ~0x0F), _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
 		}
@@ -2859,11 +2859,11 @@ void GUI_EoB::printKatakanaOptions(int page) {
 	_currentKanaPage = page;
 	_screen->copyRegion(160, 44, 144, 108, 160, 84, 2, 0, Screen::CR_NO_P_CHECK);
 	for (int i = 0; i < 4; i++)
-		_screen->printShadedText(_vm->_katakanaLines[page * 4 + i], 152, (i << 4) + 112, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+		_screen->printShadedText(_vm->_textInputCharacterLines[page * 4 + i], 152, (i << 4) + 112, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
 
 	static uint16 kanaSelCrds[] = { 224, 272, 186 };
 	for (int i = 0; i < 3; i++)
-		_screen->printShadedText(_vm->_katakanaSelectStrings[i], kanaSelCrds[i], 176, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+		_screen->printShadedText(_vm->_textInputSelectStrings[i], kanaSelCrds[i], 176, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
 }
 
 void GUI_EoB::transferWaitBox() {
diff --git a/engines/kyra/resource/resource.h b/engines/kyra/resource/resource.h
index 64247d14e8..3c89cad4f8 100644
--- a/engines/kyra/resource/resource.h
+++ b/engines/kyra/resource/resource.h
@@ -396,6 +396,28 @@ enum KyraResources {
 	kEoBBaseMonsterDistAttStrings,
 
 	kEoBBaseEncodeMonsterDefs,
+	kEoBBaseEncodeMonsterDefs00,
+	kEoBBaseEncodeMonsterDefs01,
+	kEoBBaseEncodeMonsterDefs02,
+	kEoBBaseEncodeMonsterDefs03,
+	kEoBBaseEncodeMonsterDefs04,
+	kEoBBaseEncodeMonsterDefs05,
+	kEoBBaseEncodeMonsterDefs06,
+	kEoBBaseEncodeMonsterDefs07,
+	kEoBBaseEncodeMonsterDefs08,
+	kEoBBaseEncodeMonsterDefs09,
+	kEoBBaseEncodeMonsterDefs10,
+	kEoBBaseEncodeMonsterDefs11,
+	kEoBBaseEncodeMonsterDefs12,
+	kEoBBaseEncodeMonsterDefs13,
+	kEoBBaseEncodeMonsterDefs14,
+	kEoBBaseEncodeMonsterDefs15,
+	kEoBBaseEncodeMonsterDefs16,
+	kEoBBaseEncodeMonsterDefs17,
+	kEoBBaseEncodeMonsterDefs18,
+	kEoBBaseEncodeMonsterDefs19,
+	kEoBBaseEncodeMonsterDefs20,
+	kEoBBaseEncodeMonsterDefs21,
 	kEoBBaseNpcPresets,
 
 	kEoBBaseWllFlagPreset,
@@ -448,6 +470,8 @@ enum KyraResources {
 	kEoBBaseBookNumbers,
 	kEoBBaseMageSpellsList,
 	kEoBBaseClericSpellsList,
+	kEoBBaseMageSpellsList2,
+	kEoBBaseClericSpellsList2,
 	kEoBBaseSpellNames,
 	kEoBBaseMagicStrings1,
 	kEoBBaseMagicStrings2,
@@ -495,6 +519,11 @@ enum KyraResources {
 	kEoBBaseLevelSounds1,
 	kEoBBaseLevelSounds2,
 
+	kEoBBaseTextInputCharacterLines,
+	kEoBBaseTextInputSelectStrings,
+
+	kEoB1DefaultPartyStats,
+	kEoB1DefaultPartyNames,
 	kEoB1MainMenuStrings,
 	kEoB1BonusStrings,
 
@@ -543,6 +572,9 @@ enum KyraResources {
 	kEoB1CreditsStrings,
 	kEoB1CreditsCharWdth,
 
+	kEoB1CreditsStrings2,
+	kEoB1CreditsTileGrid,
+
 	kEoB1DoorShapeDefs,
 	kEoB1DoorSwitchShapeDefs,
 	kEoB1DoorSwitchCoords,
@@ -573,6 +605,126 @@ enum KyraResources {
 	kEoB1PalCycleData,
 	kEoB1PalCycleStyle1,
 	kEoB1PalCycleStyle2,
+	kEoB1PalettesSega,
+	kEoB1PatternTable0,
+	kEoB1PatternTable1,
+	kEoB1PatternTable2,
+	kEoB1PatternTable3,
+	kEoB1PatternTable4,
+	kEoB1PatternTable5,
+	kEoB1PatternAddTable1,
+	kEoB1PatternAddTable2,
+
+	kEoB1MonsterAnimFrames00,
+	kEoB1MonsterAnimFrames01,
+	kEoB1MonsterAnimFrames02,
+	kEoB1MonsterAnimFrames03,
+	kEoB1MonsterAnimFrames04,
+	kEoB1MonsterAnimFrames05,
+	kEoB1MonsterAnimFrames06,
+	kEoB1MonsterAnimFrames07,
+	kEoB1MonsterAnimFrames08,
+	kEoB1MonsterAnimFrames09,
+	kEoB1MonsterAnimFrames10,
+	kEoB1MonsterAnimFrames11,
+	kEoB1MonsterAnimFrames12,
+	kEoB1MonsterAnimFrames13,
+	kEoB1MonsterAnimFrames14,
+	kEoB1MonsterAnimFrames15,
+	kEoB1MonsterAnimFrames16,
+	kEoB1MonsterAnimFrames17,
+	kEoB1MonsterAnimFrames18,
+	kEoB1MonsterAnimFrames19,
+	kEoB1MonsterAnimFrames20,
+	kEoB1MonsterAnimFrames21,
+	kEoB1MonsterAnimFrames22,
+	kEoB1MonsterAnimFrames23,
+	kEoB1MonsterAnimFrames24,
+	kEoB1MonsterAnimFrames25,
+	kEoB1MonsterAnimFrames26,
+	kEoB1MonsterAnimFrames27,
+	kEoB1MonsterAnimFrames28,
+	kEoB1MonsterAnimFrames29,
+	kEoB1MonsterAnimFrames30,
+	kEoB1MonsterAnimFrames31,
+	kEoB1MonsterAnimFrames32,
+	kEoB1MonsterAnimFrames33,
+	kEoB1MonsterAnimFrames34,
+	kEoB1MonsterAnimFrames35,
+	kEoB1MonsterAnimFrames36,
+	kEoB1MonsterAnimFrames37,
+	kEoB1MonsterAnimFrames38,
+	kEoB1MonsterAnimFrames39,
+	kEoB1MonsterAnimFrames40,
+	kEoB1MonsterAnimFrames41,
+	kEoB1MonsterAnimFrames42,
+	kEoB1MonsterAnimFrames43,
+	kEoB1MonsterAnimFrames44,
+	kEoB1MonsterAnimFrames45,
+	kEoB1MonsterAnimFrames46,
+	kEoB1MonsterAnimFrames47,
+	kEoB1MonsterAnimFrames48,
+	kEoB1MonsterAnimFrames49,
+	kEoB1MonsterAnimFrames50,
+	kEoB1MonsterAnimFrames51,
+	kEoB1MonsterAnimFrames52,
+	kEoB1MonsterAnimFrames53,
+	kEoB1MonsterAnimFrames54,
+	kEoB1MonsterAnimFrames55,
+	kEoB1MonsterAnimFrames56,
+	kEoB1MonsterAnimFrames57,
+	kEoB1MonsterAnimFrames58,
+	kEoB1MonsterAnimFrames59,
+	kEoB1MonsterAnimFrames60,
+	kEoB1MonsterAnimFrames61,
+	kEoB1MonsterAnimFrames62,
+	kEoB1MonsterAnimFrames63,
+	kEoB1MonsterAnimFrames64,
+	kEoB1MonsterAnimFrames65,
+	kEoB1MonsterAnimFrames66,
+	kEoB1MonsterAnimFrames67,
+	kEoB1MonsterAnimFrames68,
+	kEoB1MonsterAnimFrames69,
+	kEoB1MonsterAnimFrames70,
+	kEoB1MonsterAnimFrames71,
+	kEoB1MonsterAnimFrames72,
+	kEoB1MonsterAnimFrames73,
+	kEoB1MonsterAnimFrames74,
+	kEoB1MonsterAnimFrames75,
+	kEoB1MonsterAnimFrames76,
+	kEoB1MonsterAnimFrames77,
+	kEoB1MonsterAnimFrames78,
+	kEoB1MonsterAnimFrames79,
+	kEoB1MonsterAnimFrames80,
+	kEoB1MonsterAnimFrames81,
+	kEoB1MonsterAnimFrames82,
+	kEoB1MonsterAnimFrames83,
+	kEoB1MonsterAnimFrames84,
+	kEoB1MonsterAnimFrames85,
+	kEoB1MonsterAnimFrames86,
+	kEoB1MonsterAnimFrames87,
+	kEoB1MonsterAnimFrames88,
+	kEoB1MonsterAnimFrames89,
+	kEoB1MonsterAnimFrames90,
+	kEoB1MonsterAnimFrames91,
+	kEoB1MonsterAnimFrames92,
+	kEoB1MonsterAnimFrames93,
+	kEoB1MonsterAnimFrames94,
+	kEoB1MonsterAnimFrames95,
+	kEoB1MonsterAnimFrames96,
+	kEoB1MonsterAnimFrames97,
+	kEoB1MonsterAnimFrames98,
+	kEoB1MonsterAnimFrames99,
+	kEoB1MonsterAnimFrames100,
+	kEoB1MonsterAnimFrames101,
+	kEoB1MonsterAnimFrames102,
+	kEoB1MonsterAnimFrames103,
+	kEoB1MonsterAnimFrames104,
+	kEoB1MonsterAnimFrames105,
+	kEoB1MonsterAnimFrames106,
+	kEoB1MonsterAnimFrames107,
+	kEoB1MonsterAnimFrames108,
+	kEoB1MonsterAnimFrames109,
 
 	kEoB1NpcShpData,
 	kEoB1NpcSubShpIndex1,
@@ -590,10 +742,24 @@ enum KyraResources {
 	kEoB1Npc6Strings,
 	kEoB1Npc7Strings,
 
+	kEoB1ParchmentStrings,
 	kEoB1ItemNames,
+	kEoB1SpeechAnimData,
+	kEoB1WdAnimSprites,
+	kEoB1SequenceTrackMap,
+
+	kEoB1MapStrings1,
+	kEoB1MapStrings2,
+	kEoB1MapStrings3,
+	kEoB1MapLevelData,
+
 	kEoB1Ascii2SjisTable1,
 	kEoB1Ascii2SjisTable2,
 	kEoB1FontLookupTable,
+	kEoB1CharWidthTable1,
+	kEoB1CharWidthTable2,
+	kEoB1CharWidthTable3,
+	kEoB1CharTilesTable,
 
 	kEoB2MainMenuStrings,
 	kEoB2MainMenuUtilStrings,
@@ -948,8 +1114,6 @@ enum KyraResources {
 
 	kEoB2UtilMenuStrings,
 	kEoB2Config2431Strings,
-	kEoB2KatakanaLines,
-	kEoB2KanaSelectStrings,
 	kEoB2FontDmpSearchTbl,
 	kEoB2Ascii2SjisTables,
 	kEoB2Ascii2SjisTables2,
diff --git a/engines/kyra/resource/staticres.cpp b/engines/kyra/resource/staticres.cpp
index 61f5f2dc15..343b02bb50 100644
--- a/engines/kyra/resource/staticres.cpp
+++ b/engines/kyra/resource/staticres.cpp
@@ -39,7 +39,7 @@
 
 namespace Kyra {
 
-#define RESFILE_VERSION 101
+#define RESFILE_VERSION 102
 
 namespace {
 bool checkKyraDat(Common::SeekableReadStream *file) {
@@ -106,6 +106,7 @@ const IndexTable iPlatformTable[] = {
 	{ Common::kPlatformAmiga, 1 },
 	{ Common::kPlatformFMTowns, 2 },
 	{ Common::kPlatformPC98, 3 },
+	{ Common::kPlatformSegaCD, 4 },
 	{ Common::kPlatformMacintosh, 0 }, // HACK: Should be type "4", but as long as we can't extract Macintosh data, we need to use DOS data.
 	{ -1, -1 }
 };
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index 7814161460..b0805f1013 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -351,6 +351,9 @@ void EoBCoreEngine::initStaticResource() {
 	_characterStatusStrings12 = _staticres->loadStrings(kEoBBaseCharStatusStrings12, temp);
 	_characterStatusStrings13 = _staticres->loadStrings(_flags.gameID == GI_EOB2 ? kEoBBaseCharStatusStrings132 : kEoBBaseCharStatusStrings131, temp);
 
+	_textInputCharacterLines = _staticres->loadStrings(kEoBBaseTextInputCharacterLines, temp);
+	_textInputSelectStrings = _staticres->loadStrings(kEoBBaseTextInputSelectStrings, temp);
+
 	_levelGainStrings = _staticres->loadStrings(kEoBBaseLevelGainStrings, temp);
 	for (int i = 0; i < 5; i++)
 		_expRequirementTables[i] = _staticres->loadRawDataBe32(kEoBBaseExperienceTable0 + i, temp);
@@ -1466,8 +1469,6 @@ void DarkMoonEngine::initStaticResource() {
 
 	_utilMenuStrings = _staticres->loadStrings(kEoB2UtilMenuStrings, temp);
 	_2431Strings = _staticres->loadStrings(kEoB2Config2431Strings, temp);
-	_katakanaLines = _staticres->loadStrings(kEoB2KatakanaLines, temp);
-	_katakanaSelectStrings = _staticres->loadStrings(kEoB2KanaSelectStrings, temp);
 
 	_ascii2SjisTables = _staticres->loadStrings(kEoB2Ascii2SjisTables, temp);
 	_ascii2SjisTables2 = _staticres->loadStrings(kEoB2Ascii2SjisTables2, temp);


Commit: e4a33abfb5425814f115c5806f1709555f9dd933
    https://github.com/scummvm/scummvm/commit/e4a33abfb5425814f115c5806f1709555f9dd933
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:08+02:00

Commit Message:
KYRA: (EOB/SegaCD) - implement opening credits

Initial file handling, palette and graphics support. The opening credits get played satisfactory. Following up with the cinematics player will be much easier now...

Changed paths:
  A engines/kyra/graphics/screen_eob_amiga.cpp
  A engines/kyra/graphics/screen_eob_pc98.cpp
  A engines/kyra/graphics/screen_eob_segacd.cpp
  A engines/kyra/graphics/screen_eob_towns.cpp
  A engines/kyra/resource/resource_segacd.cpp
  A engines/kyra/resource/resource_segacd.h
  A engines/kyra/sequence/seqplayer_eob_segacd.cpp
  A engines/kyra/sequence/seqplayer_eob_segacd.h
  A engines/kyra/sequence/seqplayer_lok.cpp
  A engines/kyra/sequence/seqplayer_lok.h
  A engines/kyra/sound/sound_segacd_eob.cpp
  R engines/kyra/sequence/seqplayer.cpp
  R engines/kyra/sequence/seqplayer.h
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eob.h
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h
    engines/kyra/engine/kyra_lok.cpp
    engines/kyra/graphics/screen_eob.cpp
    engines/kyra/graphics/screen_eob.h
    engines/kyra/module.mk
    engines/kyra/resource/resource.cpp
    engines/kyra/resource/resource.h
    engines/kyra/resource/resource_intern.h
    engines/kyra/resource/staticres_eob.cpp
    engines/kyra/sequence/sequences_eob.cpp
    engines/kyra/sequence/sequences_lok.cpp
    engines/kyra/sound/sound.h
    engines/kyra/sound/sound_intern.h


diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index ee6cd41a85..d81782cb1d 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -23,7 +23,9 @@
 #ifdef ENABLE_EOB
 
 #include "kyra/engine/eob.h"
+#include "kyra/sequence/seqplayer_eob_segacd.h"
 #include "kyra/resource/resource.h"
+#include "kyra/resource/resource_segacd.h"
 #include "kyra/sound/sound.h"
 
 namespace Kyra {
@@ -47,10 +49,15 @@ EoBEngine::EoBEngine(OSystem *system, const GameFlags &flags)
 	_useMainMenuGUISettings = false;
 	_ttlCfg = 0;
 	_xdth = false;
+
+	_seqPlayer = 0;
+	_sres = 0;
 }
 
 EoBEngine::~EoBEngine() {
 	delete[] _itemsOverlay;
+	delete _seqPlayer;
+	delete _sres;
 }
 
 Common::Error EoBEngine::init() {
@@ -101,6 +108,11 @@ Common::Error EoBEngine::init() {
 	} else if (_configRenderMode == Common::kRenderEGA || _configRenderMode == Common::kRenderCGA) {
 		_vcnFilePattern = "%s.ECN";
 		_vmpFilePattern = "%s.EMP";
+	} else if (_flags.platform == Common::kPlatformSegaCD) {
+		_sres = new SegaCDResource(_res);
+		assert(_sres);
+		_seqPlayer = new SegaSequencePlayer(this, _sres);
+		assert(_seqPlayer);
 	}
 
 	return Common::kNoError;
diff --git a/engines/kyra/engine/eob.h b/engines/kyra/engine/eob.h
index 2d078ea58b..a06808bf0f 100644
--- a/engines/kyra/engine/eob.h
+++ b/engines/kyra/engine/eob.h
@@ -29,6 +29,8 @@
 
 namespace Kyra {
 
+class SegaCDResource;
+class SegaSequencePlayer;
 class EoBEngine : public EoBCoreEngine {
 friend class GUI_EoB;
 friend class EoBSeqPlayerCommon;
@@ -69,14 +71,14 @@ private:
 	};
 
 	static const RenderModePalFile _renderModePalFiles[3];
-	static const TitleScreenConfig _titleConfig[4];
+	static const TitleScreenConfig _titleConfig[5];
 	const TitleScreenConfig *_ttlCfg;
 
 	// Main loop
 	void startupNew() override;
 	void startupLoad() override;
 
-	// Intro/Outro
+	// Intro/Outro/Sequence Playback
 	enum IntroPart {
 		kOnlyCredits = 0,
 		kOnlyIntro,
@@ -87,7 +89,11 @@ private:
 	void seq_playFinale() override;
 	void seq_xdeath() override;
 
+	void seq_segaOpeningCredits();
+	bool seq_segaPlaySequence(int id);
+
 	const char *const *_finBonusStrings;
+	SegaSequencePlayer *_seqPlayer;
 	bool _xdth;
 
 	// characters
@@ -146,6 +152,10 @@ private:
 	int resurrectionSelectDialogue() override;
 	void healParty();
 
+	// Resource
+	SegaCDResource *_sres;
+
+	// GUI
 	const KyraRpgGUISettings *guiSettings() const override;
 	void useMainMenuGUISettings(bool toggle) override { _useMainMenuGUISettings = toggle; }
 
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 64e52a192e..9a5a58bbfc 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -470,7 +470,7 @@ Common::Error EoBCoreEngine::init() {
 	_res = new Resource(this);
 	assert(_res);
 	_res->reset();
-
+ 
 	_staticres = new StaticResource(this);
 	assert(_staticres);
 	if (!_staticres->init())
@@ -478,27 +478,39 @@ Common::Error EoBCoreEngine::init() {
 
 	// We start the respective sound driver even if "No Music" has
 	// been selected, because we don't have a null driver class (and
-	// don't really need one). We just disable the sound in the settings.
+	// don't really need one). We just disable the sound here.
 	MidiDriver::DeviceHandle dev = 0;
-	if (_flags.platform == Common::kPlatformDOS) {
+	switch (_flags.platform) {
+	case Common::kPlatformDOS: {
 		int flags = MDT_ADLIB | MDT_PCSPK;
 		dev = MidiDriver::detectDevice(_flags.gameID == GI_EOB1 ? flags | MDT_PCJR : flags);
 		MusicType type = MidiDriver::getMusicType(dev);
 		_sound = new SoundPC_v1(this, _mixer, type == MT_ADLIB ? Sound::kAdLib : type == MT_PCSPK ? Sound::kPCSpkr : Sound::kPCjr);
-	} else if (_flags.platform == Common::kPlatformFMTowns) {
+		} break;
+	case Common::kPlatformFMTowns:
 		dev = MidiDriver::detectDevice(MDT_TOWNS);
-		// SoundTowns_Darkmoon requires initialized _staticres
 		_sound = new SoundTowns_Darkmoon(this, _mixer);
-	} else if (_flags.platform == Common::kPlatformPC98) {
+		break;
+	case Common::kPlatformPC98:
 		if (_flags.gameID == GI_EOB1) {
 			dev = MidiDriver::detectDevice(MDT_PC98);
 			_sound = new SoundPC98_EoB(this, _mixer);
 		} else {
 			dev = MidiDriver::detectDevice(MDT_PC98 | MDT_MIDI);
+			/**/
 		}
-	} else if (_flags.platform == Common::kPlatformAmiga) {
+		break;
+	case Common::kPlatformAmiga:
 		dev = MidiDriver::detectDevice(MDT_AMIGA);
 		_sound = new SoundAmiga_EoB(this, _mixer);
+		break;
+	case Common::kPlatformSegaCD:
+		dev = MidiDriver::detectDevice(/*MDT_SEGACD*/MDT_TOWNS);
+		_sound = new SoundSegaCD_EoB(this, _mixer);
+		break;
+	default:
+		// Dummy error message. Unsupported platforms don't have detection entries.
+		error("Unsupported platform '%d'", _flags.platform);
 	}
 
 	assert(_sound);
@@ -506,6 +518,8 @@ Common::Error EoBCoreEngine::init() {
 
 	if (_flags.platform == Common::kPlatformPC98)
 		_sound->loadSfxFile("EFECT.OBJ");
+	else if (_flags.platform == Common::kPlatformSegaCD)
+		_sound->loadSfxFile("FMSE");
 
 	// Setup volume settings (and read in all ConfigManager settings)
 	_configNullSound = (MidiDriver::getMusicType(dev) == MT_NULL);
@@ -530,25 +544,7 @@ Common::Error EoBCoreEngine::init() {
 	assert(_inf);
 	setDebugger(new Debugger_EoB(this));
 	
-	if (_flags.platform == Common::kPlatformAmiga) {
-		if (_res->exists("EOBF6.FONT"))
-			_screen->loadFont(Screen::FID_6_FNT, "EOBF6.FONT");
-		else if (_res->exists("FONTS/EOBF6.FONT"))
-			_screen->loadFont(Screen::FID_6_FNT, "FONTS/EOBF6.FONT");
-		else
-			AmigaDOSFont::errorDialog(0);
-
-		if (_res->exists("EOBF8.FONT"))
-			_screen->loadFont(Screen::FID_8_FNT, "EOBF8.FONT");
-		else if (_res->exists("FONTS/EOBF8.FONT"))
-			_screen->loadFont(Screen::FID_8_FNT, "FONTS/EOBF8.FONT");
-		else
-			AmigaDOSFont::errorDialog(0);
-
-	} else {
-		_screen->loadFont(Screen::FID_6_FNT, "FONT6.FNT");
-		_screen->loadFont(Screen::FID_8_FNT, "FONT8.FNT");
-	}
+	loadFonts();
 
 	Common::Error err = KyraRpgEngine::init();
 	if (err.getCode() != Common::kNoError)
@@ -636,21 +632,55 @@ Common::Error EoBCoreEngine::init() {
 	return Common::kNoError;
 }
 
+void EoBCoreEngine::loadFonts() {
+	// Only the fonts that are based on game resource files are loaded here. ScummVM builtin fonts like the
+	// FM-Towns ROM font or the PC-98 SJIS font get initialized in Screen::init() and Screen_EoB::init().
+
+	if (_flags.platform == Common::kPlatformAmiga) {
+		if (_res->exists("EOBF6.FONT"))
+			_screen->loadFont(Screen::FID_6_FNT, "EOBF6.FONT");
+		else if (_res->exists("FONTS/EOBF6.FONT"))
+			_screen->loadFont(Screen::FID_6_FNT, "FONTS/EOBF6.FONT");
+		else
+			AmigaDOSFont::errorDialog(0);
+
+		if (_res->exists("EOBF8.FONT"))
+			_screen->loadFont(Screen::FID_8_FNT, "EOBF8.FONT");
+		else if (_res->exists("FONTS/EOBF8.FONT"))
+			_screen->loadFont(Screen::FID_8_FNT, "FONTS/EOBF8.FONT");
+		else
+			AmigaDOSFont::errorDialog(0);
+
+	} else if (_flags.platform != Common::kPlatformSegaCD) {
+		_screen->loadFont(Screen::FID_6_FNT, "FONT6.FNT");
+		_screen->loadFont(Screen::FID_8_FNT, "FONT8.FNT");
+	}
+
+	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, "FONT12.FNT");
+	} else if (_flags.platform == Common::kPlatformSegaCD) {
+		//_screen->loadFont(Screen::FID_8_FNT, "FONTK12");
+		//_screen->loadFont(Screen::FID_6_FNT, "FONT8SH");
+	}
+}
+
 Common::Error EoBCoreEngine::go() {
 	static_cast<Debugger_EoB *>(getDebugger())->initialize();
 	_txt->removePageBreakFlag();
-	_screen->setFont(_flags.platform == Common::kPlatformPC98 ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT);
-	loadItemsAndDecorationsShapes();
-	_screen->setMouseCursor(0, 0, _itemIconShapes[0]);
+	//_screen->setFont(_flags.platform == Common::kPlatformPC98 ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT);
+	//loadItemsAndDecorationsShapes();
+	//_screen->setMouseCursor(0, 0, _itemIconShapes[0]);
 
 	// Import original save game files (especially the "Quick Start Party")
 	if (ConfMan.getBool("importOrigSaves")) {
-		importOriginalSaveFile(-1);
-		ConfMan.setBool("importOrigSaves", false);
-		ConfMan.flushToDisk();
+		//importOriginalSaveFile(-1);
+		//ConfMan.setBool("importOrigSaves", false);
+		//ConfMan.flushToDisk();
 	}
 
-	loadItemDefs();
+	//loadItemDefs();
 	int action = 0;
 
 	for (bool repeatLoop = true; repeatLoop; repeatLoop ^= true) {
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index b3464df850..224da7a7d9 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -263,6 +263,7 @@ public:
 protected:
 	// Startup
 	Common::Error init() override;
+	void loadFonts();
 	Common::Error go() override;
 
 	// Main Menu, Intro, Finale
@@ -848,6 +849,7 @@ protected:
 	int countResurrectionCandidates();
 
 	void seq_portal();
+	bool seq_playSegaSequence(int id) { return true; }
 	bool checkPassword();
 
 	Common::String convertAsciiToSjis(Common::String str);
diff --git a/engines/kyra/engine/kyra_lok.cpp b/engines/kyra/engine/kyra_lok.cpp
index a6b0a20dab..d695754b0f 100644
--- a/engines/kyra/engine/kyra_lok.cpp
+++ b/engines/kyra/engine/kyra_lok.cpp
@@ -22,7 +22,7 @@
 
 #include "kyra/engine/kyra_lok.h"
 #include "kyra/resource/resource.h"
-#include "kyra/sequence/seqplayer.h"
+#include "kyra/sequence/seqplayer_lok.h"
 #include "kyra/engine/sprites.h"
 #include "kyra/graphics/animator_lok.h"
 #include "kyra/gui/debugger.h"
diff --git a/engines/kyra/graphics/screen_eob.cpp b/engines/kyra/graphics/screen_eob.cpp
index cf29f08483..4ae699e80f 100644
--- a/engines/kyra/graphics/screen_eob.cpp
+++ b/engines/kyra/graphics/screen_eob.cpp
@@ -66,8 +66,9 @@ Screen_EoB::Screen_EoB(EoBCoreEngine *vm, OSystem *system) : Screen(vm, system,
 	_cyclePalette = 0;
 	_cpsFilePattern = "%s.";
 	_activePalCycle = 0;
-	for (int i = 0; i < 10; ++i)
-		_palette16c[i] = 0;	
+	_segaRenderer = 0;
+	memset(_brState, 0, sizeof(_brState));
+	memset(_segaPalette, 0, sizeof(_segaPalette));
 }
 
 Screen_EoB::~Screen_EoB() {
@@ -80,6 +81,7 @@ Screen_EoB::~Screen_EoB() {
 	delete[] _cgaDitheringTables[0];
 	delete[] _cgaDitheringTables[1];
 	delete[] _cyclePalette;
+	delete _segaRenderer;
 }
 
 bool Screen_EoB::init() {
@@ -92,12 +94,10 @@ bool Screen_EoB::init() {
 			_shpBuffer = new uint8[SCREEN_H * SCREEN_W];
 			_convertHiColorBuffer = new uint8[SCREEN_H * SCREEN_W];
 			enableHiColorMode(true);			
-			loadFont(FID_SJIS_SMALL_FNT, "FONT.DMP");
 			assert(_fonts[FID_SJIS_FNT]);
 			_fonts[FID_SJIS_FNT]->setStyle(Font::kFSFat);
 			_fonts[FID_SJIS_LARGE_FNT] = new SJISFontLarge(_sjisFontShared);
 		} else if (_vm->game() == GI_EOB1 && _vm->gameFlags().platform == Common::kPlatformPC98) {
-			loadFont(FID_SJIS_SMALL_FNT, "FONT12.FNT");
 			_fonts[FID_SJIS_FNT] = new SJISFontEoB1PC98(_sjisFontShared, /*12,*/ _vm->staticres()->loadRawDataBe16(kEoB1Ascii2SjisTable1, temp), _vm->staticres()->loadRawDataBe16(kEoB1Ascii2SjisTable2, temp));
 		}
 
@@ -118,12 +118,16 @@ bool Screen_EoB::init() {
 			memset(_cgaScaleTable, 0, 256 * sizeof(uint8));
 			for (int i = 0; i < 256; i++)
 				_cgaScaleTable[i] = ((i & 0xF0) >> 2) | (i & 0x03);
-		}
-
-		const uint8 *pal16c = _vm->staticres()->loadRawData(kEoB1Palettes16c, temp);
-		if (pal16c) {
-			for (int i = 0; i < 10; i++)
-				_palette16c[i] = pal16c + i * 48;
+		} else if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
+			_segaRenderer = new SegaRenderer(this);
+			_segaRenderer->setResolution(320, 224);
+			_segaRenderer->setPlaneTableLocation(SegaRenderer::kPlaneA, 0xC000);
+			_segaRenderer->setPlaneTableLocation(SegaRenderer::kPlaneB, 0xE000);
+			_segaRenderer->setPlaneTableLocation(SegaRenderer::kWindowPlane, 0xF000);
+			_segaRenderer->setupPlaneAB(SegaRenderer::kPlaneA, 1024, 256);
+			_segaRenderer->setupPlaneAB(SegaRenderer::kPlaneB, 1024, 256);
+			_segaRenderer->setupWindowPlane(0, 0, SegaRenderer::kWinToLeft, SegaRenderer::kWinToTop);
+			_segaRenderer->setHScrollTableLocation(0xD800);
 		}
 
 		static const char *cpsExt[] = { "CPS", "EGA", "SHP", "BIN" };
@@ -1501,16 +1505,18 @@ bool Screen_EoB::loadFont(FontId fontId, const char *filename) {
 	if (fnt)
 		delete fnt;
 
-	if (!scumm_stricmp(filename, "FONT.DMP"))
-		fnt = new SJISFont12x12(_vm->staticres()->loadRawDataBe16(kEoB2FontDmpSearchTbl, temp));
-	else if (!scumm_stricmp(filename, "FONT12.FNT"))
-		fnt = new Font12x12PC98(12, _vm->staticres()->loadRawDataBe16(kEoB1Ascii2SjisTable1, temp),
-			_vm->staticres()->loadRawDataBe16(kEoB1Ascii2SjisTable2, temp), _vm->staticres()->loadRawData(kEoB1FontLookupTable, temp));
-	else if (_isAmiga)
+	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)
+			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
+	} 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);
+	}
 
 	assert(fnt);
 
@@ -1524,330 +1530,6 @@ bool Screen_EoB::loadFont(FontId fontId, const char *filename) {
 	return ret;
 }
 
-void Screen_EoB::decodeSHP(const uint8 *data, int dstPage) {
-	int32 bytesLeft = READ_LE_UINT32(data);
-	const uint8 *src = data + 4;
-	uint8 *dst = getPagePtr(dstPage);
-
-	if (bytesLeft < 0) {
-		memcpy(dst, data, 64000);
-		return;
-	}
-
-	while (bytesLeft > 0) {
-		uint8 code = *src++;
-		bytesLeft--;
-
-		for (int i = 8; i; i--) {
-			if (code & 0x80) {
-				uint16 copyOffs = (src[0] << 4) | (src[1] >> 4);
-				uint8 count = (src[1] & 0x0F) + 3;
-				src += 2;
-				bytesLeft -= 2;
-				const uint8 *copySrc = dst - 1 - copyOffs;
-				while (count--)
-					*dst++ = *copySrc++;
-			} else if (bytesLeft) {
-				*dst++ = *src++;
-				bytesLeft--;
-			} else {
-				break;
-			}
-			code <<= 1;
-		}
-	}
-}
-
-void Screen_EoB::convertToHiColor(int page) {
-	if (!_16bitPalette)
-		return;
-	uint16 *dst = (uint16 *)getPagePtr(page);
-	memcpy(_convertHiColorBuffer, dst, SCREEN_H * SCREEN_W);
-	uint8 *src = _convertHiColorBuffer;
-	for (int s = SCREEN_H * SCREEN_W; s; --s)
-		*dst++ = _16bitPalette[*src++];
-}
-
-void Screen_EoB::shadeRect(int x1, int y1, int x2, int y2, int shadingLevel) {
-	if (!_16bitPalette)
-		return;
-
-	int l = _16bitShadingLevel;
-	_16bitShadingLevel = shadingLevel;
-
-	if (_curPage == 0 || _curPage == 1)
-		addDirtyRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
-
-	uint16 *dst = (uint16*)(getPagePtr(_curPage) + y1 * SCREEN_W * _bytesPerPixel + x1 * _bytesPerPixel);
-
-	for (; y1 < y2; ++y1) {
-		uint16 *ptr = dst;
-		for (int i = 0; i < x2 - x1; i++) {
-			*ptr = shade16bitColor(*ptr);
-			ptr++;
-		}
-		dst += SCREEN_W;
-	}
-
-	_16bitShadingLevel = l;
-}
-
-void Screen_EoB::selectPC98Palette(int paletteIndex, Palette &dest, int brightness, bool set) {
-	if (paletteIndex < 0 || paletteIndex > 9)
-		return;
-	if (!_use16ColorMode || !_palette16c[paletteIndex])
-		return;
-
-	uint8 pal[48];
-	for (int i = 0; i < 48; ++i)
-		pal[i] = CLIP<int>(_palette16c[paletteIndex][i] + brightness, 0, 15);
-	loadPalette(pal, dest, 48);
-	
-	if (set)
-		setScreenPalette(dest);
-}
-
-void Screen_EoB::decodeBIN(const uint8 *src, uint8 *dst, uint16 inSize) {
-	const uint8 *end = src + inSize;
-	memset(_dsTempPage, 0, 2048);
-	int tmpDstOffs = 0;
-
-	while (src < end) {
-		uint8 code = *src++;
-		if (!(code & 0x80)) {
-			int offs = code << 4;
-			code = *src++;
-			offs |= (code >> 4);
-			int len = (code & 0x0F) + 2;
-			int tmpSrcOffs = (tmpDstOffs - offs) & 0x7FF;
-			const uint8 *tmpSrc2 = dst;
-
-			for (int len2 = len; len2; len2--) {
-				*dst++ = _dsTempPage[tmpSrcOffs++];
-				tmpSrcOffs &= 0x7FF;
-			}
-
-			while (len--) {
-				_dsTempPage[tmpDstOffs++] = *tmpSrc2++;
-				tmpDstOffs &= 0x7FF;
-			}
-
-		} else if (code & 0x40) {
-			int len = code & 7;
-			if (code & 0x20)
-				len = (len << 8) | *src++;
-			len += 2;
-
-			int planes = ((code >> 3) & 3) + 1;
-			while (len--) {
-				for (int i = 0; i < planes; ++i) {
-					*dst++ = _dsTempPage[tmpDstOffs++] = src[i];
-					tmpDstOffs &= 0x7FF;
-				}
-			}
-			src += planes;
-		} else {
-			for (int len = (code & 0x3F) + 1; len; len--) {
-				*dst++ = _dsTempPage[tmpDstOffs++] = *src++;
-				tmpDstOffs &= 0x7FF;
-			}
-		}
-	}
-}
-
-void Screen_EoB::decodePC98PlanarBitmap(uint8 *srcDstBuffer, uint8 *tmpBuffer, uint16 size) {
-	assert(tmpBuffer != srcDstBuffer);
-	memcpy(tmpBuffer, srcDstBuffer, size);
-	const uint8 *src = tmpBuffer;
-	uint8 *dst1 = srcDstBuffer;
-	uint8 *dst2 = srcDstBuffer + 4;
-	size >>= 3;
-	while (size--) {
-		for (int i = 0; i < 4; ++i) {
-			uint8 col1 = 0;
-			uint8 col2 = 0;
-			for (int ii = 0; ii < 4; ++ii) {
-				col1 |= ((src[ii] >> (7 - i)) & 1) << ii;
-				col2 |= ((src[ii] >> (3 - i)) & 1) << ii;
-			}
-			*dst1++ = col1;
-			*dst2++ = col2;
-		}
-		src += 4;
-		dst1 += 4;
-		dst2 += 4;
-	}
-}
-
-void Screen_EoB::initPC98PaletteCycle(int paletteIndex, PalCycleData *data) {
-	if (!_use16ColorMode || !_cyclePalette)
-		return;
-
-	_activePalCycle = data;
-
-	if (data)
-		memcpy(_cyclePalette, _palette16c[paletteIndex], 48);
-	else
-		memset(_cyclePalette, 0, 48);
-}
-
-void Screen_EoB::updatePC98PaletteCycle(int brightness) {
-	if (_activePalCycle) {
-		for (int i = 0; i < 48; ++i) {
-			if (--_activePalCycle[i].delay)
-				continue;
-			for (int8 in = 32; in == 32; ) {
-				in = *_activePalCycle[i].data++;
-				if (in < 16 && in > -16) {
-					_cyclePalette[i] += in;
-					_activePalCycle[i].delay = *_activePalCycle[i].data++;
-				} else if (in < 32) {
-					_cyclePalette[i] = in - 16;
-					_activePalCycle[i].delay = *_activePalCycle[i].data++;
-				} else if (in == 32)
-					_activePalCycle[i].data += READ_BE_INT16(_activePalCycle[i].data);
-			}
-		}
-	}
-
-	uint8 pal[48];
-	for (int i = 0; i < 48; ++i)
-		pal[i] = CLIP<int>(_cyclePalette[i] + brightness, 0, 15);
-	loadPalette(pal, *_palettes[0], 48);
-	setScreenPalette(*_palettes[0]);
-}
-
-static uint32 _decodeFrameAmiga_x = 0;
-
-bool decodeFrameAmiga_readNextBit(const uint8 *&data, uint32 &code, uint32 &chk) {
-	_decodeFrameAmiga_x = code & 1;
-	code >>= 1;
-	if (code)
-		return _decodeFrameAmiga_x;
-
-	data -= 4;
-	code = READ_BE_UINT32(data);
-	chk ^= code;
-	_decodeFrameAmiga_x = code & 1;
-	code = (code >> 1) | (1 << 31);
-
-	return _decodeFrameAmiga_x;
-}
-
-uint32 decodeFrameAmiga_readBits(const uint8 *&data, uint32 &code, uint32 &chk, int count) {
-	uint32 res = 0;
-	while (count--) {
-		decodeFrameAmiga_readNextBit(data, code, chk);
-		uint32 bt1 = _decodeFrameAmiga_x;
-		_decodeFrameAmiga_x = res >> 31;
-		res = (res << 1) | bt1;
-	}
-	return res;
-}
-
-void Screen_EoB::loadSpecialAmigaCPS(const char *fileName, int destPage, bool isGraphics) {
-	uint32 fileSize = 0;
-	const uint8 *file = _vm->resource()->fileData(fileName, &fileSize);
-
-	if (!file)
-		error("Screen_EoB::loadSpecialAmigaCPS(): Failed to load file '%s'", file);
-
-	uint32 inSize = READ_BE_UINT32(file);
-	const uint8 *pos = file;
-
-	// Check whether the file starts with the actual compression header.
-	// If this is not the case, there should a palette before the header.
-	// Unlike normal CPS files these files never have more than one palette.
-	if (((inSize + 15) & ~3) != ((fileSize + 3) & ~3)) {
-		Common::MemoryReadStream in(pos, 64);
-		_palettes[0]->loadAmigaPalette(in, 0, 32);
-		pos += 64;
-	}
-
-	inSize = READ_BE_UINT32(pos);
-	uint32 outSize = READ_BE_UINT32(pos + 4);
-	uint32 chk = READ_BE_UINT32(pos + 8);
-
-	pos = pos + 8 + inSize;
-	uint8 *dstStart = _pagePtrs[destPage];
-	uint8 *dst = dstStart + outSize;
-
-	uint32 val = READ_BE_UINT32(pos);
-	_decodeFrameAmiga_x = 0;
-	chk ^= val;
-
-	while (dst > dstStart) {
-		int para = -1;
-		int para2 = 0;
-
-		if (decodeFrameAmiga_readNextBit(pos, val, chk)) {
-			uint32 code = decodeFrameAmiga_readBits(pos, val, chk, 2);
-
-			if (code == 3) {
-				para = para2 = 8;
-			} else {
-				int cnt = 0;
-				if (code < 2) {
-					cnt = 3 + code;
-					para2 = 9 + code;
-				} else {
-					cnt = decodeFrameAmiga_readBits(pos, val, chk, 8) + 1;
-					para2 = 12;
-				}
-					
-				code = decodeFrameAmiga_readBits(pos, val, chk, para2);
-				while (cnt--) {
-					dst--;
-					*dst = dst[code & 0xFFFF];
-				}
-			}
-		} else {
-			if (decodeFrameAmiga_readNextBit(pos, val, chk)) {
-				uint32 code = decodeFrameAmiga_readBits(pos, val, chk, 8);
-				dst--;
-				*dst = dst[code & 0xFFFF];
-				dst--;
-				*dst = dst[code & 0xFFFF];
-
-			} else {
-				para = 3;				
-			}
-		}
-
-		if (para > 0) {
-			uint32 code = decodeFrameAmiga_readBits(pos, val, chk, para);
-			uint32 cnt = (code & 0xFFFF) + para2 + 1;
-
-			while (cnt--) {
-				for (int i = 0; i < 8; ++i) {
-					decodeFrameAmiga_readNextBit(pos, val, chk);
-					uint32 bt1 = _decodeFrameAmiga_x;
-					_decodeFrameAmiga_x = code >> 31;
-					code = (code << 1) | bt1;
-				}
-				*(--dst) = code & 0xFF;
-			}
-		}
-	}
-
-	delete[] file;
-
-	if (chk)
-		error("Screen_EoB::loadSpecialAmigaCPS(): Checksum error");
-
-	if (isGraphics)
-		convertAmigaGfx(_pagePtrs[destPage], 320, 200);
-}
-
-void Screen_EoB::setDualPalettes(Palette &top, Palette &bottom) {
-	// The original supports simultaneous fading of both palettes, but doesn't make any use of that
-	// feature. The fade rate is always set to 0. So I see no need to implement that.
-	_palettes[0]->copy(top, 0, 32, 0);
-	_palettes[0]->copy(bottom, 0, 32, 32);
-	setScreenPalette(*_palettes[0]);
-	enableDualPaletteMode(120);
-}
-
 void Screen_EoB::updateDirtyRects() {
 	if (!_useHiResEGADithering) {
 		Screen::updateDirtyRects();
diff --git a/engines/kyra/graphics/screen_eob.h b/engines/kyra/graphics/screen_eob.h
index 3038bb13f5..a930f43b0a 100644
--- a/engines/kyra/graphics/screen_eob.h
+++ b/engines/kyra/graphics/screen_eob.h
@@ -29,8 +29,10 @@
 
 namespace Kyra {
 
+class SegaRenderer;
 class EoBCoreEngine;
 class Screen_EoB : public Screen {
+friend class SegaRenderer;
 public:
 	Screen_EoB(EoBCoreEngine *vm, OSystem *system);
 	~Screen_EoB() override;
@@ -92,7 +94,7 @@ public:
 	void shadeRect(int x1, int y1, int x2, int y2, int shadingLevel);
 
 	// PC-98 specific
-	void selectPC98Palette(int paletteIndex, Palette &dest, int brightness = 0, bool set = false);
+	void selectPC98Palette(int palID, Palette &dest, int brightness = 0, bool set = false);
 	void decodeBIN(const uint8 *src, uint8 *dst, uint16 inSize);
 	void decodePC98PlanarBitmap(uint8 *srcDstBuffer, uint8 *tmpBuffer, uint16 size = 64000);
 
@@ -101,7 +103,7 @@ public:
 		uint8 delay;
 	};
 
-	void initPC98PaletteCycle(int paletteIndex, PalCycleData *data);
+	void initPC98PaletteCycle(int palID, PalCycleData *data);
 	void updatePC98PaletteCycle(int brightness);
 
 	PalCycleData *_activePalCycle;
@@ -114,6 +116,15 @@ public:
 	// registers on the fly at vertical beam position 120).
 	void setDualPalettes(Palette &top, Palette &bottom);
 
+	// SegaCD specific
+	void sega_selectPalette(int srcPalID, int dstPalID, int brightness = 0, bool set = false);
+	void sega_fadePalette(int delay, int brEnd, int dstPalID = -1);
+	void sega_fadeToBlack(int delay) { sega_fadePalette(delay, -8); }
+	void sega_fadeToWhite(int delay) { sega_fadePalette(delay, 8); }
+	void sega_fadeToNeutral(int delay) { sega_fadePalette(delay, 0); }
+	void sega_paletteOps(int opPal, int par1, int par2);
+	SegaRenderer *sega_getRenderer() const { return _segaRenderer; }
+
 private:
 	void updateDirtyRects() override;
 	void ditherRect(const uint8 *src, uint8 *dst, int dstPitch, int srcW, int srcH, int colorKey = -1);
@@ -150,9 +161,6 @@ private:
 	uint8 *_egaDitheringTable;
 	uint8 *_egaDitheringTempPage;
 
-	// hard coded 16 color palettes for PC98 version of EOB1
-	const uint8 *_palette16c[10];
-
 	Common::String _cpsFilePattern;
 
 	const uint16 _cursorColorKey16Bit;
@@ -160,6 +168,85 @@ private:
 	static const uint8 _egaMatchTable[];
 	static const ScreenDim _screenDimTable[];
 	static const int _screenDimTableCount;
+
+	// SegaCD specific
+	SegaRenderer *_segaRenderer;
+	uint16 _segaPalette[64];
+	uint8 _brState[4];
+};
+
+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
+	};
+
+public:
+	SegaRenderer(Screen_EoB *screen);
+	~SegaRenderer();
+
+	void setResolution(int w, int h);
+	void setPlaneTableLocation(int plane, uint16 addr);
+	void setupPlaneAB(int plane, uint16 w, uint16 h);
+	void setupWindowPlane(int blockX, int blockY, int horizontalMode, int verticalMode);
+	void setHScrollTableLocation(int addr);
+	void setPitch(int pitch);
+	void setHScrollMode(int mode);	
+
+	void loadToVRAM(const uint16 *data, int dataSize, int addr);
+	void loadToVRAM(Common::SeekableReadStreamEndian *in, int addr, bool compressedData = false);
+	void memsetVRAM(int addr, uint8 val, int len);
+	void fillRectWithTiles(int addr, int x, int y, int w, int h, uint16 nameTblEntry, bool incr = false);
+	
+	void render(int destPageNum);
+
+private:
+	void renderTile(uint8 *dst, int destX, uint16 *nameTable, int hScrollTableIndex);
+	void renderLineFragment(uint8 *&dst, const uint8 *src, int start, int end, uint8 pal);
+
+	void checkUpdateDirtyRects(int addr, int len);
+	void addDirtyRect(int x, int y, int w, int h);
+	void sendDirtyRectsToScreen();
+	void clearDirtyRects();
+
+	struct SegaPlane {
+		SegaPlane() : blockX(0), blockY(0), w(0), h(0), nameTable(0) {}
+		int blockX, blockY;
+		uint16 w, h;
+		uint16 *nameTable;
+		uint16 nameTableSize;
+	};
+
+	SegaPlane _planes[3];
+	uint8 *_vram;
+	uint16 *_hScrollTable;
+	uint8 _hScrollMode;
+	uint16 _pitch;
+
+	struct DRChainEntry {
+		DRChainEntry(DRChainEntry *chain, int x, int y, int w, int h) : next(chain), rect(x, y, x + w, y + h) {}
+		Common::Rect rect;
+		DRChainEntry *next;
+	} *_drChain;
+
+	Screen_EoB *_screen;
+
+	uint16 _screenW, _screenH, _blocksW, _blocksH;
 };
 
 /**
diff --git a/engines/kyra/graphics/screen_eob_amiga.cpp b/engines/kyra/graphics/screen_eob_amiga.cpp
new file mode 100644
index 0000000000..df6e88f879
--- /dev/null
+++ b/engines/kyra/graphics/screen_eob_amiga.cpp
@@ -0,0 +1,164 @@
+/* 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.
+ *
+ */
+
+
+#ifdef ENABLE_EOB
+
+#include "kyra/resource/resource.h"
+#include "common/memstream.h"
+
+namespace Kyra {
+
+static uint32 _decodeFrameAmiga_x = 0;
+
+bool decodeFrameAmiga_readNextBit(const uint8 *&data, uint32 &code, uint32 &chk) {
+	_decodeFrameAmiga_x = code & 1;
+	code >>= 1;
+	if (code)
+		return _decodeFrameAmiga_x;
+
+	data -= 4;
+	code = READ_BE_UINT32(data);
+	chk ^= code;
+	_decodeFrameAmiga_x = code & 1;
+	code = (code >> 1) | (1 << 31);
+
+	return _decodeFrameAmiga_x;
+}
+
+uint32 decodeFrameAmiga_readBits(const uint8 *&data, uint32 &code, uint32 &chk, int count) {
+	uint32 res = 0;
+	while (count--) {
+		decodeFrameAmiga_readNextBit(data, code, chk);
+		uint32 bt1 = _decodeFrameAmiga_x;
+		_decodeFrameAmiga_x = res >> 31;
+		res = (res << 1) | bt1;
+	}
+	return res;
+}
+
+void Screen_EoB::loadSpecialAmigaCPS(const char *fileName, int destPage, bool isGraphics) {
+	uint32 fileSize = 0;
+	const uint8 *file = _vm->resource()->fileData(fileName, &fileSize);
+
+	if (!file)
+		error("Screen_EoB::loadSpecialAmigaCPS(): Failed to load file '%s'", file);
+
+	uint32 inSize = READ_BE_UINT32(file);
+	const uint8 *pos = file;
+
+	// Check whether the file starts with the actual compression header.
+	// If this is not the case, there should a palette before the header.
+	// Unlike normal CPS files these files never have more than one palette.
+	if (((inSize + 15) & ~3) != ((fileSize + 3) & ~3)) {
+		Common::MemoryReadStream in(pos, 64);
+		_palettes[0]->loadAmigaPalette(in, 0, 32);
+		pos += 64;
+	}
+
+	inSize = READ_BE_UINT32(pos);
+	uint32 outSize = READ_BE_UINT32(pos + 4);
+	uint32 chk = READ_BE_UINT32(pos + 8);
+
+	pos = pos + 8 + inSize;
+	uint8 *dstStart = _pagePtrs[destPage];
+	uint8 *dst = dstStart + outSize;
+
+	uint32 val = READ_BE_UINT32(pos);
+	_decodeFrameAmiga_x = 0;
+	chk ^= val;
+
+	while (dst > dstStart) {
+		int para = -1;
+		int para2 = 0;
+
+		if (decodeFrameAmiga_readNextBit(pos, val, chk)) {
+			uint32 code = decodeFrameAmiga_readBits(pos, val, chk, 2);
+
+			if (code == 3) {
+				para = para2 = 8;
+			} else {
+				int cnt = 0;
+				if (code < 2) {
+					cnt = 3 + code;
+					para2 = 9 + code;
+				} else {
+					cnt = decodeFrameAmiga_readBits(pos, val, chk, 8) + 1;
+					para2 = 12;
+				}
+					
+				code = decodeFrameAmiga_readBits(pos, val, chk, para2);
+				while (cnt--) {
+					dst--;
+					*dst = dst[code & 0xFFFF];
+				}
+			}
+		} else {
+			if (decodeFrameAmiga_readNextBit(pos, val, chk)) {
+				uint32 code = decodeFrameAmiga_readBits(pos, val, chk, 8);
+				dst--;
+				*dst = dst[code & 0xFFFF];
+				dst--;
+				*dst = dst[code & 0xFFFF];
+
+			} else {
+				para = 3;				
+			}
+		}
+
+		if (para > 0) {
+			uint32 code = decodeFrameAmiga_readBits(pos, val, chk, para);
+			uint32 cnt = (code & 0xFFFF) + para2 + 1;
+
+			while (cnt--) {
+				for (int i = 0; i < 8; ++i) {
+					decodeFrameAmiga_readNextBit(pos, val, chk);
+					uint32 bt1 = _decodeFrameAmiga_x;
+					_decodeFrameAmiga_x = code >> 31;
+					code = (code << 1) | bt1;
+				}
+				*(--dst) = code & 0xFF;
+			}
+		}
+	}
+
+	delete[] file;
+
+	if (chk)
+		error("Screen_EoB::loadSpecialAmigaCPS(): Checksum error");
+
+	if (isGraphics)
+		convertAmigaGfx(_pagePtrs[destPage], 320, 200);
+}
+
+void Screen_EoB::setDualPalettes(Palette &top, Palette &bottom) {
+	// The original supports simultaneous fading of both palettes, but doesn't make any use of that
+	// feature. The fade rate is always set to 0. So I see no need to implement that.
+	_palettes[0]->copy(top, 0, 32, 0);
+	_palettes[0]->copy(bottom, 0, 32, 32);
+	setScreenPalette(*_palettes[0]);
+	enableDualPaletteMode(120);
+}
+
+} // End of namespace Kyra
+
+#endif // ENABLE_EOB
diff --git a/engines/kyra/graphics/screen_eob_pc98.cpp b/engines/kyra/graphics/screen_eob_pc98.cpp
new file mode 100644
index 0000000000..0a435b97a3
--- /dev/null
+++ b/engines/kyra/graphics/screen_eob_pc98.cpp
@@ -0,0 +1,168 @@
+/* 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.
+ *
+ */
+
+
+#ifdef ENABLE_EOB
+
+#include "kyra/resource/resource.h"
+
+namespace Kyra {
+
+void Screen_EoB::selectPC98Palette(int palID, Palette &dest, int brightness, bool set) {
+	if (palID < 0 || palID > 9)
+		return;
+	if (!_use16ColorMode)
+		return;
+
+	int temp = 0;
+	const uint8 *pal16c = _vm->staticres()->loadRawData(kEoB1Palettes16c, temp);
+	if (!pal16c)
+		return;
+
+	uint8 pal[48];
+	for (int i = 0; i < 48; ++i)
+		pal[i] = CLIP<int>(pal16c[palID * 48 + i] + brightness, 0, 15);
+	loadPalette(pal, dest, 48);
+	
+	if (set)
+		setScreenPalette(dest);
+}
+
+void Screen_EoB::decodeBIN(const uint8 *src, uint8 *dst, uint16 inSize) {
+	const uint8 *end = src + inSize;
+	memset(_dsTempPage, 0, 2048);
+	int tmpDstOffs = 0;
+
+	while (src < end) {
+		uint8 code = *src++;
+		if (!(code & 0x80)) {
+			int offs = code << 4;
+			code = *src++;
+			offs |= (code >> 4);
+			int len = (code & 0x0F) + 2;
+			int tmpSrcOffs = (tmpDstOffs - offs) & 0x7FF;
+			const uint8 *tmpSrc2 = dst;
+
+			for (int len2 = len; len2; len2--) {
+				*dst++ = _dsTempPage[tmpSrcOffs++];
+				tmpSrcOffs &= 0x7FF;
+			}
+
+			while (len--) {
+				_dsTempPage[tmpDstOffs++] = *tmpSrc2++;
+				tmpDstOffs &= 0x7FF;
+			}
+
+		} else if (code & 0x40) {
+			int len = code & 7;
+			if (code & 0x20)
+				len = (len << 8) | *src++;
+			len += 2;
+
+			int planes = ((code >> 3) & 3) + 1;
+			while (len--) {
+				for (int i = 0; i < planes; ++i) {
+					*dst++ = _dsTempPage[tmpDstOffs++] = src[i];
+					tmpDstOffs &= 0x7FF;
+				}
+			}
+			src += planes;
+		} else {
+			for (int len = (code & 0x3F) + 1; len; len--) {
+				*dst++ = _dsTempPage[tmpDstOffs++] = *src++;
+				tmpDstOffs &= 0x7FF;
+			}
+		}
+	}
+}
+
+void Screen_EoB::decodePC98PlanarBitmap(uint8 *srcDstBuffer, uint8 *tmpBuffer, uint16 size) {
+	assert(tmpBuffer != srcDstBuffer);
+	memcpy(tmpBuffer, srcDstBuffer, size);
+	const uint8 *src = tmpBuffer;
+	uint8 *dst1 = srcDstBuffer;
+	uint8 *dst2 = srcDstBuffer + 4;
+	size >>= 3;
+	while (size--) {
+		for (int i = 0; i < 4; ++i) {
+			uint8 col1 = 0;
+			uint8 col2 = 0;
+			for (int ii = 0; ii < 4; ++ii) {
+				col1 |= ((src[ii] >> (7 - i)) & 1) << ii;
+				col2 |= ((src[ii] >> (3 - i)) & 1) << ii;
+			}
+			*dst1++ = col1;
+			*dst2++ = col2;
+		}
+		src += 4;
+		dst1 += 4;
+		dst2 += 4;
+	}
+}
+
+void Screen_EoB::initPC98PaletteCycle(int palID, PalCycleData *data) {
+	if (!_use16ColorMode || !_cyclePalette)
+		return;
+	if (palID < 0 || palID > 9)
+		return;
+
+	_activePalCycle = data;
+	int temp = 0;
+	const uint8 *pal16c = _vm->staticres()->loadRawData(kEoB1Palettes16c, temp);
+	if (!pal16c)
+		return;
+
+	if (data)
+		memcpy(_cyclePalette, &pal16c[palID * 48], 48);
+	else
+		memset(_cyclePalette, 0, 48);
+}
+
+void Screen_EoB::updatePC98PaletteCycle(int brightness) {
+	if (_activePalCycle) {
+		for (int i = 0; i < 48; ++i) {
+			if (--_activePalCycle[i].delay)
+				continue;
+			for (int8 in = 32; in == 32; ) {
+				in = *_activePalCycle[i].data++;
+				if (in < 16 && in > -16) {
+					_cyclePalette[i] += in;
+					_activePalCycle[i].delay = *_activePalCycle[i].data++;
+				} else if (in < 32) {
+					_cyclePalette[i] = in - 16;
+					_activePalCycle[i].delay = *_activePalCycle[i].data++;
+				} else if (in == 32)
+					_activePalCycle[i].data += READ_BE_INT16(_activePalCycle[i].data);
+			}
+		}
+	}
+
+	uint8 pal[48];
+	for (int i = 0; i < 48; ++i)
+		pal[i] = CLIP<int>(_cyclePalette[i] + brightness, 0, 15);
+	loadPalette(pal, *_palettes[0], 48);
+	setScreenPalette(*_palettes[0]);
+}
+
+} // End of namespace Kyra
+
+#endif // ENABLE_EOB
diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
new file mode 100644
index 0000000000..2f21e8b4b6
--- /dev/null
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -0,0 +1,434 @@
+/* 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.
+ *
+ */
+
+
+#ifdef ENABLE_EOB
+
+#include "common/system.h"
+#include "graphics/palette.h"
+#include "kyra/resource/resource.h"
+
+namespace Kyra {
+
+struct PaletteFader {
+	PaletteFader() : _brCur(0), _brDest(0), _fadeIncr(0), _fadeDelay(0), _fadeTimer(0) {}
+	int16 _brCur;
+	int16 _brDest;
+	int16 _fadeIncr;
+	int16 _fadeDelay;
+	int16 _fadeTimer;
+};
+
+void Screen_EoB::sega_selectPalette(int srcPalID, int dstPalID, int brightness, bool set) {
+	if (srcPalID < -1 || srcPalID > 59 || dstPalID < 0 || dstPalID > 3)
+		return;
+
+	const uint16 *src = &_segaPalette[dstPalID << 4];
+	uint8 rgbColors[48];
+	uint8 *dst = rgbColors;
+
+	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 < 16; ++i) {
+		uint16 in = *src++;
+		_segaPalette[dstPalID << 4 | i] = in;
+#if 0
+		static const uint8 col[8] = { 0, 52, 87, 116, 144, 172, 206, 255 };
+		*dst++ = col[CLIP<int>(((in & 0x00F) >> 1) + brightness, 0, 7)];
+		*dst++ = col[CLIP<int>(((in & 0x0F0) >> 5) + brightness, 0, 7)];
+		*dst++ = col[CLIP<int>(((in & 0xF00) >> 9) + brightness, 0, 7)];
+#else
+		*dst++ = CLIP<int>(((in & 0x00F) >> 1) + brightness, 0, 7) * 255 / 7;
+		*dst++ = CLIP<int>(((in & 0x0F0) >> 5) + brightness, 0, 7) * 255 / 7;
+		*dst++ = CLIP<int>(((in & 0xF00) >> 9) + brightness, 0, 7) * 255 / 7;
+#endif
+	}
+
+	getPalette(0).copy(rgbColors, 0, 16, dstPalID << 4);
+
+	if (set) {
+		_vm->_system->getPaletteManager()->setPalette(rgbColors, dstPalID << 4, 16);
+		_paletteChanged = true;
+	}
+}
+
+void Screen_EoB::sega_fadePalette(int delay, int brEnd, int dstPalID) {
+	int first = 0;
+	int last = 3;
+	if (dstPalID >= 0)
+		first = last = dstPalID;
+
+	PaletteFader faders[4];
+
+	for (int i = first; i <= last; ++i) {
+		PaletteFader &f = faders[i];
+		f._brCur = _brState[i];
+		if (f._brCur < brEnd)
+			f._fadeIncr = 1;
+		else if (f._brCur > brEnd)
+			f._fadeIncr = -1;
+		else
+			continue;
+
+		f._brDest = brEnd;
+		f._fadeDelay = f._fadeTimer = delay;
+	}
+
+	for (bool runLoop = true; runLoop; ) {
+		uint32 end = _vm->_system->getMillis() + 17;
+		bool update = false;
+		for (int i = first; i <= last; ++i) {
+			_brState[i] = 127;
+			PaletteFader &f = faders[i];
+			if (f._fadeDelay == 0 && f._brCur != f._brDest) {
+				f._brCur = f._brDest;
+				update = true;
+			}
+			if (f._brCur == f._brDest)
+				continue;
+			if (--f._fadeTimer)
+				continue;
+
+			f._brCur += f._fadeIncr;
+			f._fadeTimer = f._fadeDelay;
+			_brState[i] = faders[i]._brCur;
+		}
+		
+		for (int i = first; i <= last; ++i) {
+			if (_brState[i] != 127) {
+				sega_selectPalette(-1, i, _brState[i], true);
+				update = true;
+				_brState[i] = 127;
+			}
+		}
+
+		if (update)
+			updateScreen();
+
+		runLoop = false;
+		for (int i = first; i <= last; ++i) {
+			if (faders[i]._brCur != faders[i]._brDest)
+				runLoop = true;
+			_brState[i] = faders[i]._brCur;
+		}
+
+		_vm->delayUntil(end);
+
+		if (_vm->shouldQuit()) {
+			for (int i = first; i <= last; ++i)
+				faders[i]._fadeDelay = 0;
+		}
+	}
+}
+
+void Screen_EoB::sega_paletteOps(int op, int par1, int par2) {
+	assert(op >= 0 && op <= 6);
+	switch (op) {
+	case 6:
+		// Force palette update and wait for completion
+		break;
+	case 5:
+		// Force palette update, don't wait
+		break;
+	case 4:
+		assert(0);
+		break;
+	default:
+		sega_fadePalette(par2, par1, op);
+	}
+}
+
+SegaRenderer::SegaRenderer(Screen_EoB *screen) : _screen(screen), _drChain(0), _pitch(64), _hScrollMode(0), _hScrollTable(0) {
+	_vram = new uint8[0x10000];
+	assert(_vram);
+	memset(_vram, 0, 0x10000 * sizeof(uint8));
+	setResolution(320, 224);
+}
+
+SegaRenderer::~SegaRenderer() {
+	clearDirtyRects();
+	delete[] _vram;
+}
+
+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;
+}
+
+void SegaRenderer::setPlaneTableLocation(int plane, uint16 addr) {
+	assert(plane >= kPlaneA && plane <= kWindowPlane);
+	assert(addr <= 0xFFFF);
+	_planes[plane].nameTable = (uint16*)(&_vram[addr]);
+}
+
+void SegaRenderer::setupPlaneAB(int plane, uint16 w, uint16 h) {
+	assert(plane >= kPlaneA && plane <= kPlaneB);
+	_planes[plane].w = w;
+	_planes[plane].h = h;
+	_planes[plane].nameTableSize = (w * h) >> 6;
+}
+
+void SegaRenderer::setupWindowPlane(int blockX, int blockY, int horizontalMode, int verticalMode) {
+	_planes[kWindowPlane].blockX = horizontalMode ? blockX : 0;
+	_planes[kWindowPlane].blockY = verticalMode ? blockY : 0;
+	_planes[kWindowPlane].w = horizontalMode ? _blocksW - blockX : blockX;
+	_planes[kWindowPlane].h = verticalMode ? _blocksH - blockY : blockY;
+}
+
+void SegaRenderer::setHScrollTableLocation(int addr) {
+	assert(addr <= 0xFFFF);
+	_hScrollTable = (uint16*)(&_vram[addr]);
+}
+
+void SegaRenderer::setPitch(int pitch) {
+	_pitch = pitch;
+}
+
+void SegaRenderer::setHScrollMode(int mode) {
+	_hScrollMode = mode;
+}
+
+void SegaRenderer::loadToVRAM(const uint16 *data, int dataSize, int addr) {
+	assert(data);
+	assert(addr <= 0xFFFF);
+	memcpy(_vram + addr, data, dataSize);
+	checkUpdateDirtyRects(addr, dataSize);
+}
+
+void SegaRenderer::loadToVRAM(Common::SeekableReadStreamEndian *in, int addr, bool compressedData) {
+	assert(in);
+	assert(addr <= 0xFFFF);
+
+	uint8 *dst = _vram + addr;
+
+	if (compressedData) {
+		uint16 decodeSize = 0;
+		uint8 *data = new uint8[in->size()];
+		uint32 readSize = in->read(data, in->size());
+		decodeSize = READ_LE_UINT16(data + 2);
+		assert(decodeSize < readSize);
+		assert(decodeSize < 0x10000 - addr);
+		_screen->decodeBIN(data + 4, dst, decodeSize);
+		delete[] data;
+	} else {
+		assert(in->size() < 0x10000 - addr);
+		in->read(dst, in->size());
+	}
+	addDirtyRect(0, 0, _screenW, _screenH);
+}
+
+void SegaRenderer::memsetVRAM(int addr, uint8 val, int len) {
+	assert(addr + len <= 0xFFFF);
+	memset(_vram + addr, val, len);
+	void checkUpdateDirtyRects(int addr, int len);
+}
+
+void SegaRenderer::fillRectWithTiles(int addr, int x, int y, int w, int h, uint16 nameTblEntry, bool incr) {
+	addDirtyRect(x << 3, y << 3, w << 3, h << 3);
+	uint16 *dst = (uint16*)(&_vram[addr]);
+	dst += (y * _pitch + x);
+	if (incr) {
+		while (h--) {
+			for (int i = w; i; --i)
+				*dst++ = nameTblEntry++;
+			dst += (_pitch - w);
+		}
+	} else {
+		while (h--) {
+			for (int i = w; i; --i)
+				*dst++ = nameTblEntry;
+			dst += (_pitch - w);
+		}
+	}
+}
+
+void SegaRenderer::render(int destPageNum) {
+	uint8 *renderBuffer = _screen->getPagePtr(destPageNum);
+	memset(renderBuffer, 0, _screenW * _screenH);
+
+	if (destPageNum == 0)
+		sendDirtyRectsToScreen();
+
+	// It is assumed that no dirty rects are needed if the rendering takes place to any other page than page 0.
+	// So if you'd render to e. g. page 2 and afterwards to page 0 your dirty rects would be lost. This is
+	// intentional for now, since I assume that this will be in harmony with the actual usage of the renderer.
+	clearDirtyRects();
+
+	// Planes A and B
+	const uint8 order[2] = { kPlaneB, kPlaneA };
+	for (int p = 0; p < ARRAYSIZE(order); ++p) {
+		uint16 *ntbl = _planes[p].nameTable;
+		uint8 *dst = renderBuffer;
+
+		for (int y = 0; y < _blocksH; ++y) {
+			int hscrTblIdx = (_hScrollMode == kHScrollFullScreen) ? p : (y << 4) + p;
+			uint8 *dst2 = dst;
+			for (int x = 0; x < _blocksW; ++x) {
+				renderTile(dst, x, ntbl, hscrTblIdx);
+				dst += 8;
+			}
+			ntbl += _pitch;
+			dst = dst2 + (_screenW << 3);
+		}
+
+		if (_planes[kPlaneA].nameTable == _planes[kPlaneB].nameTable)
+			break;
+	}
+
+	// Window Plane
+	// TODO: implement support if necessary. The following lines are just a modified copy/paste
+	//       from above and nowhere guaranteed to work properly.
+	/*{
+		SegaPlane *p = &_planes[kWindowPlane];
+		uint16 *ntbl = p->nameTable;
+		uint8 *dst = _renderBuffer;
+
+		for (int y = p->blockY; y < p->blockY + p->h; ++y) {
+			uint8 *dst2 = dst;
+			for (int x = p->blockX; x < p->blockX + p->w; ++x) {
+				renderTile(dst, x, ntbl, -1);
+				dst += 8;
+			}
+			ntbl += _blocksW;
+			dst = dst2 + (_screenW << 3);
+		}
+	}*/
+
+	// Sprites
+	// TODO: implement support if necessary.
+
+	// Priority Tiles
+	// All tiles that have been skipped so far due to a priority flag will be drawn now.
+	/*
+	  TODO: implement support if necessary.
+	*/
+
+	_screen->copyBlockToPage(destPageNum, 0, 0, _screenW, Screen::SCREEN_H, _renderBuffer);
+}
+
+void SegaRenderer::renderTile(uint8 *dst, int destX, uint16 *nameTable, int hScrollTableIndex) {
+	for (int bY = 0; bY < 8; ++bY) {
+		uint8 *dst2 = dst;
+		uint16 hscrNt = 0;
+		uint16 hscrPx = 0;
+
+		if (hScrollTableIndex != -1) {
+			hscrNt = _hScrollTable[hScrollTableIndex] & 0x3FF;
+			hscrPx = hscrNt & 7;
+			hscrNt >>= 3;
+		}
+
+		uint16 nt = nameTable[(destX + hscrNt) % _pitch];
+
+		// Check all the following things
+		// TODO: implement support if necessary.
+		// Priority
+		assert(!(nt & 0x8000));
+		// Vertical flip
+		assert(!(nt & 0x1000));
+		// Horizontal flip
+		assert(!(nt & 0x800));
+
+		renderLineFragment(dst, &_vram[((nt & 0x7FF) << 5) + (bY << 2) + (hscrPx >> 1)], hscrPx, 8, ((nt >> 13) & 3) << 4);
+
+		nt = nameTable[(destX + hscrNt + 1) % _pitch];
+		// Priority
+		assert(!(nt & 0x8000));
+		// Vertical flip
+		assert(!(nt & 0x1000));
+		// Horizontal flip
+		assert(!(nt & 0x800));
+
+		renderLineFragment(dst, &_vram[((nt & 0x7FF) << 5) + (bY << 2)], 0, hscrPx, ((nt >> 13) & 3) << 4);
+	
+		if (hScrollTableIndex != -1 && _hScrollMode == kHScroll1PixelRows)
+			hScrollTableIndex += 2;
+		dst = dst2 + _screenW;
+	}
+}
+
+void SegaRenderer::renderLineFragment(uint8 *&dst, const uint8 *src, int start, int end, uint8 pal) {
+	for (int bX = start; bX < end; ++bX) {
+		uint8 col = (bX & 1) ? *src++ & 0x0F : *src >> 4;
+		if (col)
+			*dst = pal | col;
+		dst++;
+	}
+}
+
+void SegaRenderer::checkUpdateDirtyRects(int addr, int len) {
+	//void *tbl[] = { _vram, _hScrollTable, , _planes[kPlaneA].nameTable, _planes[kPlaneB].nameTable, _planes[kWindowPlane].nameTable };
+	addDirtyRect(0, 0, _screenW, _screenH);
+	/*uint8 *addE = &_vram[addr];
+	int x = 0;
+	int y = 0;
+	int w = 0;
+	int h = 0;
+
+	if (addE >= (uint8*)_planes[kPlaneA].nameTable && addE < (uint8*)_planes[kPlaneA].nameTable + _planes[kPlaneA].nameTableSize) {
+		x = ((addE - (uint8*)_planes[kPlaneA].nameTable) >> 1) % _pitch;
+		y = ((addE - (uint8*)_planes[kPlaneA].nameTable) >> 1) / _pitch;
+		w = len > _pitch;
+	}*/
+
+	//if (addr >= _hScrollTable && addr < _hScrollTable + 0x400);
+}
+
+void SegaRenderer::addDirtyRect(int x, int y, int w, int h) {
+	_drChain = new DRChainEntry(_drChain, x, y, w, h);
+}
+
+void SegaRenderer::sendDirtyRectsToScreen() {
+	for (DRChainEntry *e = _drChain; e; e = e->next) {
+		int w = e->rect.width();
+		int h = e->rect.height();
+		if (w == _screenW && h == _screenH) {
+			_screen->_forceFullUpdate = true;
+			return;
+		} 
+		_screen->addDirtyRect(e->rect.left, e->rect.top, w, h);
+	}
+}
+
+void SegaRenderer::clearDirtyRects() {
+	while (_drChain) {
+		DRChainEntry *e = _drChain->next;
+		delete _drChain;
+		_drChain = e;
+	}
+}
+
+} // End of namespace Kyra
+
+#endif // ENABLE_EOB
diff --git a/engines/kyra/graphics/screen_eob_towns.cpp b/engines/kyra/graphics/screen_eob_towns.cpp
new file mode 100644
index 0000000000..132f04a758
--- /dev/null
+++ b/engines/kyra/graphics/screen_eob_towns.cpp
@@ -0,0 +1,100 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+ #ifdef ENABLE_EOB
+
+#include "kyra/resource/resource.h"
+
+namespace Kyra {
+
+void Screen_EoB::decodeSHP(const uint8 *data, int dstPage) {
+	int32 bytesLeft = READ_LE_UINT32(data);
+	const uint8 *src = data + 4;
+	uint8 *dst = getPagePtr(dstPage);
+
+	if (bytesLeft < 0) {
+		memcpy(dst, data, 64000);
+		return;
+	}
+
+	while (bytesLeft > 0) {
+		uint8 code = *src++;
+		bytesLeft--;
+
+		for (int i = 8; i; i--) {
+			if (code & 0x80) {
+				uint16 copyOffs = (src[0] << 4) | (src[1] >> 4);
+				uint8 count = (src[1] & 0x0F) + 3;
+				src += 2;
+				bytesLeft -= 2;
+				const uint8 *copySrc = dst - 1 - copyOffs;
+				while (count--)
+					*dst++ = *copySrc++;
+			} else if (bytesLeft) {
+				*dst++ = *src++;
+				bytesLeft--;
+			} else {
+				break;
+			}
+			code <<= 1;
+		}
+	}
+}
+
+void Screen_EoB::convertToHiColor(int page) {
+	if (!_16bitPalette)
+		return;
+	uint16 *dst = (uint16 *)getPagePtr(page);
+	memcpy(_convertHiColorBuffer, dst, SCREEN_H * SCREEN_W);
+	uint8 *src = _convertHiColorBuffer;
+	for (int s = SCREEN_H * SCREEN_W; s; --s)
+		*dst++ = _16bitPalette[*src++];
+}
+
+void Screen_EoB::shadeRect(int x1, int y1, int x2, int y2, int shadingLevel) {
+	if (!_16bitPalette)
+		return;
+
+	int l = _16bitShadingLevel;
+	_16bitShadingLevel = shadingLevel;
+
+	if (_curPage == 0 || _curPage == 1)
+		addDirtyRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+
+	uint16 *dst = (uint16*)(getPagePtr(_curPage) + y1 * SCREEN_W * _bytesPerPixel + x1 * _bytesPerPixel);
+
+	for (; y1 < y2; ++y1) {
+		uint16 *ptr = dst;
+		for (int i = 0; i < x2 - x1; i++) {
+			*ptr = shade16bitColor(*ptr);
+			ptr++;
+		}
+		dst += SCREEN_W;
+	}
+
+	_16bitShadingLevel = l;
+}
+
+} // End of namespace Kyra
+
+#endif // ENABLE_EOB
diff --git a/engines/kyra/module.mk b/engines/kyra/module.mk
index 3e0b23f1ec..1af999e03b 100644
--- a/engines/kyra/module.mk
+++ b/engines/kyra/module.mk
@@ -55,7 +55,7 @@ MODULE_OBJS := \
 	script/script_mr.o \
 	script/script.o \
 	script/script_tim.o \
-	sequence/seqplayer.o \
+	sequence/seqplayer_lok.o \
 	sequence/sequences_lok.o \
 	sequence/sequences_v2.o \
 	sequence/sequences_hof.o \
@@ -121,14 +121,21 @@ MODULE_OBJS += \
 	engine/sprites_eob.o \
 	engine/timer_eob.o \
 	graphics/screen_eob.o \
+	graphics/screen_eob_amiga.o \
+	graphics/screen_eob_pc98.o \
+	graphics/screen_eob_segacd.o \
+	graphics/screen_eob_towns.o \
 	gui/gui_eob.o \
 	gui/saveload_eob.o \
+	resource/resource_segacd.o \
 	resource/staticres_eob.o \
 	script/script_eob.o \
+	sequence/seqplayer_eob_segacd.o \
 	sequence/sequences_eob.o \
 	sequence/sequences_darkmoon.o \
 	sound/sound_amiga_eob.o \
 	sound/sound_pc98_eob.o \
+	sound/sound_segacd_eob.o \
 	sound/sound_towns_darkmoon.o \
 	sound/drivers/audiomaster2.o \
 	sound/drivers/mlalf98.o \
diff --git a/engines/kyra/resource/resource.cpp b/engines/kyra/resource/resource.cpp
index 6b8538046a..1423a6412c 100644
--- a/engines/kyra/resource/resource.cpp
+++ b/engines/kyra/resource/resource.cpp
@@ -28,29 +28,7 @@
 
 namespace Kyra {
 
-class EndianAwareStreamWrapper : public Common::SeekableReadStreamEndian {
-public:
-	EndianAwareStreamWrapper(Common::SeekableReadStream *stream, bool bigEndian, bool disposeAfterUse = true) : Common::SeekableReadStreamEndian(bigEndian), Common::ReadStreamEndian(bigEndian), _stream(stream), _dispose(disposeAfterUse) {}
-	~EndianAwareStreamWrapper() 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;
-};
-
-Resource::Resource(KyraEngine_v1 *vm) : _archiveCache(), _files(), _archiveFiles(), _protectedFiles(), _loaders(), _vm(vm) {
+Resource::Resource(KyraEngine_v1 *vm) : _archiveCache(), _files(), _archiveFiles(), _protectedFiles(), _loaders(), _vm(vm), _bigEndianPlatForm(vm->gameFlags().platform == Common::kPlatformAmiga || vm->gameFlags().platform == Common::kPlatformSegaCD) {
 	initializeLoaders();
 
 	// Initialize directories for playing from CD or with original
@@ -346,7 +324,7 @@ Common::SeekableReadStream *Resource::createReadStream(const Common::String &fil
 
 Common::SeekableReadStreamEndian *Resource::createEndianAwareReadStream(const Common::String &file) {
 	Common::SeekableReadStream *stream = _files.createReadStreamForMember(file);
-	return stream ? new EndianAwareStreamWrapper(stream, _vm->gameFlags().platform == Common::kPlatformAmiga) : 0;
+	return stream ? new EndianAwareStreamWrapper(stream, _bigEndianPlatForm) : 0;
 }
 
 Common::Archive *Resource::loadArchive(const Common::String &name, Common::ArchiveMemberPtr member) {
diff --git a/engines/kyra/resource/resource.h b/engines/kyra/resource/resource.h
index 3c89cad4f8..1bf5a313ee 100644
--- a/engines/kyra/resource/resource.h
+++ b/engines/kyra/resource/resource.h
@@ -95,6 +95,7 @@ protected:
 	typedef Common::List<Common::SharedPtr<ResArchiveLoader> > LoaderList;
 	LoaderList _loaders;
 
+	const bool _bigEndianPlatForm;
 	KyraEngine_v1 *_vm;
 };
 
diff --git a/engines/kyra/resource/resource_intern.h b/engines/kyra/resource/resource_intern.h
index dfc5f4b1ec..17c9643978 100644
--- a/engines/kyra/resource/resource_intern.h
+++ b/engines/kyra/resource/resource_intern.h
@@ -28,6 +28,7 @@
 #include "common/hashmap.h"
 #include "common/str.h"
 #include "common/list.h"
+#include "common/stream.h"
 
 namespace Kyra {
 
@@ -141,6 +142,28 @@ public:
 	static Common::Archive *load(Resource *owner, const Common::String &filename, const Common::String &extension, const uint8 offset);
 };
 
+class EndianAwareStreamWrapper : public Common::SeekableReadStreamEndian {
+public:
+	EndianAwareStreamWrapper(Common::SeekableReadStream *stream, bool bigEndian, bool disposeAfterUse = true) : Common::SeekableReadStreamEndian(bigEndian), Common::ReadStreamEndian(bigEndian), _stream(stream), _dispose(disposeAfterUse) {}
+	~EndianAwareStreamWrapper() { if (_dispose) delete _stream; }
+
+	// Common::Stream interface
+	bool err() const { return _stream->err(); }
+
+	// Common::ReadStream interface
+	bool eos() const { return _stream->eos(); }
+	uint32 read(void *dataPtr, uint32 dataSize) { return _stream->read(dataPtr, dataSize); }
+
+	// Common::SeekableReadStream interface
+	int32 pos() const { return _stream->pos(); }
+	int32 size() const { return _stream->size(); }
+	bool seek(int32 offset, int whence = SEEK_SET) { return _stream->seek(offset, whence); }
+
+private:
+	Common::SeekableReadStream *_stream;
+	bool _dispose;
+};
+
 } // End of namespace Kyra
 
 #endif
diff --git a/engines/kyra/resource/resource_segacd.cpp b/engines/kyra/resource/resource_segacd.cpp
new file mode 100644
index 0000000000..89c547e1c0
--- /dev/null
+++ b/engines/kyra/resource/resource_segacd.cpp
@@ -0,0 +1,109 @@
+/* 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 "kyra/resource/resource.h"
+#include "kyra/resource/resource_intern.h"
+#include "kyra/resource/resource_segacd.h"
+#include "common/substream.h"
+
+namespace Kyra {
+
+SegaCDResource::SegaCDResource(Resource *res) : _res(res), _str(0), _offsetTable(0), _numResources(0) {	
+}
+
+SegaCDResource::~SegaCDResource() {
+	unloadContainer();
+}
+
+bool SegaCDResource::loadContainer(const Common::String &filename) {
+	unloadContainer();
+
+	_str = _res->createEndianAwareReadStream(filename);
+	if (!_str) {
+		error("SegaCDResource: File '%s' not found.", filename);
+		return false;
+	}
+
+	uint32 first = _str->readUint32();
+	_numResources = first >> 2;
+	_offsetTable = new uint32[_numResources + 1];
+	_offsetTable[0] = first;
+	for (int i = 1; i < _numResources; ++i) {
+		_offsetTable[i] = _str->readUint32();
+		if (_offsetTable[i] == 0)
+			_numResources = i;
+	}
+	_offsetTable[_numResources] = _str->size();
+
+	return true;
+}
+
+void SegaCDResource::unloadContainer() {
+	delete[] _offsetTable;
+	delete _str;
+	_offsetTable = 0;
+	_numResources = 0;
+	_str = 0;
+}
+
+Common::SeekableReadStreamEndian *SegaCDResource::getEndianAwareResourceStream(int resID) {
+	if (!_str || !_offsetTable || resID >= _numResources)
+		return 0;
+
+	Common::SeekableReadStream *str = getResourceStream(resID);
+	if (!str)
+		return 0;
+
+	return new EndianAwareStreamWrapper(str, _str->isBE(), true);
+}
+
+Common::SeekableReadStream *SegaCDResource::getResourceStream(int resID) {
+	if (!_str || !_offsetTable || resID >= _numResources)
+		return 0;
+
+	return new Common::SeekableSubReadStream(_str, _offsetTable[resID], _offsetTable[resID + 1], DisposeAfterUse::NO);
+}
+
+uint8 *SegaCDResource::fileData(int resID, uint32 *resLen) {
+	if (!_str || !_offsetTable || resID >= _numResources)
+		return 0;
+
+	uint32 len = _offsetTable[resID + 1] - _offsetTable[resID];
+	uint8 *res = new uint8[len];
+
+	_str->seek(_offsetTable[resID], SEEK_SET);
+	_str->read(res, len);
+
+	if (resLen)
+		*resLen = len;
+
+	return res;
+}
+
+uint8 **SegaCDResource::loadAllResources(uint32 *numRes) {
+	if (!_str || !_offsetTable)
+		return 0;
+
+	return 0;
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/resource/resource_segacd.h b/engines/kyra/resource/resource_segacd.h
new file mode 100644
index 0000000000..e5ca65e0bf
--- /dev/null
+++ b/engines/kyra/resource/resource_segacd.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 KYRA_RESOURCE_SEGACD_H
+#define KYRA_RESOURCE_SEGACD_H
+
+#include "common/scummsys.h"
+#include "common/str.h"
+
+namespace Common {
+	class SeekableReadStream;
+	class SeekableReadStreamEndian;
+}
+
+namespace Kyra {
+
+class Resource;
+class SegaCDResource {
+public:
+	SegaCDResource(Resource *res);
+	~SegaCDResource();
+
+	bool loadContainer(const Common::String &filename);
+	void unloadContainer();
+
+	Common::SeekableReadStreamEndian *getEndianAwareResourceStream(int resID);
+	Common::SeekableReadStream *getResourceStream(int resID);
+
+	uint8 *fileData(int resID, uint32 *resLen);
+	uint8 **loadAllResources(uint32 *numRes);
+
+private:
+	Resource *_res;
+	uint32 *_offsetTable;
+	int _numResources;
+	Common::SeekableReadStreamEndian *_str;
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index b0805f1013..45d91bc028 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -1395,7 +1395,7 @@ const EoBEngine::RenderModePalFile EoBEngine::_renderModePalFiles[3] = {
 	{	-1, "" }
 };
 
-const EoBEngine::TitleScreenConfig EoBEngine::_titleConfig[4] = {
+const EoBEngine::TitleScreenConfig EoBEngine::_titleConfig[5] = {
 	{
 		Common::kPlatformDOS,
 		Common::UNK_LANG,
@@ -1444,6 +1444,18 @@ const EoBEngine::TitleScreenConfig EoBEngine::_titleConfig[4] = {
 		76, 164, 175, 31, 14, 13, -1,
 		0
 	},
+	{
+		Common::kPlatformSegaCD,
+		Common::UNK_LANG,
+		"EOBTITLE",
+		&_renderModePalFiles[2],
+		1,
+		2,
+		false,
+		77, 161, 173, 29, 1, 2, 12,
+		76, 160, 175, 31, 1, 2, -1,
+		-8
+	}
 };
 
 void DarkMoonEngine::initStaticResource() {
diff --git a/engines/kyra/sequence/seqplayer_eob_segacd.cpp b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
new file mode 100644
index 0000000000..7240465711
--- /dev/null
+++ b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
@@ -0,0 +1,48 @@
+/* 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 "kyra/engine/eob.h"
+#include "kyra/sequence/seqplayer_eob_segacd.h"
+#include "kyra/resource/resource.h"
+#include "kyra/resource/resource_segacd.h"
+
+namespace Kyra {
+
+SegaSequencePlayer::SegaSequencePlayer(EoBEngine *vm, SegaCDResource *res) : _vm(vm), _res(res) {
+}
+
+SegaSequencePlayer::~SegaSequencePlayer() {
+
+}
+
+bool SegaSequencePlayer::play(int seqID) {
+	if (!_res->loadContainer("VISUAL"))
+		return false;
+
+	Common::SeekableReadStreamEndian *data = _res->getEndianAwareResourceStream(seqID);
+	if (!data)
+		return false;
+
+	return true;
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/sequence/seqplayer_eob_segacd.h b/engines/kyra/sequence/seqplayer_eob_segacd.h
new file mode 100644
index 0000000000..319bf4ef78
--- /dev/null
+++ b/engines/kyra/sequence/seqplayer_eob_segacd.h
@@ -0,0 +1,48 @@
+/* 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 KYRA_SEQPLAYER_SEGACD_H
+#define KYRA_SEQPLAYER_SEGACD_H
+
+namespace Common {
+	class SeekableReadStream;
+	class SeekableReadStreamEndian;
+}
+
+namespace Kyra {
+class EoBEngine;
+class SegaCDResource;
+class SegaSequencePlayer {
+public:
+	SegaSequencePlayer(EoBEngine *vm, SegaCDResource *res);
+	~SegaSequencePlayer();
+
+	bool play(int seqID);
+
+protected:
+	EoBEngine *_vm;
+	SegaCDResource *_res;
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/sequence/seqplayer.cpp b/engines/kyra/sequence/seqplayer_lok.cpp
similarity index 99%
rename from engines/kyra/sequence/seqplayer.cpp
rename to engines/kyra/sequence/seqplayer_lok.cpp
index d039a352f8..cc74440973 100644
--- a/engines/kyra/sequence/seqplayer.cpp
+++ b/engines/kyra/sequence/seqplayer_lok.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "kyra/sequence/seqplayer.h"
+#include "kyra/sequence/seqplayer_lok.h"
 #include "kyra/resource/resource.h"
 #include "kyra/sound/sound.h"
 
diff --git a/engines/kyra/sequence/seqplayer.h b/engines/kyra/sequence/seqplayer_lok.h
similarity index 100%
rename from engines/kyra/sequence/seqplayer.h
rename to engines/kyra/sequence/seqplayer_lok.h
diff --git a/engines/kyra/sequence/sequences_eob.cpp b/engines/kyra/sequence/sequences_eob.cpp
index 1970efb53a..07e38c49b9 100644
--- a/engines/kyra/sequence/sequences_eob.cpp
+++ b/engines/kyra/sequence/sequences_eob.cpp
@@ -25,6 +25,8 @@
 #include "kyra/engine/eob.h"
 #include "kyra/graphics/screen_eob.h"
 #include "kyra/resource/resource.h"
+#include "kyra/resource/resource_segacd.h"
+#include "kyra/sequence/seqplayer_eob_segacd.h"
 #include "kyra/sound/sound.h"
 
 #include "common/system.h"
@@ -2211,7 +2213,7 @@ int EoBEngine::mainMenu() {
 			_sound->loadSoundFile(0);
 			_screen->hideMouse();
 
-			seq_playIntro(_flags.platform == Common::kPlatformPC98 ? kOnlyCredits : kCreditsAndIntro);
+			seq_playIntro(_flags.platform == Common::kPlatformPC98 || _flags.platform == Common::kPlatformSegaCD ? kOnlyCredits : kCreditsAndIntro);
 
 			_screen->showMouse();
 			_sound->selectAudioResourceSet(kMusicIngame);
@@ -2242,7 +2244,14 @@ int EoBEngine::mainMenuLoop() {
 }
 
 void EoBEngine::seq_playIntro(int part) {
-	EoBIntroPlayer(this, _screen).start((EoBIntroPlayer::IntroPart)part);
+	if (_flags.platform == Common::kPlatformSegaCD) {
+		if (part == kOnlyCredits)
+			seq_segaOpeningCredits();
+		else
+			_seqPlayer->play(53, 53);
+	} else {
+		EoBIntroPlayer(this, _screen).start((EoBIntroPlayer::IntroPart)part);
+	}
 }
 
 void EoBEngine::seq_playFinale() {
@@ -2351,6 +2360,125 @@ void EoBEngine::seq_xdeath() {
 	gui_drawAllCharPortraitsWithStats();
 }
 
+#define updateScrollState(scrollTable, step) \
+	for (int iii = 0; iii < 228; ++iii) \
+		((int16*)scrollTable)[iii << 1] = ((int16*)scrollTable)[(iii << 1) + 1] = (iii & 1) ? -step : step;
+
+void EoBEngine::seq_segaOpeningCredits() {
+	uint16 *scrollTable = new uint16[0x200];
+	memset(scrollTable, 0, 0x200 * sizeof(uint16));
+
+	_screen->sega_getRenderer()->setPitch(128);
+	_screen->sega_getRenderer()->setPlaneTableLocation(SegaRenderer::kPlaneA, 0xE000);
+	_screen->sega_getRenderer()->setupPlaneAB(SegaRenderer::kPlaneA, 1024, 256);
+	_screen->sega_getRenderer()->setHScrollMode(SegaRenderer::kHScroll1PixelRows);
+	
+	_screen->sega_getRenderer()->fillRectWithTiles(0xC000, 0, 0, 40, 28, 0);
+	_screen->sega_getRenderer()->fillRectWithTiles(0xE000, 0, 0, 128, 28, 1);
+	_screen->sega_getRenderer()->fillRectWithTiles(0xE000, 0, 0, 40, 28, 1, true);
+	_screen->sega_selectPalette(7, 3, 0, false);
+
+	updateScrollState(scrollTable, 320);
+	_screen->sega_getRenderer()->loadToVRAM(scrollTable, 0x400, 0xD800);
+
+	_sres->loadContainer("CREDIT");
+	Common::SeekableReadStreamEndian *in = _sres->getEndianAwareResourceStream(1);
+	_screen->sega_getRenderer()->loadToVRAM(in, 32, true);
+	delete in;
+
+	_screen->sega_selectPalette(50, 0, 0);
+	_screen->sega_getRenderer()->render(0);
+
+	_allowSkip = true;
+	resetSkipFlag();
+
+	_screen->sega_fadeToNeutral(3);
+
+	for (int i = 0; i < 8 && !(shouldQuit() || skipFlag()); ++i) {
+		updateScrollState(scrollTable, 320);
+		_screen->sega_getRenderer()->loadToVRAM(scrollTable, 0x400, 0xD800);
+		_screen->sega_selectPalette(i == 3 ? 59 : 50, 0, 0, true);
+
+		in = _sres->getEndianAwareResourceStream(i);
+		_screen->sega_getRenderer()->loadToVRAM(in, 32, true);
+		delete in;
+
+		_screen->sega_getRenderer()->render(0);
+		_screen->updateScreen();
+
+		_screen->sega_paletteOps(6, 0, 0);
+
+		int mod = 141;
+		for (int ii = 9730; ii > 0 && !(shouldQuit() || skipFlag()); ii -= mod) {
+			uint32 end = _system->getMillis() + 16;
+			updateScrollState(scrollTable, ii / 30);
+			_screen->sega_getRenderer()->loadToVRAM(scrollTable, 0x400, 0xD800);
+			_screen->sega_getRenderer()->render(0);
+			_screen->updateScreen();
+			mod--;
+			delayUntil(end);
+		}
+
+		delay(3000);
+
+		if (i == 7)
+			_screen->sega_getRenderer()->fillRectWithTiles(0xE000, 40, 0, 88, 28, 0, false);
+
+		mod = -1;
+		for (int ii = 0; ii <= 3240 && !(shouldQuit() || skipFlag()); ii += mod) {
+			uint32 end = _system->getMillis() + 16;
+			updateScrollState(scrollTable, ii / 10);
+			_screen->sega_getRenderer()->loadToVRAM(scrollTable, 0x400, 0xD800);
+			_screen->sega_getRenderer()->render(0);
+			_screen->updateScreen();
+			mod++;
+			delayUntil(end);
+		}
+
+		delay(500);
+	}
+
+	_screen->sega_fadeToBlack(0);
+	_screen->sega_getRenderer()->setPlaneTableLocation(SegaRenderer::kPlaneA, 0xC000);
+	_screen->sega_getRenderer()->setupPlaneAB(SegaRenderer::kPlaneA, 512, 512);
+	_screen->sega_getRenderer()->setHScrollMode(SegaRenderer::kHScrollFullScreen);
+	_screen->sega_getRenderer()->memsetVRAM(0xD800, 0, 0x400);
+	_screen->sega_getRenderer()->setPitch(64);
+	_screen->sega_selectPalette(0, 0);
+
+	in = _sres->getEndianAwareResourceStream(8);
+	_screen->sega_getRenderer()->loadToVRAM(in, 32, true);
+	delete in;
+
+	_screen->sega_getRenderer()->memsetVRAM(0x8C20, 0xCC, 0x700);
+
+	for (int y = 0; y < 28; y += 4) {
+		for (int x = 0; x < 40; x += 4)
+			_screen->sega_getRenderer()->fillRectWithTiles(0xC000, x, y, 8, 7, 0x461, true);
+	}
+	_screen->sega_getRenderer()->fillRectWithTiles(0xE000, 0, 0, 40, 28, 1, true);
+	_screen->sega_getRenderer()->fillRectWithTiles(0xC000, 0, 0, 40, 28, 0);
+	_screen->sega_getRenderer()->render(0);
+	_screen->sega_fadeToNeutral(3);
+
+	while (!(shouldQuit() || skipFlag())) {
+		delay(20);
+	}
+
+	_allowSkip = false;
+	resetSkipFlag();
+
+	_screen->sega_getRenderer()->fillRectWithTiles(0xE000, 0, 19, 40, 9, 1);
+	_screen->sega_getRenderer()->render(0);
+	_screen->updateScreen();
+}
+
+bool EoBEngine::seq_segaPlaySequence(int id) {
+	if (_flags.platform != Common::kPlatformSegaCD)
+		return true;
+	return _seqPlayer->play(id);
+}
+
 #undef displaySubtitle
 #undef printSub
 
diff --git a/engines/kyra/sequence/sequences_lok.cpp b/engines/kyra/sequence/sequences_lok.cpp
index f22e0d44d5..dd36d5dbff 100644
--- a/engines/kyra/sequence/sequences_lok.cpp
+++ b/engines/kyra/sequence/sequences_lok.cpp
@@ -21,7 +21,7 @@
  */
 
 #include "kyra/engine/kyra_lok.h"
-#include "kyra/sequence/seqplayer.h"
+#include "kyra/sequence/seqplayer_lok.h"
 #include "kyra/resource/resource.h"
 #include "kyra/engine/sprites.h"
 #include "kyra/graphics/wsamovie.h"
diff --git a/engines/kyra/sound/sound.h b/engines/kyra/sound/sound.h
index 47b66950ce..b57d649faf 100644
--- a/engines/kyra/sound/sound.h
+++ b/engines/kyra/sound/sound.h
@@ -102,7 +102,8 @@ public:
 		kPC98,
 		kPCSpkr,
 		kPCjr,
-		kAmiga
+		kAmiga,
+		kSegaCD
 	};
 
 	virtual kType getMusicType() const = 0;
diff --git a/engines/kyra/sound/sound_intern.h b/engines/kyra/sound/sound_intern.h
index c7f724e3c9..4c36c44029 100644
--- a/engines/kyra/sound/sound_intern.h
+++ b/engines/kyra/sound/sound_intern.h
@@ -477,6 +477,39 @@ private:
 	bool _ready;
 };
 
+class SoundSegaCD_EoB : public Sound {
+public:
+	SoundSegaCD_EoB(KyraEngine_v1 *vm, Audio::Mixer *mixer);
+	virtual ~SoundSegaCD_EoB();
+
+	kType getMusicType() const;
+
+	bool init();
+	void initAudioResourceInfo(int set, void *info);
+	void selectAudioResourceSet(int set);
+	bool hasSoundFile(uint file) const { return false; }
+	void loadSoundFile(uint file) {}
+	void loadSoundFile(Common::String file) {}
+	void loadSfxFile(Common::String file);
+	void playTrack(uint8 track);
+	void haltTrack();
+	void playSoundEffect(uint8 track, uint8);
+	bool isPlaying() const;
+	void beginFadeOut() {}
+	void updateVolumeSettings();
+
+private:
+	KyraEngine_v1 *_vm;
+	//MLALF98 *_driver;
+
+	//SoundResourceInfo_PC *_resInfo[3];
+	//int _currentResourceSet;
+
+	//uint32 _sfxDelay;
+
+	bool _ready;
+};
+
 #endif
 
 } // End of namespace Kyra
diff --git a/engines/kyra/sound/sound_segacd_eob.cpp b/engines/kyra/sound/sound_segacd_eob.cpp
new file mode 100644
index 0000000000..afb81335fd
--- /dev/null
+++ b/engines/kyra/sound/sound_segacd_eob.cpp
@@ -0,0 +1,116 @@
+/* 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.
+ *
+ */
+
+#ifdef ENABLE_EOB
+
+#include "kyra/sound/sound_intern.h"
+#include "kyra/resource/resource.h"
+//#include "kyra/sound/drivers/mlalf98.h"
+#include "common/config-manager.h"
+#include "backends/audiocd/audiocd.h"
+
+namespace Kyra {
+
+SoundSegaCD_EoB::SoundSegaCD_EoB(KyraEngine_v1 *vm, Audio::Mixer *mixer) : Sound(vm, mixer),
+	_vm(vm), /*_driver(0), _currentResourceSet(-1), _sfxDelay(0),*/ _ready(false) {
+	//memset(_resInfo, 0, sizeof(_resInfo));
+}
+
+SoundSegaCD_EoB::~SoundSegaCD_EoB() {
+	//delete _driver;
+
+	for (int i = 0; i < 3; i++)
+		initAudioResourceInfo(i, 0);
+}
+
+Sound::kType SoundSegaCD_EoB::getMusicType() const {
+	return kSegaCD;
+}
+
+bool SoundSegaCD_EoB::init() {
+	//_driver = new MLALF98(_mixer, MLALF98::kType9801_86);
+	g_system->getAudioCDManager()->open();
+	_ready = true;
+	return true;
+}
+
+void SoundSegaCD_EoB::initAudioResourceInfo(int set, void *info) {
+	//delete _resInfo[set];
+	//_resInfo[set] = info ? new SoundResourceInfo_PC(*(SoundResourceInfo_PC*)info) : 0;
+}
+
+void SoundSegaCD_EoB::selectAudioResourceSet(int set) {
+	/*if (set == _currentResourceSet || !_ready)
+		return;
+
+	if (!_resInfo[set])
+		return;
+
+	_currentResourceSet = set;*/
+}
+
+void SoundSegaCD_EoB::loadSfxFile(Common::String file) {
+
+}
+
+void SoundSegaCD_EoB::playTrack(uint8 track) {
+	static const uint8 levelCDATracks[13] = {
+		7, 7, 7, 7, 6, 6, 6, 4, 4, 4, 5, 5, 10
+	};
+
+	if (!_musicEnabled || !_ready)
+		return;
+	
+	g_system->getAudioCDManager()->play(track, 1, 0, 0);
+}
+
+void SoundSegaCD_EoB::haltTrack() {
+	if (!_musicEnabled || !_ready)
+		return;
+	g_system->getAudioCDManager()->stop();
+}
+
+void SoundSegaCD_EoB::playSoundEffect(uint8 track, uint8) {
+	if (!_sfxEnabled || !_ready)
+		return;
+//	_driver->startSoundEffect(track);
+}
+
+bool SoundSegaCD_EoB::isPlaying() const {
+	return g_system->getAudioCDManager()->isPlaying();
+}
+
+void SoundSegaCD_EoB::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: 7365c25ce15c8478c02a391fae1906b38556e280
    https://github.com/scummvm/scummvm/commit/7365c25ce15c8478c02a391fae1906b38556e280
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:08+02:00

Commit Message:
KYRA: (EOB/SegaCD) - set screen height to 224 pixels

This version uses a NTSC 224 pixels screen height.

Changed paths:
    engines/kyra/graphics/screen.cpp
    engines/kyra/graphics/screen.h
    engines/kyra/graphics/screen_eob.h
    engines/kyra/graphics/screen_eob_segacd.cpp


diff --git a/engines/kyra/graphics/screen.cpp b/engines/kyra/graphics/screen.cpp
index 526c0fb2a8..0ae0160ceb 100644
--- a/engines/kyra/graphics/screen.cpp
+++ b/engines/kyra/graphics/screen.cpp
@@ -39,7 +39,8 @@ namespace Kyra {
 
 Screen::Screen(KyraEngine_v1 *vm, OSystem *system, const ScreenDim *dimTable, const int dimTableSize)
 	: _system(system), _vm(vm), _sjisInvisibleColor(0), _dimTable(dimTable), _dimTableCount(dimTableSize),
-	_cursorColorKey((vm->game() == GI_KYRA1 || vm->game() == GI_EOB1 || vm->game() == GI_EOB2) ? 0xFF : 0) {
+	_cursorColorKey((vm->game() == GI_KYRA1 || vm->game() == GI_EOB1 || vm->game() == GI_EOB2) ? 0xFF : 0),
+	_screenHeight(vm->gameFlags().platform == Common::kPlatformSegaCD ? SCREEN_H_SEGA_NTSC : SCREEN_H) {
 	_debugEnabled = false;
 	_maskMinY = _maskMaxY = -1;
 
@@ -67,7 +68,8 @@ Screen::Screen(KyraEngine_v1 *vm, OSystem *system, const ScreenDim *dimTable, co
 	_16bitConversionPalette = 0;
 	_16bitShadingLevel = 0;
 	_bytesPerPixel = 1;
-	_4bitPixelPacking = false;
+	_4bitPixelPacking = _useAmigaExtraColors = _isAmiga = _isSegaCD = _use16ColorMode = false;
+	_useSJIS = _useOverlays = false;
 
 	_currentFont = FID_8_FNT;
 	_currentFontType = FTYPE_ASCII;
@@ -112,6 +114,7 @@ bool Screen::init() {
 	_use16ColorMode = _vm->gameFlags().use16ColorMode;
 	_4bitPixelPacking = (_use16ColorMode && _vm->game() == GI_LOL);
 	_isAmiga = (_vm->gameFlags().platform == Common::kPlatformAmiga);
+	_isSegaCD = (_vm->gameFlags().platform == Common::kPlatformSegaCD);
 	// Amiga copper palette magic requires the use of more than 32 colors for some purposes.
 	_useAmigaExtraColors = (_isAmiga && _vm->game() == GI_EOB2);
 
@@ -291,6 +294,9 @@ void Screen::setResolution() {
 			width = 320;
 	}
 
+	if (_vm->gameFlags().platform == Common::kPlatformSegaCD)
+		height = 224;
+
 	if (_useHiColorScreen) {
 		Graphics::PixelFormat px(2, 5, 5, 5, 0, 10, 5, 0, 0);
 		Common::List<Graphics::PixelFormat> tryModes = _system->getSupportedFormats();
@@ -333,7 +339,7 @@ void Screen::enableHiColorMode(bool enabled) {
 		_bytesPerPixel = 1;
 	}
 
-	resetPagePtrsAndBuffers(SCREEN_PAGE_SIZE * _bytesPerPixel);
+	resetPagePtrsAndBuffers(_isSegaCD ? SCREEN_W * _screenHeight : SCREEN_PAGE_SIZE * _bytesPerPixel);
 }
 
 void Screen::updateScreen() {
@@ -362,7 +368,7 @@ void Screen::updateScreen() {
 
 void Screen::updateDirtyRects() {
 	if (_forceFullUpdate) {
-		_system->copyRectToScreen(getCPagePtr(0), SCREEN_W, 0, 0, SCREEN_W, SCREEN_H);
+		_system->copyRectToScreen(getCPagePtr(0), SCREEN_W, 0, 0, SCREEN_W, _screenHeight);
 	} else {
 		const byte *page0 = getCPagePtr(0);
 		Common::List<Common::Rect>::iterator it;
@@ -721,7 +727,7 @@ void Screen::copyWsaRect(int x, int y, int w, int h, int dimState, int plotFunc,
 
 int Screen::getPagePixel(int pageNum, int x, int y) {
 	assert(pageNum < SCREEN_PAGE_NUM);
-	assert(x >= 0 && x < SCREEN_W && y >= 0 && y < SCREEN_H);
+	assert(x >= 0 && x < SCREEN_W && y >= 0 && y < _screenHeight);
 	if (_bytesPerPixel == 1)
 		return _pagePtrs[pageNum][y * SCREEN_W + x];
 	else
@@ -730,7 +736,7 @@ int Screen::getPagePixel(int pageNum, int x, int y) {
 
 void Screen::setPagePixel(int pageNum, int x, int y, uint8 color) {
 	assert(pageNum < SCREEN_PAGE_NUM);
-	assert(x >= 0 && x < SCREEN_W && y >= 0 && y < SCREEN_H);
+	assert(x >= 0 && x < SCREEN_W && y >= 0 && y < _screenHeight);
 
 	if (pageNum == 0 || pageNum == 1)
 		addDirtyRect(x, y, 1, 1);
@@ -935,7 +941,7 @@ void Screen::disableDualPaletteMode() {
 }
 
 void Screen::copyToPage0(int y, int h, uint8 page, uint8 *seqBuf) {
-	assert(y + h <= SCREEN_H);
+	assert(y + h <= _screenHeight);
 	const uint8 *src = getPagePtr(page) + y * SCREEN_W;
 	uint8 *dstPage = getPagePtr(0) + y * SCREEN_W;
 	for (int i = 0; i < h; ++i) {
@@ -977,10 +983,10 @@ void Screen::copyRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPag
 		h += y2;
 		y1 -= y2;
 		y2 = 0;
-	} else if (y2 + h >= SCREEN_H) {
-		if (y2 > SCREEN_H)
+	} else if (y2 + h >= _screenHeight) {
+		if (y2 > _screenHeight)
 			return;
-		h = SCREEN_H - y2;
+		h = _screenHeight - y2;
 	}
 
 	const uint8 *src = getPagePtr(srcPage) + y1 * SCREEN_W * _bytesPerPixel + x1 * _bytesPerPixel;
@@ -1023,8 +1029,8 @@ void Screen::copyRegionToBuffer(int pageNum, int x, int y, int w, int h, uint8 *
 		dest += (-y) * w * _bytesPerPixel;
 		h += y;
 		y = 0;
-	} else if (y + h > SCREEN_H) {
-		h = SCREEN_H - y;
+	} else if (y + h > _screenHeight) {
+		h = _screenHeight - y;
 	}
 
 	if (x < 0) {
@@ -1048,8 +1054,8 @@ void Screen::copyPage(uint8 srcPage, uint8 dstPage) {
 	uint8 *src = getPagePtr(srcPage);
 	uint8 *dst = getPagePtr(dstPage);
 	if (src != dst)
-		memcpy(dst, src, SCREEN_W * SCREEN_H * _bytesPerPixel);
-	copyOverlayRegion(0, 0, 0, 0, SCREEN_W, SCREEN_H, srcPage, dstPage);
+		memcpy(dst, src, SCREEN_W * _screenHeight * _bytesPerPixel);
+	copyOverlayRegion(0, 0, 0, 0, SCREEN_W, _screenHeight, srcPage, dstPage);
 
 	if (dstPage == 0 || dstPage == 1)
 		_forceFullUpdate = true;
@@ -1060,8 +1066,8 @@ void Screen::copyBlockToPage(int pageNum, int x, int y, int w, int h, const uint
 		src += (-y) * w * _bytesPerPixel;
 		h += y;
 		y = 0;
-	} else if (y + h > SCREEN_H) {
-		h = SCREEN_H - y;
+	} else if (y + h > _screenHeight) {
+		h = _screenHeight - y;
 	}
 
 	if (x < 0) {
@@ -1146,7 +1152,7 @@ void Screen::shuffleScreen(int sx, int sy, int w, int h, int srcPage, int dstPag
 }
 
 void Screen::fillRect(int x1, int y1, int x2, int y2, uint8 color, int pageNum, bool xored) {
-	assert(x2 < SCREEN_W && y2 < SCREEN_H);
+	assert(x2 < SCREEN_W && y2 < _screenHeight);
 	uint16 color16 = 0;
 	if (pageNum == -1)
 		pageNum = _curPage;
@@ -1413,7 +1419,7 @@ void Screen::printText(const char *str, int x, int y, uint8 color1, uint8 color2
 	int x_start = x;
 	if (y < 0)
 		y = 0;
-	else if (y >= SCREEN_H)
+	else if (y >= _screenHeight)
 		return;
 
 	while (1) {
@@ -1434,7 +1440,7 @@ void Screen::printText(const char *str, int x, int y, uint8 color1, uint8 color2
 			if (x + charWidth > _textMarginRight) {
 				x = x_start;
 				y += (charHeightFnt + _charOffset);
-				if (y >= SCREEN_H)
+				if (y >= _screenHeight)
 					break;
 			}
 
@@ -1467,7 +1473,7 @@ void Screen::drawChar(uint16 c, int x, int y) {
 
 	if (x < 0 || y < 0)
 		return;
-	if (x + charWidth > SCREEN_W || y + charHeight > SCREEN_H)
+	if (x + charWidth > SCREEN_W || y + charHeight > _screenHeight)
 		return;
 
 	if (useOverlay) {
@@ -3364,7 +3370,7 @@ void Screen::addDirtyRect(int x, int y, int w, int h) {
 	Common::Rect r(x, y, x + w, y + h);
 
 	// Clip rectangle
-	r.clip(SCREEN_W, SCREEN_H);
+	r.clip(SCREEN_W, _screenHeight);
 
 	// If it is empty after clipping, we are done
 	if (r.isEmpty())
diff --git a/engines/kyra/graphics/screen.h b/engines/kyra/graphics/screen.h
index 730076637a..7adc9b012b 100644
--- a/engines/kyra/graphics/screen.h
+++ b/engines/kyra/graphics/screen.h
@@ -354,6 +354,7 @@ public:
 	enum {
 		SCREEN_W = 320,
 		SCREEN_H = 200,
+		SCREEN_H_SEGA_NTSC = 224,
 		SCREEN_PAGE_SIZE = 320 * 200 + 1024,
 		SCREEN_OVL_SJIS_SIZE = 640 * 400,
 		SCREEN_PAGE_NUM = 16,
@@ -602,9 +603,11 @@ protected:
 	bool _useHiColorScreen;
 	bool _isAmiga;
 	bool _useAmigaExtraColors;
+	bool _isSegaCD;
 	Common::RenderMode _renderMode;
 	int _bytesPerPixel;
 	int _screenPageSize;
+	const int _screenHeight;
 	
 	Common::SharedPtr<Graphics::FontSJIS> _sjisFontShared;
 	uint8 _sjisInvisibleColor;
diff --git a/engines/kyra/graphics/screen_eob.h b/engines/kyra/graphics/screen_eob.h
index a930f43b0a..3bfa57e02f 100644
--- a/engines/kyra/graphics/screen_eob.h
+++ b/engines/kyra/graphics/screen_eob.h
@@ -119,8 +119,8 @@ public:
 	// SegaCD specific
 	void sega_selectPalette(int srcPalID, int dstPalID, int brightness = 0, bool set = false);
 	void sega_fadePalette(int delay, int brEnd, int dstPalID = -1);
-	void sega_fadeToBlack(int delay) { sega_fadePalette(delay, -8); }
-	void sega_fadeToWhite(int delay) { sega_fadePalette(delay, 8); }
+	void sega_fadeToBlack(int delay) { sega_fadePalette(delay, -7); }
+	void sega_fadeToWhite(int delay) { sega_fadePalette(delay, 7); }
 	void sega_fadeToNeutral(int delay) { sega_fadePalette(delay, 0); }
 	void sega_paletteOps(int opPal, int par1, int par2);
 	SegaRenderer *sega_getRenderer() const { return _segaRenderer; }
@@ -172,7 +172,7 @@ private:
 	// SegaCD specific
 	SegaRenderer *_segaRenderer;
 	uint16 _segaPalette[64];
-	uint8 _brState[4];
+	int8 _brState[4];
 };
 
 class SegaRenderer {
diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index 2f21e8b4b6..59668e0820 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -157,6 +157,7 @@ void Screen_EoB::sega_paletteOps(int op, int par1, int par2) {
 		// Force palette update, don't wait
 		break;
 	case 4:
+		// TODO
 		assert(0);
 		break;
 	default:
@@ -334,7 +335,7 @@ void SegaRenderer::render(int destPageNum) {
 	  TODO: implement support if necessary.
 	*/
 
-	_screen->copyBlockToPage(destPageNum, 0, 0, _screenW, Screen::SCREEN_H, _renderBuffer);
+	_screen->copyBlockToPage(destPageNum, 0, 0, _screenW, _screenH, _renderBuffer);
 }
 
 void SegaRenderer::renderTile(uint8 *dst, int destX, uint16 *nameTable, int hScrollTableIndex) {


Commit: 8f0399aaebd4c38f16b90f229807ffab91e70277
    https://github.com/scummvm/scummvm/commit/8f0399aaebd4c38f16b90f229807ffab91e70277
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:08+02:00

Commit Message:
KYRA: (EOB/SegaCD) - recover changes lost during rebase

Changed paths:
    engines/kyra/resource/resource_intern.h


diff --git a/engines/kyra/resource/resource_intern.h b/engines/kyra/resource/resource_intern.h
index 17c9643978..bb2fbdebaa 100644
--- a/engines/kyra/resource/resource_intern.h
+++ b/engines/kyra/resource/resource_intern.h
@@ -145,19 +145,19 @@ public:
 class EndianAwareStreamWrapper : public Common::SeekableReadStreamEndian {
 public:
 	EndianAwareStreamWrapper(Common::SeekableReadStream *stream, bool bigEndian, bool disposeAfterUse = true) : Common::SeekableReadStreamEndian(bigEndian), Common::ReadStreamEndian(bigEndian), _stream(stream), _dispose(disposeAfterUse) {}
-	~EndianAwareStreamWrapper() { if (_dispose) delete _stream; }
+	~EndianAwareStreamWrapper() override { if (_dispose) delete _stream; }
 
 	// Common::Stream interface
-	bool err() const { return _stream->err(); }
+	bool err() const override { return _stream->err(); }
 
 	// Common::ReadStream interface
-	bool eos() const { return _stream->eos(); }
-	uint32 read(void *dataPtr, uint32 dataSize) { return _stream->read(dataPtr, dataSize); }
+	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 { return _stream->pos(); }
-	int32 size() const { return _stream->size(); }
-	bool seek(int32 offset, int whence = SEEK_SET) { return _stream->seek(offset, whence); }
+	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;


Commit: 466a927738fa7a79327b69211912a9ba5b769b99
    https://github.com/scummvm/scummvm/commit/466a927738fa7a79327b69211912a9ba5b769b99
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:09+02:00

Commit Message:
KYRA: (EOB/SegaCD) - implement sequence player

This also (hopefully) involves most of the necessary graphics code. Text support is still missing. Otherwise, the intro and outro play mostly fine (except for some special opcodes).

Changed paths:
  A engines/kyra/graphics/screen_eob_segacd.h
  A engines/kyra/text/text_eob_segacd.cpp
  A engines/kyra/text/text_eob_segacd.h
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eob.h
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h
    engines/kyra/engine/scene_eob.cpp
    engines/kyra/graphics/screen_eob.cpp
    engines/kyra/graphics/screen_eob.h
    engines/kyra/graphics/screen_eob_segacd.cpp
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/module.mk
    engines/kyra/resource/resource_segacd.cpp
    engines/kyra/resource/resource_segacd.h
    engines/kyra/sequence/seqplayer_eob_segacd.cpp
    engines/kyra/sequence/seqplayer_eob_segacd.h
    engines/kyra/sequence/sequences_eob.cpp
    engines/kyra/sound/sound_segacd_eob.cpp
    engines/kyra/text/text_rpg.h


diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index d81782cb1d..820bb27fdc 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -27,6 +27,7 @@
 #include "kyra/resource/resource.h"
 #include "kyra/resource/resource_segacd.h"
 #include "kyra/sound/sound.h"
+#include "kyra/text/text_eob_segacd.h"
 
 namespace Kyra {
 
@@ -111,8 +112,10 @@ Common::Error EoBEngine::init() {
 	} else if (_flags.platform == Common::kPlatformSegaCD) {
 		_sres = new SegaCDResource(_res);
 		assert(_sres);
-		_seqPlayer = new SegaSequencePlayer(this, _sres);
+		_seqPlayer = new SegaSequencePlayer(this, _screen, _sres);
 		assert(_seqPlayer);
+		_txt = new TextDisplayer_SegaCD(this, _screen);
+		assert(_txt);
 	}
 
 	return Common::kNoError;
diff --git a/engines/kyra/engine/eob.h b/engines/kyra/engine/eob.h
index a06808bf0f..5bd13052e7 100644
--- a/engines/kyra/engine/eob.h
+++ b/engines/kyra/engine/eob.h
@@ -37,6 +37,7 @@ friend class EoBSeqPlayerCommon;
 friend class EoBIntroPlayer;
 friend class EoBPC98FinalePlayer;
 friend class EoBAmigaFinalePlayer;
+friend class SegaSequencePlayer;
 public:
 	EoBEngine(OSystem *system, const GameFlags &flags);
 	~EoBEngine() override;
@@ -90,7 +91,9 @@ private:
 	void seq_xdeath() override;
 
 	void seq_segaOpeningCredits();
-	bool seq_segaPlaySequence(int id);
+	void seq_segaSetupSequence(int id);
+	bool seq_segaPlaySequence(int sequenceId, int setupID = -1);
+
 
 	const char *const *_finBonusStrings;
 	SegaSequencePlayer *_seqPlayer;
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 9a5a58bbfc..a53592fd1c 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -32,6 +32,7 @@
 
 #include "common/config-manager.h"
 #include "common/translation.h"
+#include "common/debug-channels.h"
 
 #include "gui/error.h"
 
@@ -538,8 +539,10 @@ Common::Error EoBCoreEngine::init() {
 
 	_gui = new GUI_EoB(this);
 	assert(_gui);
-	_txt = new TextDisplayer_rpg(this, _screen);
-	assert(_txt);
+	if (_flags.platform != Common::kPlatformSegaCD) {
+		_txt = new TextDisplayer_rpg(this, _screen);
+		assert(_txt);
+	}
 	_inf = new EoBInfProcessor(this, _screen);
 	assert(_inf);
 	setDebugger(new Debugger_EoB(this));
@@ -629,6 +632,9 @@ Common::Error EoBCoreEngine::init() {
 	memset(_monsterStoneOverlay, (_flags.platform == Common::kPlatformAmiga) ? guiSettings()->colors.guiColorWhite : 0x0D, 16 * sizeof(uint8));
 	_monsterFlashOverlay[0] = _monsterStoneOverlay[0] = 0;
 
+	gDebugLevel = 7;
+	DebugMan.enableDebugChannel(kDebugLevelSequence);
+
 	return Common::kNoError;
 }
 
@@ -661,8 +667,8 @@ void EoBCoreEngine::loadFonts() {
 	} else if (_flags.platform == Common::kPlatformPC98) {
 		_screen->loadFont(Screen::FID_SJIS_SMALL_FNT, "FONT12.FNT");
 	} else if (_flags.platform == Common::kPlatformSegaCD) {
-		//_screen->loadFont(Screen::FID_8_FNT, "FONTK12");
-		//_screen->loadFont(Screen::FID_6_FNT, "FONT8SH");
+		_screen->loadFont(Screen::FID_8_FNT, "FONTK12");
+		_screen->loadFont(Screen::FID_6_FNT, "FONT8SH");
 	}
 }
 
@@ -2774,10 +2780,22 @@ void EoBCoreEngine::explodeMonster(EoBMonsterInPlay *m) {
 	m->flags &= ~2;
 }
 
-void EoBCoreEngine::snd_playSong(int track) {
+void EoBCoreEngine::snd_playSong(int track, bool loop) {
+	if (_flags.platform == Common::kPlatformSegaCD && !loop)
+		track |= 0x80;
 	_sound->playTrack(track);
 }
 
+void EoBCoreEngine::snd_playLevelScore() {
+	if (_flags.platform == Common::kPlatformPC98) {
+		if (_flags.gameID == GI_EOB1)
+			snd_playSong(_currentLevel + 1);
+	} else if (_flags.platform == Common::kPlatformSegaCD) {
+		static const uint8 levelTracksSegaCD[13] = { 7, 7, 7, 7, 6, 6, 6, 4, 4, 4, 5, 5, 10 };
+		snd_playSong(levelTracksSegaCD[_currentLevel]);
+	}
+}
+
 void EoBCoreEngine::snd_playSoundEffect(int track, int volume) {
 	if ((track < 1) || (_flags.gameID == GI_EOB2 && track > 119) || shouldQuit())
 		return;
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index 224da7a7d9..863eac1124 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -1190,7 +1190,8 @@ protected:
 	const uint8 *_numSpellsMage;
 
 	// sound
-	void snd_playSong(int id);
+	void snd_playSong(int id, bool loop = true);
+	void snd_playLevelScore();
 	void snd_playSoundEffect(int id, int volume=0xFF) override;
 	void snd_stopSound();
 	void snd_fadeOut(int del = 160);
diff --git a/engines/kyra/engine/scene_eob.cpp b/engines/kyra/engine/scene_eob.cpp
index 100c9f2bdb..64207b7532 100644
--- a/engines/kyra/engine/scene_eob.cpp
+++ b/engines/kyra/engine/scene_eob.cpp
@@ -108,8 +108,7 @@ void EoBCoreEngine::loadLevel(int level, int sub) {
 	_screen->setCurPage(0);
 	setHandItem(_itemInHand);
 
-	if (_flags.platform == Common::kPlatformPC98)
-		snd_playSong(level + 1);
+	snd_playLevelScore();
 }
 
 void EoBCoreEngine::readLevelFileData(int level) {
diff --git a/engines/kyra/graphics/screen_eob.cpp b/engines/kyra/graphics/screen_eob.cpp
index 4ae699e80f..46b96f9144 100644
--- a/engines/kyra/graphics/screen_eob.cpp
+++ b/engines/kyra/graphics/screen_eob.cpp
@@ -67,8 +67,10 @@ Screen_EoB::Screen_EoB(EoBCoreEngine *vm, OSystem *system) : Screen(vm, system,
 	_cpsFilePattern = "%s.";
 	_activePalCycle = 0;
 	_segaRenderer = 0;
-	memset(_brState, 0, sizeof(_brState));
-	memset(_segaPalette, 0, sizeof(_segaPalette));
+	_segaAnimator = 0;
+	_segaCustomPalettes = 0;
+	_palFaders = 0;
+	memset(_segaCurPalette, 0, sizeof(_segaCurPalette));
 }
 
 Screen_EoB::~Screen_EoB() {
@@ -81,7 +83,10 @@ Screen_EoB::~Screen_EoB() {
 	delete[] _cgaDitheringTables[0];
 	delete[] _cgaDitheringTables[1];
 	delete[] _cyclePalette;
+	delete[] _segaCustomPalettes;
+	delete[] _palFaders;
 	delete _segaRenderer;
+	delete _segaAnimator;
 }
 
 bool Screen_EoB::init() {
@@ -119,15 +124,10 @@ bool Screen_EoB::init() {
 			for (int i = 0; i < 256; i++)
 				_cgaScaleTable[i] = ((i & 0xF0) >> 2) | (i & 0x03);
 		} else if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
-			_segaRenderer = new SegaRenderer(this);
-			_segaRenderer->setResolution(320, 224);
-			_segaRenderer->setPlaneTableLocation(SegaRenderer::kPlaneA, 0xC000);
-			_segaRenderer->setPlaneTableLocation(SegaRenderer::kPlaneB, 0xE000);
-			_segaRenderer->setPlaneTableLocation(SegaRenderer::kWindowPlane, 0xF000);
-			_segaRenderer->setupPlaneAB(SegaRenderer::kPlaneA, 1024, 256);
-			_segaRenderer->setupPlaneAB(SegaRenderer::kPlaneB, 1024, 256);
-			_segaRenderer->setupWindowPlane(0, 0, SegaRenderer::kWinToLeft, SegaRenderer::kWinToTop);
-			_segaRenderer->setHScrollTableLocation(0xD800);
+			sega_initGraphics();
+			_segaCustomPalettes = new uint16[128];
+			_palFaders = new PaletteFader[4];
+			memset(_segaCustomPalettes, 0, 128 * sizeof(uint16));
 		}
 
 		static const char *cpsExt[] = { "CPS", "EGA", "SHP", "BIN" };
@@ -434,7 +434,8 @@ void Screen_EoB::setScreenPalette(const Palette &pal) {
 			createFadeTable16bit((const uint16*)(pal.getData()), &_16bitPalette[i * 256], 0, i * 85);
 	} else if (_useHiResEGADithering && pal.getNumColors() != 16) {
 		generateEGADitheringTable(pal);
-	} else if ((_renderMode == Common::kRenderEGA) && pal.getNumColors() == 16) {
+	} else if (_isSegaCD || (_renderMode == Common::kRenderEGA && pal.getNumColors() == 16)) {
+		_paletteChanged = true;
 		_screenPalette->copy(pal);
 		_system->getPaletteManager()->setPalette(_screenPalette->getData(), 0, _screenPalette->getNumColors());
 	} else if (_renderMode != Common::kRenderCGA && _renderMode != Common::kRenderEGA) {
@@ -1513,6 +1514,8 @@ bool Screen_EoB::loadFont(FontId fontId, const char *filename) {
 				_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) {
+			fnt = new SegaCDFont();
 	} 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);
diff --git a/engines/kyra/graphics/screen_eob.h b/engines/kyra/graphics/screen_eob.h
index 3bfa57e02f..c3a2698766 100644
--- a/engines/kyra/graphics/screen_eob.h
+++ b/engines/kyra/graphics/screen_eob.h
@@ -29,8 +29,9 @@
 
 namespace Kyra {
 
-class SegaRenderer;
 class EoBCoreEngine;
+class SegaRenderer;
+class SegaAnimator;
 class Screen_EoB : public Screen {
 friend class SegaRenderer;
 public:
@@ -117,13 +118,18 @@ public:
 	void setDualPalettes(Palette &top, Palette &bottom);
 
 	// SegaCD specific
-	void sega_selectPalette(int srcPalID, int dstPalID, int brightness = 0, bool set = false);
-	void sega_fadePalette(int delay, int brEnd, int dstPalID = -1);
+	void sega_initGraphics();
+	void sega_selectPalette(int srcPalID, int dstPalID, bool set = false);
+	void sega_loadCustomPaletteData(Common::ReadStream *in);
+	void sega_updatePaletteFaders(int palID);
+	void sega_fadePalette(int delay, int16 brEnd, int dstPalID = -1, bool waitForCompletion = true, bool noUpdate = false);
 	void sega_fadeToBlack(int delay) { sega_fadePalette(delay, -7); }
 	void sega_fadeToWhite(int delay) { sega_fadePalette(delay, 7); }
 	void sega_fadeToNeutral(int delay) { sega_fadePalette(delay, 0); }
-	void sega_paletteOps(int opPal, int par1, int par2);
+	void sega_paletteOps(int16 opPal, int16 par1, int16 par2);
+
 	SegaRenderer *sega_getRenderer() const { return _segaRenderer; }
+	SegaAnimator *sega_getAnimator() const { return _segaAnimator; }
 
 private:
 	void updateDirtyRects() override;
@@ -170,83 +176,21 @@ private:
 	static const int _screenDimTableCount;
 
 	// SegaCD specific
-	SegaRenderer *_segaRenderer;
-	uint16 _segaPalette[64];
-	int8 _brState[4];
-};
-
-class SegaRenderer {
-public:
-	enum Plane {
-		kPlaneA = 0,
-		kPlaneB = 1,
-		kWindowPlane = 2
-	};
-
-	enum WindowMode {
-		kWinToLeft = 0,
-		kWinToTop = 0,
-		kWinToRight = 1,
-		kWinToBottom = 1
+	struct PaletteFader {
+		PaletteFader() : _brCur(0), _brDest(0), _fadeIncr(0), _fadeDelay(0), _fadeTimer(0), _needRefresh(false) {}
+		int16 _brCur;
+		int16 _brDest;
+		int16 _fadeIncr;
+		int16 _fadeDelay;
+		int16 _fadeTimer;
+		bool _needRefresh;
 	};
 
-	enum HScrollMode {
-		kHScrollFullScreen = 0,
-		kHScroll8PixelRows,
-		kHScroll1PixelRows
-	};
-
-public:
-	SegaRenderer(Screen_EoB *screen);
-	~SegaRenderer();
-
-	void setResolution(int w, int h);
-	void setPlaneTableLocation(int plane, uint16 addr);
-	void setupPlaneAB(int plane, uint16 w, uint16 h);
-	void setupWindowPlane(int blockX, int blockY, int horizontalMode, int verticalMode);
-	void setHScrollTableLocation(int addr);
-	void setPitch(int pitch);
-	void setHScrollMode(int mode);	
-
-	void loadToVRAM(const uint16 *data, int dataSize, int addr);
-	void loadToVRAM(Common::SeekableReadStreamEndian *in, int addr, bool compressedData = false);
-	void memsetVRAM(int addr, uint8 val, int len);
-	void fillRectWithTiles(int addr, int x, int y, int w, int h, uint16 nameTblEntry, bool incr = false);
-	
-	void render(int destPageNum);
-
-private:
-	void renderTile(uint8 *dst, int destX, uint16 *nameTable, int hScrollTableIndex);
-	void renderLineFragment(uint8 *&dst, const uint8 *src, int start, int end, uint8 pal);
-
-	void checkUpdateDirtyRects(int addr, int len);
-	void addDirtyRect(int x, int y, int w, int h);
-	void sendDirtyRectsToScreen();
-	void clearDirtyRects();
-
-	struct SegaPlane {
-		SegaPlane() : blockX(0), blockY(0), w(0), h(0), nameTable(0) {}
-		int blockX, blockY;
-		uint16 w, h;
-		uint16 *nameTable;
-		uint16 nameTableSize;
-	};
-
-	SegaPlane _planes[3];
-	uint8 *_vram;
-	uint16 *_hScrollTable;
-	uint8 _hScrollMode;
-	uint16 _pitch;
-
-	struct DRChainEntry {
-		DRChainEntry(DRChainEntry *chain, int x, int y, int w, int h) : next(chain), rect(x, y, x + w, y + h) {}
-		Common::Rect rect;
-		DRChainEntry *next;
-	} *_drChain;
-
-	Screen_EoB *_screen;
-
-	uint16 _screenW, _screenH, _blocksW, _blocksH;
+	PaletteFader *_palFaders;
+	SegaRenderer *_segaRenderer;
+	SegaAnimator *_segaAnimator;
+	uint16 _segaCurPalette[64];
+	uint16 *_segaCustomPalettes;
 };
 
 /**
@@ -435,6 +379,24 @@ private:
 	const int _height, _width;
 };
 
+class SegaCDFont : public Font {
+public:
+	SegaCDFont();
+	~SegaCDFont() override;
+
+private:
+	bool load(Common::SeekableReadStream &file) override;
+	int getHeight() const override { return _height; }
+	int getWidth() const override { return _width; }
+	int getCharWidth(uint16 c) const override { return _width; }
+	void setColorMap(const uint8 *src) override { _colorMap = src; }
+	void drawChar(uint16 c, byte *dst, int pitch, int) const override;
+
+	uint8 *_data;
+	const uint8 *_colorMap;
+	const int _height, _width;
+};
+
 } // End of namespace Kyra
 
 #endif // ENABLE_EOB
diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index 59668e0820..42b0ed07d8 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -24,29 +24,36 @@
 #ifdef ENABLE_EOB
 
 #include "common/system.h"
-#include "graphics/palette.h"
 #include "kyra/resource/resource.h"
+#include "kyra/graphics/screen_eob.h"
+#include "kyra/graphics/screen_eob_segacd.h"
 
 namespace Kyra {
 
-struct PaletteFader {
-	PaletteFader() : _brCur(0), _brDest(0), _fadeIncr(0), _fadeDelay(0), _fadeTimer(0) {}
-	int16 _brCur;
-	int16 _brDest;
-	int16 _fadeIncr;
-	int16 _fadeDelay;
-	int16 _fadeTimer;
-};
+void Screen_EoB::sega_initGraphics() {
+	_segaRenderer = new SegaRenderer(this);
+	_segaRenderer->setResolution(320, 224);
+	_segaRenderer->setPlaneTableLocation(SegaRenderer::kPlaneA, 0xC000);
+	_segaRenderer->setPlaneTableLocation(SegaRenderer::kPlaneB, 0xE000);
+	_segaRenderer->setPlaneTableLocation(SegaRenderer::kWindowPlane, 0xF000);
+	_segaRenderer->setupPlaneAB(1024, 256);
+	_segaRenderer->setupWindowPlane(0, 0, SegaRenderer::kWinToLeft, SegaRenderer::kWinToTop);
+	_segaRenderer->setHScrollTableLocation(0xD800);
+	_segaRenderer->setSpriteTableLocation(0xDC00);
+	_segaAnimator = new SegaAnimator(_segaRenderer);
+}
 
-void Screen_EoB::sega_selectPalette(int srcPalID, int dstPalID, int brightness, bool set) {
+void Screen_EoB::sega_selectPalette(int srcPalID, int dstPalID, bool set) {
 	if (srcPalID < -1 || srcPalID > 59 || dstPalID < 0 || dstPalID > 3)
 		return;
 
-	const uint16 *src = &_segaPalette[dstPalID << 4];
+	const uint16 *src = &_segaCurPalette[dstPalID << 4];
 	uint8 rgbColors[48];
 	uint8 *dst = rgbColors;
 
-	if (srcPalID >= 0) {
+	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)
@@ -57,97 +64,146 @@ void Screen_EoB::sega_selectPalette(int srcPalID, int dstPalID, int brightness,
 	// R: bits 1, 2, 3   G: bits 5, 6, 7   B: bits 9, 10, 11
 	for (int i = 0; i < 16; ++i) {
 		uint16 in = *src++;
-		_segaPalette[dstPalID << 4 | i] = in;
+		_segaCurPalette[dstPalID << 4 | i] = in;
 #if 0
 		static const uint8 col[8] = { 0, 52, 87, 116, 144, 172, 206, 255 };
 		*dst++ = col[CLIP<int>(((in & 0x00F) >> 1) + brightness, 0, 7)];
 		*dst++ = col[CLIP<int>(((in & 0x0F0) >> 5) + brightness, 0, 7)];
 		*dst++ = col[CLIP<int>(((in & 0xF00) >> 9) + brightness, 0, 7)];
 #else
-		*dst++ = CLIP<int>(((in & 0x00F) >> 1) + brightness, 0, 7) * 255 / 7;
-		*dst++ = CLIP<int>(((in & 0x0F0) >> 5) + brightness, 0, 7) * 255 / 7;
-		*dst++ = CLIP<int>(((in & 0xF00) >> 9) + brightness, 0, 7) * 255 / 7;
+		*dst++ = CLIP<int>(((in & 0x00F) >> 1) + _palFaders[dstPalID]._brCur, 0, 7) * 255 / 7;
+		*dst++ = CLIP<int>(((in & 0x0F0) >> 5) + _palFaders[dstPalID]._brCur, 0, 7) * 255 / 7;
+		*dst++ = CLIP<int>(((in & 0xF00) >> 9) + _palFaders[dstPalID]._brCur, 0, 7) * 255 / 7;
 #endif
 	}
 
 	getPalette(0).copy(rgbColors, 0, 16, dstPalID << 4);
+/*
+	// Make half intensity palette
+	dst = rgbColors;
+	for (int i = 0; i < 16; ++i) {
+		*dst++ >= 1;
+		*dst++ >= 1;
+		*dst++ >= 1;
+	}
+
+	getPalette(0).copy(rgbColors, 0, 16, (dstPalID << 4) | 0x40);
+	memcpy(rgbColors, getPalette(0).getData() + (dstPalID << 4), 48);
+
+	// Make double intensity palette
+	dst = rgbColors;
+	for (int i = 0; i < 16; ++i) {
+		*dst++ <= 1;
+		*dst++ <= 1;
+		*dst++ <= 1;
+	}
+
+	getPalette(0).copy(rgbColors, 0, 16, (dstPalID << 4) | 0x80);
+*/
+
+	if (set) 
+		setScreenPalette(getPalette(0));
+}
 
-	if (set) {
-		_vm->_system->getPaletteManager()->setPalette(rgbColors, dstPalID << 4, 16);
-		_paletteChanged = true;
+void Screen_EoB::sega_loadCustomPaletteData(Common::ReadStream *in) {
+	uint16 *dst = _segaCustomPalettes;
+	for (int i = 0; i < 8; ++i) {
+		*dst++ = 0;
+		in->readUint16BE();
+		if (in->eos())
+			break;
+		for (int ii = 1; ii < 16; ++ii)
+			*dst++ = in->readUint16BE();
 	}
 }
 
-void Screen_EoB::sega_fadePalette(int delay, int brEnd, int dstPalID) {
+void Screen_EoB::sega_updatePaletteFaders(int palID) {
 	int first = 0;
 	int last = 3;
-	if (dstPalID >= 0)
-		first = last = dstPalID;
 
-	PaletteFader faders[4];
+	if (palID >= 0)
+		first = last = palID;
 
+	bool update = false;
 	for (int i = first; i <= last; ++i) {
-		PaletteFader &f = faders[i];
-		f._brCur = _brState[i];
-		if (f._brCur < brEnd)
-			f._fadeIncr = 1;
-		else if (f._brCur > brEnd)
-			f._fadeIncr = -1;
-		else
+		PaletteFader &f = _palFaders[i];
+		f._needRefresh = false;
+		if (f._fadeDelay == 0 && f._brCur != f._brDest) {
+			f._brCur = f._brDest;
+			f._needRefresh = true;
+		}
+		if (f._brCur == f._brDest)
+			continue;
+		if (--f._fadeTimer)
 			continue;
 
-		f._brDest = brEnd;
-		f._fadeDelay = f._fadeTimer = delay;
+		f._brCur += f._fadeIncr;
+		f._fadeTimer = f._fadeDelay;
+		f._needRefresh = true;
 	}
 
-	for (bool runLoop = true; runLoop; ) {
-		uint32 end = _vm->_system->getMillis() + 17;
-		bool update = false;
+	for (int i = first; i <= last; ++i) {
+		if (_palFaders[i]._needRefresh) {
+			sega_selectPalette(-1, i, true);
+			update = true;
+			_palFaders[i]._needRefresh = false;
+		}
+	}
+
+	if (update)
+		updateScreen();
+}
+
+void Screen_EoB::sega_fadePalette(int delay, int16 brEnd, int dstPalID, bool waitForCompletion, bool noUpdate) {
+	int first = 0;
+	int last = 3;
+	uint32 tickMillis = 0;
+
+	if (dstPalID >= 0)
+		first = last = dstPalID;
+
+	if (!noUpdate) {
 		for (int i = first; i <= last; ++i) {
-			_brState[i] = 127;
-			PaletteFader &f = faders[i];
-			if (f._fadeDelay == 0 && f._brCur != f._brDest) {
-				f._brCur = f._brDest;
-				update = true;
-			}
-			if (f._brCur == f._brDest)
-				continue;
-			if (--f._fadeTimer)
+			PaletteFader &f = _palFaders[i];
+			f._needRefresh = false;
+			if (f._brCur < brEnd)
+				f._fadeIncr = 1;
+			else if (f._brCur > brEnd)
+				f._fadeIncr = -1;
+			else
 				continue;
 
-			f._brCur += f._fadeIncr;
-			f._fadeTimer = f._fadeDelay;
-			_brState[i] = faders[i]._brCur;
-		}
-		
-		for (int i = first; i <= last; ++i) {
-			if (_brState[i] != 127) {
-				sega_selectPalette(-1, i, _brState[i], true);
-				update = true;
-				_brState[i] = 127;
-			}
+			f._brDest = brEnd;
+			f._fadeDelay = f._fadeTimer = delay;
 		}
+	}
+
+	if (!waitForCompletion)
+		return;
 
-		if (update)
-			updateScreen();
+	for (bool runLoop = true; runLoop; ) {
+		uint32 now = _vm->_system->getMillis();
+		sega_updatePaletteFaders(dstPalID);
 
 		runLoop = false;
 		for (int i = first; i <= last; ++i) {
-			if (faders[i]._brCur != faders[i]._brDest)
+			if (_palFaders[i]._brCur != _palFaders[i]._brDest)
 				runLoop = true;
-			_brState[i] = faders[i]._brCur;
 		}
 
-		_vm->delayUntil(end);
+		tickMillis += 16667;
+		uint32 ms = tickMillis / 1000;
+		_vm->delayUntil(now + ms);
+		tickMillis -= (ms * 1000);
 
 		if (_vm->shouldQuit()) {
 			for (int i = first; i <= last; ++i)
-				faders[i]._fadeDelay = 0;
+				_palFaders[i]._fadeDelay = 0;
 		}
 	}
 }
 
-void Screen_EoB::sega_paletteOps(int op, int par1, int par2) {
+void Screen_EoB::sega_paletteOps(int16 op, int16 par1, int16 par2) {
 	assert(op >= 0 && op <= 6);
 	switch (op) {
 	case 6:
@@ -161,20 +217,86 @@ void Screen_EoB::sega_paletteOps(int op, int par1, int par2) {
 		assert(0);
 		break;
 	default:
-		sega_fadePalette(par2, par1, op);
+		sega_fadePalette(par2, par1, op, false);
 	}
 }
 
-SegaRenderer::SegaRenderer(Screen_EoB *screen) : _screen(screen), _drChain(0), _pitch(64), _hScrollMode(0), _hScrollTable(0) {
+#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), _drChain(0), _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);
 	memset(_vram, 0, 0x10000 * sizeof(uint8));
+	_vsram = new uint16[40];
+	assert(_vsram);
+	memset(_vsram, 0, 40 * sizeof(uint16));
+	int temp;
+
+#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
+
+	for (int i = 0; i < 6; ++i)
+		_patternTables[i] = _screen->_vm->staticres()->loadRawDataBe16(kEoB1PatternTable0 + i, temp);
+
 	setResolution(320, 224);
 }
 
 SegaRenderer::~SegaRenderer() {
 	clearDirtyRects();
 	delete[] _vram;
+	delete[] _vsram;
+	delete[] _spriteMask;
 }
 
 void SegaRenderer::setResolution(int w, int h) {
@@ -185,6 +307,12 @@ void SegaRenderer::setResolution(int w, int h) {
 	_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) {
@@ -193,16 +321,21 @@ void SegaRenderer::setPlaneTableLocation(int plane, uint16 addr) {
 	_planes[plane].nameTable = (uint16*)(&_vram[addr]);
 }
 
-void SegaRenderer::setupPlaneAB(int plane, uint16 w, uint16 h) {
-	assert(plane >= kPlaneA && plane <= kPlaneB);
-	_planes[plane].w = w;
-	_planes[plane].h = h;
-	_planes[plane].nameTableSize = (w * h) >> 6;
+void SegaRenderer::setupPlaneAB(int w, int h) {
+	for (int i = 0; i < 2; ++i) {
+		if (w != -1)
+			_planes[i].w = w;
+		if (h != -1)
+			_planes[i].h = h;
+		_planes[i].nameTableSize = (w * h) >> 6;
+	}
 }
 
 void SegaRenderer::setupWindowPlane(int blockX, int blockY, int horizontalMode, int verticalMode) {
-	_planes[kWindowPlane].blockX = horizontalMode ? blockX : 0;
-	_planes[kWindowPlane].blockY = verticalMode ? blockY : 0;
+	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;
 }
@@ -212,6 +345,11 @@ void SegaRenderer::setHScrollTableLocation(int addr) {
 	_hScrollTable = (uint16*)(&_vram[addr]);
 }
 
+void SegaRenderer::setSpriteTableLocation(int addr) {
+	assert(addr <= 0xFFFF);
+	_spriteTable = (uint16*)(&_vram[addr]);
+}
+
 void SegaRenderer::setPitch(int pitch) {
 	_pitch = pitch;
 }
@@ -220,9 +358,13 @@ void SegaRenderer::setHScrollMode(int mode) {
 	_hScrollMode = mode;
 }
 
-void SegaRenderer::loadToVRAM(const uint16 *data, int dataSize, int addr) {
+void SegaRenderer::setVScrollMode(int mode) {
+	_vScrollMode = mode;
+}
+
+void SegaRenderer::loadToVRAM(const void *data, int dataSize, int addr) {
 	assert(data);
-	assert(addr <= 0xFFFF);
+	assert(addr + dataSize <= 0xFFFF);
 	memcpy(_vram + addr, data, dataSize);
 	checkUpdateDirtyRects(addr, dataSize);
 }
@@ -255,25 +397,72 @@ void SegaRenderer::memsetVRAM(int addr, uint8 val, int len) {
 	void checkUpdateDirtyRects(int addr, int len);
 }
 
-void SegaRenderer::fillRectWithTiles(int addr, int x, int y, int w, int h, uint16 nameTblEntry, bool incr) {
+void SegaRenderer::fillRectWithTiles(int vramArea, int x, int y, int w, int h, uint16 nameTblEntry, bool incr, bool topToBottom, int presetPatternID) {
+	uint16 addr = vramArea ? (vramArea == 1 ? 0xE000 : 0xF000) : 0xC000;
 	addDirtyRect(x << 3, y << 3, w << 3, h << 3);
+	if (y & 0x8000) {
+		y &= ~0x8000;
+		addr = 0xE000;
+	}
+
 	uint16 *dst = (uint16*)(&_vram[addr]);
 	dst += (y * _pitch + x);
-	if (incr) {
+	int ptch = _pitch - w;
+
+	assert(addr + 2 * (y * _pitch + x + h * _pitch + w) <= 0xFFFF);
+
+	if (presetPatternID > -1) {
+		assert(presetPatternID < 6);
+		const uint16 *tbl = _patternTables[presetPatternID];
 		while (h--) {
+			const uint16 *pos = tbl;
 			for (int i = w; i; --i)
-				*dst++ = nameTblEntry++;
-			dst += (_pitch - w);
+				*dst++ = nameTblEntry + *pos++;
+			dst += ptch;
+			tbl += w;
+		}
+	} else if (incr) {
+		if (topToBottom) {
+			while (w--) {
+				uint16 *dst2 = dst;
+				for (int i = h; i; --i) {
+					*dst = nameTblEntry++;
+					dst += _pitch;
+				}
+				dst = ++dst2;
+			}
+		} else {
+			while (h--) {
+				for (int i = w; i; --i)
+					*dst++ = nameTblEntry++;
+				dst += ptch;
+			}
 		}
 	} else {
-		while (h--) {
-			for (int i = w; i; --i)
-				*dst++ = nameTblEntry;
-			dst += (_pitch - w);
+		if (topToBottom) {
+			while (w--) {
+				uint16 *dst2 = dst;
+				for (int i = h; i; --i) {
+					*dst = nameTblEntry;
+					dst += _pitch;
+				}
+				dst = ++dst2;
+			}
+		} else {
+			while (h--) {
+				for (int i = w; i; --i)
+					*dst++ = nameTblEntry;
+				dst += ptch;
+			}
 		}
 	}
 }
 
+void SegaRenderer::writeVSRAMValue(int addr, uint16 value) {
+	assert(addr < 80);
+	_vsram[addr >> 1] = value;
+}
+
 void SegaRenderer::render(int destPageNum) {
 	uint8 *renderBuffer = _screen->getPagePtr(destPageNum);
 	memset(renderBuffer, 0, _screenW * _screenH);
@@ -286,107 +475,308 @@ void SegaRenderer::render(int destPageNum) {
 	// intentional for now, since I assume that this will be in harmony with the actual usage of the renderer.
 	clearDirtyRects();
 
-	// Planes A and B
-	const uint8 order[2] = { kPlaneB, kPlaneA };
-	for (int p = 0; p < ARRAYSIZE(order); ++p) {
-		uint16 *ntbl = _planes[p].nameTable;
-		uint8 *dst = renderBuffer;
-
-		for (int y = 0; y < _blocksH; ++y) {
-			int hscrTblIdx = (_hScrollMode == kHScrollFullScreen) ? p : (y << 4) + p;
-			uint8 *dst2 = dst;
-			for (int x = 0; x < _blocksW; ++x) {
-				renderTile(dst, x, ntbl, hscrTblIdx);
-				dst += 8;
-			}
-			ntbl += _pitch;
-			dst = dst2 + (_screenW << 3);
+	// Plane B
+	renderPlanePart(kPlaneB, renderBuffer, 0, 0, _blocksW, _blocksH);
+
+	// Plane A (only draw if the nametable is not identical to that of plane B)
+	if (_planes[kPlaneA].nameTable != _planes[kPlaneB].nameTable) {
+		// 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].h && _planes[kWindowPlane].w) {
+			SegaPlane *p = &_planes[kWindowPlane];
+			renderPlanePart(kPlaneA, renderBuffer, 0, 0, p->blockX, _blocksH);
+			renderPlanePart(kPlaneA, renderBuffer, 0, 0, _blocksW, p->blockY);
+			renderPlanePart(kPlaneA, renderBuffer, p->blockX + p->w, 0, _blocksW, _blocksH);
+			renderPlanePart(kPlaneA, renderBuffer, 0, p->blockY + p->h, _blocksH, _blocksH);
+		} else {
+			renderPlanePart(kPlaneA, renderBuffer, 0, 0, _blocksW, _blocksH);
 		}
-
-		if (_planes[kPlaneA].nameTable == _planes[kPlaneB].nameTable)
-			break;
 	}
 
 	// Window Plane
-	// TODO: implement support if necessary. The following lines are just a modified copy/paste
-	//       from above and nowhere guaranteed to work properly.
-	/*{
+	if (_planes[kWindowPlane].h && _planes[kWindowPlane].w) {
 		SegaPlane *p = &_planes[kWindowPlane];
-		uint16 *ntbl = p->nameTable;
-		uint8 *dst = _renderBuffer;
+		renderPlanePart(kWindowPlane, renderBuffer, p->blockX, p->blockY, p->blockX + p->w, p->blockY + p->h);
+	}
 
-		for (int y = p->blockY; y < p->blockY + p->h; ++y) {
+	// 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;
+
+		uint8 *dst = renderBuffer + y * _screenW + x;
+		uint8 *msk = _spriteMask + y * _screenW + x;
+
+		for (int blX = 0; blX < bW; ++blX) {
 			uint8 *dst2 = dst;
-			for (int x = p->blockX; x < p->blockX + p->w; ++x) {
-				renderTile(dst, x, ntbl, -1);
-				dst += 8;
+			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);
 			}
-			ntbl += _blocksW;
-			dst = dst2 + (_screenW << 3);
+			dst = dst2 + 8;
+			msk = msk2 + 8;
 		}
-	}*/
 
-	// Sprites
-	// TODO: implement support if necessary.
+		pos = next ? &_spriteTable[next << 2] : 0;
+	}
 
 	// Priority Tiles
-	// All tiles that have been skipped so far due to a priority flag will be drawn now.
-	/*
-	  TODO: implement support if necessary.
-	*/
+	// 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 in a chain. Should be faster...
+	// The priority sprite tiles aren't rendered against the sprite mask table here which is wrong.
+	// Priority sprites are a rare thing, though. EOB probably doesn't have any...
+	for (PrioTileRenderObj *e = _prioChainStart; e; e = e->_next)
+		mRenderLineFragment(e->_hflip, e->_start & 1, e->_end & 1, 0, e->_dst, 0, 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];
+	uint16 *ntbl = p->nameTable + y1 * _pitch + x1;
+	uint8 *dst = dstBuffer + (y1 << 3) * _screenW + (x1 << 3);
 
-	_screen->copyBlockToPage(destPageNum, 0, 0, _screenW, _screenH, _renderBuffer);
+	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;
+			renderPlaneTile(dst, x, ntbl, vScrollTableIndex, hScrollTableIndex, _pitch, p->nameTableSize);
+			dst += 8;
+		}
+		ntbl += _pitch;
+		dst = dst2 + (_screenW << 3);
+	}
 }
 
-void SegaRenderer::renderTile(uint8 *dst, int destX, uint16 *nameTable, int hScrollTableIndex) {
-	for (int bY = 0; bY < 8; ++bY) {
+void SegaRenderer::renderPlaneTile(uint8 *dst, int destX, uint16 *nameTable, int vScrollTableIndex, int hScrollTableIndex, uint16 pitch, uint16 nameTableSize) {
+	uint16 vscrNt = 0;
+	uint16 vscrPx = 0;
+
+	if (vScrollTableIndex != -1) {
+		vscrNt = _vsram[vScrollTableIndex] & 0x3FF;
+		vscrPx = vscrNt & 7;
+		vscrNt >>= 3;
+	}
+
+	for (int bY = vscrPx; bY < vscrPx + 8; ++bY) {
 		uint8 *dst2 = dst;
 		uint16 hscrNt = 0;
 		uint16 hscrPx = 0;
 
+		if (bY == 8) {
+			nameTable += pitch;
+			if (hScrollTableIndex != -1 && _hScrollMode == kHScroll8PixelRows)
+				hScrollTableIndex += 16;
+		}
+
 		if (hScrollTableIndex != -1) {
 			hscrNt = _hScrollTable[hScrollTableIndex] & 0x3FF;
 			hscrPx = hscrNt & 7;
 			hscrNt >>= 3;
 		}
 
-		uint16 nt = nameTable[(destX + hscrNt) % _pitch];
+		uint16 nt = nameTable[((vscrNt * pitch) % nameTableSize) + ((destX + hscrNt) % pitch)];
+		uint16 pal = ((nt >> 13) & 3) << 4;
+		bool hflip = (nt & 0x800);
+		int y = bY % 8;
+		if (nt & 0x1000) // vflip
+			y = 7 - y;
 
-		// Check all the following things
-		// TODO: implement support if necessary.
-		// Priority
-		assert(!(nt & 0x8000));
-		// Vertical flip
-		assert(!(nt & 0x1000));
-		// Horizontal flip
-		assert(!(nt & 0x800));
-
-		renderLineFragment(dst, &_vram[((nt & 0x7FF) << 5) + (bY << 2) + (hscrPx >> 1)], hscrPx, 8, ((nt >> 13) & 3) << 4);
-
-		nt = nameTable[(destX + hscrNt + 1) % _pitch];
-		// Priority
-		assert(!(nt & 0x8000));
-		// Vertical flip
-		assert(!(nt & 0x1000));
-		// Horizontal flip
-		assert(!(nt & 0x800));
+		// 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);
+			nt = nameTable[((vscrNt * pitch) % nameTableSize) + ((destX + hscrNt + 1) % pitch)];
+			pal = ((nt >> 13) & 3) << 4;
+			hflip = (nt & 0x800);
+			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)
+		}
 
-		renderLineFragment(dst, &_vram[((nt & 0x7FF) << 5) + (bY << 2)], 0, hscrPx, ((nt >> 13) & 3) << 4);
-	
 		if (hScrollTableIndex != -1 && _hScrollMode == kHScroll1PixelRows)
 			hScrollTableIndex += 2;
 		dst = dst2 + _screenW;
 	}
 }
 
-void SegaRenderer::renderLineFragment(uint8 *&dst, const uint8 *src, int start, int end, uint8 pal) {
-	for (int bX = start; bX < end; ++bX) {
-		uint8 col = (bX & 1) ? *src++ & 0x0F : *src >> 4;
+#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) {
+			/*if (col == 0x3E)
+				*dst |= 0x40;
+			else if (col == 0x3E)
+				*dst |= 0x80;
+			else*/
+				*dst = pal | col;
+			*mask = 0;
+		}
+		dst++;
+		mask++;
+		if (col2 & *mask) {
+			/*if (col2 == 0x3E)
+				*dst |= 0x40;
+			else if (col2 == 0x3E)
+				*dst |= 0x80;
+			else*/
+				*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) {
+			/*if (col == 0x3E)
+				*dst |= 0x40;
+			else if (col == 0x3E)
+				*dst |= 0x80;
+			else*/
+				*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) {
+				/*if (col == 0x3E)
+					*dst |= 0x40;
+				else if (col == 0x3E)
+					*dst |= 0x80;
+				else*/
+					*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
 
 void SegaRenderer::checkUpdateDirtyRects(int addr, int len) {
 	//void *tbl[] = { _vram, _hScrollTable, , _planes[kPlaneA].nameTable, _planes[kPlaneB].nameTable, _planes[kWindowPlane].nameTable };
@@ -411,25 +801,155 @@ void SegaRenderer::addDirtyRect(int x, int y, int w, int h) {
 }
 
 void SegaRenderer::sendDirtyRectsToScreen() {
-	for (DRChainEntry *e = _drChain; e; e = e->next) {
-		int w = e->rect.width();
-		int h = e->rect.height();
+	for (DRChainEntry *e = _drChain; e; e = e->_next) {
+		int w = e->_rect.width();
+		int h = e->_rect.height();
 		if (w == _screenW && h == _screenH) {
 			_screen->_forceFullUpdate = true;
 			return;
 		} 
-		_screen->addDirtyRect(e->rect.left, e->rect.top, w, h);
+		_screen->addDirtyRect(e->_rect.left, e->_rect.top, w, h);
 	}
 }
 
 void SegaRenderer::clearDirtyRects() {
 	while (_drChain) {
-		DRChainEntry *e = _drChain->next;
+		DRChainEntry *e = _drChain->_next;
 		delete _drChain;
 		_drChain = e;
 	}
 }
 
+void SegaRenderer::initPrioRenderTask(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal, bool hflip) {
+	_prioChainEnd = new PrioTileRenderObj(_prioChainEnd, dst, mask, src, start, end, pal, hflip);
+	if (!_prioChainStart)
+		_prioChainStart = _prioChainEnd;
+}
+
+void SegaRenderer::clearPrioChain() {
+	while (_prioChainEnd) {
+		_prioChainEnd->_next = 0;
+		PrioTileRenderObj *e = _prioChainEnd->_pred;
+		delete _prioChainEnd;
+		_prioChainEnd = e;
+	}
+	_prioChainStart = 0;
+}
+
+SegaAnimator::SegaAnimator(SegaRenderer *renderer) : _renderer(renderer), _needUpdate(false) {
+	_sprites = new Sprite[80];
+	assert(_sprites);
+	memset(_sprites, 0, sizeof(Sprite) * 80);
+	_tempBuffer = new uint16[320];
+	assert(_tempBuffer);
+	memset(_tempBuffer, 0, sizeof(uint16) * 320);
+	int linkCnt = 1;
+	for (int i = 1; i < 317; i += 4)
+		_tempBuffer[i] = linkCnt++;
+	clearSprites();
+	_renderer->memsetVRAM(0xDC00, 0, 0x400);
+}
+
+SegaAnimator::~SegaAnimator() {
+	delete[] _sprites;
+}
+
+void SegaAnimator::initSprite(int id, int16 x, int16 y, uint16 nameTbl, uint16 hw) {
+	assert(id < 80);
+	Sprite &s = _sprites[id];
+	s.x = x;
+	s.y = y;
+	s.nameTbl = nameTbl;
+	s.hw = hw;
+	_needUpdate = true;
+}
+
+void SegaAnimator::clearSprites() {
+	for (Sprite *s = _sprites; s != &_sprites[80]; ++s)
+		s->x = 0x4000;
+	_needUpdate = true;
+}
+
+void SegaAnimator::moveMorphSprite(int id, uint16 nameTbl, int16 addX, int16 addY) {
+	assert(id < 80);
+	Sprite &s = _sprites[id];
+	s.x += addX;
+	s.y += addY;
+	s.nameTbl = nameTbl;
+	_needUpdate = true;
+}
+
+void SegaAnimator::moveSprites(int id, uint16 num, int16 addX, int16 addY) {
+	assert(id < 80);
+	Sprite *s = &_sprites[id];
+	while (num--) {
+		s->x += addX;
+		s->y += addY;
+		s++;
+	}
+	_needUpdate = true;
+}
+
+void SegaAnimator::moveSprites2(int id, uint16 num, int16 addX, int16 addY) {
+	assert(id < 80);
+	Sprite *s = &_sprites[id];
+	uint16 sbx = s->x;
+	uint16 sby = s->y;
+	while (num--) {
+		s->x = s->x - sbx + addX;
+		s->y = s->y - sby + addY;
+		s++;
+	}
+	_needUpdate = true;
+}
+
+void SegaAnimator::update() {
+	if (!_needUpdate)
+		return;
+	
+	uint16 *dst = _tempBuffer;
+	for (Sprite *s = _sprites; s != &_sprites[80]; ++s) {
+		if (s->x == 0x4000)
+			continue;
+		*dst++ = (uint16)(s->y + 128);
+		*dst++ = (*dst & 0xFF) | (s->hw << 8);
+		*dst++ = s->nameTbl;
+		*dst++ = (uint16)(s->x + 128);
+	}
+
+	for (dst; dst < &_tempBuffer[320]; dst += 4)
+		*dst = 0;
+
+	_renderer->loadToVRAM(_tempBuffer, 640, 0xDC00);
+	_needUpdate = false;
+}
+
+SegaCDFont::SegaCDFont() : Font(), _data(0), _colorMap(0), _width(0), _height(0) {
+
+}
+
+SegaCDFont::~SegaCDFont() {
+	delete[] _data;
+}
+
+bool SegaCDFont::load(Common::SeekableReadStream &file) {
+	uint32 size = file.size();
+	if (!size)
+		return false;
+
+	delete[] _data;
+
+	_data = new uint8[size];
+	file.read(_data, size);
+	return true;
+}
+
+void SegaCDFont::drawChar(uint16 c, byte *dst, int pitch, int) const {
+
+}
+
+#undef mRenderLineFragment
+
 } // End of namespace Kyra
 
 #endif // ENABLE_EOB
diff --git a/engines/kyra/graphics/screen_eob_segacd.h b/engines/kyra/graphics/screen_eob_segacd.h
new file mode 100644
index 0000000000..bc69674b02
--- /dev/null
+++ b/engines/kyra/graphics/screen_eob_segacd.h
@@ -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.
+ *
+ */
+
+#ifndef KYRA_SCREEN_EOB_SEGACD_H
+#define KYRA_SCREEN_EOB_SEGACD_H
+
+#ifdef ENABLE_EOB
+
+#include "kyra/graphics/screen_eob.h"
+
+namespace Kyra {
+
+#define SEGA_PERFORMANCE		true
+
+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(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 w, int h);
+	// 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, int dataSize, int addr);
+	void loadToVRAM(Common::SeekableReadStreamEndian *in, int 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, int presetPatternID = -1);
+	void writeVSRAMValue(int addr, uint16 value);
+	
+	void render(int destPageNum);
+
+private:
+	void renderPlanePart(int plane, uint8 *dstBuffer, int x1, int y1, int x2, int y2);
+	void renderPlaneTile(uint8 *dst, int destX, uint16 *nameTable, int vScrollTableIndex, int hScrollTableIndex, uint16 pitch, uint16 nameTableSize);
+	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 checkUpdateDirtyRects(int addr, int len);
+	void addDirtyRect(int x, int y, int w, int h);
+	void sendDirtyRectsToScreen();
+	void clearDirtyRects();
+
+	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), nameTable(0) {}
+		int blockX, blockY;
+		uint16 w, h;
+		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 DRChainEntry {
+		DRChainEntry(DRChainEntry *chain, int x, int y, int w, int h) : _next(chain), _rect(x, y, x + w, y + h) {}
+		Common::Rect _rect;
+		DRChainEntry *_next;
+	} *_drChain;
+
+	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;
+	};
+
+	PrioTileRenderObj *_prioChainStart, *_prioChainEnd;
+	uint16 _screenW, _screenH, _blocksW, _blocksH;
+	Screen_EoB *_screen;
+
+	const uint16 *_patternTables[6];
+};
+
+class SegaAnimator {
+public:
+	SegaAnimator(SegaRenderer *renderer);
+	~SegaAnimator();
+
+	void initSprite(int id, int16 x, int16 y, uint16 nameTbl, uint16 hw);
+	void clearSprites();
+	void moveMorphSprite(int id, uint16 nameTbl, int16 addX, int16 addY);
+	void moveSprites(int id, uint16 num, int16 addX, int16 addY);
+	void moveSprites2(int id, uint16 num, int16 addX, int16 addY);
+
+	void update();
+
+private:
+	struct Sprite {
+		int16 x;
+		int16 y;
+		uint16 nameTbl;
+		uint16 hw;
+	};
+
+	uint16 *_tempBuffer;
+	Sprite *_sprites;
+	SegaRenderer *_renderer;
+	bool _needUpdate;
+};
+
+} // End of namespace Kyra
+
+#endif // ENABLE_EOB
+
+#endif
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 2e4cdb2998..4b28b79411 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -2295,7 +2295,7 @@ void GUI_EoB::runCampMenu() {
 					_vm->_configMusic ^= true;
 					_vm->writeSettings();
 					if (_vm->_configMusic)
-						_vm->snd_playSong(_vm->_currentLevel + 1);
+						_vm->snd_playLevelScore();
 					else
 						_vm->snd_playSong(0);
 				} else {
diff --git a/engines/kyra/module.mk b/engines/kyra/module.mk
index 1af999e03b..75428e64e2 100644
--- a/engines/kyra/module.mk
+++ b/engines/kyra/module.mk
@@ -139,7 +139,8 @@ MODULE_OBJS += \
 	sound/sound_towns_darkmoon.o \
 	sound/drivers/audiomaster2.o \
 	sound/drivers/mlalf98.o \
-	sound/drivers/pcspeaker_v1.o
+	sound/drivers/pcspeaker_v1.o \
+	text/text_eob_segacd.o
 endif
 
 # This module can be built as a plugin
diff --git a/engines/kyra/resource/resource_segacd.cpp b/engines/kyra/resource/resource_segacd.cpp
index 89c547e1c0..7b3f8996ce 100644
--- a/engines/kyra/resource/resource_segacd.cpp
+++ b/engines/kyra/resource/resource_segacd.cpp
@@ -34,7 +34,7 @@ SegaCDResource::~SegaCDResource() {
 	unloadContainer();
 }
 
-bool SegaCDResource::loadContainer(const Common::String &filename) {
+bool SegaCDResource::loadContainer(const Common::String &filename, uint32 offset, uint32 size) {
 	unloadContainer();
 
 	_str = _res->createEndianAwareReadStream(filename);
@@ -43,16 +43,23 @@ bool SegaCDResource::loadContainer(const Common::String &filename) {
 		return false;
 	}
 
+	_str->seek(offset, SEEK_SET);
+
 	uint32 first = _str->readUint32();
 	_numResources = first >> 2;
 	_offsetTable = new uint32[_numResources + 1];
-	_offsetTable[0] = first;
+	_offsetTable[0] = offset + first;
+
 	for (int i = 1; i < _numResources; ++i) {
-		_offsetTable[i] = _str->readUint32();
+		_offsetTable[i] = offset + _str->readUint32();
 		if (_offsetTable[i] == 0)
 			_numResources = i;
 	}
-	_offsetTable[_numResources] = _str->size();
+
+	if (size)
+		assert(offset + size <= _str->size());
+
+	_offsetTable[_numResources] = size ? offset + size : _str->size();
 
 	return true;
 }
diff --git a/engines/kyra/resource/resource_segacd.h b/engines/kyra/resource/resource_segacd.h
index e5ca65e0bf..368e229534 100644
--- a/engines/kyra/resource/resource_segacd.h
+++ b/engines/kyra/resource/resource_segacd.h
@@ -39,7 +39,7 @@ public:
 	SegaCDResource(Resource *res);
 	~SegaCDResource();
 
-	bool loadContainer(const Common::String &filename);
+	bool loadContainer(const Common::String &filename, uint32 offset = 0, uint32 size = 0);
 	void unloadContainer();
 
 	Common::SeekableReadStreamEndian *getEndianAwareResourceStream(int resID);
diff --git a/engines/kyra/sequence/seqplayer_eob_segacd.cpp b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
index 7240465711..a1c43aa230 100644
--- a/engines/kyra/sequence/seqplayer_eob_segacd.cpp
+++ b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
@@ -21,28 +21,557 @@
  */
 
 #include "kyra/engine/eob.h"
-#include "kyra/sequence/seqplayer_eob_segacd.h"
+#include "kyra/graphics/screen_eob.h"
+#include "kyra/graphics/screen_eob_segacd.h"
 #include "kyra/resource/resource.h"
 #include "kyra/resource/resource_segacd.h"
+#include "kyra/sequence/seqplayer_eob_segacd.h"
+#include "common/system.h"
 
 namespace Kyra {
 
-SegaSequencePlayer::SegaSequencePlayer(EoBEngine *vm, SegaCDResource *res) : _vm(vm), _res(res) {
+SegaSequencePlayer::SegaSequencePlayer(EoBEngine *vm, Screen_EoB *screen, SegaCDResource *res) : _vm(vm), _screen(screen), _res(res), _tileSets(0), _debugResyncCnt(0), _varUnkX1(0),
+	_var1(false), _waterdeepScene(0), _update2(0), _waitFlag(false), _waterdeepSceneTimer(0), _unkSEQ2(0), _renderer(_screen->sega_getRenderer()), _animator(_screen->sega_getAnimator()) {
+#define SQOPC(x) _opcodes.push_back(new SQOpcode(this, &SegaSequencePlayer::x, #x))
+	SQOPC(s_initDrawObject);
+	SQOPC(s_drawTileSet);
+	SQOPC(s_loadTileDataSingle);
+	SQOPC(s_3);
+	SQOPC(s_4);
+	SQOPC(s_fillRect);
+	SQOPC(s_6);
+	SQOPC(s_7);
+	SQOPC(s_8);
+	SQOPC(s_9_dispText);
+	SQOPC(s_fadeToNeutral);
+	SQOPC(s_fadeToBlack);
+	SQOPC(s_fadeToNeutral2);
+	SQOPC(s_fadeToWhite);
+	SQOPC(s_setPalette);
+	SQOPC(s_vScroll);
+	SQOPC(s_hScroll);
+	SQOPC(s_paletteOps);
+	SQOPC(s_initSprite);
+	SQOPC(s_fillRectWithPattern);
+	SQOPC(s_loadTileDataMult);
+	SQOPC(s_21);
+	SQOPC(s_22);
+	SQOPC(s_initSprite2);
+	SQOPC(s_drawTileSetCustom);
+	SQOPC(s_waitForPaletteFade);
+	SQOPC(s_clearSprites);
+	SQOPC(s_27);
+	SQOPC(s_moveSprites);
+	SQOPC(s_moveMorphSprite);
+	SQOPC(s_unpauseCD);
+	SQOPC(s_enableWaterDeepAnimations);
+	SQOPC(s_32);
+	SQOPC(s_setUpdate2);
+	SQOPC(s_orbEffect);
+	SQOPC(s_stopCD);
+	SQOPC(s_playCD);
+	SQOPC(s_displayText);
+	SQOPC(s_loadCustomPalettes);
+	SQOPC(s_playSoundEffect);
+#undef SQOPC
+
+	_vScrollTimers = new ScrollTimer[2];
+	assert(_vScrollTimers);
+	_hScrollTimers = new ScrollTimer[2];
+	assert(_hScrollTimers);
+	_tileSets = new TileSet[100];
+	assert(_tileSets);
+	memset(_tileSets, 0, 100 * sizeof(TileSet));
+	_drawObjects = new DrawObject[100];
+	assert(_drawObjects);
+	memset(_drawObjects, 0, 100 * sizeof(DrawObject));
+	_tempBuffer = new uint8[65000];
+	assert(_tempBuffer);
+	memset(_tempBuffer, 0, 65000 * sizeof(uint8));
+	_varUnkX1;
+	memset(_varUnkX2, 0, sizeof(_varUnkX2));
+
+	int temp;
+	_wdDsX = _vm->staticres()->loadRawDataBe16(kEoB1IntroWdDsX, temp);
+	_wdDsY = _vm->staticres()->loadRawData(kEoB1IntroWdDsY, temp);
 }
 
 SegaSequencePlayer::~SegaSequencePlayer() {
+	delete[] _drawObjects;
+	delete[] _tileSets;
+	delete[] _vScrollTimers;
+	delete[] _hScrollTimers;
+	delete[] _tempBuffer;
 
+	for (Common::Array<SQOpcode*>::iterator i = _opcodes.begin(); i != _opcodes.end(); ++i)
+		delete (*i);
 }
 
-bool SegaSequencePlayer::play(int seqID) {
-	if (!_res->loadContainer("VISUAL"))
-		return false;
+bool SegaSequencePlayer::play(int id) {
+	//uint8 shadowColor = 0xEE;
+
+	_screen->sega_fadeToBlack(2);
+
+	//gfx37(0);
+	_animator->clearSprites();
+	setVScrollTimers(0, 1, 0, 0, 1, 0);
+	setHScrollTimers(0, 1, 0, 0, 1, 0);
+	_vm->_txt->clearDim(2);
+
+	_renderer->fillRectWithTiles(2, 0, 0, 40, 28, 0xE6C2);
+	_renderer->fillRectWithTiles(0, 0, 0, 64, 42, 0);
+	_renderer->fillRectWithTiles(1, 0, 0, 64, 28, 0);
+	_renderer->fillRectWithTiles(2, 1, (id == 53 || id == 54) ? 22 : 20, 38, 6, 0xE51C, true);
+
+	_debugResyncCnt = 0;
+
+	for (bool runLoop = true; runLoop && !(_vm->shouldQuit() || _vm->skipFlag()); ) {
+		uint32 offset = (id - 1) * 0x3C000;
+		if (id >= 54)
+			offset -= 0x8000;
+		if (id >= 55)
+			offset -= 0x18000;
+
+		uint32 size = (id == 53) ? 0x34000 : ((id == 54) ? 0x24000 : 0x3C000);
+
+		if (!_res->loadContainer("VISUAL", offset, size))
+			return false;
+
+		Common::SeekableReadStreamEndian *in = _res->getEndianAwareResourceStream(0);
+		if (!in)
+			return false;
+		_screen->sega_loadCustomPaletteData(in);
+		delete in;
+
+		_screen->sega_selectPalette(0, 31, false);
+		_screen->sega_selectPalette(1, 32, false);
+		_screen->sega_selectPalette(3, 30, false);
+
+		in = _res->getEndianAwareResourceStream(2);
+		if (!in)
+			return false;
+		uint32 len = in->size();
+		uint8 *tileData = new uint8[len];
+		in->read(tileData, len);
+		delete in;
+
+		in = _res->getEndianAwareResourceStream(1);
+		if (!in)
+			return false;
+		memset(_tileSets, 0, 100 * sizeof(TileSet));
+		for (TileSet *t = _tileSets; !in->eos(); ++t) {
+			uint32 off = in->readUint32();
+			if ((off & 0xFFFF) == 0xFFFF)
+				break;
+			t->data = (const uint16*)(tileData + off);
+			t->width = in->readUint16();
+			t->height = in->readUint16();
+		}
+		delete in;
+
+		in = _res->getEndianAwareResourceStream(3);
+		if (!in)
+			return false;
+		len = in->size();
+		uint8 *frames = new uint8[len];
+		in->read(frames, len);
+		delete in;
+
+		if (id == 53)
+			_vm->delay(2000);
+		else if (id == 55 || id == 56)
+			_vm->snd_playSong(2);
+
+		run(frames);
+
+		delete[] frames;
+		delete[] tileData;
+		if (++id != 54)
+			runLoop = false;
+	}
 
-	Common::SeekableReadStreamEndian *data = _res->getEndianAwareResourceStream(seqID);
-	if (!data)
-		return false;
+	debugC(3, kDebugLevelSequence, "Total millis out of sync: %d", _debugResyncCnt);
 
 	return true;
 }
 
+void SegaSequencePlayer::setWaitFlag(bool enable) {
+	_waitFlag = enable;
+}
+
+void SegaSequencePlayer::run(const uint8 *data) {
+	_var1 = _waterdeepScene = _update2 = false;
+	uint32 frameCounter = 0;
+	uint32 nextFrame = 0;
+
+	for (bool runLoop = true; runLoop && !(_vm->shouldQuit() || _vm->skipFlag()); ) {
+		uint16 frameSize = READ_BE_UINT16(data);
+		if (!frameSize)
+			return;
+
+		uint32 frameStart = _vm->_system->getMillis();
+		uint16 timeStamp = READ_BE_UINT16(data + 2);
+		uint32 lastFrame = nextFrame;
+		nextFrame = timeStamp * 16;
+		if (nextFrame < lastFrame)
+			frameCounter = 0;
+
+		if (frameCounter >= nextFrame) {
+			debugC(5, kDebugLevelSequence, "SeqPlayer: Timestamp %08d", timeStamp);
+			for (uint16 timeStamp2 = timeStamp; timeStamp2 == timeStamp; ) {
+				uint16 op = READ_BE_UINT16(data + 4);
+				_opcodes[op]->run(data + 6);
+
+				frameSize = READ_BE_UINT16(data);
+				data += (frameSize & ~1);
+
+				timeStamp2 = READ_BE_UINT16(data + 2);
+			}
+		}
+
+		if (_waterdeepScene)
+			animateWaterdeepScene();
+
+		if (_update2)
+			update2();
+
+		updateScrollTimers();
+		_animator->update();
+		_renderer->render(0);
+		_screen->sega_updatePaletteFaders(-1);
+		_screen->updateScreen();
+
+		uint32 now = _vm->_system->getMillis();
+		int diff = now - (frameStart + 16);
+		if (diff < 0)
+			_vm->delay(frameStart + 16 - now);
+		else if (diff) {
+			frameCounter += diff;
+			// This will be triggered with higher values whenever there is a palette fading and the code waits for it
+			// to finish (the code will wait for s_fadeToBlack() and s_fadeToNeutral() but not for s_paletteOps(), unless
+			// followed by s_waitForPaletteFade()). This doesn't cause issues, since it is the intended original behavior,
+			// but it needs to be compensated in the timing. The original does that automatically, since the frameCounter
+			// comes from the vblank interrupt vector. I don't use _system->getMillis directly for the frame timer, since
+			// it will be messed up when activating the GMM during sequence playback.
+			debugC(4, kDebugLevelSequence, "    Out of Sync. Catching up millis: %d", diff);
+			_debugResyncCnt += diff;
+		}
+		frameCounter += 16;
+
+		/*if (_finFlag & 0x80) {
+			if (_waitFlag || _curVis == 55 || _curVis == 56)
+				return true;
+		}*/
+	}
+}
+
+void SegaSequencePlayer::setVScrollTimers(uint16 destA, int incrA, int delayA, uint16 destB, int incrB, int delayB) {
+	_vScrollTimers[0]._offsDest = destA;
+	_vScrollTimers[0]._incr = incrA;
+	_vScrollTimers[0]._timer = _vScrollTimers[0]._delay = delayA;
+	_vScrollTimers[1]._offsDest = destB;
+	_vScrollTimers[1]._incr = incrB;
+	_vScrollTimers[1]._timer = _vScrollTimers[1]._delay = delayB;
+}
+
+void SegaSequencePlayer::setHScrollTimers(uint16 destA, int incrA, int delayA, uint16 destB, int incrB, int delayB) {
+	_hScrollTimers[0]._offsDest = destA;
+	_hScrollTimers[0]._incr = incrA;
+	_hScrollTimers[0]._timer = _hScrollTimers[0]._delay = delayA;
+	_hScrollTimers[1]._offsDest = destB;
+	_hScrollTimers[1]._incr = incrB;
+	_hScrollTimers[1]._timer = _hScrollTimers[1]._delay = delayB;
+}
+
+void SegaSequencePlayer::updateScrollTimers() {
+	for (int i = 0; i <= 4; ++i) {
+		ScrollTimer &t = i < 2 ? _vScrollTimers[i] : _hScrollTimers[i - 2];
+		if (t._delay == 0 && t._offsCur != t._offsDest)
+			t._offsCur = t._offsDest;
+		if (t._offsCur == t._offsDest)
+			continue;
+		if (--t._timer)
+			continue;
+
+		t._offsCur += t._incr;
+		t._timer = t._delay;
+	}
+
+	_renderer->writeVSRAMValue(0, _vScrollTimers[0]._offsCur);
+	_renderer->writeVSRAMValue(2, _vScrollTimers[1]._offsCur);
+	uint16 hscr[2] = { _hScrollTimers[0]._offsCur, _hScrollTimers[1]._offsCur };
+	_renderer->loadToVRAM(hscr, 4, 0xD800);
+}
+
+void SegaSequencePlayer::animateWaterdeepScene() {
+	static uint8 spr[31] = {
+		0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03,	0x03, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06,
+		0x07, 0x07, 0x07, 0x08, 0x08, 0x09, 0x09, 0x0a,	0x0a, 0x0b, 0x0b, 0x0c, 0x0c, 0x0d, 0x0d
+	};
+
+	if (--_waterdeepSceneTimer > 0)
+		return;
+	_waterdeepSceneTimer = 5;
+
+	for (int i = 0; i < 5; ++i) {
+		int rnd = _vm->_rnd.getRandomNumber(30);
+		DrawObject *d = &_drawObjects[10 + rnd];
+		_animator->initSprite(spr[rnd] + 3, _wdDsX[spr[rnd]] - 80, _wdDsY[spr[rnd]] + 32, d->nTblVal, d->addr);
+	}
+}
+
+void SegaSequencePlayer::update2() {
+
+}
+
+void SegaSequencePlayer::update3() {
+
+}
+
+#define ARG(x) READ_BE_UINT16(pos + x)
+#define S_ARG(x) (int16)ARG(x)
+
+void SegaSequencePlayer::s_initDrawObject(const uint8 *pos) {
+	int idx = ARG(0);
+	DrawObject *w = &_drawObjects[idx];
+	TileSet *t = &_tileSets[idx];
+	w->agg = ARG(2);
+	w->tileData = t->data;
+	w->width = t->width;
+	w->height = t->height;
+	w->nTblVal = ARG(4) == 0xFFFF ? _drawObjects[idx - 1].width * _drawObjects[idx - 1].height + _drawObjects[idx - 1].nTblVal : (ARG(4) == 0xFFFE ? _drawObjects[idx - 1].nTblVal : ARG(4));
+	w->x = ARG(6);
+	w->y = ARG(8);
+	w->addr = ARG(10);
+}
+
+void SegaSequencePlayer::s_drawTileSet(const uint8 *pos) {
+	DrawObject *w = &_drawObjects[ARG(0)];
+	_renderer->fillRectWithTiles(w->addr, w->x, w->y, w->width, w->height, w->nTblVal, true);
+}
+
+void SegaSequencePlayer::s_loadTileDataSingle(const uint8 *pos) {
+	DrawObject *w = &_drawObjects[ARG(0)];
+	_renderer->loadToVRAM(w->tileData, (w->width * w-> height) << 5, (w->nTblVal & 0x7FF) << 5);
+}
+
+void SegaSequencePlayer::s_3(const uint8 *pos) {
+
+}
+
+void SegaSequencePlayer::s_4(const uint8 *pos) {
+
+}
+
+void SegaSequencePlayer::s_fillRect(const uint8 *pos) {
+	_renderer->fillRectWithTiles(ARG(8), ARG(0), ARG(2), ARG(4), ARG(6), ARG(10));
+}
+
+void SegaSequencePlayer::s_6(const uint8 *pos) {
+
+}
+
+void SegaSequencePlayer::s_7(const uint8 *pos) {
+
+}
+
+void SegaSequencePlayer::s_8(const uint8 *pos) {
+
+}
+
+void SegaSequencePlayer::s_9_dispText(const uint8 *pos) {
+
+}
+
+void SegaSequencePlayer::s_fadeToNeutral(const uint8 *pos) {
+	_screen->sega_fadeToNeutral(ARG(0));
+}
+
+void SegaSequencePlayer::s_fadeToBlack(const uint8 *pos) {
+	_screen->sega_fadeToBlack(ARG(0));
+}
+
+void SegaSequencePlayer::s_fadeToNeutral2(const uint8 *pos) {
+	_screen->sega_fadeToNeutral(ARG(0));
+}
+
+void SegaSequencePlayer::s_fadeToWhite(const uint8 *pos) {
+	_screen->sega_fadeToWhite(ARG(0));
+}
+
+void SegaSequencePlayer::s_setPalette(const uint8 *pos) {
+	_screen->sega_selectPalette(ARG(2) + 31, ARG(0), false);
+}
+
+void SegaSequencePlayer::s_vScroll(const uint8 *pos) {
+	setVScrollTimers(ARG(0), S_ARG(2), ARG(4), ARG(6), S_ARG(8), ARG(10));
+}
+
+void SegaSequencePlayer::s_hScroll(const uint8 *pos) {
+	setHScrollTimers(ARG(0), S_ARG(2), ARG(4), ARG(6), S_ARG(8), ARG(10));
+}
+
+void SegaSequencePlayer::s_paletteOps(const uint8 *pos) {
+	_screen->sega_paletteOps(S_ARG(0), S_ARG(2), S_ARG(4));
+}
+
+void SegaSequencePlayer::s_initSprite(const uint8 *pos) {
+	DrawObject *d = &_drawObjects[ARG(2)];
+	_animator->initSprite(ARG(0), S_ARG(4), S_ARG(6), d->nTblVal, d->addr);
+}
+
+void SegaSequencePlayer::s_fillRectWithPattern(const uint8 *pos) {
+	_renderer->fillRectWithTiles(ARG(8), ARG(0), ARG(2), ARG(4), ARG(6), ARG(10), false, false, ARG(12));
+}
+
+void SegaSequencePlayer::s_loadTileDataMult(const uint8 *pos) {
+	for (DrawObject *w = &_drawObjects[ARG(0)]; w != &_drawObjects[ARG(0) + ARG(2)]; ++w)
+		_renderer->loadToVRAM(w->tileData, (w->width * w->height) << 5, (w->nTblVal & 0x7FF) << 5);
+}
+
+void SegaSequencePlayer::s_21(const uint8 *pos) {
+
+}
+
+void SegaSequencePlayer::s_22(const uint8 *pos) {
+
+}
+
+void SegaSequencePlayer::s_initSprite2(const uint8 *pos) {
+	_animator->initSprite(ARG(0), S_ARG(4), S_ARG(6), ARG(2), ARG(8));
+}
+
+void SegaSequencePlayer::s_drawTileSetCustom(const uint8 *pos) {
+	DrawObject *w = &_drawObjects[ARG(0)];
+	_renderer->fillRectWithTiles(w->addr, ARG(2), ARG(4), w->width, w->height, w->nTblVal, true);
+}
+
+void SegaSequencePlayer::s_waitForPaletteFade(const uint8*) {
+	_screen->sega_fadePalette(0, 0, -1, true, true);
+}
+
+void SegaSequencePlayer::s_clearSprites(const uint8*) {
+	_animator->clearSprites();
+}
+
+void SegaSequencePlayer::s_27(const uint8 *pos) {
+
+}
+
+void SegaSequencePlayer::s_moveSprites(const uint8 *pos) {
+	_animator->moveSprites(ARG(0), ARG(2), S_ARG(4), S_ARG(6));
+}
+
+void SegaSequencePlayer::s_moveMorphSprite(const uint8 *pos) {
+	_animator->moveMorphSprite(ARG(0), ARG(2), S_ARG(4), S_ARG(6));
+}
+
+void SegaSequencePlayer::s_unpauseCD(const uint8 *pos) {
+	// Do nothing. We don't support this in our AudioCD API. The original will use s_playCD() to seek to a track,
+	// wait for the seek to finish and then pause. It then used this opcode to actually start the playback. Since
+	// s_playCD() and s_unpauseCD() always seem to be called on the same frame we can just start the playback
+	// normally. The only difference to the playback of songs is that there is no looping.
+}
+
+void SegaSequencePlayer::s_enableWaterDeepAnimations(const uint8 *pos) {
+	_waterdeepScene = ARG(0);
+	_waterdeepSceneTimer = 0;
+}
+
+void SegaSequencePlayer::s_32(const uint8 *pos) {
+	if (ARG(0) == 100) {
+		_varUnkX1 = ARG(2);
+	} else {
+		assert(ARG(0) < 6);
+		_varUnkX2[ARG(0) * 2] = ARG(2);
+		_varUnkX2[ARG(0) * 2 + 1] = ARG(4);
+	}
+}
+
+void SegaSequencePlayer::s_setUpdate2(const uint8 *pos) {
+	_update2 = ARG(0);
+	_unkSEQ2 = 0;
+	if (_update2)
+		update3();
+}
+
+void SegaSequencePlayer::s_orbEffect(const uint8*) {
+	_renderer->memsetVRAM(0x2AA0, 0, 22528);
+	DrawObject *d = &_drawObjects[16];
+	memset(_tempBuffer, 0, 22528);
+	memcpy(_tempBuffer + 128, d->tileData, (d->width * d->height) << 5);
+	_renderer->fillRectWithTiles(0, 4, 0, 32, 22, 0x2155, true, true);
+
+	memset(&_tempBuffer[0xF600], 0, 512 * sizeof(uint8));
+	uint8 *dst2 = &_tempBuffer[0xF600 + (7 << 4) + 6];
+	uint16 t = 1;
+	for (int h = 0; h < 9; ++h) {
+		uint8 *dst = dst2;
+		for (int w = 0; w < 10; ++w) {
+			*dst++ = t & 0xFF;
+			*dst++ = t++ >> 4;;
+		}
+		dst2 += 32;
+	}
+	int z = 512;
+	for (int i = 0; i < 90; ++i) {
+		uint16 *dst = (uint16*)(&_tempBuffer[0x6A00]);
+		uint16 a = 0x58000 - z * 128;
+		uint16 b = 0x59000 - z * 88;
+		for (int ii = 0; ii < 90; ++ii) {
+			*dst++ = a >> 8;
+			*dst++ = b >> 8;
+			*dst++ = z;
+			*dst++ = 0;
+			b += z;
+		}
+		_renderer->loadToVRAM(&_tempBuffer[0x6A00], 22528, 0x2AA0);
+		z += 16;
+	}
+}
+
+void SegaSequencePlayer::s_stopCD(const uint8*) {
+	_vm->snd_stopSound();
+}
+
+void SegaSequencePlayer::s_playCD(const uint8 *pos) {
+	int track = _cdaTracks[ARG(0)];
+	if (track)
+		_vm->snd_playSong(track, false);
+
+	if (_waitFlag) {
+		while (!(_vm->shouldQuit() || _vm->skipFlag()))
+			_vm->delay(20);
+	}
+}
+
+void SegaSequencePlayer::s_displayText(const uint8 *pos) {
+
+}
+
+void SegaSequencePlayer::s_loadCustomPalettes(const uint8 *pos) {
+	Common::SeekableReadStreamEndian *in = _res->getEndianAwareResourceStream(0);
+	in->seek(ARG(0) << 5);
+	_screen->sega_loadCustomPaletteData(in);
+	delete in;
+}
+
+void SegaSequencePlayer::s_playSoundEffect(const uint8 *pos) {
+	_vm->snd_playSoundEffect(ARG(0));
+}
+
+#undef S_ARG
+#undef ARG
+
+const uint8 SegaSequencePlayer::_cdaTracks[60] = {
+	0x00, 0x0d, 0x0f, 0x11, 0x12, 0x13, 0x14, 0x15,
+	0x00, 0x1d, 0x1e, 0x00, 0x0e, 0x16, 0x37, 0x38,
+	0x22, 0x00, 0x23, 0x24, 0x25, 0x1c, 0x18, 0x10,
+	0x1f, 0x17, 0x00, 0x1b, 0x21, 0x00, 0x00, 0x00,
+	0x30, 0x31, 0x26, 0x27, 0x28, 0x29, 0x35, 0x36,
+	0x33, 0x34, 0x2c, 0x2b, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00
+};
+
 } // End of namespace Kyra
diff --git a/engines/kyra/sequence/seqplayer_eob_segacd.h b/engines/kyra/sequence/seqplayer_eob_segacd.h
index 319bf4ef78..4c4770dc4c 100644
--- a/engines/kyra/sequence/seqplayer_eob_segacd.h
+++ b/engines/kyra/sequence/seqplayer_eob_segacd.h
@@ -30,17 +30,141 @@ namespace Common {
 
 namespace Kyra {
 class EoBEngine;
+class Screen_EoB;
+class SegaRenderer;
 class SegaCDResource;
 class SegaSequencePlayer {
 public:
-	SegaSequencePlayer(EoBEngine *vm, SegaCDResource *res);
+	SegaSequencePlayer(EoBEngine *vm, Screen_EoB *screen, SegaCDResource *res);
 	~SegaSequencePlayer();
 
-	bool play(int seqID);
+	bool play(int id);
+	void setWaitFlag(bool enable);
+
+private:
+	void run(const uint8 *data);
+	void setVScrollTimers(uint16 destA, int incrA, int delayA, uint16 destB, int incrB, int delayB);
+	void setHScrollTimers(uint16 destA, int incrA, int delayA, uint16 destB, int incrB, int delayB);
+	void updateScrollTimers();
+	void animateWaterdeepScene();
+	void update2();
+	void update3();
+
+	struct TileSet {
+		const uint16 *data;
+		uint16 width;
+		uint16 height;
+	};
+
+	TileSet *_tileSets;
+
+	struct ScrollTimer {
+		ScrollTimer() : _offsCur(0), _offsDest(0), _incr(0), _delay(0), _timer(0) {}
+		uint16 _offsCur;
+		uint16 _offsDest;
+		int16 _incr;
+		int16 _delay;
+		int16 _timer;
+	};
+
+	ScrollTimer *_vScrollTimers;
+	ScrollTimer *_hScrollTimers;
+
+	struct DrawObject {
+		uint16 agg;
+		const uint16 *tileData;
+		uint16 width;
+		uint16 height;
+		uint16 nTblVal;
+		uint16 x;
+		uint16 y;
+		uint16 addr;
+	};
+
+	bool _var1;
+	uint16 _waterdeepScene;
+	uint16 _update2;
+	uint16 _varUnkX1;
+	uint16 _varUnkX2[12];
+
+	bool _waitFlag;
+	int _waterdeepSceneTimer, _unkSEQ2;
+	uint8 *_tempBuffer;
+
+	uint32 _debugResyncCnt;
+
+	DrawObject *_drawObjects;
 
-protected:
 	EoBEngine *_vm;
+	Screen_EoB *_screen;
+	SegaRenderer *_renderer;
+	SegaAnimator *_animator;
 	SegaCDResource *_res;
+
+	const uint16 *_wdDsX;
+	const uint8 *_wdDsY;
+
+private:
+	class SQOpcode : public Common::Functor1Mem<const uint8*, void, SegaSequencePlayer> {
+	public:
+		typedef Common::Functor1Mem<const uint8*, void, SegaSequencePlayer> SQFunc;
+		SQOpcode(SegaSequencePlayer *sq, const typename SQFunc::FuncType &func, const char *dbgName) : SQFunc(sq, func), _msg(Common::String::format("    %s()", dbgName)) {}
+		~SQOpcode() override {}
+		void run(const uint8 *arg) {
+			assert(arg);
+			debugC(7, kDebugLevelSequence, "%s", _msg.c_str());
+			if (SQFunc::isValid())
+				SQFunc::operator()(arg);
+		}
+	private:
+		Common::String _msg;
+	};
+
+	Common::Array<SQOpcode*> _opcodes;
+
+	void s_initDrawObject(const uint8 *pos);
+	void s_drawTileSet(const uint8 *pos);
+	void s_loadTileDataSingle(const uint8 *pos);
+	void s_3(const uint8 *pos);
+	void s_4(const uint8 *pos);
+	void s_fillRect(const uint8 *pos);
+	void s_6(const uint8 *pos);
+	void s_7(const uint8 *pos);
+	void s_8(const uint8 *pos);
+	void s_9_dispText(const uint8 *pos);
+	void s_fadeToNeutral(const uint8 *pos);
+	void s_fadeToBlack(const uint8 *pos);
+	void s_fadeToNeutral2(const uint8 *pos);
+	void s_fadeToWhite(const uint8 *pos);
+	void s_setPalette(const uint8 *pos);
+	void s_vScroll(const uint8 *pos);
+	void s_hScroll(const uint8 *pos);
+	void s_paletteOps(const uint8 *pos);
+	void s_initSprite(const uint8 *pos);
+	void s_fillRectWithPattern(const uint8 *pos);
+	void s_loadTileDataMult(const uint8 *pos);
+	void s_21(const uint8 *pos);
+	void s_22(const uint8 *pos);
+	void s_initSprite2(const uint8 *pos);
+	void s_drawTileSetCustom(const uint8 *pos);
+	void s_waitForPaletteFade(const uint8*);
+	void s_clearSprites(const uint8*);
+	void s_27(const uint8 *pos);
+	void s_moveSprites(const uint8 *pos);
+	void s_moveMorphSprite(const uint8 *pos);
+	void s_unpauseCD(const uint8 *pos);
+	void s_enableWaterDeepAnimations(const uint8 *pos);
+	void s_32(const uint8 *pos);
+	void s_setUpdate2(const uint8 *pos);
+	void s_orbEffect(const uint8*);
+	void s_stopCD(const uint8*);
+	void s_playCD(const uint8 *pos);
+	void s_displayText(const uint8 *pos);
+	void s_loadCustomPalettes(const uint8 *pos);
+	void s_playSoundEffect(const uint8 *pos);
+
+private:
+	static const uint8 _cdaTracks[60];
 };
 
 } // End of namespace Kyra
diff --git a/engines/kyra/sequence/sequences_eob.cpp b/engines/kyra/sequence/sequences_eob.cpp
index 07e38c49b9..a3724f3a9e 100644
--- a/engines/kyra/sequence/sequences_eob.cpp
+++ b/engines/kyra/sequence/sequences_eob.cpp
@@ -24,6 +24,7 @@
 
 #include "kyra/engine/eob.h"
 #include "kyra/graphics/screen_eob.h"
+#include "kyra/graphics/screen_eob_segacd.h"
 #include "kyra/resource/resource.h"
 #include "kyra/resource/resource_segacd.h"
 #include "kyra/sequence/seqplayer_eob_segacd.h"
@@ -2245,10 +2246,10 @@ int EoBEngine::mainMenuLoop() {
 
 void EoBEngine::seq_playIntro(int part) {
 	if (_flags.platform == Common::kPlatformSegaCD) {
-		if (part == kOnlyCredits)
+		if (part != kOnlyCredits)
 			seq_segaOpeningCredits();
 		else
-			_seqPlayer->play(53, 53);
+			seq_segaPlaySequence(53, 53);
 	} else {
 		EoBIntroPlayer(this, _screen).start((EoBIntroPlayer::IntroPart)part);
 	}
@@ -2370,13 +2371,13 @@ void EoBEngine::seq_segaOpeningCredits() {
 
 	_screen->sega_getRenderer()->setPitch(128);
 	_screen->sega_getRenderer()->setPlaneTableLocation(SegaRenderer::kPlaneA, 0xE000);
-	_screen->sega_getRenderer()->setupPlaneAB(SegaRenderer::kPlaneA, 1024, 256);
+	_screen->sega_getRenderer()->setupPlaneAB(1024, 256);
 	_screen->sega_getRenderer()->setHScrollMode(SegaRenderer::kHScroll1PixelRows);
 	
-	_screen->sega_getRenderer()->fillRectWithTiles(0xC000, 0, 0, 40, 28, 0);
-	_screen->sega_getRenderer()->fillRectWithTiles(0xE000, 0, 0, 128, 28, 1);
-	_screen->sega_getRenderer()->fillRectWithTiles(0xE000, 0, 0, 40, 28, 1, true);
-	_screen->sega_selectPalette(7, 3, 0, false);
+	_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 40, 28, 0);
+	_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 128, 28, 1);
+	_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 28, 1, true);
+	_screen->sega_selectPalette(7, 3, false);
 
 	updateScrollState(scrollTable, 320);
 	_screen->sega_getRenderer()->loadToVRAM(scrollTable, 0x400, 0xD800);
@@ -2397,7 +2398,7 @@ void EoBEngine::seq_segaOpeningCredits() {
 	for (int i = 0; i < 8 && !(shouldQuit() || skipFlag()); ++i) {
 		updateScrollState(scrollTable, 320);
 		_screen->sega_getRenderer()->loadToVRAM(scrollTable, 0x400, 0xD800);
-		_screen->sega_selectPalette(i == 3 ? 59 : 50, 0, 0, true);
+		_screen->sega_selectPalette(i == 3 ? 59 : 50, 0, true);
 
 		in = _sres->getEndianAwareResourceStream(i);
 		_screen->sega_getRenderer()->loadToVRAM(in, 32, true);
@@ -2422,7 +2423,7 @@ void EoBEngine::seq_segaOpeningCredits() {
 		delay(3000);
 
 		if (i == 7)
-			_screen->sega_getRenderer()->fillRectWithTiles(0xE000, 40, 0, 88, 28, 0, false);
+			_screen->sega_getRenderer()->fillRectWithTiles(1, 40, 0, 88, 28, 0, false);
 
 		mod = -1;
 		for (int ii = 0; ii <= 3240 && !(shouldQuit() || skipFlag()); ii += mod) {
@@ -2440,7 +2441,7 @@ void EoBEngine::seq_segaOpeningCredits() {
 
 	_screen->sega_fadeToBlack(0);
 	_screen->sega_getRenderer()->setPlaneTableLocation(SegaRenderer::kPlaneA, 0xC000);
-	_screen->sega_getRenderer()->setupPlaneAB(SegaRenderer::kPlaneA, 512, 512);
+	_screen->sega_getRenderer()->setupPlaneAB(512, 512);
 	_screen->sega_getRenderer()->setHScrollMode(SegaRenderer::kHScrollFullScreen);
 	_screen->sega_getRenderer()->memsetVRAM(0xD800, 0, 0x400);
 	_screen->sega_getRenderer()->setPitch(64);
@@ -2454,10 +2455,10 @@ void EoBEngine::seq_segaOpeningCredits() {
 
 	for (int y = 0; y < 28; y += 4) {
 		for (int x = 0; x < 40; x += 4)
-			_screen->sega_getRenderer()->fillRectWithTiles(0xC000, x, y, 8, 7, 0x461, true);
+			_screen->sega_getRenderer()->fillRectWithTiles(0, x, y, 8, 7, 0x461, true);
 	}
-	_screen->sega_getRenderer()->fillRectWithTiles(0xE000, 0, 0, 40, 28, 1, true);
-	_screen->sega_getRenderer()->fillRectWithTiles(0xC000, 0, 0, 40, 28, 0);
+	_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 28, 1, true);
+	_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 40, 28, 0);
 	_screen->sega_getRenderer()->render(0);
 	_screen->sega_fadeToNeutral(3);
 
@@ -2468,17 +2469,46 @@ void EoBEngine::seq_segaOpeningCredits() {
 	_allowSkip = false;
 	resetSkipFlag();
 
-	_screen->sega_getRenderer()->fillRectWithTiles(0xE000, 0, 19, 40, 9, 1);
+	_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 19, 40, 9, 1);
 	_screen->sega_getRenderer()->render(0);
 	_screen->updateScreen();
 }
 
-bool EoBEngine::seq_segaPlaySequence(int id) {
+void EoBEngine::seq_segaSetupSequence(int id) {
+	if (_flags.platform != Common::kPlatformSegaCD || id == -1)
+		return;
+	
+	_screen->sega_fadeToBlack(1);
+	//gfxM45(0);
+	for (int i = 0; i < 6; i++) {
+		_characters[i].damageTaken = 0;
+		_characters[i].slotStatus[0] = _characters[i].slotStatus[1] = 0;
+		gui_drawCharPortraitWithStats(i);
+	}
+
+	_screen->sega_getRenderer()->setupWindowPlane(0, (id == 53 || id == 54) ? 23 : 18, SegaRenderer::kWinToRight, SegaRenderer::kWinToBottom);
+	_screen->sega_getRenderer()->memsetVRAM(0xD840, 0xEE, 512);
+	_screen->sega_getAnimator()->clearSprites();
+	_screen->setScreenDim(2);
+
+}
+
+bool EoBEngine::seq_segaPlaySequence(int sequenceId, int setupID) {
 	if (_flags.platform != Common::kPlatformSegaCD)
 		return true;
-	return _seqPlayer->play(id);
+
+	_allowSkip = true;
+	resetSkipFlag();
+
+	seq_segaSetupSequence(setupID);
+
+	_allowSkip = false;
+	resetSkipFlag();
+
+	return _seqPlayer->play(sequenceId);
 }
 
+#undef updateScrollState
 #undef displaySubtitle
 #undef printSub
 
diff --git a/engines/kyra/sound/sound_segacd_eob.cpp b/engines/kyra/sound/sound_segacd_eob.cpp
index afb81335fd..52c660cb42 100644
--- a/engines/kyra/sound/sound_segacd_eob.cpp
+++ b/engines/kyra/sound/sound_segacd_eob.cpp
@@ -73,14 +73,14 @@ void SoundSegaCD_EoB::loadSfxFile(Common::String file) {
 }
 
 void SoundSegaCD_EoB::playTrack(uint8 track) {
-	static const uint8 levelCDATracks[13] = {
-		7, 7, 7, 7, 6, 6, 6, 4, 4, 4, 5, 5, 10
-	};
-
 	if (!_musicEnabled || !_ready)
 		return;
-	
-	g_system->getAudioCDManager()->play(track, 1, 0, 0);
+
+	int loop = track >> 7;
+	track &= 0x7F;
+
+	g_system->getAudioCDManager()->play(track - 1, loop, 0, 0);
+	g_system->getAudioCDManager()->update();
 }
 
 void SoundSegaCD_EoB::haltTrack() {
diff --git a/engines/kyra/text/text_eob_segacd.cpp b/engines/kyra/text/text_eob_segacd.cpp
new file mode 100644
index 0000000000..ad872b1049
--- /dev/null
+++ b/engines/kyra/text/text_eob_segacd.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.
+ *
+ */
+
+#if defined(ENABLE_EOB) || defined(ENABLE_LOL)
+
+#include "kyra/engine/eob.h"
+#include "kyra/graphics/screen_eob.h"
+#include "kyra/graphics/screen_eob_segacd.h"
+#include "kyra/text/text_eob_segacd.h"
+
+namespace Kyra {
+
+TextDisplayer_SegaCD::TextDisplayer_SegaCD(EoBEngine *engine, Screen_EoB *scr) : TextDisplayer_rpg(engine, scr), _renderer(scr->sega_getRenderer()), _curDim(0) {
+	assert(_renderer);
+}
+
+TextDisplayer_SegaCD::~TextDisplayer_SegaCD() {
+
+}
+
+int TextDisplayer_SegaCD::clearDim(int dim) {
+	int res = _curDim;
+	_curDim = dim;
+	const ScreenDim *s = &_dimTable[dim];
+	uint32 size = (s->w * s->h) >> 1;
+	uint8 *buf = new uint8[size];
+	memset(buf, s->unkA, size);
+	_renderer->loadToVRAM(buf, size, (s->unkC & 0x7FF) << 5);
+	delete[] buf;
+	return res;
+}
+
+const ScreenDim TextDisplayer_SegaCD::_dimTable[3] = {
+	{ 0x0001, 0x0017, 0x0118, 0x0018, 0xff, 0x44, 0x2597, 0x0000 },
+	{ 0x0012, 0x0009, 0x00a0, 0x0080, 0xff, 0x00, 0x0153, 0x0028 },
+	{ 0x0001, 0x0014, 0x0130, 0x0030, 0xff, 0xee, 0xe51c, 0x0000 }
+};
+
+} // End of namespace Kyra
+
+#endif // (ENABLE_EOB || ENABLE_LOL)
diff --git a/engines/kyra/text/text_eob_segacd.h b/engines/kyra/text/text_eob_segacd.h
new file mode 100644
index 0000000000..d140b01a18
--- /dev/null
+++ b/engines/kyra/text/text_eob_segacd.h
@@ -0,0 +1,69 @@
+/* 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.
+ *
+ */
+
+#if defined(ENABLE_EOB) || defined(ENABLE_LOL)
+
+#ifndef KYRA_TEXT_EOB_SEGACD_H
+#define KYRA_TEXT_EOB_SEGACD_H
+
+#include "kyra/text/text_rpg.h"
+
+namespace Kyra {
+
+class EoBEngine;
+class Screen_EoB;
+
+class TextDisplayer_SegaCD : public TextDisplayer_rpg {
+public:
+	TextDisplayer_SegaCD(EoBEngine *engine, Screen_EoB *scr);
+	virtual ~TextDisplayer_SegaCD();
+
+	/*void setupField(int dim, bool mode);
+
+	void printDialogueText(int stringId, const char *pageBreakString);
+	void printDialogueText(const char *str, bool wait = false);
+	void printMessage(const char *str, int textColor = -1, ...);*/
+
+	int clearDim(int dim) override;
+	//void clearCurDim() override;
+	
+	/*void resetDimTextPositions(int dim);
+	void resetPageBreakString();
+	void setPageBreakFlag();
+	void removePageBreakFlag();
+	*/
+	//void allowPageBreak(bool mode) { _allowPageBreak = mode; }
+	//void setWaitButtonMode(int mode) { _waitButtonMode = mode; }
+	//int lineCount() const { return _lineCount; }
+
+private:
+	SegaRenderer *_renderer;
+	int _curDim;
+
+	static const ScreenDim _dimTable[3];
+};
+
+} // End of namespace Kyra
+
+#endif
+
+#endif // ENABLE_EOB || ENABLE_LOL
diff --git a/engines/kyra/text/text_rpg.h b/engines/kyra/text/text_rpg.h
index ed7940d7f7..1fe66f75b7 100644
--- a/engines/kyra/text/text_rpg.h
+++ b/engines/kyra/text/text_rpg.h
@@ -43,7 +43,7 @@ public:
 	void printDialogueText(const char *str, bool wait = false);
 	void printMessage(const char *str, int textColor = -1, ...);
 
-	int clearDim(int dim);
+	virtual int clearDim(int dim);
 	void clearCurDim();
 
 	void resetDimTextPositions(int dim);


Commit: 71c8007be62c66ab075c6a37684e285f66a7d430
    https://github.com/scummvm/scummvm/commit/71c8007be62c66ab075c6a37684e285f66a7d430
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:09+02:00

Commit Message:
KYRA: (EOB) - move some graphics code between files

Changed paths:
    engines/kyra/POTFILES
    engines/kyra/graphics/screen_eob.cpp
    engines/kyra/graphics/screen_eob_amiga.cpp
    engines/kyra/graphics/screen_eob_pc98.cpp
    engines/kyra/graphics/screen_eob_towns.cpp


diff --git a/engines/kyra/POTFILES b/engines/kyra/POTFILES
index e5f6b60482..cf6c284535 100644
--- a/engines/kyra/POTFILES
+++ b/engines/kyra/POTFILES
@@ -2,4 +2,4 @@ engines/kyra/detection.cpp
 engines/kyra/engine/eobcommon.cpp
 engines/kyra/engine/lol.cpp
 engines/kyra/gui/saveload_eob.cpp
-engines/kyra/graphics/screen_eob.cpp
+engines/kyra/graphics/screen_eob_amiga.cpp
diff --git a/engines/kyra/graphics/screen_eob.cpp b/engines/kyra/graphics/screen_eob.cpp
index 46b96f9144..8e201956bc 100644
--- a/engines/kyra/graphics/screen_eob.cpp
+++ b/engines/kyra/graphics/screen_eob.cpp
@@ -32,15 +32,12 @@
 #include "kyra/engine/util.h"
 
 #include "common/system.h"
-#include "common/translation.h"
 #include "common/memstream.h"
 
 #include "graphics/cursorman.h"
 #include "graphics/palette.h"
 #include "graphics/sjis.h"
 
-#include "gui/error.h"
-
 namespace Kyra {
 
 Screen_EoB::Screen_EoB(EoBCoreEngine *vm, OSystem *system) : Screen(vm, system, _screenDimTable, _screenDimTableCount), _cursorColorKey16Bit(0x8000) {
@@ -2004,433 +2001,6 @@ void OldDOSFont::unload() {
 	_bitmapOffsets = 0;
 }
 
-AmigaDOSFont::AmigaDOSFont(Resource *res, bool needsLocalizedFont) : _res(res), _needsLocalizedFont(needsLocalizedFont), _width(0), _height(0), _first(0), _last(0), _content(0), _numElements(0), _selectedElement(0), _maxPathLen(256) {
-	assert(_res);
-}
-
-bool AmigaDOSFont::load(Common::SeekableReadStream &file) {
-	unload();
-
-	uint16 id = file.readUint16BE();
-	// We only support type 0x0f00, since this is the only type used for EOB 
-	if (id != 0x0f00)
-		return false;
-
-	_numElements = file.readUint16BE();
-	_content = new FontContent[_numElements];
-	char *cfile = new char[_maxPathLen];
-
-	for (int i = 0; i < _numElements; ++i) {
-		file.read(cfile, _maxPathLen);
-		_content[i].height = file.readUint16BE();;
-		_content[i].style = file.readByte();
-		_content[i].flags = file.readByte();
-		_content[i].contentFile = cfile;
-
-		for (int ii = 0; ii < i; ++ii) {
-			if (_content[ii].contentFile == _content[i].contentFile && _content[ii].data.get())
-				_content[i].data = _content[ii].data;
-		}
-
-		if (!_content[i].data.get()) {
-			TextFont *contentData = loadContentFile(cfile);
-			if (contentData) {
-				_content[i].data = Common::SharedPtr<TextFont>(contentData);
-			} else {
-				unload();
-				return false;
-			}
-		}
-
-		if (!(_content[i].flags & 0x40) && (_content[i].height != _content[i].data->height)) {
-			warning("Amiga DOS Font construction / scaling not implemented.");
-		}
-	}
-	
-	delete[] cfile;
-
-	selectMode(0);
-
-	return true;
-}
-
-int AmigaDOSFont::getCharWidth(uint16 c) const {
-	if (c < _first || c > _last)
-		return 0;
-	c -= _first;
-
-	int width = _content[_selectedElement].data->spacing ? _content[_selectedElement].data->spacing[c] : _content[_selectedElement].data->width;
-
-	/*if (_content[_selectedElement].data->kerning)
-		width += _content[_selectedElement].data->kerning[c];*/
-
-	return width;
-}
-
-void AmigaDOSFont::drawChar(uint16 c, byte *dst, int pitch, int) const {
-	if (c < _first || c > _last || !dst)
-		return;
-
-	static const uint16 table[] = {
-		0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00,
-		0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff
-	};
-
-	c -= _first;
-
-	int w = _content[_selectedElement].data->spacing ? _content[_selectedElement].data->spacing[c] : _content[_selectedElement].data->width;
-	int xbits = _content[_selectedElement].data->location[c * 2 + 1];
-	int h = _content[_selectedElement].data->height;
-	
-	uint16 bitPos = _content[_selectedElement].data->location[c * 2] & 0x0F;
-	uint16 mod = _content[_selectedElement].data->modulo;
-	const uint8 *data = _content[_selectedElement].data->bitmap + ((_content[_selectedElement].data->location[c * 2] >> 3) & ~1);
-	uint32 xbt_mask = xbits ? table[(xbits - 1) & 0x0F] << 16 : 0;
-
-	for (int y = 0; y < h; ++y) {
-		uint32 mask = 0x80000000;
-		uint32 bits = (READ_BE_UINT32(data) << bitPos) & xbt_mask;
-		data += mod;
-		
-		for (int x = 0; x < w; ++x) {
-			if (bits & mask) {
-				if (_colorMap[1])
-					*dst = _colorMap[1];
-			} else {
-				if (_colorMap[0])
-					*dst = _colorMap[0];
-			}
-			mask >>= 1;
-			dst++;
-		}
-		dst += (pitch - w);
-	}
-}
-
-uint8 AmigaDOSFont::_errorDialogDisplayed = 0;
-
-void AmigaDOSFont::errorDialog(int index) {
-	if (_errorDialogDisplayed & (1 << index))
-		return;
-	_errorDialogDisplayed |= (1 << index);
-
-	// I've made rather elaborate dialogs here, since the Amiga font file handling is quite prone to cause problems for users.
-	// This will hopefully prevent unnecessary forum posts and bug reports.
-	if (index == 0) {
-		::GUI::displayErrorDialog(_(
-			"This AMIGA version requires the following font files:\n\nEOBF6.FONT\nEOBF6/6\nEOBF8.FONT\nEOBF8/8\n\n"
-			"If you used the orginal installer for the installation these files\nshould be located in the AmigaDOS system 'Fonts/' folder.\n"
-			"Please copy them into the EOB game data directory.\n"
-		));
-		
-		error("Failed to load font files.");
-	} else if (index == 1) {
-		::GUI::displayErrorDialog(_(
-			"This AMIGA version requires the following font files:\n\nEOBF6.FONT\nEOBF6/6\nEOBF8.FONT\nEOBF8/8\n\n"
-			"This is a localized (non-English) version of EOB II which uses language specific characters\n"
-			"contained only in the specific font files that came with your game. You cannot use the font\n"
-			"files from the English version or from any EOB I game which seems to be what you are doing.\n\n"
-			"The game will continue, but the language specific characters will not be displayed.\n"
-			"Please copy the correct font files into your EOB II game data directory.\n\n"
-		));
-	}
-}
-
-void AmigaDOSFont::unload() {
-	delete[] _content;
-}
-
-AmigaDOSFont::TextFont *AmigaDOSFont::loadContentFile(const Common::String fileName) {
-	Common::SeekableReadStreamEndian *str = _res->createEndianAwareReadStream(fileName);
-
-	if (!str && fileName.contains('/')) {
-		// These content files are usually located in sub directories (i. e. the eobf8.font
-		// has a sub dir named 'eobf8' with a file '8' in it). In case someone put the content
-		// files directly in the game directory we still try to open it.
-		Common::String fileNameAlt = fileName;
-		while (fileNameAlt.firstChar() != '/')
-			fileNameAlt.deleteChar(0);
-		fileNameAlt.deleteChar(0);
-
-		str = _res->createEndianAwareReadStream(fileNameAlt);
-
-		if (!str) {
-			// Someone might even have copied the floppy disks to the game directory with the
-			// full sub directory structure. So we also try that...
-			fileNameAlt = "fonts/";
-			fileNameAlt += fileName;
-
-			str = _res->createEndianAwareReadStream(fileNameAlt);
-		}
-
-		if (!str)
-			errorDialog(0);
-	}
-
-	uint32 hunkId = str->readUint32();
-	// Except for some sanity checks we skip all of the Amiga hunk file magic
-	if (hunkId != 0x03f3)
-		return 0;
-	str->seek(20, SEEK_CUR);
-
-	uint32 hunkType = str->readUint32();
-	if (hunkType != 0x3E9)
-		return 0;
-	uint32 dataSize = str->readUint32() * 4;
-	int32 hunkStartPos = str->pos();
-
-	str->seek(34, SEEK_CUR);
-	TextFont *fnt = new TextFont();
-	int32 fntStartPos = str->pos();
-	str->seek(44, SEEK_CUR);
-	fnt->height = str->readUint16();
-	str->seek(2, SEEK_CUR);
-	fnt->width = str->readUint16();
-	fnt->baseLine = str->readUint16();
-	str->seek(4, SEEK_CUR);
-	fnt->firstChar = str->readByte();
-	fnt->lastChar = str->readByte();
-
-	if (_needsLocalizedFont && fnt->lastChar <= 127)
-		errorDialog(1);
-
-	str->seek(18, SEEK_CUR);
-	int32 curPos = str->pos();
-	uint32 bufferSize = dataSize - (curPos - fntStartPos);
-	uint8 *buffer = new uint8[bufferSize];
-	str->read(buffer, bufferSize);
-
-	str->seek(curPos - 18, SEEK_SET);
-	uint32 offset = str->readUint32();
-	fnt->bitmap = offset ? buffer + offset - (curPos - hunkStartPos) : 0;
-	fnt->modulo = str->readUint16();
-
-	offset = str->readUint32();
-	uint16 *loc = (uint16*) (offset ? buffer + offset - (curPos - hunkStartPos) : 0);
-	for (int i = 0; i <= (fnt->lastChar - fnt->firstChar) * 2 + 1; ++i)
-		loc[i] = READ_BE_UINT16(&loc[i]);
-	fnt->location = loc;
-
-	offset = str->readUint32();
-	int16 *idat = offset ? (int16*)(buffer + offset - (curPos - hunkStartPos)) : 0;
-	if (idat) {
-		for (int i = 0; i <= (fnt->lastChar - fnt->firstChar) * 2 + 1; ++i)
-			idat[i] = (int16)READ_BE_UINT16(&idat[i]);
-	}
-	fnt->spacing = idat;
-
-	offset = str->readUint32();
-	// This warning will only show up if someone tries to use this code elsewhere. It cannot happen with EOB fonts.
-	if (offset)
-		warning("Trying to load an AmigaDOS font with kerning data. This is not implemented. Font Rendering will not be accurate.");
-	idat = offset ? (int16*)(buffer + offset - (curPos - hunkStartPos)) : 0;
-	if (idat) {
-		for (int i = 0; i <= (fnt->lastChar - fnt->firstChar) * 2 + 1; ++i)
-			idat[i] = (int16)READ_BE_UINT16(&idat[i]);
-	}
-	fnt->kerning = idat;	
-
-	fnt->data = buffer;
-
-	delete str;
-
-	return fnt;
-}
-
-void AmigaDOSFont::selectMode(int mode) {
-	if (mode < 0 || mode > _numElements - 1)
-		return;
-
-	_selectedElement = mode;
-
-	_width = _content[mode].data->width;
-	_height = _content[mode].data->height;
-	_first = _content[mode].data->firstChar;
-	_last = _content[mode].data->lastChar;
-}
-
-SJISFontEoB1PC98::SJISFontEoB1PC98(Common::SharedPtr<Graphics::FontSJIS> &font, /*uint8 shadowColor,*/ const uint16 *convTable1, const uint16 *convTable2)	: SJISFont(font, 0, false, false, 0),
-	/*_shadowColor(shadowColor),*/ _convTable1(convTable1), _convTable2(convTable2), _defaultConv(true) {
-	assert(_convTable1);
-	assert(_convTable2);
-}
-
-int SJISFontEoB1PC98::getCharWidth(uint16 c) const {
-	return SJISFont::getCharWidth(convert(c));
-}
-
-void SJISFontEoB1PC98::drawChar(uint16 c, byte *dst, int pitch, int) const {
-	c = convert(c);
-	_font->setDrawingMode(_style == kFSLeftShadow ? Graphics::FontSJIS::kShadowLeftMode : Graphics::FontSJIS::kDefaultMode);
-	_font->toggleFatPrint(false);
-	_font->drawChar(dst, c, 640, 1, _colorMap[1], _colorMap[0], 640, 400);
-}
-
-uint16 SJISFontEoB1PC98::convert(uint16 c) const {
-	uint8 l = c & 0xFF;
-	uint8 h = c >> 8;
-
-	if (c < 128) {
-		assert(l > 31);
-		c = _convTable2[l - 32];
-	} else if (l > 160 && l < 225) {
-		bool done = false;
-		if (_defaultConv) {
-			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)
-			c = _convTable2[l - 64];
-	}
-
-	return c;
-}
-
-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);
-
-	_width = _height = 12;
-	_numGlyphs = 275;
-	_bmpOffs = new uint16[_numGlyphs];
-	for (int i = 0; i < _numGlyphs; ++i)
-		_bmpOffs[i] = lookupTable[i] * 24;
-}
-
-Font12x12PC98::~Font12x12PC98() {
-	delete[] _bmpOffs;
-}
-
-bool Font12x12PC98::load(Common::SeekableReadStream &file) {
-	unload();
-
-	_width = _height = 12;
-	_numGlyphs = 275;
-	_bitmapOffsets = _bmpOffs;
-
-	_data = new uint8[file.size()];
-	assert(_data);
-
-	file.read(_data, file.size());
-	if (file.err())
-		return false;
-
-	return true;
-}
-
-uint16 Font12x12PC98::convert(uint16 c) const {
-	uint8 l = c & 0xFF;
-	uint8 h = c >> 8;
-
-	if (c < 128) {
-		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 (!done)
-			c = _convTable2[l - 64];
-	}
-	
-	c = SWAP_BYTES_16(c);
-	if (c < 0x813F)
-		c = 1;
-	else if (c < 0x824F)
-		c -= 0x813F;
-	else if (c < 0x833F)
-		c -= 0x81EE;
-	else if (c > 0x839F)
-		c = 1;
-	else
-		c -= 0x828D;
-
-	return c;
-}
-
-SJISFontLarge::SJISFontLarge(Common::SharedPtr<Graphics::FontSJIS> &font) : SJISFont(font, 0, false, false, 0) {
-	_sjisWidth = _font->getMaxFontWidth();
-	_fontHeight = _font->getFontHeight();
-	_asciiWidth = _font->getCharWidth('a');
-}
-
-void SJISFontLarge::drawChar(uint16 c, byte *dst, int pitch, int) const {
-	_font->setDrawingMode(Graphics::FontSJIS::kDefaultMode);
-	_font->toggleFatPrint(false);
-	_font->drawChar(dst, c, 320, 1, _colorMap[1], _colorMap[0], 320, 200);
-}
-
-SJISFont12x12::SJISFont12x12(const uint16 *searchTable) : _height(6), _width(6), _data(0) {
-	assert(searchTable);
-	for (int i = 0; i < 148; i++)
-		_searchTable[searchTable[i]] = i + 1;
-}
-
-bool SJISFont12x12::load(Common::SeekableReadStream &file) {
-	delete[] _data;
-	int size = 148 * 24;
-	if (file.size() < size)
-		return false;
-
-	_data = new uint8[size];
-	file.read(_data, size);
-
-	return true;
-}
-
-void SJISFont12x12::unload() {
-	delete[] _data;
-	_data = 0;
-	_searchTable.clear();
-}
-
-void SJISFont12x12::drawChar(uint16 c, byte *dst, int pitch, int) const {
-	int offs = _searchTable[c];
-	if (!offs)
-		return;
-
-	const uint8 *src = _data + (offs - 1) * 24;
-	uint8 color1 = _colorMap[1];
-	
-	int bt = 0;
-	uint16 chr = 0;
-
-	for (int i = 0; i < 192; ++i) {
-		if (!bt) {
-			chr = *src++;
-			bt = 8;
-		}		
-		if (chr & 0x80)
-			*dst = color1;
-		dst++;
-		if (--bt)
-			chr <<= 1;
-		else if (i & 8)
-			dst += (pitch - 16);
-	}
-}
-
 } // End of namespace Kyra
 
 #endif // ENABLE_EOB
diff --git a/engines/kyra/graphics/screen_eob_amiga.cpp b/engines/kyra/graphics/screen_eob_amiga.cpp
index df6e88f879..876ddf75d1 100644
--- a/engines/kyra/graphics/screen_eob_amiga.cpp
+++ b/engines/kyra/graphics/screen_eob_amiga.cpp
@@ -24,7 +24,11 @@
 #ifdef ENABLE_EOB
 
 #include "kyra/resource/resource.h"
+
 #include "common/memstream.h"
+#include "common/translation.h"
+
+#include "gui/error.h"
 
 namespace Kyra {
 
@@ -159,6 +163,251 @@ void Screen_EoB::setDualPalettes(Palette &top, Palette &bottom) {
 	enableDualPaletteMode(120);
 }
 
+AmigaDOSFont::AmigaDOSFont(Resource *res, bool needsLocalizedFont) : _res(res), _needsLocalizedFont(needsLocalizedFont), _width(0), _height(0), _first(0), _last(0), _content(0), _numElements(0), _selectedElement(0), _maxPathLen(256) {
+	assert(_res);
+}
+
+bool AmigaDOSFont::load(Common::SeekableReadStream &file) {
+	unload();
+
+	uint16 id = file.readUint16BE();
+	// We only support type 0x0f00, since this is the only type used for EOB 
+	if (id != 0x0f00)
+		return false;
+
+	_numElements = file.readUint16BE();
+	_content = new FontContent[_numElements];
+	char *cfile = new char[_maxPathLen];
+
+	for (int i = 0; i < _numElements; ++i) {
+		file.read(cfile, _maxPathLen);
+		_content[i].height = file.readUint16BE();;
+		_content[i].style = file.readByte();
+		_content[i].flags = file.readByte();
+		_content[i].contentFile = cfile;
+
+		for (int ii = 0; ii < i; ++ii) {
+			if (_content[ii].contentFile == _content[i].contentFile && _content[ii].data.get())
+				_content[i].data = _content[ii].data;
+		}
+
+		if (!_content[i].data.get()) {
+			TextFont *contentData = loadContentFile(cfile);
+			if (contentData) {
+				_content[i].data = Common::SharedPtr<TextFont>(contentData);
+			} else {
+				unload();
+				return false;
+			}
+		}
+
+		if (!(_content[i].flags & 0x40) && (_content[i].height != _content[i].data->height)) {
+			warning("Amiga DOS Font construction / scaling not implemented.");
+		}
+	}
+
+	delete[] cfile;
+
+	selectMode(0);
+
+	return true;
+}
+
+int AmigaDOSFont::getCharWidth(uint16 c) const {
+	if (c < _first || c > _last)
+		return 0;
+	c -= _first;
+
+	int width = _content[_selectedElement].data->spacing ? _content[_selectedElement].data->spacing[c] : _content[_selectedElement].data->width;
+
+	/*if (_content[_selectedElement].data->kerning)
+		width += _content[_selectedElement].data->kerning[c];*/
+
+	return width;
+}
+
+void AmigaDOSFont::drawChar(uint16 c, byte *dst, int pitch, int) const {
+	if (c < _first || c > _last || !dst)
+		return;
+
+	static const uint16 table[] = {
+		0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00,
+		0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff
+	};
+
+	c -= _first;
+
+	int w = _content[_selectedElement].data->spacing ? _content[_selectedElement].data->spacing[c] : _content[_selectedElement].data->width;
+	int xbits = _content[_selectedElement].data->location[c * 2 + 1];
+	int h = _content[_selectedElement].data->height;
+
+	uint16 bitPos = _content[_selectedElement].data->location[c * 2] & 0x0F;
+	uint16 mod = _content[_selectedElement].data->modulo;
+	const uint8 *data = _content[_selectedElement].data->bitmap + ((_content[_selectedElement].data->location[c * 2] >> 3) & ~1);
+	uint32 xbt_mask = xbits ? table[(xbits - 1) & 0x0F] << 16 : 0;
+
+	for (int y = 0; y < h; ++y) {
+		uint32 mask = 0x80000000;
+		uint32 bits = (READ_BE_UINT32(data) << bitPos) & xbt_mask;
+		data += mod;
+
+		for (int x = 0; x < w; ++x) {
+			if (bits & mask) {
+				if (_colorMap[1])
+					*dst = _colorMap[1];
+			} else {
+				if (_colorMap[0])
+					*dst = _colorMap[0];
+			}
+			mask >>= 1;
+			dst++;
+		}
+		dst += (pitch - w);
+	}
+}
+
+uint8 AmigaDOSFont::_errorDialogDisplayed = 0;
+
+void AmigaDOSFont::errorDialog(int index) {
+	if (_errorDialogDisplayed & (1 << index))
+		return;
+	_errorDialogDisplayed |= (1 << index);
+
+	// I've made rather elaborate dialogs here, since the Amiga font file handling is quite prone to cause problems for users.
+	// This will hopefully prevent unnecessary forum posts and bug reports.
+	if (index == 0) {
+		::GUI::displayErrorDialog(_(
+			"This AMIGA version requires the following font files:\n\nEOBF6.FONT\nEOBF6/6\nEOBF8.FONT\nEOBF8/8\n\n"
+			"If you used the orginal installer for the installation these files\nshould be located in the AmigaDOS system 'Fonts/' folder.\n"
+			"Please copy them into the EOB game data directory.\n"
+		));
+
+		error("Failed to load font files.");
+	} else if (index == 1) {
+		::GUI::displayErrorDialog(_(
+			"This AMIGA version requires the following font files:\n\nEOBF6.FONT\nEOBF6/6\nEOBF8.FONT\nEOBF8/8\n\n"
+			"This is a localized (non-English) version of EOB II which uses language specific characters\n"
+			"contained only in the specific font files that came with your game. You cannot use the font\n"
+			"files from the English version or from any EOB I game which seems to be what you are doing.\n\n"
+			"The game will continue, but the language specific characters will not be displayed.\n"
+			"Please copy the correct font files into your EOB II game data directory.\n\n"
+		));
+	}
+}
+
+void AmigaDOSFont::unload() {
+	delete[] _content;
+}
+
+AmigaDOSFont::TextFont *AmigaDOSFont::loadContentFile(const Common::String fileName) {
+	Common::SeekableReadStreamEndian *str = _res->createEndianAwareReadStream(fileName);
+
+	if (!str && fileName.contains('/')) {
+		// These content files are usually located in sub directories (i. e. the eobf8.font
+		// has a sub dir named 'eobf8' with a file '8' in it). In case someone put the content
+		// files directly in the game directory we still try to open it.
+		Common::String fileNameAlt = fileName;
+		while (fileNameAlt.firstChar() != '/')
+			fileNameAlt.deleteChar(0);
+		fileNameAlt.deleteChar(0);
+
+		str = _res->createEndianAwareReadStream(fileNameAlt);
+
+		if (!str) {
+			// Someone might even have copied the floppy disks to the game directory with the
+			// full sub directory structure. So we also try that...
+			fileNameAlt = "fonts/";
+			fileNameAlt += fileName;
+
+			str = _res->createEndianAwareReadStream(fileNameAlt);
+		}
+
+		if (!str)
+			errorDialog(0);
+	}
+
+	uint32 hunkId = str->readUint32();
+	// Except for some sanity checks we skip all of the Amiga hunk file magic
+	if (hunkId != 0x03f3)
+		return 0;
+	str->seek(20, SEEK_CUR);
+
+	uint32 hunkType = str->readUint32();
+	if (hunkType != 0x3E9)
+		return 0;
+	uint32 dataSize = str->readUint32() * 4;
+	int32 hunkStartPos = str->pos();
+
+	str->seek(34, SEEK_CUR);
+	TextFont *fnt = new TextFont();
+	int32 fntStartPos = str->pos();
+	str->seek(44, SEEK_CUR);
+	fnt->height = str->readUint16();
+	str->seek(2, SEEK_CUR);
+	fnt->width = str->readUint16();
+	fnt->baseLine = str->readUint16();
+	str->seek(4, SEEK_CUR);
+	fnt->firstChar = str->readByte();
+	fnt->lastChar = str->readByte();
+
+	if (_needsLocalizedFont && fnt->lastChar <= 127)
+		errorDialog(1);
+
+	str->seek(18, SEEK_CUR);
+	int32 curPos = str->pos();
+	uint32 bufferSize = dataSize - (curPos - fntStartPos);
+	uint8 *buffer = new uint8[bufferSize];
+	str->read(buffer, bufferSize);
+
+	str->seek(curPos - 18, SEEK_SET);
+	uint32 offset = str->readUint32();
+	fnt->bitmap = offset ? buffer + offset - (curPos - hunkStartPos) : 0;
+	fnt->modulo = str->readUint16();
+
+	offset = str->readUint32();
+	uint16 *loc = (uint16*)(offset ? buffer + offset - (curPos - hunkStartPos) : 0);
+	for (int i = 0; i <= (fnt->lastChar - fnt->firstChar) * 2 + 1; ++i)
+		loc[i] = READ_BE_UINT16(&loc[i]);
+	fnt->location = loc;
+
+	offset = str->readUint32();
+	int16 *idat = offset ? (int16*)(buffer + offset - (curPos - hunkStartPos)) : 0;
+	if (idat) {
+		for (int i = 0; i <= (fnt->lastChar - fnt->firstChar) * 2 + 1; ++i)
+			idat[i] = (int16)READ_BE_UINT16(&idat[i]);
+	}
+	fnt->spacing = idat;
+
+	offset = str->readUint32();
+	// This warning will only show up if someone tries to use this code elsewhere. It cannot happen with EOB fonts.
+	if (offset)
+		warning("Trying to load an AmigaDOS font with kerning data. This is not implemented. Font Rendering will not be accurate.");
+	idat = offset ? (int16*)(buffer + offset - (curPos - hunkStartPos)) : 0;
+	if (idat) {
+		for (int i = 0; i <= (fnt->lastChar - fnt->firstChar) * 2 + 1; ++i)
+			idat[i] = (int16)READ_BE_UINT16(&idat[i]);
+	}
+	fnt->kerning = idat;
+
+	fnt->data = buffer;
+
+	delete str;
+
+	return fnt;
+}
+
+void AmigaDOSFont::selectMode(int mode) {
+	if (mode < 0 || mode > _numElements - 1)
+		return;
+
+	_selectedElement = mode;
+
+	_width = _content[mode].data->width;
+	_height = _content[mode].data->height;
+	_first = _content[mode].data->firstChar;
+	_last = _content[mode].data->lastChar;
+}
+
 } // End of namespace Kyra
 
 #endif // ENABLE_EOB
diff --git a/engines/kyra/graphics/screen_eob_pc98.cpp b/engines/kyra/graphics/screen_eob_pc98.cpp
index 0a435b97a3..0af6826e12 100644
--- a/engines/kyra/graphics/screen_eob_pc98.cpp
+++ b/engines/kyra/graphics/screen_eob_pc98.cpp
@@ -24,6 +24,7 @@
 #ifdef ENABLE_EOB
 
 #include "kyra/resource/resource.h"
+#include "graphics/sjis.h"
 
 namespace Kyra {
 
@@ -163,6 +164,126 @@ void Screen_EoB::updatePC98PaletteCycle(int brightness) {
 	setScreenPalette(*_palettes[0]);
 }
 
+SJISFontEoB1PC98::SJISFontEoB1PC98(Common::SharedPtr<Graphics::FontSJIS> &font, /*uint8 shadowColor,*/ const uint16 *convTable1, const uint16 *convTable2) : SJISFont(font, 0, false, false, 0),
+/*_shadowColor(shadowColor),*/ _convTable1(convTable1), _convTable2(convTable2), _defaultConv(true) {
+	assert(_convTable1);
+	assert(_convTable2);
+}
+
+int SJISFontEoB1PC98::getCharWidth(uint16 c) const {
+	return SJISFont::getCharWidth(convert(c));
+}
+
+void SJISFontEoB1PC98::drawChar(uint16 c, byte *dst, int pitch, int) const {
+	c = convert(c);
+	_font->setDrawingMode(_style == kFSLeftShadow ? Graphics::FontSJIS::kShadowLeftMode : Graphics::FontSJIS::kDefaultMode);
+	_font->toggleFatPrint(false);
+	_font->drawChar(dst, c, 640, 1, _colorMap[1], _colorMap[0], 640, 400);
+}
+
+uint16 SJISFontEoB1PC98::convert(uint16 c) const {
+	uint8 l = c & 0xFF;
+	uint8 h = c >> 8;
+
+	if (c < 128) {
+		assert(l > 31);
+		c = _convTable2[l - 32];
+	} else if (l > 160 && l < 225) {
+		bool done = false;
+		if (_defaultConv) {
+			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)
+			c = _convTable2[l - 64];
+	}
+
+	return c;
+}
+
+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);
+
+	_width = _height = 12;
+	_numGlyphs = 275;
+	_bmpOffs = new uint16[_numGlyphs];
+	for (int i = 0; i < _numGlyphs; ++i)
+		_bmpOffs[i] = lookupTable[i] * 24;
+}
+
+Font12x12PC98::~Font12x12PC98() {
+	delete[] _bmpOffs;
+}
+
+bool Font12x12PC98::load(Common::SeekableReadStream &file) {
+	unload();
+
+	_width = _height = 12;
+	_numGlyphs = 275;
+	_bitmapOffsets = _bmpOffs;
+
+	_data = new uint8[file.size()];
+	assert(_data);
+
+	file.read(_data, file.size());
+	if (file.err())
+		return false;
+
+	return true;
+}
+
+uint16 Font12x12PC98::convert(uint16 c) const {
+	uint8 l = c & 0xFF;
+	uint8 h = c >> 8;
+
+	if (c < 128) {
+		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 (!done)
+			c = _convTable2[l - 64];
+	}
+
+	c = SWAP_BYTES_16(c);
+	if (c < 0x813F)
+		c = 1;
+	else if (c < 0x824F)
+		c -= 0x813F;
+	else if (c < 0x833F)
+		c -= 0x81EE;
+	else if (c > 0x839F)
+		c = 1;
+	else
+		c -= 0x828D;
+
+	return c;
+}
+
 } // End of namespace Kyra
 
 #endif // ENABLE_EOB
diff --git a/engines/kyra/graphics/screen_eob_towns.cpp b/engines/kyra/graphics/screen_eob_towns.cpp
index 132f04a758..e5f6686f74 100644
--- a/engines/kyra/graphics/screen_eob_towns.cpp
+++ b/engines/kyra/graphics/screen_eob_towns.cpp
@@ -24,6 +24,7 @@
  #ifdef ENABLE_EOB
 
 #include "kyra/resource/resource.h"
+#include "graphics/sjis.h"
 
 namespace Kyra {
 
@@ -95,6 +96,68 @@ void Screen_EoB::shadeRect(int x1, int y1, int x2, int y2, int shadingLevel) {
 	_16bitShadingLevel = l;
 }
 
+SJISFontLarge::SJISFontLarge(Common::SharedPtr<Graphics::FontSJIS> &font) : SJISFont(font, 0, false, false, 0) {
+	_sjisWidth = _font->getMaxFontWidth();
+	_fontHeight = _font->getFontHeight();
+	_asciiWidth = _font->getCharWidth('a');
+}
+
+void SJISFontLarge::drawChar(uint16 c, byte *dst, int pitch, int) const {
+	_font->setDrawingMode(Graphics::FontSJIS::kDefaultMode);
+	_font->toggleFatPrint(false);
+	_font->drawChar(dst, c, 320, 1, _colorMap[1], _colorMap[0], 320, 200);
+}
+
+SJISFont12x12::SJISFont12x12(const uint16 *searchTable) : _height(6), _width(6), _data(0) {
+	assert(searchTable);
+	for (int i = 0; i < 148; i++)
+		_searchTable[searchTable[i]] = i + 1;
+}
+
+bool SJISFont12x12::load(Common::SeekableReadStream &file) {
+	delete[] _data;
+	int size = 148 * 24;
+	if (file.size() < size)
+		return false;
+
+	_data = new uint8[size];
+	file.read(_data, size);
+
+	return true;
+}
+
+void SJISFont12x12::unload() {
+	delete[] _data;
+	_data = 0;
+	_searchTable.clear();
+}
+
+void SJISFont12x12::drawChar(uint16 c, byte *dst, int pitch, int) const {
+	int offs = _searchTable[c];
+	if (!offs)
+		return;
+
+	const uint8 *src = _data + (offs - 1) * 24;
+	uint8 color1 = _colorMap[1];
+
+	int bt = 0;
+	uint16 chr = 0;
+
+	for (int i = 0; i < 192; ++i) {
+		if (!bt) {
+			chr = *src++;
+			bt = 8;
+		}
+		if (chr & 0x80)
+			*dst = color1;
+		dst++;
+		if (--bt)
+			chr <<= 1;
+		else if (i & 8)
+			dst += (pitch - 16);
+	}
+}
+
 } // End of namespace Kyra
 
 #endif // ENABLE_EOB


Commit: 5e8691b726dfcc7a00bc41f3aed1ac5648b89b46
    https://github.com/scummvm/scummvm/commit/5e8691b726dfcc7a00bc41f3aed1ac5648b89b46
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:09+02:00

Commit Message:
KYRA: (EOB) - minor font code cleanup

Changed paths:
    engines/kyra/graphics/screen.cpp
    engines/kyra/graphics/screen.h
    engines/kyra/graphics/screen_eob.h


diff --git a/engines/kyra/graphics/screen.cpp b/engines/kyra/graphics/screen.cpp
index 0ae0160ceb..4976713297 100644
--- a/engines/kyra/graphics/screen.cpp
+++ b/engines/kyra/graphics/screen.cpp
@@ -72,7 +72,6 @@ Screen::Screen(KyraEngine_v1 *vm, OSystem *system, const ScreenDim *dimTable, co
 	_useSJIS = _useOverlays = false;
 
 	_currentFont = FID_8_FNT;
-	_currentFontType = FTYPE_ASCII;
 	_paletteChanged = true;
 	_textMarginRight = SCREEN_W;
 	_customDimTable = 0;
@@ -1348,7 +1347,6 @@ bool Screen::loadFont(FontId fontId, const char *filename) {
 Screen::FontId Screen::setFont(FontId fontId) {
 	FontId prev = _currentFont;
 	_currentFont = fontId;
-	_currentFontType = _currentFont >= FID_SJIS_FNT ? FTYPE_SJIS : FTYPE_ASCII;
 
 	assert(_fonts[_currentFont]);
 	return prev;
@@ -1372,10 +1370,10 @@ int Screen::getTextWidth(const char *str) {
 	int maxLineLen = 0;
 
 	FontId curFont = _currentFont;
-	FontType curType = _currentFontType;
+	Font::Type curType = _fonts[curFont]->getType();
 
 	while (1) {
-		if (_sjisMixedFontMode && curType == FTYPE_ASCII)
+		if (_sjisMixedFontMode && curType == Font::kASCII)
 			setFont((*str & 0x80) ? ((_vm->game() == GI_EOB2 && curFont == FID_6_FNT) ? FID_SJIS_SMALL_FNT : FID_SJIS_FNT) : curFont);
 
 		uint c = fetchChar(str);
@@ -1409,7 +1407,7 @@ void Screen::printText(const char *str, int x, int y, uint8 color1, uint8 color2
 	setTextColor(cmap8, 0, 1);
 
 	FontId curFont = _currentFont;
-	FontType curType = _currentFontType;
+	Font::Type curType = _fonts[curFont]->getType();
 
 	if (x < 0)
 		x = 0;
@@ -1423,7 +1421,7 @@ void Screen::printText(const char *str, int x, int y, uint8 color1, uint8 color2
 		return;
 
 	while (1) {
-		if (_sjisMixedFontMode && curType == FTYPE_ASCII)
+		if (_sjisMixedFontMode && curType == Font::kASCII)
 			setFont((*str & 0x80) ? ((_vm->game() == GI_EOB2 && curFont == FID_6_FNT) ? FID_SJIS_SMALL_FNT : FID_SJIS_FNT) : curFont);
 
 		uint8 charHeightFnt = getFontHeight();
@@ -1451,7 +1449,7 @@ void Screen::printText(const char *str, int x, int y, uint8 color1, uint8 color2
 }
 
 uint16 Screen::fetchChar(const char *&s) const {
-	if (_currentFontType == FTYPE_ASCII)
+	if (_fonts[_currentFont]->getType() == Font::kASCII)
 		return (uint8)*s++;
 
 	uint16 ch = (uint8)*s++;
diff --git a/engines/kyra/graphics/screen.h b/engines/kyra/graphics/screen.h
index 7adc9b012b..3a3c8b1282 100644
--- a/engines/kyra/graphics/screen.h
+++ b/engines/kyra/graphics/screen.h
@@ -60,6 +60,16 @@ struct ScreenDim {
  * A class that handles KYRA fonts.
  */
 class Font {
+public:
+	/* Font types
+	 * Currently, we actually only care about oneByte and twoByte, but
+	 * naming it like this makes it easier to extend if the need arises.
+	 */
+	enum Type {
+		kASCII = 0,
+		kSJIS
+	};
+
 public:
 	virtual ~Font() {}
 
@@ -73,6 +83,11 @@ public:
 	 */
 	virtual bool usesOverlay() const { return false; }
 
+	/**
+	* Whether the font is Ascii or Sjis.
+	*/
+	virtual Type getType() const = 0;
+
 	/**
 	 * The font height.
 	 */
@@ -133,6 +148,7 @@ public:
 	~DOSFont() override { unload(); }
 
 	bool load(Common::SeekableReadStream &file) override;
+	Type getType() const override { return kASCII; }
 	int getHeight() const override { return _height; }
 	int getWidth() const override { return _width; }
 	int getCharWidth(uint16 c) const override;
@@ -164,6 +180,7 @@ public:
 	~AMIGAFont() override { unload(); }
 
 	bool load(Common::SeekableReadStream &file) override;
+	Type getType() const override { return kASCII; }
 	int getHeight() const override { return _height; }
 	int getWidth() const override { return _width; }
 	int getCharWidth(uint16 c) const override;
@@ -196,6 +213,7 @@ public:
 	~SJISFont() override {}
 
 	bool usesOverlay() const override { return true; }
+	Type getType() const override { return kSJIS; }
 
 	bool load(Common::SeekableReadStream &) override { return true; }
 	int getHeight() const override;
@@ -394,11 +412,6 @@ public:
 		FID_NUM
 	};
 
-	enum FontType {
-		FTYPE_ASCII = 0,
-		FTYPE_SJIS
-	};
-
 	Screen(KyraEngine_v1 *vm, OSystem *system, const ScreenDim *dimTable, const int dimTableSize);
 	virtual ~Screen();
 
@@ -541,7 +554,6 @@ public:
 	uint8 *_shapePages[2];
 	int _maskMinY, _maskMaxY;
 	FontId _currentFont;
-	FontType _currentFontType;
 
 	// decoding functions
 	static void decodeFrame1(const uint8 *src, uint8 *dst, uint32 size);
diff --git a/engines/kyra/graphics/screen_eob.h b/engines/kyra/graphics/screen_eob.h
index c3a2698766..796f319011 100644
--- a/engines/kyra/graphics/screen_eob.h
+++ b/engines/kyra/graphics/screen_eob.h
@@ -204,6 +204,7 @@ public:
 	~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; }
 	int getCharWidth(uint16 c) const override;
@@ -243,6 +244,7 @@ public:
 	~AmigaDOSFont() override { unload(); }
 
 	bool load(Common::SeekableReadStream &file) override;
+	Type getType() const override { return kASCII; }
 	int getHeight() const override { return _height; }
 	int getWidth() const override { return _width; }
 	int getCharWidth(uint16 c) const override;
@@ -330,6 +332,7 @@ public:
 	Font12x12PC98(uint8 shadowColor, const uint16 *convTable1, const uint16 *convTable2, const uint8 *lookupTable);
 	~Font12x12PC98() override;
 	bool usesOverlay() const override { return true; }
+	Type getType() const override { return kSJIS; }
 	int getHeight() const override { return _height >> 1; }
 	int getWidth() const override { return _width >> 1; }
 	int getCharWidth(uint16 c) const override { return _width >> 1; };
@@ -362,6 +365,7 @@ public:
 	~SJISFont12x12() override { unload(); }
 
 	bool load(Common::SeekableReadStream &file) override;
+	Type getType() const override { return kSJIS; }
 	bool usesOverlay() const override { return true; }
 	int getHeight() const override { return _height; }
 	int getWidth() const override { return _width; }
@@ -384,14 +388,17 @@ public:
 	SegaCDFont();
 	~SegaCDFont() override;
 
-private:
 	bool load(Common::SeekableReadStream &file) override;
+	Type getType() const override { return kSJIS; }
 	int getHeight() const override { return _height; }
 	int getWidth() const override { return _width; }
 	int getCharWidth(uint16 c) const override { return _width; }
 	void setColorMap(const uint8 *src) override { _colorMap = src; }
 	void drawChar(uint16 c, byte *dst, int pitch, int) const override;
 
+private:
+	uint16 convert(uint16 c) const;
+
 	uint8 *_data;
 	const uint8 *_colorMap;
 	const int _height, _width;


Commit: 91b18f83437d0dd3b09b84d53e6946c8e180b849
    https://github.com/scummvm/scummvm/commit/91b18f83437d0dd3b09b84d53e6946c8e180b849
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:09+02:00

Commit Message:
KYRA: (EOB/SegaCD) - more graphics and sequence player code

- add support for the Sega Font and add text display methods at least for the sequence player
- finish sequence player (add all missing opcodes and other missing code portions)
- some improvement to the resource class
- some renaming and cleanup

Changed paths:
    engines/kyra/engine/chargen.cpp
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eob.h
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h
    engines/kyra/graphics/screen.cpp
    engines/kyra/graphics/screen.h
    engines/kyra/graphics/screen_eob.cpp
    engines/kyra/graphics/screen_eob.h
    engines/kyra/graphics/screen_eob_pc98.cpp
    engines/kyra/graphics/screen_eob_segacd.cpp
    engines/kyra/graphics/screen_eob_segacd.h
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/gui/gui_hof.cpp
    engines/kyra/gui/gui_lok.cpp
    engines/kyra/gui/gui_mr.cpp
    engines/kyra/gui/gui_v1.cpp
    engines/kyra/gui/gui_v2.cpp
    engines/kyra/resource/resource_segacd.cpp
    engines/kyra/resource/resource_segacd.h
    engines/kyra/resource/staticres_eob.cpp
    engines/kyra/script/script_tim.cpp
    engines/kyra/sequence/seqplayer_eob_segacd.cpp
    engines/kyra/sequence/seqplayer_eob_segacd.h
    engines/kyra/sequence/seqplayer_lok.cpp
    engines/kyra/sequence/sequences_eob.cpp
    engines/kyra/sequence/sequences_hof.cpp
    engines/kyra/sequence/sequences_lok.cpp
    engines/kyra/sequence/sequences_lol.cpp
    engines/kyra/sound/sound_segacd_eob.cpp
    engines/kyra/text/text.cpp
    engines/kyra/text/text_eob_segacd.cpp
    engines/kyra/text/text_eob_segacd.h
    engines/kyra/text/text_hof.cpp
    engines/kyra/text/text_mr.cpp
    engines/kyra/text/text_rpg.cpp
    engines/kyra/text/text_rpg.h


diff --git a/engines/kyra/engine/chargen.cpp b/engines/kyra/engine/chargen.cpp
index a41e5738f9..b7bc24d965 100644
--- a/engines/kyra/engine/chargen.cpp
+++ b/engines/kyra/engine/chargen.cpp
@@ -43,9 +43,11 @@ public:
 	CharacterGenerator(EoBCoreEngine *vm, Screen_EoB *screen);
 	~CharacterGenerator();
 
-	bool start(EoBCharacter *characters, uint8 ***faceShapes);
+	bool start(EoBCharacter *characters, uint8 ***faceShapes, bool defaultParty);
 
 private:
+	bool createCustomParty(uint8 ***faceShapes);
+	void createDefaultParty();
 	void init();
 	void initButtonsFromList(int first, int numButtons);
 	void initButton(int index, int x, int y, int w, int h, int keyCode);
@@ -99,6 +101,9 @@ private:
 	const uint8 *_chargenRaceMinStats;
 	const uint16 *_chargenRaceMaxStats;
 
+	const char *const *_chargenDefaultNames;
+	const uint8 *_chargenDefaultStats;
+
 	const EoBChargenButtonDef *_chargenButtonDefs;
 
 	static const EoBChargenButtonDef _chargenButtonDefsDOS[];
@@ -135,6 +140,7 @@ CharacterGenerator::CharacterGenerator(EoBCoreEngine *vm, Screen_EoB *screen) :
 	memset(_chargenSelectedPortraits2, 0, sizeof(_chargenSelectedPortraits2));
 	memset(_chargenMinStats, 0, sizeof(_chargenMinStats));
 	memset(_chargenMaxStats, 0, sizeof(_chargenMaxStats));
+	memset(_chargenButtonLabels, 0, sizeof(_chargenButtonLabels));
 
 	int temp;
 	_chargenStrings1 = _vm->staticres()->loadStrings(kEoBBaseChargenStrings1, temp);
@@ -144,6 +150,8 @@ CharacterGenerator::CharacterGenerator(EoBCoreEngine *vm, Screen_EoB *screen) :
 	_chargenClassMinStats = _vm->staticres()->loadRawData(kEoBBaseChargenClassMinStats, temp);
 	_chargenRaceMinStats = _vm->staticres()->loadRawData(kEoBBaseChargenRaceMinStats, temp);
 	_chargenRaceMaxStats = _vm->staticres()->loadRawDataBe16(kEoBBaseChargenRaceMaxStats, temp);
+	_chargenDefaultNames = _vm->staticres()->loadStrings(kEoB1DefaultPartyNames, temp);
+	_chargenDefaultStats = _vm->staticres()->loadRawData(kEoB1DefaultPartyStats, temp);
 
 	EoBChargenButtonDef *chargenButtonDefs = new EoBChargenButtonDef[41];
 	memcpy(chargenButtonDefs, _chargenButtonDefsDOS, 41 * sizeof(EoBChargenButtonDef));
@@ -176,7 +184,7 @@ CharacterGenerator::~CharacterGenerator() {
 	_screen->clearPage(2);
 }
 
-bool CharacterGenerator::start(EoBCharacter *characters, uint8 ***faceShapes) {
+bool CharacterGenerator::start(EoBCharacter *characters, uint8 ***faceShapes, bool defaultParty) {
 	if (!characters || !faceShapes) {
 		warning("CharacterGenerator::start: Called without character data");
 		return true;
@@ -188,6 +196,24 @@ bool CharacterGenerator::start(EoBCharacter *characters, uint8 ***faceShapes) {
 	_vm->snd_stopSound();
 	_vm->delay(_vm->_tickLength);
 
+	if (defaultParty)
+		createDefaultParty();
+	else if (!createCustomParty(faceShapes))
+		return false;
+
+	if (!_vm->shouldQuit()) {
+		processSpecialButton(15);
+		finish();
+	}
+
+	if (_vm->game() == GI_EOB2)
+		_vm->snd_fadeOut();
+
+	*faceShapes = _faceShapes;
+	return true;
+}
+
+bool CharacterGenerator::createCustomParty(uint8 ***faceShapes) {
 	init();
 
 	_screen->setScreenDim(2);
@@ -253,18 +279,14 @@ bool CharacterGenerator::start(EoBCharacter *characters, uint8 ***faceShapes) {
 		}
 	}
 
-	if (!_vm->shouldQuit()) {
-		processSpecialButton(15);
-		finish();
-	}
-
-	if (_vm->game() == GI_EOB2)
-		_vm->snd_fadeOut();
-
-	*faceShapes = _faceShapes;
 	return true;
 }
 
+void CharacterGenerator::createDefaultParty() {
+	assert(_chargenDefaultNames);
+	assert(_chargenDefaultStats);
+}
+
 void CharacterGenerator::init() {
 	_screen->loadShapeSetBitmap("CHARGENA", 5, 3);
 	if (_faceShapes) {
@@ -1990,9 +2012,9 @@ void TransferPartyWiz::giveKhelbensCoin() {
 
 // Start functions
 
-bool EoBCoreEngine::startCharacterGeneration() {
+bool EoBCoreEngine::startCharacterGeneration(bool defaultParty) {
 	_sound->selectAudioResourceSet(_flags.platform == Common::kPlatformAmiga ? kMusicIntro : kMusicIngame);
-	return CharacterGenerator(this, _screen).start(_characters, &_faceShapes);
+	return CharacterGenerator(this, _screen).start(_characters, &_faceShapes, defaultParty);
 }
 
 bool EoBCoreEngine::startPartyTransfer() {
diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index 820bb27fdc..7bf285e61b 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -23,9 +23,10 @@
 #ifdef ENABLE_EOB
 
 #include "kyra/engine/eob.h"
-#include "kyra/sequence/seqplayer_eob_segacd.h"
+#include "kyra/graphics/screen_eob_segacd.h"
 #include "kyra/resource/resource.h"
 #include "kyra/resource/resource_segacd.h"
+#include "kyra/sequence/seqplayer_eob_segacd.h"
 #include "kyra/sound/sound.h"
 #include "kyra/text/text_eob_segacd.h"
 
@@ -89,6 +90,8 @@ Common::Error EoBEngine::init() {
 	if (_flags.platform == Common::kPlatformPC98) {
 		_screen->modifyScreenDim(28, 0x0A, 0xA4, 0x15, 0x18);
 		_screen->modifyScreenDim(12, 0x01, 0x04, 0x14, 0x9A);
+	//} else if (_flags.platform == Common::kPlatformSegaCD) {
+		//_screen->modifyScreenDim(28, 0x01, 0x17, 0x23, 0x18);
 	} else {
 		_screen->modifyScreenDim(12, 0x01, 0x04, 0x14, 0xA0);
 	}
@@ -118,6 +121,32 @@ Common::Error EoBEngine::init() {
 		assert(_txt);
 	}
 
+	//////////////////////////////77
+	////////////////////////////////
+	////////////////////////////////
+	_sres->loadContainer("ITEM");
+	uint32 tsize;
+	uint8 *in = _sres->resData(0, &tsize);
+	_screen->sega_getRenderer()->loadToVRAM(in, 128, 0xFF80);
+	delete[] in;
+	int hw = 5;
+	_screen->sega_getAnimator()->initSprite(0, 0, 0, 0xE7FC, 5);
+	_screen->sega_getAnimator()->update();
+	_screen->sega_getRenderer()->render(2, true);
+	_screen->sega_getAnimator()->clearSprites();
+	_screen->sega_getAnimator()->update();
+	int cp = _screen->setCurPage(2);
+	_itemIconShapes = new const uint8*[_numItemIconShapes];
+	memset(_itemIconShapes, 0, _numItemIconShapes * sizeof(uint8*));
+	_itemIconShapes[0] = _screen->encodeShape(0, 0, ((hw & 3) + 1), ((hw >> 2) + 1) << 3);
+	_screen->setCurPage(cp);
+	/////////////////////7
+	//////////////////////
+	////////////////
+
+
+
+
 	return Common::kNoError;
 }
 
diff --git a/engines/kyra/engine/eob.h b/engines/kyra/engine/eob.h
index 5bd13052e7..bdc06a2ea2 100644
--- a/engines/kyra/engine/eob.h
+++ b/engines/kyra/engine/eob.h
@@ -91,8 +91,8 @@ private:
 	void seq_xdeath() override;
 
 	void seq_segaOpeningCredits();
-	void seq_segaSetupSequence(int id);
-	bool seq_segaPlaySequence(int sequenceId, int setupID = -1);
+	void seq_segaSetupSequence(int sequenceId);
+	bool seq_segaPlaySequence(int sequenceId, bool init = false);
 
 
 	const char *const *_finBonusStrings;
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index a53592fd1c..a3b3ebf503 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -32,7 +32,6 @@
 
 #include "common/config-manager.h"
 #include "common/translation.h"
-#include "common/debug-channels.h"
 
 #include "gui/error.h"
 
@@ -632,9 +631,6 @@ Common::Error EoBCoreEngine::init() {
 	memset(_monsterStoneOverlay, (_flags.platform == Common::kPlatformAmiga) ? guiSettings()->colors.guiColorWhite : 0x0D, 16 * sizeof(uint8));
 	_monsterFlashOverlay[0] = _monsterStoneOverlay[0] = 0;
 
-	gDebugLevel = 7;
-	DebugMan.enableDebugChannel(kDebugLevelSequence);
-
 	return Common::kNoError;
 }
 
@@ -668,16 +664,20 @@ void EoBCoreEngine::loadFonts() {
 		_screen->loadFont(Screen::FID_SJIS_SMALL_FNT, "FONT12.FNT");
 	} else if (_flags.platform == Common::kPlatformSegaCD) {
 		_screen->loadFont(Screen::FID_8_FNT, "FONTK12");
-		_screen->loadFont(Screen::FID_6_FNT, "FONT8SH");
+		//_screen->loadFont(Screen::FID_6_FNT, "FONT8SH");
+		_screen->setFontStyles(Screen::FID_8_FNT, _flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleFat);
 	}
 }
 
 Common::Error EoBCoreEngine::go() {
 	static_cast<Debugger_EoB *>(getDebugger())->initialize();
 	_txt->removePageBreakFlag();
-	//_screen->setFont(_flags.platform == Common::kPlatformPC98 ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT);
+	_screen->setFont(_flags.platform == Common::kPlatformPC98 ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT);
 	//loadItemsAndDecorationsShapes();
-	//_screen->setMouseCursor(0, 0, _itemIconShapes[0]);
+
+
+	_screen->setMouseCursor(0, 0, _itemIconShapes[0]);
+	/*setHandItem(0);*/
 
 	// Import original save game files (especially the "Quick Start Party")
 	if (ConfMan.getBool("importOrigSaves")) {
@@ -707,9 +707,9 @@ Common::Error EoBCoreEngine::go() {
 			startupLoad();
 			repeatLoop = _gui->runLoadMenu(72, 14, true);
 				
-		} else if (action == -2) {
+		} else if (action == -2 || action == -4) {
 			// new game
-			repeatLoop = startCharacterGeneration();
+			repeatLoop = startCharacterGeneration(action == -4);
 			if (repeatLoop && !shouldQuit())
 				startupNew();
 		} else if (action == -3) {
@@ -720,7 +720,7 @@ Common::Error EoBCoreEngine::go() {
 		}
 	}
 
-	if (!shouldQuit() && action >= -3) {
+	if (!shouldQuit() && action >= -4) {
 		runLoop();
 
 		if (_playFinale) {
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index 863eac1124..9435716ca6 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -328,7 +328,7 @@ protected:
 	bool _runFlag;
 
 	// Character generation / party transfer
-	bool startCharacterGeneration();
+	bool startCharacterGeneration(bool defaultParty);
 	bool startPartyTransfer();
 
 	uint8 **_faceShapes;
diff --git a/engines/kyra/graphics/screen.cpp b/engines/kyra/graphics/screen.cpp
index 4976713297..d8ca112e1d 100644
--- a/engines/kyra/graphics/screen.cpp
+++ b/engines/kyra/graphics/screen.cpp
@@ -59,7 +59,8 @@ Screen::Screen(KyraEngine_v1 *vm, OSystem *system, const ScreenDim *dimTable, co
 	_sjisMixedFontMode = false;
 
 	_screenPalette = _internFadePalette = 0;
-	_animBlockPtr = _decodeShapeBuffer = 0;
+	_animBlockPtr = _textRenderBuffer = 0;
+	_textRenderBufferSize = 0;
 
 	_useHiColorScreen = _vm->gameFlags().useHiColorMode;
 	_use256ColorMode = true;
@@ -89,7 +90,7 @@ Screen::~Screen() {
 
 	delete _screenPalette;
 	delete _internFadePalette;
-	delete[] _decodeShapeBuffer;
+	delete[] _textRenderBuffer;
 	delete[] _animBlockPtr;
 	delete[] _16bitPalette;
 	delete[] _16bitConversionPalette;
@@ -242,13 +243,11 @@ bool Screen::init() {
 
 	_curDimIndex = -1;
 	_curDim = 0;
-	_charWidth = 0;
-	_charOffset = 0;
+	_charSpacing = 0;
+	_lineSpacing = 0;
 	for (int i = 0; i < ARRAYSIZE(_textColorsMap); ++i)
 		_textColorsMap[i] = i;
 	_textColorsMap16bit[0] = _textColorsMap16bit[1] = 0;
-	_decodeShapeBuffer = NULL;
-	_decodeShapeBufferSize = 0;
 	_animBlockPtr = NULL;
 	_animBlockSize = 0;
 	_mouseLockCount = 1;
@@ -1317,6 +1316,11 @@ void Screen::setTextColor16bit(const uint16 *cmap16) {
 	}
 }
 
+void Screen::setFontStyles(FontId fontId, int styles) {
+	assert(_fonts[fontId]);
+	_fonts[fontId]->setStyles(styles);
+}
+
 bool Screen::loadFont(FontId fontId, const char *filename) {
 	if (fontId == FID_SJIS_FNT) {
 		warning("Trying to replace system SJIS font");
@@ -1362,10 +1366,14 @@ int Screen::getFontWidth() const {
 
 int Screen::getCharWidth(uint16 c) const {
 	const int width = _fonts[_currentFont]->getCharWidth(c);
-	return width + ((_currentFont != FID_SJIS_FNT && _currentFont != FID_SJIS_LARGE_FNT && _currentFont != FID_SJIS_SMALL_FNT) ? _charWidth : 0);
+	return width + ((_currentFont != FID_SJIS_FNT && _currentFont != FID_SJIS_LARGE_FNT && _currentFont != FID_SJIS_SMALL_FNT) ? _charSpacing : 0);
 }
 
-int Screen::getTextWidth(const char *str) {
+int Screen::getCharHeight(uint16 c) const {
+	return _fonts[_currentFont]->getCharHeight(c);
+}
+
+int Screen::getTextWidth(const char *str, bool nextWordOnly) {
 	int curLineLen = 0;
 	int maxLineLen = 0;
 
@@ -1378,7 +1386,7 @@ int Screen::getTextWidth(const char *str) {
 
 		uint c = fetchChar(str);
 
-		if (c == 0) {
+		if (c == 0 || (nextWordOnly && (c == 32 || c == 0x4081))) {
 			break;
 		} else if (c == '\r') {
 			if (curLineLen > maxLineLen)
@@ -1393,13 +1401,13 @@ int Screen::getTextWidth(const char *str) {
 	return MAX(curLineLen, maxLineLen);
 }
 
-void Screen::printText(const char *str, int x, int y, uint8 color1, uint8 color2) {
+void Screen::printText(const char *str, int x, int y, uint8 color1, uint8 color2, int pitch) {
 	uint16 cmap16[2];
 	if (_16bitPalette) {
 		cmap16[0] = color2 ? shade16bitColor(_16bitPalette[color2]) : 0xFFFF;
 		cmap16[1] = _16bitPalette[color1];
 		setTextColor16bit(cmap16);
-	}	
+	}
 
 	uint8 cmap8[2];
 	cmap8[0] = color2;
@@ -1420,29 +1428,39 @@ void Screen::printText(const char *str, int x, int y, uint8 color1, uint8 color2
 	else if (y >= _screenHeight)
 		return;
 
+	int charHeight = 0;
+	bool enableWordWrap = _isSegaCD && _vm->gameFlags().lang != Common::JA_JPN;
+
 	while (1) {
 		if (_sjisMixedFontMode && curType == Font::kASCII)
 			setFont((*str & 0x80) ? ((_vm->game() == GI_EOB2 && curFont == FID_6_FNT) ? FID_SJIS_SMALL_FNT : FID_SJIS_FNT) : curFont);
 
-		uint8 charHeightFnt = getFontHeight();
-
 		uint c = fetchChar(str);
+		charHeight = MAX<int>(charHeight, getCharHeight(c));
 
 		if (c == 0) {
 			break;
 		} else if (c == '\r') {
 			x = x_start;
-			y += (charHeightFnt + _charOffset);
+			y += (charHeight + _lineSpacing);
 		} else {
 			int charWidth = getCharWidth(c);
-			if (x + charWidth > _textMarginRight) {
+			int needSpace = enableWordWrap ? getTextWidth(str, true) + charWidth : charWidth;
+			if (x + needSpace > _textMarginRight) {
 				x = x_start;
-				y += (charHeightFnt + _charOffset);
+				y += (charHeight + _lineSpacing);
+				if (enableWordWrap) {
+					// skip space at beginning of the line
+					c = fetchChar(str);
+					if (c == 0)
+						return;
+					charWidth = getCharWidth(c);
+				}
 				if (y >= _screenHeight)
 					break;
 			}
 
-			drawChar(c, x, y);
+			drawChar(c, x, y, pitch);
 			x += charWidth;
 		}
 	}
@@ -1461,7 +1479,7 @@ uint16 Screen::fetchChar(const char *&s) const {
 	return ch;
 }
 
-void Screen::drawChar(uint16 c, int x, int y) {
+void Screen::drawChar(uint16 c, int x, int y, int pitch) {
 	Font *fnt = _fonts[_currentFont];
 	assert(fnt);
 
@@ -1474,7 +1492,9 @@ void Screen::drawChar(uint16 c, int x, int y) {
 	if (x + charWidth > SCREEN_W || y + charHeight > _screenHeight)
 		return;
 
-	if (useOverlay) {
+	if (_isSegaCD) {
+		fnt->drawChar(c, _textRenderBuffer + (((y >> 3) * pitch + (x >> 3)) << 5) + ((y & 7) << 2) + ((x & 7) >> 1), pitch, x & 7, y & 7);
+	} else if (useOverlay) {
 		uint8 *destPage = getOverlayPtr(_curPage);
 		if (!destPage) {
 			warning("trying to draw SJIS char on unsupported page %d", _curPage);
@@ -1489,7 +1509,7 @@ void Screen::drawChar(uint16 c, int x, int y) {
 		fnt->drawChar(c, getPagePtr(_curPage) + y * SCREEN_W * _bytesPerPixel + x * _bytesPerPixel, SCREEN_W, _bytesPerPixel);
 	}
 
-	if (_curPage == 0 || _curPage == 1)
+	if (!_isSegaCD && (_curPage == 0 || _curPage == 1))
 		addDirtyRect(x, y, charWidth, charHeight);
 }
 
@@ -3754,7 +3774,7 @@ void AMIGAFont::unload() {
 }
 
 SJISFont::SJISFont(Common::SharedPtr<Graphics::FontSJIS> &font, const uint8 invisColor, bool is16Color, bool drawOutline, int extraSpacing)
-	: _colorMap(0), _font(font), _invisColor(invisColor), _isTextMode(is16Color), _style(kFSNone), _drawOutline(drawOutline), _sjisWidthOffset(extraSpacing) {
+	: _colorMap(0), _font(font), _invisColor(invisColor), _isTextMode(is16Color), _style(kStyleNone), _drawOutline(drawOutline), _sjisWidthOffset(extraSpacing) {
 	assert(_font);
 	_sjisWidth = _font->getMaxFontWidth() >> 1;
 	_fontHeight = _font->getFontHeight() >> 1;
@@ -3798,7 +3818,7 @@ void SJISFont::drawChar(uint16 c, byte *dst, int pitch, int) const {
 	else
 		_font->setDrawingMode(_drawOutline ? Graphics::FontSJIS::kOutlineMode : Graphics::FontSJIS::kDefaultMode);
 
-	_font->toggleFatPrint(_style == kFSFat);
+	_font->toggleFatPrint(_style == kStyleFat);
 	_font->drawChar(dst, c, 640, 1, color1, color2, 640, 400);
 }
 
diff --git a/engines/kyra/graphics/screen.h b/engines/kyra/graphics/screen.h
index 3a3c8b1282..359f7f78da 100644
--- a/engines/kyra/graphics/screen.h
+++ b/engines/kyra/graphics/screen.h
@@ -104,6 +104,13 @@ public:
 	 */
 	virtual int getCharWidth(uint16 c) const = 0;
 
+	/**
+	 * Gets the height of a specific character. The only font that requires an
+	 * implemenation for this is the SegaCD one. For all other font this is a
+	 * fixed value.
+	 */
+	virtual int getCharHeight(uint16 c) const { return getHeight(); }
+
 	/**
 	 * Sets a text palette map. The map contains 16 entries.
 	 */
@@ -115,15 +122,19 @@ public:
 	virtual void set16bitColorMap(const uint16 *src) {}
 	
 	enum FontStyle {
-		kFSNone = 0,
-		kFSLeftShadow,
-		kFSFat
+		kStyleNone			=	0,
+		kStyleLeftShadow	=	1	<<	0,
+		kStyleFat			=	1	<<	1,
+		kStyleNarrow		=	1	<<	2,
+		kStyleVariant		=	1	<<	3,
+		kStyleForceTwoByte	=	1	<<	4,
+		kStyleFixedWidth	=	1	<<	5
 	};
 
 	/**
 	* Sets a drawing style. Only rudimentary implementation based on what is needed.
 	*/
-	virtual void setStyle(FontStyle style) {}
+	virtual void setStyles(int styles) {}
 
 	/**
 	 * Draws a specific character.
@@ -134,6 +145,8 @@ public:
 	 * handling from outside Screen.
 	 */
 	virtual void drawChar(uint16 c, byte *dst, int pitch, int bpp) const = 0;
+
+	virtual void drawChar(uint16 c, byte *dst, int pitch, int xOffs, int yOffs) const {}
 };
 
 /**
@@ -220,7 +233,7 @@ public:
 	int getWidth() const override;
 	int getCharWidth(uint16 c) const override;
 	void setColorMap(const uint8 *src) override;
-	void setStyle(FontStyle style) override { _style = style; }
+	void setStyles(int style) override { _style = style; }
 	void drawChar(uint16 c, byte *dst, int pitch, int) const override;
 
 protected:
@@ -229,7 +242,7 @@ protected:
 	int _sjisWidth, _asciiWidth;
 	int _fontHeight;
 	const bool _drawOutline;
-	FontStyle _style;
+	int _style;
 
 private:
 	const uint8 _invisColor;
@@ -489,13 +502,15 @@ public:
 	int getFontWidth() const;
 
 	int getCharWidth(uint16 c) const;
-	int getTextWidth(const char *str);
+	int getCharHeight(uint16 c) const;
+	int getTextWidth(const char *str, bool nextWordOnly = false);
 
-	void printText(const char *str, int x, int y, uint8 color1, uint8 color2);
+	void printText(const char *str, int x, int y, uint8 color1, uint8 color2, int pitch = -1);
 
 	virtual void setTextColorMap(const uint8 *cmap) = 0;
 	void setTextColor(const uint8 *cmap, int a, int b);
 	void setTextColor16bit(const uint16 *cmap16);
+	void setFontStyles(FontId fontId, int styles);
 
 	const ScreenDim *getScreenDim(int dim) const;
 	void modifyScreenDim(int dim, int x, int y, int w, int h);
@@ -548,8 +563,8 @@ public:
 	void blockInRegion(int x, int y, int width, int height);
 	void blockOutRegion(int x, int y, int width, int height);
 
-	int _charWidth;
-	int _charOffset;
+	int _charSpacing;
+	int _lineSpacing;
 	int _curPage;
 	uint8 *_shapePages[2];
 	int _maskMinY, _maskMaxY;
@@ -595,7 +610,7 @@ protected:
 
 	// font/text specific
 	uint16 fetchChar(const char *&s) const;
-	void drawChar(uint16 c, int x, int y);
+	void drawChar(uint16 c, int x, int y, int pitch = -1);
 
 	int16 encodeShapeAndCalculateSize(uint8 *from, uint8 *to, int size);
 
@@ -639,8 +654,8 @@ protected:
 	uint8 _textColorsMap[16];
 	uint16 _textColorsMap16bit[2];
 
-	uint8 *_decodeShapeBuffer;
-	int _decodeShapeBufferSize;
+	uint8 *_textRenderBuffer;
+	int _textRenderBufferSize;
 
 	uint8 *_animBlockPtr;
 	int _animBlockSize;
diff --git a/engines/kyra/graphics/screen_eob.cpp b/engines/kyra/graphics/screen_eob.cpp
index 8e201956bc..f09c8bd93e 100644
--- a/engines/kyra/graphics/screen_eob.cpp
+++ b/engines/kyra/graphics/screen_eob.cpp
@@ -30,6 +30,7 @@
 #include "kyra/engine/eobcommon.h"
 #include "kyra/resource/resource.h"
 #include "kyra/engine/util.h"
+#include "kyra/graphics/screen_eob_segacd.h"
 
 #include "common/system.h"
 #include "common/memstream.h"
@@ -67,6 +68,7 @@ Screen_EoB::Screen_EoB(EoBCoreEngine *vm, OSystem *system) : Screen(vm, system,
 	_segaAnimator = 0;
 	_segaCustomPalettes = 0;
 	_palFaders = 0;
+	_specialColorReplace = false;
 	memset(_segaCurPalette, 0, sizeof(_segaCurPalette));
 }
 
@@ -95,9 +97,8 @@ bool Screen_EoB::init() {
 		if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
 			_shpBuffer = new uint8[SCREEN_H * SCREEN_W];
 			_convertHiColorBuffer = new uint8[SCREEN_H * SCREEN_W];
-			enableHiColorMode(true);			
-			assert(_fonts[FID_SJIS_FNT]);
-			_fonts[FID_SJIS_FNT]->setStyle(Font::kFSFat);
+			enableHiColorMode(true);
+			setFontStyles(FID_SJIS_FNT, Font::kStyleFat);
 			_fonts[FID_SJIS_LARGE_FNT] = new SJISFontLarge(_sjisFontShared);
 		} else if (_vm->game() == GI_EOB1 && _vm->gameFlags().platform == Common::kPlatformPC98) {
 			_fonts[FID_SJIS_FNT] = new SJISFontEoB1PC98(_sjisFontShared, /*12,*/ _vm->staticres()->loadRawDataBe16(kEoB1Ascii2SjisTable1, temp), _vm->staticres()->loadRawDataBe16(kEoB1Ascii2SjisTable2, temp));
@@ -124,6 +125,9 @@ bool Screen_EoB::init() {
 			sega_initGraphics();
 			_segaCustomPalettes = new uint16[128];
 			_palFaders = new PaletteFader[4];
+			_textRenderBufferSize = SCREEN_W * _screenHeight;
+			_textRenderBuffer = new uint8[_textRenderBufferSize];
+			memset(_textRenderBuffer, 0, _textRenderBufferSize);
 			memset(_segaCustomPalettes, 0, 128 * sizeof(uint16));
 		}
 
@@ -251,8 +255,10 @@ void Screen_EoB::loadFileDataToPage(Common::SeekableReadStream *s, int pageNum,
 	s->read(_pagePtrs[pageNum], size);
 }
 
-void Screen_EoB::printShadedText(const char *string, int x, int y, int col1, int col2, int shadowCol) {
-	if (_vm->gameFlags().lang != Common::JA_JPN) {
+void Screen_EoB::printShadedText(const char *string, int x, int y, int col1, int col2, int shadowCol, int pitch) {
+	if (_isSegaCD) {
+		printText(string, x + 1, y + 1, shadowCol, 0, pitch);
+	} else if (_vm->gameFlags().lang != Common::JA_JPN) {
 		printText(string, x - 1, y, shadowCol, col2);
 		printText(string, x, y + 1, shadowCol, 0);
 		printText(string, x - 1, y + 1, shadowCol, 0);
@@ -260,15 +266,13 @@ void Screen_EoB::printShadedText(const char *string, int x, int y, int col1, int
 		fillRect(x, y, x + getTextWidth(string) - 1, y + getFontHeight() - 1, col2);
 	}
 
-	if (_vm->gameFlags().use16ColorMode) {
-		assert(_fonts[_currentFont]);
-		_fonts[_currentFont]->setStyle(Font::kFSLeftShadow);
-	}
+	if (_vm->gameFlags().use16ColorMode)
+		setFontStyles(_currentFont, Font::kStyleLeftShadow);
 
-	printText(string, x, y, col1, 0);
+	printText(string, x, y, col1, 0, pitch);
 
 	if (_vm->gameFlags().use16ColorMode)
-		_fonts[_currentFont]->setStyle(Font::kFSNone);
+		setFontStyles(_currentFont, Font::kStyleNone);
 }
 
 void Screen_EoB::loadShapeSetBitmap(const char *file, int tempPage, int destPage) {
@@ -1512,7 +1516,8 @@ bool Screen_EoB::loadFont(FontId fontId, const char *filename) {
 	} else if (_isAmiga) {
 		fnt = new AmigaDOSFont(_vm->resource(), _vm->game() == GI_EOB2 && _vm->gameFlags().lang == Common::DE_DEU);
 	} else if (_isSegaCD) {
-			fnt = new SegaCDFont();
+		fnt = new SegaCDFont(_vm->staticres()->loadRawDataBe16(kEoB1Ascii2SjisTable1, temp), _vm->staticres()->loadRawDataBe16(kEoB1Ascii2SjisTable2, temp),
+			_vm->staticres()->loadRawData(kEoB1CharWidthTable1, temp), _vm->staticres()->loadRawData(kEoB1CharWidthTable1, temp), _vm->staticres()->loadRawData(kEoB1CharWidthTable1, 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);
@@ -1755,11 +1760,11 @@ const uint8 Screen_EoB::_egaMatchTable[] = {
 uint16 *OldDOSFont::_cgaDitheringTable = 0;
 int OldDOSFont::_numRef = 0;
 
-OldDOSFont::OldDOSFont(Common::RenderMode mode, uint8 shadowColor) : _renderMode(mode), _shadowColor(shadowColor) {
+OldDOSFont::OldDOSFont(Common::RenderMode mode, uint8 shadowColor) : _renderMode(mode), _shadowColor(shadowColor), _colorMap8bit(0), _colorMap16bit(0) {
 	_data = 0;
 	_width = _height = _numGlyphs = 0;
 	_bitmapOffsets = 0;
-	_style = kFSNone;
+	_style = kStyleNone;
 
 	_numRef++;
 	if (!_cgaDitheringTable && _numRef == 1) {
@@ -1824,7 +1829,7 @@ void OldDOSFont::drawChar(uint16 c, byte *dst, int pitch, int bpp) const {
 	uint16 color1 = _colorMap8bit[1];
 	uint16 color2 = _colorMap8bit[0];
 
-    if (_style == kFSLeftShadow) {
+    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);
diff --git a/engines/kyra/graphics/screen_eob.h b/engines/kyra/graphics/screen_eob.h
index 796f319011..fcfa807d16 100644
--- a/engines/kyra/graphics/screen_eob.h
+++ b/engines/kyra/graphics/screen_eob.h
@@ -49,7 +49,7 @@ public:
 
 	void loadFileDataToPage(Common::SeekableReadStream *s, int pageNum, uint32 size);
 
-	void printShadedText(const char *string, int x, int y, int col1, int col2, int shadowCol);
+	void printShadedText(const char *string, int x, int y, int col1, int col2, int shadowCol, int pitch = -1);
 
 	void loadBitmap(const char *filename, int tempPage, int dstPage, Palette *pal, bool skip = false) override;
 	void loadEoBBitmap(const char *file, const uint8 *cgaMapping, int tempPage, int destPage, int convertToPage);
@@ -127,6 +127,9 @@ public:
 	void sega_fadeToWhite(int delay) { sega_fadePalette(delay, 7); }
 	void sega_fadeToNeutral(int delay) { sega_fadePalette(delay, 0); }
 	void sega_paletteOps(int16 opPal, int16 par1, int16 par2);
+	void sega_clearTextBuffer(uint8 col);
+	void sega_loadTextBufferToVRAM(uint16 srcOffset, uint16 addr, int size);
+	void sega_gfxScale(uint8 *out, uint16 w, uint16 h, uint16 pitch, const uint8 *in, const uint16 *stampMap, const uint16 *traceVectors);
 
 	SegaRenderer *sega_getRenderer() const { return _segaRenderer; }
 	SegaAnimator *sega_getAnimator() const { return _segaAnimator; }
@@ -187,6 +190,7 @@ private:
 	};
 
 	PaletteFader *_palFaders;
+	bool _specialColorReplace;
 	SegaRenderer *_segaRenderer;
 	SegaAnimator *_segaAnimator;
 	uint16 _segaCurPalette[64];
@@ -210,13 +214,13 @@ public:
 	int getCharWidth(uint16 c) const override;
 	void setColorMap(const uint8 *src) override;
 	void set16bitColorMap(const uint16 *src) override { _colorMap16bit = src; }
-	void setStyle(FontStyle style) override { _style = style; }
+	void setStyles(int styles) override { _style = styles; }
 	void drawChar(uint16 c, byte *dst, int pitch, int bpp) const override;
 
 protected:
 	void unload();
 
-	FontStyle _style;
+	int _style;
 	const uint8 *_colorMap8bit;
 	uint8 *_data;
 	uint16 *_bitmapOffsets;
@@ -385,23 +389,33 @@ private:
 
 class SegaCDFont : public Font {
 public:
-	SegaCDFont();
+	SegaCDFont(const uint16 *convTable1, const uint16 *convTable2, const uint8 *widthTable1, const uint8 *widthTable2, const uint8 *widthTable3);
 	~SegaCDFont() override;
 
 	bool load(Common::SeekableReadStream &file) override;
 	Type getType() const override { return kSJIS; }
 	int getHeight() const override { return _height; }
 	int getWidth() const override { return _width; }
-	int getCharWidth(uint16 c) const override { return _width; }
+	int getCharWidth(uint16 c) const override;
+	int getCharHeight(uint16 c) const override;
+	void setStyles(int styles) override;
 	void setColorMap(const uint8 *src) override { _colorMap = src; }
-	void drawChar(uint16 c, byte *dst, int pitch, int) const override;
+	void drawChar(uint16 c, byte *dst, int pitch, int bpp) const override { drawChar(c, dst, pitch, 0, 0); }
+	void drawChar(uint16 c, byte *dst, int pitch, int xOffs, int yOffs) const override;
 
 private:
-	uint16 convert(uint16 c) const;
+	const uint8 *getGlyphData(uint16 c, uint8 &charWidth, uint8 &charHeight, uint8 &pitch) const;
+
+	const uint8 *_data;
+	const uint8 *_buffer;
+	bool _forceTwoByte;
+	bool _fixedWidth;
+	uint8 _style;
 
-	uint8 *_data;
 	const uint8 *_colorMap;
 	const int _height, _width;
+	const uint16 *_convTable1, *_convTable2;
+	const uint8 *_widthTable1, *_widthTable2, *_widthTable3;
 };
 
 } // End of namespace Kyra
diff --git a/engines/kyra/graphics/screen_eob_pc98.cpp b/engines/kyra/graphics/screen_eob_pc98.cpp
index 0af6826e12..7c0f13c43e 100644
--- a/engines/kyra/graphics/screen_eob_pc98.cpp
+++ b/engines/kyra/graphics/screen_eob_pc98.cpp
@@ -176,7 +176,7 @@ int SJISFontEoB1PC98::getCharWidth(uint16 c) const {
 
 void SJISFontEoB1PC98::drawChar(uint16 c, byte *dst, int pitch, int) const {
 	c = convert(c);
-	_font->setDrawingMode(_style == kFSLeftShadow ? Graphics::FontSJIS::kShadowLeftMode : Graphics::FontSJIS::kDefaultMode);
+	_font->setDrawingMode(_style == kStyleLeftShadow ? Graphics::FontSJIS::kShadowLeftMode : Graphics::FontSJIS::kDefaultMode);
 	_font->toggleFatPrint(false);
 	_font->drawChar(dst, c, 640, 1, _colorMap[1], _colorMap[0], 640, 400);
 }
diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index 42b0ed07d8..2f347a492b 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -65,11 +65,11 @@ void Screen_EoB::sega_selectPalette(int srcPalID, int dstPalID, bool set) {
 	for (int i = 0; i < 16; ++i) {
 		uint16 in = *src++;
 		_segaCurPalette[dstPalID << 4 | i] = in;
-#if 0
+#if 1
 		static const uint8 col[8] = { 0, 52, 87, 116, 144, 172, 206, 255 };
-		*dst++ = col[CLIP<int>(((in & 0x00F) >> 1) + brightness, 0, 7)];
-		*dst++ = col[CLIP<int>(((in & 0x0F0) >> 5) + brightness, 0, 7)];
-		*dst++ = col[CLIP<int>(((in & 0xF00) >> 9) + brightness, 0, 7)];
+		*dst++ = col[CLIP<int>(((in & 0x00F) >> 1) + _palFaders[dstPalID]._brCur, 0, 7)];
+		*dst++ = col[CLIP<int>(((in & 0x0F0) >> 5) + _palFaders[dstPalID]._brCur, 0, 7)];
+		*dst++ = col[CLIP<int>(((in & 0xF00) >> 9) + _palFaders[dstPalID]._brCur, 0, 7)];
 #else
 		*dst++ = CLIP<int>(((in & 0x00F) >> 1) + _palFaders[dstPalID]._brCur, 0, 7) * 255 / 7;
 		*dst++ = CLIP<int>(((in & 0x0F0) >> 5) + _palFaders[dstPalID]._brCur, 0, 7) * 255 / 7;
@@ -78,30 +78,14 @@ void Screen_EoB::sega_selectPalette(int srcPalID, int dstPalID, bool set) {
 	}
 
 	getPalette(0).copy(rgbColors, 0, 16, dstPalID << 4);
-/*
-	// Make half intensity palette
-	dst = rgbColors;
-	for (int i = 0; i < 16; ++i) {
-		*dst++ >= 1;
-		*dst++ >= 1;
-		*dst++ >= 1;
-	}
-
-	getPalette(0).copy(rgbColors, 0, 16, (dstPalID << 4) | 0x40);
-	memcpy(rgbColors, getPalette(0).getData() + (dstPalID << 4), 48);
 
-	// Make double intensity palette
-	dst = rgbColors;
-	for (int i = 0; i < 16; ++i) {
-		*dst++ <= 1;
-		*dst++ <= 1;
-		*dst++ <= 1;
+	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]);
 	}
 
-	getPalette(0).copy(rgbColors, 0, 16, (dstPalID << 4) | 0x80);
-*/
-
-	if (set) 
+	if (set)
 		setScreenPalette(getPalette(0));
 }
 
@@ -213,14 +197,61 @@ void Screen_EoB::sega_paletteOps(int16 op, int16 par1, int16 par2) {
 		// Force palette update, don't wait
 		break;
 	case 4:
-		// TODO
-		assert(0);
+		_specialColorReplace = par1;
 		break;
 	default:
 		sega_fadePalette(par2, par1, op, false);
 	}
 }
 
+void Screen_EoB::sega_clearTextBuffer(uint8 col) {
+	memset(_textRenderBuffer, col, _textRenderBufferSize);
+}
+
+void Screen_EoB::sega_loadTextBufferToVRAM(uint16 srcOffset, uint16 addr, int size) {
+	_segaRenderer->loadToVRAM(_textRenderBuffer + srcOffset, size, addr);
+}
+
+void Screen_EoB::sega_gfxScale(uint8 *out, uint16 w, uint16 h, uint16 pitch, const uint8 *in, const uint16 *stampMap, const uint16 *traceVectors) {
+	/* Implement only the required functions:
+	 - no support for stamp size other than 0
+	 - no support for rotation/flipping
+	 */
+	while (h--) {
+		uint32 xt = *traceVectors++ << 8;
+		uint32 yt = *traceVectors++ << 8;
+		int16 hStep = (int16)(*traceVectors++);
+		int16 vStep = (int16)(*traceVectors++);
+		uint8 hcnt = 0;
+		uint8 *out2 = out;
+
+		for (int x = 0; x < w; ++x) {
+			uint16 s = stampMap[((yt >> 11) & 0xF0) + ((xt >> 15) & 0x0F)];
+			uint8 val = 0;
+			//uint16 rotateFlip = (s >> 11) & 0x1C;
+			s &= 0x7FF;
+			if (!(yt & 0xF80000) && !(xt & 0xF80000) && s) {
+				val = in[(s << 7) + ((yt >> 9) & 0x3C) + ((xt >> 8) & 0x40) + ((xt >> 12) & 3)];
+				if (!(xt & 0x800))
+					val >>= 4;
+			}
+
+			if (x & 1)
+				*out++ |= (val & 0x0F);
+			else
+				*out = val << 4;
+
+			xt += hStep;
+			yt += vStep;
+			if (++hcnt == 8) {
+				out = out + (pitch << 5) + 28;
+				hcnt = 0;
+			}
+		}
+		out = out2 + 4;
+	}
+}
+
 #if SEGA_PERFORMANCE
 #define mRenderLineFragment(hFlip, oddStart, oddEnd, useMask, dst, mask, src, start, end, pal) \
 { \
@@ -257,7 +288,6 @@ SegaRenderer::SegaRenderer(Screen_EoB *screen) : _screen(screen), _drChain(0), _
 	_vsram = new uint16[40];
 	assert(_vsram);
 	memset(_vsram, 0, 40 * sizeof(uint16));
-	int temp;
 
 #if SEGA_PERFORMANCE
 	static const SegaRenderer::renderFuncD funcD[8] = {
@@ -286,9 +316,6 @@ SegaRenderer::SegaRenderer(Screen_EoB *screen) : _screen(screen), _drChain(0), _
 	_renderLineFragmentM = funcM;
 #endif
 
-	for (int i = 0; i < 6; ++i)
-		_patternTables[i] = _screen->_vm->staticres()->loadRawDataBe16(kEoB1PatternTable0 + i, temp);
-
 	setResolution(320, 224);
 }
 
@@ -317,17 +344,16 @@ void SegaRenderer::setResolution(int w, int h) {
 
 void SegaRenderer::setPlaneTableLocation(int plane, uint16 addr) {
 	assert(plane >= kPlaneA && plane <= kWindowPlane);
-	assert(addr <= 0xFFFF);
 	_planes[plane].nameTable = (uint16*)(&_vram[addr]);
 }
 
-void SegaRenderer::setupPlaneAB(int w, int h) {
+void SegaRenderer::setupPlaneAB(int pixelWidth, int pixelHeigth) {
 	for (int i = 0; i < 2; ++i) {
-		if (w != -1)
-			_planes[i].w = w;
-		if (h != -1)
-			_planes[i].h = h;
-		_planes[i].nameTableSize = (w * h) >> 6;
+		if (pixelWidth != -1)
+			_planes[i].w = pixelWidth >> 3;
+		if (pixelHeigth != -1)
+			_planes[i].h = pixelHeigth >> 3;
+		_planes[i].nameTableSize = _planes[i].w * _planes[i].h;
 	}
 }
 
@@ -338,6 +364,7 @@ void SegaRenderer::setupWindowPlane(int blockX, int blockY, int horizontalMode,
 		_planes[kWindowPlane].blockY = verticalMode ? blockY : 0;
 	_planes[kWindowPlane].w = horizontalMode ? _blocksW - blockX : blockX;
 	_planes[kWindowPlane].h = verticalMode ? _blocksH - blockY : blockY;
+	_planes[kWindowPlane].nameTableSize = _planes[kWindowPlane].w * _planes[kWindowPlane].h;
 }
 
 void SegaRenderer::setHScrollTableLocation(int addr) {
@@ -364,7 +391,7 @@ void SegaRenderer::setVScrollMode(int mode) {
 
 void SegaRenderer::loadToVRAM(const void *data, int dataSize, int addr) {
 	assert(data);
-	assert(addr + dataSize <= 0xFFFF);
+	assert(addr + dataSize <= 0x10000);
 	memcpy(_vram + addr, data, dataSize);
 	checkUpdateDirtyRects(addr, dataSize);
 }
@@ -392,12 +419,12 @@ void SegaRenderer::loadToVRAM(Common::SeekableReadStreamEndian *in, int addr, bo
 }
 
 void SegaRenderer::memsetVRAM(int addr, uint8 val, int len) {
-	assert(addr + len <= 0xFFFF);
+	assert(addr + len <= 0x10000);
 	memset(_vram + addr, val, len);
 	void checkUpdateDirtyRects(int addr, int len);
 }
 
-void SegaRenderer::fillRectWithTiles(int vramArea, int x, int y, int w, int h, uint16 nameTblEntry, bool incr, bool topToBottom, int presetPatternID) {
+void SegaRenderer::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;
 	addDirtyRect(x << 3, y << 3, w << 3, h << 3);
 	if (y & 0x8000) {
@@ -411,15 +438,13 @@ void SegaRenderer::fillRectWithTiles(int vramArea, int x, int y, int w, int h, u
 
 	assert(addr + 2 * (y * _pitch + x + h * _pitch + w) <= 0xFFFF);
 
-	if (presetPatternID > -1) {
-		assert(presetPatternID < 6);
-		const uint16 *tbl = _patternTables[presetPatternID];
+	if (patternTable) {
 		while (h--) {
-			const uint16 *pos = tbl;
+			const uint16 *pos = patternTable;
 			for (int i = w; i; --i)
 				*dst++ = nameTblEntry + *pos++;
 			dst += ptch;
-			tbl += w;
+			patternTable += w;
 		}
 	} else if (incr) {
 		if (topToBottom) {
@@ -463,7 +488,14 @@ void SegaRenderer::writeVSRAMValue(int addr, uint16 value) {
 	_vsram[addr >> 1] = value;
 }
 
-void SegaRenderer::render(int destPageNum) {
+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, bool spritesOnly) {
 	uint8 *renderBuffer = _screen->getPagePtr(destPageNum);
 	memset(renderBuffer, 0, _screenW * _screenH);
 
@@ -476,25 +508,26 @@ void SegaRenderer::render(int destPageNum) {
 	clearDirtyRects();
 
 	// Plane B
-	renderPlanePart(kPlaneB, renderBuffer, 0, 0, _blocksW, _blocksH);
+	if (!spritesOnly)
+		renderPlanePart(kPlaneB, renderBuffer, 0, 0, _blocksW, _blocksH);
 
 	// Plane A (only draw if the nametable is not identical to that of plane B)
-	if (_planes[kPlaneA].nameTable != _planes[kPlaneB].nameTable) {
+	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].h && _planes[kWindowPlane].w) {
+		if (_planes[kWindowPlane].nameTableSize) {
 			SegaPlane *p = &_planes[kWindowPlane];
 			renderPlanePart(kPlaneA, renderBuffer, 0, 0, p->blockX, _blocksH);
 			renderPlanePart(kPlaneA, renderBuffer, 0, 0, _blocksW, p->blockY);
 			renderPlanePart(kPlaneA, renderBuffer, p->blockX + p->w, 0, _blocksW, _blocksH);
-			renderPlanePart(kPlaneA, renderBuffer, 0, p->blockY + p->h, _blocksH, _blocksH);
+			renderPlanePart(kPlaneA, renderBuffer, 0, p->blockY + p->h, _blocksW, _blocksH);
 		} else {
 			renderPlanePart(kPlaneA, renderBuffer, 0, 0, _blocksW, _blocksH);
 		}
 	}
 
 	// Window Plane
-	if (_planes[kWindowPlane].h && _planes[kWindowPlane].w) {
+	if (_planes[kWindowPlane].nameTableSize && !spritesOnly) {
 		SegaPlane *p = &_planes[kWindowPlane];
 		renderPlanePart(kWindowPlane, renderBuffer, p->blockX, p->blockY, p->blockX + p->w, p->blockY + p->h);
 	}
@@ -544,12 +577,10 @@ void SegaRenderer::render(int destPageNum) {
 	// 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 in a chain. Should be faster...
-	// The priority sprite tiles aren't rendered against the sprite mask table here which is wrong.
-	// Priority sprites are a rare thing, though. EOB probably doesn't have any...
+	// 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, 0, e->_dst, 0, e->_src, e->_start, e->_end, e->_pal)
-		
+		mRenderLineFragment(e->_hflip, e->_start & 1, e->_end & 1, e->_mask, e->_dst, e->_mask, e->_src, e->_start, e->_end, e->_pal)
+
 	clearPrioChain();
 }
 
@@ -563,7 +594,7 @@ void SegaRenderer::renderPlanePart(int plane, uint8 *dstBuffer, int x1, int y1,
 		uint8 *dst2 = dst;
 		for (int x = x1; x < x2; ++x) {
 			int vScrollTableIndex = (plane == kWindowPlane) ? -1 : (_vScrollMode == kVScrollFullScreen) ? plane : (x >> 1) + plane;
-			renderPlaneTile(dst, x, ntbl, vScrollTableIndex, hScrollTableIndex, _pitch, p->nameTableSize);
+			renderPlaneTile(dst, x, ntbl, vScrollTableIndex, hScrollTableIndex, _pitch);
 			dst += 8;
 		}
 		ntbl += _pitch;
@@ -571,7 +602,7 @@ void SegaRenderer::renderPlanePart(int plane, uint8 *dstBuffer, int x1, int y1,
 	}
 }
 
-void SegaRenderer::renderPlaneTile(uint8 *dst, int destX, uint16 *nameTable, int vScrollTableIndex, int hScrollTableIndex, uint16 pitch, uint16 nameTableSize) {
+void SegaRenderer::renderPlaneTile(uint8 *dst, int destX, const uint16 *nameTable, int vScrollTableIndex, int hScrollTableIndex, uint16 pitch) {
 	uint16 vscrNt = 0;
 	uint16 vscrPx = 0;
 
@@ -593,38 +624,44 @@ void SegaRenderer::renderPlaneTile(uint8 *dst, int destX, uint16 *nameTable, int
 		}
 
 		if (hScrollTableIndex != -1) {
-			hscrNt = _hScrollTable[hScrollTableIndex] & 0x3FF;
+			hscrNt = (-_hScrollTable[hScrollTableIndex]) & 0x3FF;
 			hscrPx = hscrNt & 7;
 			hscrNt >>= 3;
 		}
 
-		uint16 nt = nameTable[((vscrNt * pitch) % nameTableSize) + ((destX + hscrNt) % pitch)];
-		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);
-			nt = nameTable[((vscrNt * pitch) % nameTableSize) + ((destX + hscrNt + 1) % pitch)];
-			pal = ((nt >> 13) & 3) << 4;
-			hflip = (nt & 0x800);
-			y = bY % 8;
+		const uint16 *pNt = &nameTable[vscrNt * pitch + ((destX + 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)], 0, hscrPx, pal, hflip);
+				initPrioRenderTask(dst, 0, &_vram[((nt & 0x7FF) << 5) + (y << 2) + (hscrPx >> 1)], hscrPx, 8, pal, hflip);
 			else
-				mRenderLineFragment(hflip, 0, hscrPx & 1, 0, dst, 0, &_vram[((nt & 0x7FF) << 5) + (y << 2)], 0, hscrPx, pal)
+				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 = &nameTable[vscrNt * pitch + ((destX + 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)
@@ -683,23 +720,13 @@ template<bool hflip, bool oddStart, bool oddEnd> void SegaRenderer::renderLineFr
 		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) {
-			/*if (col == 0x3E)
-				*dst |= 0x40;
-			else if (col == 0x3E)
-				*dst |= 0x80;
-			else*/
-				*dst = pal | col;
+			*dst = pal | col;
 			*mask = 0;
 		}
 		dst++;
 		mask++;
 		if (col2 & *mask) {
-			/*if (col2 == 0x3E)
-				*dst |= 0x40;
-			else if (col2 == 0x3E)
-				*dst |= 0x80;
-			else*/
-				*dst = pal | col2;
+			*dst = pal | col2;
 			*mask = 0;
 		}
 		dst++;
@@ -708,12 +735,7 @@ template<bool hflip, bool oddStart, bool oddEnd> void SegaRenderer::renderLineFr
 	if (oddStart != oddEnd) {
 		uint8 col = hflip ? (oddEnd ? *src-- >> 4 : *src & 0x0F) : (oddStart ? *src++ & 0x0F : *src >> 4);
 		if (col & *mask) {
-			/*if (col == 0x3E)
-				*dst |= 0x40;
-			else if (col == 0x3E)
-				*dst |= 0x80;
-			else*/
-				*dst = pal | col;
+			*dst = pal | col;
 			*mask = 0;
 		}
 		dst++;
@@ -756,12 +778,7 @@ template<bool hflip> void SegaRenderer::renderLineFragment(uint8 *dst, uint8 *ma
 		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) {
-				/*if (col == 0x3E)
-					*dst |= 0x40;
-				else if (col == 0x3E)
-					*dst |= 0x80;
-				else*/
-					*dst = pal | col;
+				*dst = pal | col;
 				*mask = 0;
 			}
 			dst++;
@@ -807,7 +824,7 @@ void SegaRenderer::sendDirtyRectsToScreen() {
 		if (w == _screenW && h == _screenH) {
 			_screen->_forceFullUpdate = true;
 			return;
-		} 
+		}
 		_screen->addDirtyRect(e->_rect.left, e->_rect.top, w, h);
 	}
 }
@@ -906,30 +923,32 @@ void SegaAnimator::moveSprites2(int id, uint16 num, int16 addX, int16 addY) {
 void SegaAnimator::update() {
 	if (!_needUpdate)
 		return;
-	
+
 	uint16 *dst = _tempBuffer;
 	for (Sprite *s = _sprites; s != &_sprites[80]; ++s) {
 		if (s->x == 0x4000)
 			continue;
 		*dst++ = (uint16)(s->y + 128);
-		*dst++ = (*dst & 0xFF) | (s->hw << 8);
+		*dst = (*dst & 0xFF) | (s->hw << 8);
+		dst++;
 		*dst++ = s->nameTbl;
 		*dst++ = (uint16)(s->x + 128);
 	}
 
-	for (dst; dst < &_tempBuffer[320]; dst += 4)
+	for ( ; dst < &_tempBuffer[320]; dst += 4)
 		*dst = 0;
 
 	_renderer->loadToVRAM(_tempBuffer, 640, 0xDC00);
 	_needUpdate = false;
 }
 
-SegaCDFont::SegaCDFont() : Font(), _data(0), _colorMap(0), _width(0), _height(0) {
-
+SegaCDFont::SegaCDFont(const uint16 *convTable1, const uint16 *convTable2, const uint8 *widthTable1, const uint8 *widthTable2, const uint8 *widthTable3) : Font(),
+	_style(0), _forceTwoByte(false), _fixedWidth(false), _convTable1(convTable1), _convTable2(convTable2), _widthTable1(widthTable1), _widthTable2(widthTable2),
+		_widthTable3(widthTable3), _buffer(0), _data(0), _colorMap(0), _width(12), _height(12) {
 }
 
 SegaCDFont::~SegaCDFont() {
-	delete[] _data;
+	delete[] _buffer;
 }
 
 bool SegaCDFont::load(Common::SeekableReadStream &file) {
@@ -937,15 +956,159 @@ bool SegaCDFont::load(Common::SeekableReadStream &file) {
 	if (!size)
 		return false;
 
-	delete[] _data;
+	delete[] _buffer;
+	uint8 *newData = new uint8[size];
+	file.read(newData, size);
+	_buffer = newData;
 
-	_data = new uint8[size];
-	file.read(_data, size);
 	return true;
 }
 
-void SegaCDFont::drawChar(uint16 c, byte *dst, int pitch, int) const {
+int SegaCDFont::getCharWidth(uint16 c) const {
+	uint8 charWidth, charHeight, charPitch;
+	getGlyphData(c, charWidth, charHeight, charPitch);
+	return charWidth;
+}
+
+int SegaCDFont::getCharHeight(uint16 c) const {
+	uint8 charWidth, charHeight, charPitch;
+	getGlyphData(c, charWidth, charHeight, charPitch);
+	return charHeight;
+}
+
+void SegaCDFont::setStyles(int styles) {
+	assert(_buffer);
+	_forceTwoByte = (styles & kStyleForceTwoByte);
+	_data = (styles & kStyleFat) ? _buffer + 131072 : _buffer;
+	_fixedWidth = (styles & kStyleFixedWidth);
+	_style = (styles & kStyleNarrow) ? 1 : (styles & kStyleVariant ? 2 : 0);
+}
+
+void SegaCDFont::drawChar(uint16 c, byte *dst, int pitch, int xOffs, int yOffs) const {
+	uint8 charWidth, charHeight, charPitch;
+
+	const uint8 *pos = getGlyphData(c, charWidth, charHeight, charPitch);
+	uint8 p = (xOffs & 1) ? 0x0F : 0xF0;
+	uint8 color1 = _colorMap[1];
+
+	color1 &= p;
+	p = ~p;
+
+	for (int y = 0; y < charHeight; ++y) {
+		c = *pos++ << 8;
+		if (charPitch != 8) {
+			c |= *pos;
+			if (y & 1) {
+				c <<= 4;
+				pos++;
+			}
+
+		}
+		uint8 *dst2 = dst;
+		for (int x = xOffs; x < charPitch + xOffs; ++x) {
+			if (c & 0x8000)
+				*dst = (*dst & p) | color1;
+			c <<= 1;
+			p = ~p;
+			color1 = (color1 << 4) | (color1 >> 4);
+			if (x & 1)
+				dst++;
+			if ((x & 7) == 7)
+				dst += 28;
+ 		}
+		dst = dst2 + 4;
+		if ((++yOffs & 7) == 0)
+			dst = dst + (pitch << 5) - 32;
+	}
+}
+
+const uint8 *SegaCDFont::getGlyphData(uint16 c, uint8 &charWidth, uint8 &charHeight, uint8 &pitch) const {
+	const uint8 *res = 0;
+	uint16 lo = 0;
+	uint16 hi = 0;
+
+	if (c == 0) {
+		charWidth = charHeight = pitch = 0;
+		return 0;
+	}
+
+	if (c < 256) {
+		if (_forceTwoByte) {
+			assert(c >= 32 && c < 224);
+			c = _convTable2[c - 32];
+			hi = c >> 8;
+			lo = c & 0xFF;
+		} else {
+			if (c < 128) {
+				if (c >= 96)
+					c += 96;
+				else
+					c -= 32;
+				if (c & 0xF000)
+					c = 0;
+			} else {
+				if (c >= 224)
+					c -= 64;
+				else if (c >= 160)
+					c -= 96;
+			}
+			charWidth = charHeight = pitch = 8;
+			return &_data[c << 3];
+		}
+	} else {
+		lo = c >> 8;
+		hi = c & 0xFF;
+	}
+
+	if (lo > 0x9E) {
+		if (hi > 0x9F)
+			hi -= 176;
+		else
+			hi -= 112;
+		hi <<= 1;
+		lo -= 126;
+	} else {
+		if (hi > 0x9F)
+			hi -= 177;
+		else
+			hi -= 113;
+		hi = (hi << 1) + 1;
+		lo -= 31;
+		if (lo >= 97)
+			lo -= 1;
+	}
+
+	c = (hi << 8) | lo;
+
+	if (c >= 0x5000)
+		c = 0x2121;
+
+	c -= _convTable1[(c >> 8) - 32];
+
+	int vrnt = 0;
+	if (c >= 376 || _style == 0)
+		vrnt = 0;
+	else if (_style == 1 || c < 188 || c >= 282)
+		vrnt = 1;
+
+	if (vrnt == 0) {
+		charWidth = (!_fixedWidth && (c < 188)) ? _widthTable1[c] : 12;
+		charHeight = pitch = 12;
+		res = &_data[0x19A0 + 18 * c];
+	} else if (_style == 2) {
+		assert(c < 188);
+		charWidth = _widthTable3[c];
+		charHeight = pitch = 12;
+		res = &_data[0x3410 + 18 * c];
+	} else {
+		assert(c < 188);
+		charWidth = _widthTable2[c];
+		charHeight = 12;
+		pitch = 8;
+		res = &_data[0x800 + 12 * c];
+	}
 
+	return res;
 }
 
 #undef mRenderLineFragment
diff --git a/engines/kyra/graphics/screen_eob_segacd.h b/engines/kyra/graphics/screen_eob_segacd.h
index bc69674b02..194597e31b 100644
--- a/engines/kyra/graphics/screen_eob_segacd.h
+++ b/engines/kyra/graphics/screen_eob_segacd.h
@@ -66,7 +66,7 @@ public:
 	// 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 w, int h);
+	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.
@@ -80,14 +80,15 @@ public:
 	void loadToVRAM(const void *data, int dataSize, int addr);
 	void loadToVRAM(Common::SeekableReadStreamEndian *in, int 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, int presetPatternID = -1);
+	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 writeVSRAMValue(int addr, uint16 value);
+	void clearPlanes();
 	
-	void render(int destPageNum);
+	void render(int destPageNum, bool spritesOnly = false);
 
 private:
 	void renderPlanePart(int plane, uint8 *dstBuffer, int x1, int y1, int x2, int y2);
-	void renderPlaneTile(uint8 *dst, int destX, uint16 *nameTable, int vScrollTableIndex, int hScrollTableIndex, uint16 pitch, uint16 nameTableSize);
+	void renderPlaneTile(uint8 *dst, int destX, const uint16 *nameTable, int vScrollTableIndex, 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);
@@ -108,7 +109,7 @@ private:
 	void clearPrioChain();
 
 	struct SegaPlane {
-		SegaPlane() : blockX(0), blockY(0), w(0), h(0), nameTable(0) {}
+		SegaPlane() : blockX(0), blockY(0), w(0), h(0), nameTable(0), nameTableSize(0) {}
 		int blockX, blockY;
 		uint16 w, h;
 		uint16 *nameTable;
@@ -152,8 +153,6 @@ private:
 	PrioTileRenderObj *_prioChainStart, *_prioChainEnd;
 	uint16 _screenW, _screenH, _blocksW, _blocksH;
 	Screen_EoB *_screen;
-
-	const uint16 *_patternTables[6];
 };
 
 class SegaAnimator {
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 4b28b79411..446b675766 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -28,6 +28,7 @@
 #include "kyra/text/text_rpg.h"
 #include "kyra/engine/timer.h"
 #include "kyra/engine/util.h"
+#include "kyra/graphics/screen_eob_segacd.h"
 
 #include "backends/keymapper/keymapper.h"
 #include "common/system.h"
@@ -2010,11 +2011,17 @@ void GUI_EoB::simpleMenu_setup(int sd, int maxItem, const char *const *strings,
 	for (int i = 0; i < _menuNumItems; i++) {
 		int item = simpleMenu_getMenuItem(i, menuItemsMask, itemOffset);
 		int ty = y + i * (lineSpacing + _screen->getFontHeight());
-		_screen->printShadedText(strings[item], x, ty, (_vm->_configRenderMode == Common::kRenderCGA) ? 1 : _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
-		if (item == v)
-			_screen->printText(strings[item], x, ty, _vm->guiSettings()->colors.guiColorLightRed, 0);
+		if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
+			_vm->_txt->printMessageAtPos(strings[item], x, ty, item == v ? 0x55 : 0xff, 0x11);
+		} else {
+			_screen->printShadedText(strings[item], x, ty, (_vm->_configRenderMode == Common::kRenderCGA) ? 1 : _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+			if (item == v)
+				_screen->printText(strings[item], x, ty, _vm->guiSettings()->colors.guiColorLightRed, 0);
+		}
 	}
 
+	if (_vm->gameFlags().platform == Common::kPlatformSegaCD)
+		_screen->sega_getRenderer()->render(0);
 	_screen->updateScreen();
 	_menuLineSpacing = lineSpacing;
 	_menuLastInFlags = 0;
@@ -2063,8 +2070,14 @@ int GUI_EoB::simpleMenu_process(int sd, const char *const *strings, void *b, int
 	}
 
 	if (newItem != currentItem) {
-		_screen->printText(strings[simpleMenu_getMenuItem(currentItem, menuItemsMask, itemOffset)], x, y + currentItem * lineH, (_vm->_configRenderMode == Common::kRenderCGA) ? 1 : _vm->guiSettings()->colors.guiColorWhite, 0);
-		_screen->printText(strings[simpleMenu_getMenuItem(newItem,  menuItemsMask, itemOffset)], x, y + newItem * lineH , _vm->guiSettings()->colors.guiColorLightRed, 0);
+		if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
+			_vm->_txt->printMessageAtPos(strings[simpleMenu_getMenuItem(currentItem, menuItemsMask, itemOffset)], x, y + currentItem * lineH, 0xFF, 0x11);
+			_vm->_txt->printMessageAtPos(strings[simpleMenu_getMenuItem(newItem, menuItemsMask, itemOffset)], x, y + newItem * lineH, 0x55, 0x11);
+			_screen->sega_getRenderer()->render(0);
+		} else {
+			_screen->printText(strings[simpleMenu_getMenuItem(currentItem, menuItemsMask, itemOffset)], x, y + currentItem * lineH, (_vm->_configRenderMode == Common::kRenderCGA) ? 1 : _vm->guiSettings()->colors.guiColorWhite, 0);
+			_screen->printText(strings[simpleMenu_getMenuItem(newItem, menuItemsMask, itemOffset)], x, y + newItem * lineH, _vm->guiSettings()->colors.guiColorLightRed, 0);
+		}
 		_screen->updateScreen();
 	}
 
@@ -2097,6 +2110,9 @@ int GUI_EoB::simpleMenu_getMenuItem(int index, int32 menuItemsMask, int itemOffs
 }
 
 void GUI_EoB::simpleMenu_flashSelection(const char *str, int x, int y, int color1, int color2, int color3) {
+	if (_vm->gameFlags().platform == Common::kPlatformSegaCD)
+		return;
+
 	for (int i = 0; i < 3; i++) {
 		_screen->printText(str, x, y, color2, color3);
 		_screen->updateScreen();
@@ -3135,12 +3151,16 @@ int GUI_EoB::selectSaveSlotDialogue(int x, int y, int id) {
 			drawSaveSlotButton(newHighlight, 0, _vm->guiSettings()->colors.guiColorLightRed);
 
 			// Display highlighted slot index in the bottom left corner to avoid people getting lost with the 990 save slots
-			Screen::FontId of = _screen->setFont(Screen::FID_6_FNT);
-			int sli = (newHighlight == 6) ? _savegameOffset : (_savegameOffset + newHighlight);
-			_screen->set16bitShadingLevel(4);
-			_screen->printText(Common::String::format("%03d/989", sli).c_str(), _saveSlotX + 5, _saveSlotY + 135, _vm->guiSettings()->colors.frame2, _vm->guiSettings()->colors.fill);
-			_screen->set16bitShadingLevel(0);
-			_screen->setFont(of);
+			if (_vm->_flags.platform == Common::kPlatformSegaCD) {
+
+			} else {
+				Screen::FontId of = _screen->setFont(Screen::FID_6_FNT);
+				int sli = (newHighlight == 6) ? _savegameOffset : (_savegameOffset + newHighlight);
+				_screen->set16bitShadingLevel(4);
+				_screen->printText(Common::String::format("%03d/989", sli).c_str(), _saveSlotX + 5, _saveSlotY + 135, _vm->guiSettings()->colors.frame2, _vm->guiSettings()->colors.fill);
+				_screen->set16bitShadingLevel(0);
+				_screen->setFont(of);
+			}
 
 			_screen->updateScreen();
 			lastHighlight = newHighlight;
diff --git a/engines/kyra/gui/gui_hof.cpp b/engines/kyra/gui/gui_hof.cpp
index 94e96fa1ef..76f0902e6f 100644
--- a/engines/kyra/gui/gui_hof.cpp
+++ b/engines/kyra/gui/gui_hof.cpp
@@ -519,11 +519,11 @@ void KyraEngine_HoF::bookPrintText(int dstPage, const uint8 *str, int x, int y,
 
 	_screen->setTextColor(_bookTextColorMap, 0, 3);
 	Screen::FontId oldFont = _screen->setFont(_flags.lang == Common::JA_JPN ? Screen::FID_SJIS_FNT : Screen::FID_BOOKFONT_FNT);
-	_screen->_charWidth = -2;
+	_screen->_charSpacing = -2;
 
 	_screen->printText((const char *)str, x, y, color, (_flags.lang == Common::JA_JPN) ? 0xF6 : 0);
 
-	_screen->_charWidth = 0;
+	_screen->_charSpacing = 0;
 	_screen->setFont(oldFont);
 	_screen->_curPage = curPageBackUp;
 }
diff --git a/engines/kyra/gui/gui_lok.cpp b/engines/kyra/gui/gui_lok.cpp
index cdd5e2acfd..7772ac4325 100644
--- a/engines/kyra/gui/gui_lok.cpp
+++ b/engines/kyra/gui/gui_lok.cpp
@@ -571,13 +571,13 @@ void GUI_LoK::setupSavegames(Menu &menu, int num) {
 			Common::strlcpy(_savegameNames[i], header.description.c_str(), ARRAYSIZE(_savegameNames[0]));
 
 			// Trim long GMM save descriptions to fit our save slots
-			_screen->_charWidth = -2;
+			_screen->_charSpacing = -2;
 			int fC = _screen->getTextWidth(_savegameNames[i]);
 			while (_savegameNames[i][0] && (fC > 240)) {
 				_savegameNames[i][strlen(_savegameNames[i]) - 1] = 0;
 				fC = _screen->getTextWidth(_savegameNames[i]);
 			}
-			_screen->_charWidth = 0;
+			_screen->_charSpacing = 0;
 
 			Util::convertISOToDOS(_savegameNames[i]);
 
@@ -683,10 +683,10 @@ void GUI_LoK::redrawTextfield() {
 	_screen->fillRect(38, 91, 287, 102, _vm->gameFlags().platform == Common::kPlatformAmiga ? 18 : 250);
 	_text->printText(_savegameName, 38, 92, 253, 0, 0);
 
-	_screen->_charWidth = -2;
+	_screen->_charSpacing = -2;
 	int width = _screen->getTextWidth(_savegameName);
 	_screen->fillRect(39 + width, 93, 45 + width, 100, _vm->gameFlags().platform == Common::kPlatformAmiga ? 31 : 254);
-	_screen->_charWidth = 0;
+	_screen->_charSpacing = 0;
 
 	_screen->updateScreen();
 }
@@ -696,9 +696,9 @@ void GUI_LoK::updateSavegameString() {
 
 	if (_keyPressed.keycode) {
 		length = strlen(_savegameName);
-		_screen->_charWidth = -2;
+		_screen->_charSpacing = -2;
 		int width = _screen->getTextWidth(_savegameName) + 7;
-		_screen->_charWidth = 0;
+		_screen->_charSpacing = 0;
 
 		char inputKey = _keyPressed.ascii;
 		Util::convertISOToDOS(inputKey);
diff --git a/engines/kyra/gui/gui_mr.cpp b/engines/kyra/gui/gui_mr.cpp
index f812daa8da..3798a098c8 100644
--- a/engines/kyra/gui/gui_mr.cpp
+++ b/engines/kyra/gui/gui_mr.cpp
@@ -309,11 +309,11 @@ void KyraEngine_MR::drawMalcolmsMoodText() {
 	const char *string = (const char *)getTableEntry(_cCodeFile, stringId[_malcolmsMood]);
 
 	Screen::FontId oldFont = _screen->setFont(Screen::FID_8_FNT);
-	_screen->_charWidth = -2;
+	_screen->_charSpacing = -2;
 
 	int width = _screen->getTextWidth(string);
 
-	_screen->_charWidth = 0;
+	_screen->_charSpacing = 0;
 	_screen->setFont(oldFont);
 
 	int pageBackUp = _screen->_curPage;
@@ -416,13 +416,13 @@ void KyraEngine_MR::drawScoreCounting(int oldScore, int newScore, int drawOld, c
 
 int KyraEngine_MR::getScoreX(const char *str) {
 	Screen::FontId oldFont = _screen->setFont(Screen::FID_8_FNT);
-	_screen->_charWidth = -2;
+	_screen->_charSpacing = -2;
 
 	int width = _screen->getTextWidth(str);
 	int x = 160 + (width / 2) - 32;
 
 	_screen->setFont(oldFont);
-	_screen->_charWidth = 0;
+	_screen->_charSpacing = 0;
 	return x;
 }
 
@@ -773,11 +773,11 @@ void KyraEngine_MR::printAlbumText(int page, const char *str, int x, int y, uint
 	_screen->setTextColor(colorMap, 0, 3);
 
 	Screen::FontId oldFont = _screen->setFont(Screen::FID_BOOKFONT_FNT);
-	_screen->_charWidth = -2;
+	_screen->_charSpacing = -2;
 
 	_screen->printText(str, x, y, c0, 0);
 
-	_screen->_charWidth = 0;
+	_screen->_charSpacing = 0;
 	_screen->setFont(oldFont);
 	_screen->_curPage = oldPage;
 }
diff --git a/engines/kyra/gui/gui_v1.cpp b/engines/kyra/gui/gui_v1.cpp
index 50a5628f98..d3e4bc565f 100644
--- a/engines/kyra/gui/gui_v1.cpp
+++ b/engines/kyra/gui/gui_v1.cpp
@@ -481,10 +481,10 @@ int MainMenu::handle(int dim) {
 	_screen->setTextColorMap(colorMap);
 
 	Screen::FontId oldFont = _screen->setFont(_static.font);
-	int charWidthBackUp = _screen->_charWidth;
+	int charWidthBackUp = _screen->_charSpacing;
 
 	if (_vm->game() != GI_LOL)
-		_screen->_charWidth = -2;
+		_screen->_charSpacing = -2;
 	_screen->setScreenDim(dim);
 
 	int backUpX = _screen->_curDim->sx;
@@ -551,7 +551,7 @@ int MainMenu::handle(int dim) {
 		command = -1;
 
 	_screen->copyRegion(backUpX, backUpY, backUpX, backUpY, backUpWidth, backUpHeight, 3, 0);
-	_screen->_charWidth = charWidthBackUp;
+	_screen->_charSpacing = charWidthBackUp;
 	_screen->setFont(oldFont);
 
 	return command;
diff --git a/engines/kyra/gui/gui_v2.cpp b/engines/kyra/gui/gui_v2.cpp
index b0a8bc18c8..c9a281f73e 100644
--- a/engines/kyra/gui/gui_v2.cpp
+++ b/engines/kyra/gui/gui_v2.cpp
@@ -448,13 +448,13 @@ void GUI_v2::setupSavegameNames(Menu &menu, int num) {
 			Util::convertISOToDOS(s);
 
 			// Trim long GMM save descriptions to fit our save slots
-			_screen->_charWidth = -2;
+			_screen->_charSpacing = -2;
 			int fC = _screen->getTextWidth(s);
 			while (s[0] && fC > 240) {
 				s[strlen(s) - 1]  = 0;
 				fC = _screen->getTextWidth(s);
 			}
-			_screen->_charWidth = 0;
+			_screen->_charSpacing = 0;
 
 			menu.item[i].saveSlot = _saveSlots[i + _savegameOffset];
 			menu.item[i].enabled = true;
@@ -827,9 +827,9 @@ bool GUI_v2::checkSavegameDescription(const char *buffer, int size) {
 
 int GUI_v2::getCharWidth(uint8 c) {
 	Screen::FontId old = _screen->setFont(Screen::FID_8_FNT);
-	_screen->_charWidth = -2;
+	_screen->_charSpacing = -2;
 	int width = _screen->getCharWidth(c);
-	_screen->_charWidth = 0;
+	_screen->_charSpacing = 0;
 	_screen->setFont(old);
 	return width;
 }
diff --git a/engines/kyra/resource/resource_segacd.cpp b/engines/kyra/resource/resource_segacd.cpp
index 7b3f8996ce..9a9861ef07 100644
--- a/engines/kyra/resource/resource_segacd.cpp
+++ b/engines/kyra/resource/resource_segacd.cpp
@@ -27,7 +27,7 @@
 
 namespace Kyra {
 
-SegaCDResource::SegaCDResource(Resource *res) : _res(res), _str(0), _offsetTable(0), _numResources(0) {	
+SegaCDResource::SegaCDResource(Resource *res) : _res(res), _str(0), _resTable(0), _numResources(0) {
 }
 
 SegaCDResource::~SegaCDResource() {
@@ -39,78 +39,84 @@ bool SegaCDResource::loadContainer(const Common::String &filename, uint32 offset
 
 	_str = _res->createEndianAwareReadStream(filename);
 	if (!_str) {
-		error("SegaCDResource: File '%s' not found.", filename);
+		error("SegaCDResource: File '%s' not found.", filename.c_str());
 		return false;
 	}
 
 	_str->seek(offset, SEEK_SET);
-
 	uint32 first = _str->readUint32();
 	_numResources = first >> 2;
-	_offsetTable = new uint32[_numResources + 1];
-	_offsetTable[0] = offset + first;
 
 	for (int i = 1; i < _numResources; ++i) {
-		_offsetTable[i] = offset + _str->readUint32();
-		if (_offsetTable[i] == 0)
+		uint32 next = _str->readUint32();
+		if (next == 0) {
 			_numResources = i;
+		} else if (next < first) {
+			first = next;
+			_numResources = first >> 2;
+		}
 	}
 
-	if (size)
-		assert(offset + size <= _str->size());
+	_str->seek(offset, SEEK_SET);
+	_resTable = new TableEntry[_numResources];
+	for (int i = 0; i < _numResources; ++i)
+		_resTable[i]._offset = offset + _str->readUint32();
 
-	_offsetTable[_numResources] = size ? offset + size : _str->size();
+	if (size)
+		assert(offset + size <= (uint32)_str->size());
+
+	for (int i = 0; i < _numResources; ++i) {
+		uint32 next = size ? offset + size : _str->size();
+		for (int ii = 0; ii < _numResources; ++ii) {
+			if (_resTable[ii]._offset <= _resTable[i]._offset)
+				continue;
+			next = MIN<uint32>(_resTable[ii]._offset, next);
+		}
+		_resTable[i]._len = next - _resTable[i]._offset;
+	}
 
 	return true;
 }
 
 void SegaCDResource::unloadContainer() {
-	delete[] _offsetTable;
+	delete[] _resTable;
 	delete _str;
-	_offsetTable = 0;
+	_resTable = 0;
 	_numResources = 0;
 	_str = 0;
 }
 
-Common::SeekableReadStreamEndian *SegaCDResource::getEndianAwareResourceStream(int resID) {
-	if (!_str || !_offsetTable || resID >= _numResources)
+Common::SeekableReadStreamEndian *SegaCDResource::resStreamEndianAware(int resID) {
+	if (!_str || !_resTable || resID >= _numResources)
 		return 0;
 
-	Common::SeekableReadStream *str = getResourceStream(resID);
+	Common::SeekableReadStream *str = resStream(resID);
 	if (!str)
 		return 0;
 
 	return new EndianAwareStreamWrapper(str, _str->isBE(), true);
 }
 
-Common::SeekableReadStream *SegaCDResource::getResourceStream(int resID) {
-	if (!_str || !_offsetTable || resID >= _numResources)
+Common::SeekableReadStream *SegaCDResource::resStream(int resID) {
+	if (!_str || !_resTable || resID >= _numResources)
 		return 0;
 
-	return new Common::SeekableSubReadStream(_str, _offsetTable[resID], _offsetTable[resID + 1], DisposeAfterUse::NO);
+	return new Common::SeekableSubReadStream(_str, _resTable[resID]._offset, _resTable[resID]._offset + _resTable[resID]._len, DisposeAfterUse::NO);
 }
 
-uint8 *SegaCDResource::fileData(int resID, uint32 *resLen) {
-	if (!_str || !_offsetTable || resID >= _numResources)
+uint8 *SegaCDResource::resData(int resID, uint32 *resLen) {
+	if (!_str || !_resTable || resID >= _numResources)
 		return 0;
 
-	uint32 len = _offsetTable[resID + 1] - _offsetTable[resID];
-	uint8 *res = new uint8[len];
+	uint8 *res = new uint8[_resTable[resID]._len];
 
-	_str->seek(_offsetTable[resID], SEEK_SET);
-	_str->read(res, len);
+	_str->seek(_resTable[resID]._offset, SEEK_SET);
+	_str->read(res, _resTable[resID]._len);
 
 	if (resLen)
-		*resLen = len;
+		*resLen = _resTable[resID]._len;
 
 	return res;
 }
 
-uint8 **SegaCDResource::loadAllResources(uint32 *numRes) {
-	if (!_str || !_offsetTable)
-		return 0;
-
-	return 0;
-}
-
 } // End of namespace Kyra
diff --git a/engines/kyra/resource/resource_segacd.h b/engines/kyra/resource/resource_segacd.h
index 368e229534..be87dd9d10 100644
--- a/engines/kyra/resource/resource_segacd.h
+++ b/engines/kyra/resource/resource_segacd.h
@@ -42,15 +42,21 @@ public:
 	bool loadContainer(const Common::String &filename, uint32 offset = 0, uint32 size = 0);
 	void unloadContainer();
 
-	Common::SeekableReadStreamEndian *getEndianAwareResourceStream(int resID);
-	Common::SeekableReadStream *getResourceStream(int resID);
+	Common::SeekableReadStreamEndian *resStreamEndianAware(int resID);
+	Common::SeekableReadStream *resStream(int resID);
 
-	uint8 *fileData(int resID, uint32 *resLen);
-	uint8 **loadAllResources(uint32 *numRes);
+	uint8 *resData(int resID, uint32 *resLen);
 
 private:
 	Resource *_res;
-	uint32 *_offsetTable;
+
+	struct TableEntry {
+		TableEntry() : _offset(0), _len(0) {}
+		uint32 _offset;
+		uint32 _len;
+	};
+
+	TableEntry *_resTable;
 	int _numResources;
 	Common::SeekableReadStreamEndian *_str;
 };
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index 45d91bc028..af0b277d6e 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -1447,7 +1447,7 @@ const EoBEngine::TitleScreenConfig EoBEngine::_titleConfig[5] = {
 	{
 		Common::kPlatformSegaCD,
 		Common::UNK_LANG,
-		"EOBTITLE",
+		"",
 		&_renderModePalFiles[2],
 		1,
 		2,
diff --git a/engines/kyra/script/script_tim.cpp b/engines/kyra/script/script_tim.cpp
index 11fe9666fa..4e73ddfa52 100644
--- a/engines/kyra/script/script_tim.cpp
+++ b/engines/kyra/script/script_tim.cpp
@@ -312,10 +312,10 @@ void TIMInterpreter::displayText(uint16 textId, int16 flags) {
 
 		_screen->setFont(sjisMode ? Screen::FID_SJIS_TEXTMODE_FNT : Screen::FID_8_FNT);
 		_screen->setTextColorMap(colorMap);
-		_screen->_charWidth = -2;
+		_screen->_charSpacing = -2;
 	}
 
-	_screen->_charOffset = -4;
+	_screen->_lineSpacing = -4;
 	_screen->copyRegionToBuffer(0, 0, 160, 320, 40, _textAreaBuffer);
 	_textDisplayed = true;
 
@@ -354,14 +354,14 @@ void TIMInterpreter::displayText(uint16 textId, int16 flags) {
 		}
 	}
 
-	_screen->_charOffset = 0;
+	_screen->_lineSpacing = 0;
 
 	if (flags < 0) {
 		static const uint8 colorMap[] = { 0x00, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0x00, 0x00, 0x00, 0x00 };
 
 		_screen->setFont(sjisMode ? Screen::FID_SJIS_TEXTMODE_FNT : Screen::FID_INTRO_FNT);
 		_screen->setTextColorMap(colorMap);
-		_screen->_charWidth = 0;
+		_screen->_charSpacing = 0;
 	}
 }
 
@@ -381,9 +381,9 @@ void TIMInterpreter::displayText(uint16 textId, int16 flags, uint8 color) {
 
 	static const uint8 colorMap[] = { 0x00, 0xA0, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
 	_screen->setTextColorMap(colorMap);
-	_screen->_charWidth = 0;
+	_screen->_charSpacing = 0;
 	if (!_vm->gameFlags().use16ColorMode)
-		_screen->_charOffset = -4;
+		_screen->_lineSpacing = -4;
 
 	if (!flags)
 		_screen->copyRegionToBuffer(0, 0, 0, 320, 40, _textAreaBuffer);
diff --git a/engines/kyra/sequence/seqplayer_eob_segacd.cpp b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
index a1c43aa230..cc2d83850b 100644
--- a/engines/kyra/sequence/seqplayer_eob_segacd.cpp
+++ b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
@@ -30,19 +30,20 @@
 
 namespace Kyra {
 
-SegaSequencePlayer::SegaSequencePlayer(EoBEngine *vm, Screen_EoB *screen, SegaCDResource *res) : _vm(vm), _screen(screen), _res(res), _tileSets(0), _debugResyncCnt(0), _varUnkX1(0),
-	_var1(false), _waterdeepScene(0), _update2(0), _waitFlag(false), _waterdeepSceneTimer(0), _unkSEQ2(0), _renderer(_screen->sega_getRenderer()), _animator(_screen->sega_getAnimator()) {
+SegaSequencePlayer::SegaSequencePlayer(EoBEngine *vm, Screen_EoB *screen, SegaCDResource *res) : _vm(vm), _screen(screen), _res(res), _tileSets(0), _debugResyncCnt(0), _speechAnimType(0),
+	_playingID(1), _waterdeepScene(0), _playSpeechAnimation(0), _waitFlag(false), _waterdeepSceneTimer(0), _speechAnimTimer(0), _speechAnimNo(0), _speechAnimFrame(0),
+	_newTrack(-1), _renderer(_screen->sega_getRenderer()), _animator(_screen->sega_getAnimator()) {
 #define SQOPC(x) _opcodes.push_back(new SQOpcode(this, &SegaSequencePlayer::x, #x))
 	SQOPC(s_initDrawObject);
 	SQOPC(s_drawTileSet);
 	SQOPC(s_loadTileDataSingle);
-	SQOPC(s_3);
-	SQOPC(s_4);
+	SQOPC(s_drawTileSetCustom);
+	SQOPC(s_drawTileSetCustomTopToBottom);
 	SQOPC(s_fillRect);
-	SQOPC(s_6);
-	SQOPC(s_7);
-	SQOPC(s_8);
-	SQOPC(s_9_dispText);
+	SQOPC(s_void);
+	SQOPC(s_initSprite);
+	SQOPC(s_removeSprite);
+	SQOPC(s_displayTextJp);
 	SQOPC(s_fadeToNeutral);
 	SQOPC(s_fadeToBlack);
 	SQOPC(s_fadeToNeutral2);
@@ -51,26 +52,26 @@ SegaSequencePlayer::SegaSequencePlayer(EoBEngine *vm, Screen_EoB *screen, SegaCD
 	SQOPC(s_vScroll);
 	SQOPC(s_hScroll);
 	SQOPC(s_paletteOps);
-	SQOPC(s_initSprite);
+	SQOPC(s_initSpriteCustomCoords);
 	SQOPC(s_fillRectWithPattern);
-	SQOPC(s_loadTileDataMult);
-	SQOPC(s_21);
-	SQOPC(s_22);
-	SQOPC(s_initSprite2);
-	SQOPC(s_drawTileSetCustom);
+	SQOPC(s_loadTileDataSeries);
+	SQOPC(s_drawTileSetSeries);
+	SQOPC(s_initSpriteSeries);
+	SQOPC(s_initSpriteCustom);
+	SQOPC(s_drawTileSetCustomCoords);
 	SQOPC(s_waitForPaletteFade);
 	SQOPC(s_clearSprites);
-	SQOPC(s_27);
+	SQOPC(s_moveSprites2);
 	SQOPC(s_moveSprites);
 	SQOPC(s_moveMorphSprite);
 	SQOPC(s_unpauseCD);
-	SQOPC(s_enableWaterDeepAnimations);
-	SQOPC(s_32);
-	SQOPC(s_setUpdate2);
-	SQOPC(s_orbEffect);
+	SQOPC(s_toggleWaterDeepAnimations);
+	SQOPC(s_assignSpeechAnimGraphics);
+	SQOPC(s_toggleSpeechAnimation);
+	SQOPC(s_orbZoomOutEffect);
 	SQOPC(s_stopCD);
 	SQOPC(s_playCD);
-	SQOPC(s_displayText);
+	SQOPC(s_displayTextEn);
 	SQOPC(s_loadCustomPalettes);
 	SQOPC(s_playSoundEffect);
 #undef SQOPC
@@ -85,15 +86,30 @@ SegaSequencePlayer::SegaSequencePlayer(EoBEngine *vm, Screen_EoB *screen, SegaCD
 	_drawObjects = new DrawObject[100];
 	assert(_drawObjects);
 	memset(_drawObjects, 0, 100 * sizeof(DrawObject));
-	_tempBuffer = new uint8[65000];
-	assert(_tempBuffer);
-	memset(_tempBuffer, 0, 65000 * sizeof(uint8));
-	_varUnkX1;
-	memset(_varUnkX2, 0, sizeof(_varUnkX2));
+
+	memset(_speechAnimDrawOps, 0, sizeof(_speechAnimDrawOps));
+
+	_scaleSrcBuffer = new uint8[0x5800];
+	assert(_scaleSrcBuffer);
+	memset(_scaleSrcBuffer, 0, 0x5800 * sizeof(uint8));
+	_scaleOutBuffer = new uint8[0x5800];
+	assert(_scaleOutBuffer);
+	memset(_scaleOutBuffer, 0, 0x5800 * sizeof(uint8));
+	_scaleStampMap = new uint16[0x100];
+	assert(_scaleStampMap);
+	memset(_scaleStampMap, 0, 0x100 * sizeof(uint16));
+	_scaleTraceVectors = new uint16[0x580];
+	assert(_scaleTraceVectors);
+	memset(_scaleTraceVectors, 0, 0x580 * sizeof(uint16));
 
 	int temp;
 	_wdDsX = _vm->staticres()->loadRawDataBe16(kEoB1IntroWdDsX, temp);
 	_wdDsY = _vm->staticres()->loadRawData(kEoB1IntroWdDsY, temp);
+	_wdAnimSprites = _vm->staticres()->loadRawData(kEoB1WdAnimSprites, temp);
+	_speechAnimData = _vm->staticres()->loadRawData(kEoB1SpeechAnimData, temp);
+	_cdaTracks = _vm->staticres()->loadRawData(kEoB1SequenceTrackMap, temp);
+	for (int i = 0; i < 6; ++i)
+		_patternTables[i] = _vm->staticres()->loadRawDataBe16(kEoB1PatternTable0 + i, temp);
 }
 
 SegaSequencePlayer::~SegaSequencePlayer() {
@@ -101,18 +117,17 @@ SegaSequencePlayer::~SegaSequencePlayer() {
 	delete[] _tileSets;
 	delete[] _vScrollTimers;
 	delete[] _hScrollTimers;
-	delete[] _tempBuffer;
+	delete[] _scaleSrcBuffer;
+	delete[] _scaleOutBuffer;
+	delete[] _scaleStampMap;
+	delete[] _scaleTraceVectors;
 
 	for (Common::Array<SQOpcode*>::iterator i = _opcodes.begin(); i != _opcodes.end(); ++i)
 		delete (*i);
 }
 
 bool SegaSequencePlayer::play(int id) {
-	//uint8 shadowColor = 0xEE;
-
 	_screen->sega_fadeToBlack(2);
-
-	//gfx37(0);
 	_animator->clearSprites();
 	setVScrollTimers(0, 1, 0, 0, 1, 0);
 	setHScrollTimers(0, 1, 0, 0, 1, 0);
@@ -124,6 +139,11 @@ bool SegaSequencePlayer::play(int id) {
 	_renderer->fillRectWithTiles(2, 1, (id == 53 || id == 54) ? 22 : 20, 38, 6, 0xE51C, true);
 
 	_debugResyncCnt = 0;
+	_playingID = id;
+	_newTrack = -1;
+
+	_vm->_allowSkip = true;
+	_vm->resetSkipFlag();
 
 	for (bool runLoop = true; runLoop && !(_vm->shouldQuit() || _vm->skipFlag()); ) {
 		uint32 offset = (id - 1) * 0x3C000;
@@ -137,17 +157,17 @@ bool SegaSequencePlayer::play(int id) {
 		if (!_res->loadContainer("VISUAL", offset, size))
 			return false;
 
-		Common::SeekableReadStreamEndian *in = _res->getEndianAwareResourceStream(0);
+		Common::SeekableReadStreamEndian *in = _res->resStreamEndianAware(0);
 		if (!in)
 			return false;
 		_screen->sega_loadCustomPaletteData(in);
 		delete in;
 
-		_screen->sega_selectPalette(0, 31, false);
-		_screen->sega_selectPalette(1, 32, false);
-		_screen->sega_selectPalette(3, 30, false);
+		_screen->sega_selectPalette(31, 0, false);
+		_screen->sega_selectPalette(32, 1, false);
+		_screen->sega_selectPalette(30, 3, false);
 
-		in = _res->getEndianAwareResourceStream(2);
+		in = _res->resStreamEndianAware(2);
 		if (!in)
 			return false;
 		uint32 len = in->size();
@@ -155,7 +175,7 @@ bool SegaSequencePlayer::play(int id) {
 		in->read(tileData, len);
 		delete in;
 
-		in = _res->getEndianAwareResourceStream(1);
+		in = _res->resStreamEndianAware(1);
 		if (!in)
 			return false;
 		memset(_tileSets, 0, 100 * sizeof(TileSet));
@@ -169,7 +189,7 @@ bool SegaSequencePlayer::play(int id) {
 		}
 		delete in;
 
-		in = _res->getEndianAwareResourceStream(3);
+		in = _res->resStreamEndianAware(3);
 		if (!in)
 			return false;
 		len = in->size();
@@ -192,6 +212,15 @@ bool SegaSequencePlayer::play(int id) {
 
 	debugC(3, kDebugLevelSequence, "Total millis out of sync: %d", _debugResyncCnt);
 
+	if (_vm->shouldQuit() || _vm->skipFlag()) {
+		_vm->snd_stopSound();
+		_screen->sega_fadeToBlack(5);
+	}
+
+	_vm->_allowSkip = false;
+	_vm->resetSkipFlag();
+
+	_playingID = 1;
 	return true;
 }
 
@@ -200,7 +229,7 @@ void SegaSequencePlayer::setWaitFlag(bool enable) {
 }
 
 void SegaSequencePlayer::run(const uint8 *data) {
-	_var1 = _waterdeepScene = _update2 = false;
+	_waterdeepScene = _playSpeechAnimation = false;
 	uint32 frameCounter = 0;
 	uint32 nextFrame = 0;
 
@@ -232,8 +261,8 @@ void SegaSequencePlayer::run(const uint8 *data) {
 		if (_waterdeepScene)
 			animateWaterdeepScene();
 
-		if (_update2)
-			update2();
+		if (_playSpeechAnimation)
+			updateSpeechAnimations();
 
 		updateScrollTimers();
 		_animator->update();
@@ -284,7 +313,7 @@ void SegaSequencePlayer::setHScrollTimers(uint16 destA, int incrA, int delayA, u
 }
 
 void SegaSequencePlayer::updateScrollTimers() {
-	for (int i = 0; i <= 4; ++i) {
+	for (int i = 0; i < 4; ++i) {
 		ScrollTimer &t = i < 2 ? _vScrollTimers[i] : _hScrollTimers[i - 2];
 		if (t._delay == 0 && t._offsCur != t._offsDest)
 			t._offsCur = t._offsDest;
@@ -299,16 +328,11 @@ void SegaSequencePlayer::updateScrollTimers() {
 
 	_renderer->writeVSRAMValue(0, _vScrollTimers[0]._offsCur);
 	_renderer->writeVSRAMValue(2, _vScrollTimers[1]._offsCur);
-	uint16 hscr[2] = { _hScrollTimers[0]._offsCur, _hScrollTimers[1]._offsCur };
+	uint16 hscr[2] = { (uint16)_hScrollTimers[0]._offsCur, (uint16)_hScrollTimers[1]._offsCur };
 	_renderer->loadToVRAM(hscr, 4, 0xD800);
 }
 
 void SegaSequencePlayer::animateWaterdeepScene() {
-	static uint8 spr[31] = {
-		0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03,	0x03, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06,
-		0x07, 0x07, 0x07, 0x08, 0x08, 0x09, 0x09, 0x0a,	0x0a, 0x0b, 0x0b, 0x0c, 0x0c, 0x0d, 0x0d
-	};
-
 	if (--_waterdeepSceneTimer > 0)
 		return;
 	_waterdeepSceneTimer = 5;
@@ -316,16 +340,46 @@ void SegaSequencePlayer::animateWaterdeepScene() {
 	for (int i = 0; i < 5; ++i) {
 		int rnd = _vm->_rnd.getRandomNumber(30);
 		DrawObject *d = &_drawObjects[10 + rnd];
-		_animator->initSprite(spr[rnd] + 3, _wdDsX[spr[rnd]] - 80, _wdDsY[spr[rnd]] + 32, d->nTblVal, d->addr);
+		_animator->initSprite(_wdAnimSprites[rnd] + 3, _wdDsX[_wdAnimSprites[rnd]] - 80, _wdDsY[_wdAnimSprites[rnd]] + 32, d->nTblVal, d->addr);
 	}
 }
 
-void SegaSequencePlayer::update2() {
+void SegaSequencePlayer::updateSpeechAnimations() {
+	if (--_speechAnimTimer > 0)
+		return;
 
-}
+	int animDrawOp = -1;
 
-void SegaSequencePlayer::update3() {
+	for (bool runLoop = true; runLoop; ) {
+		if (_speechAnimTimer == 0) {
+			const uint8 *pos = &_speechAnimData[_speechAnimNo * 12 + _speechAnimFrame];
+			_speechAnimTimer = pos[1];
+			if (pos[0] != 0xFF) {
+				animDrawOp = pos[0];
+				runLoop = false;
+			}
+			_speechAnimFrame += 2;
+		} else {
+			_speechAnimTimer = 0;
+		}
+
+		if (animDrawOp == -1) {
+			_speechAnimNo = (_speechAnimType == 2) ? _vm->_rnd.getRandomNumberRng(4, 6) : _vm->_rnd.getRandomNumberRng(0, 3);
+			_speechAnimFrame = 0;
+		}
+	}
+
+	updateSpeechAnimGraphics(animDrawOp);
+}
 
+void SegaSequencePlayer::updateSpeechAnimGraphics(int animDrawOp) {
+	assert(animDrawOp < 6);
+	DrawObject *d = &_drawObjects[_speechAnimDrawOps[animDrawOp * 2]];
+	if (_speechAnimDrawOps[animDrawOp * 2 + 1])
+		_renderer->loadToVRAM(d->tileData, (d->width * d->height) << 5, (d->nTblVal & 0x7FF) << 5);
+	else
+		_renderer->fillRectWithTiles(d->addr, d->x, d->y, d->width, d->height, d->nTblVal, true);
+		
 }
 
 #define ARG(x) READ_BE_UINT16(pos + x)
@@ -355,32 +409,44 @@ void SegaSequencePlayer::s_loadTileDataSingle(const uint8 *pos) {
 	_renderer->loadToVRAM(w->tileData, (w->width * w-> height) << 5, (w->nTblVal & 0x7FF) << 5);
 }
 
-void SegaSequencePlayer::s_3(const uint8 *pos) {
-
+void SegaSequencePlayer::s_drawTileSetCustom(const uint8 *pos) {
+	_renderer->fillRectWithTiles(ARG(8), ARG(0), ARG(2), ARG(4), ARG(6), ARG(10), true);
 }
 
-void SegaSequencePlayer::s_4(const uint8 *pos) {
-
+void SegaSequencePlayer::s_drawTileSetCustomTopToBottom(const uint8 *pos) {
+	_renderer->fillRectWithTiles(ARG(8), ARG(0), ARG(2), ARG(4), ARG(6), ARG(10), true, true);
 }
 
 void SegaSequencePlayer::s_fillRect(const uint8 *pos) {
 	_renderer->fillRectWithTiles(ARG(8), ARG(0), ARG(2), ARG(4), ARG(6), ARG(10));
 }
 
-void SegaSequencePlayer::s_6(const uint8 *pos) {
-
+void SegaSequencePlayer::s_initSprite(const uint8 *pos) {
+	DrawObject *d = &_drawObjects[ARG(2)];
+	_animator->initSprite(ARG(0), d->x << 3, d->y << 3, d->nTblVal, d->addr);
 }
 
-void SegaSequencePlayer::s_7(const uint8 *pos) {
-
+void SegaSequencePlayer::s_removeSprite(const uint8 *pos) {
+	_animator->initSprite(ARG(0), 0x4000, 0, 0, 0);
 }
 
-void SegaSequencePlayer::s_8(const uint8 *pos) {
+void SegaSequencePlayer::s_displayTextJp(const uint8 *pos) {
+	if (_vm->gameFlags().lang != Common::JA_JPN)
+		return;
 
-}
+	const char *str = (const char*)pos;
+	_vm->_txt->clearDim(2);
 
-void SegaSequencePlayer::s_9_dispText(const uint8 *pos) {
+	int w = _screen->getTextWidth(str);
+	int x = 0;
+	int y = 0;
 
+	if (w < 288) {
+		x = 152 - (w >> 1);
+		y = 16;
+	}
+
+	_vm->_txt->printMessageAtPos(str, x, y, -1, 0xEE);
 }
 
 void SegaSequencePlayer::s_fadeToNeutral(const uint8 *pos) {
@@ -415,33 +481,37 @@ void SegaSequencePlayer::s_paletteOps(const uint8 *pos) {
 	_screen->sega_paletteOps(S_ARG(0), S_ARG(2), S_ARG(4));
 }
 
-void SegaSequencePlayer::s_initSprite(const uint8 *pos) {
+void SegaSequencePlayer::s_initSpriteCustomCoords(const uint8 *pos) {
 	DrawObject *d = &_drawObjects[ARG(2)];
 	_animator->initSprite(ARG(0), S_ARG(4), S_ARG(6), d->nTblVal, d->addr);
 }
 
 void SegaSequencePlayer::s_fillRectWithPattern(const uint8 *pos) {
-	_renderer->fillRectWithTiles(ARG(8), ARG(0), ARG(2), ARG(4), ARG(6), ARG(10), false, false, ARG(12));
+	assert(ARG(12) < 6);
+	_renderer->fillRectWithTiles(ARG(8), ARG(0), ARG(2), ARG(4), ARG(6), ARG(10), false, false, _patternTables[ARG(12)]);
 }
 
-void SegaSequencePlayer::s_loadTileDataMult(const uint8 *pos) {
-	for (DrawObject *w = &_drawObjects[ARG(0)]; w != &_drawObjects[ARG(0) + ARG(2)]; ++w)
-		_renderer->loadToVRAM(w->tileData, (w->width * w->height) << 5, (w->nTblVal & 0x7FF) << 5);
+void SegaSequencePlayer::s_loadTileDataSeries(const uint8 *pos) {
+	for (DrawObject *d = &_drawObjects[ARG(0)]; d != &_drawObjects[ARG(0) + ARG(2)]; ++d)
+		_renderer->loadToVRAM(d->tileData, (d->width * d->height) << 5, (d->nTblVal & 0x7FF) << 5);
 }
 
-void SegaSequencePlayer::s_21(const uint8 *pos) {
-
+void SegaSequencePlayer::s_drawTileSetSeries(const uint8 *pos) {
+	for (DrawObject *d = &_drawObjects[ARG(0)]; d != &_drawObjects[ARG(0) + ARG(2)]; ++d)
+		_renderer->fillRectWithTiles(d->addr, d->x, d->y, d->width, d->height, d->nTblVal, true);
 }
 
-void SegaSequencePlayer::s_22(const uint8 *pos) {
-
+void SegaSequencePlayer::s_initSpriteSeries(const uint8 *pos) {
+	int id = ARG(0);
+	for (DrawObject *d = &_drawObjects[ARG(2)]; d != &_drawObjects[ARG(2) + ARG(4)]; ++d)
+		_animator->initSprite(id++, d->x << 3, d->y << 3, d->nTblVal, d->addr);
 }
 
-void SegaSequencePlayer::s_initSprite2(const uint8 *pos) {
+void SegaSequencePlayer::s_initSpriteCustom(const uint8 *pos) {
 	_animator->initSprite(ARG(0), S_ARG(4), S_ARG(6), ARG(2), ARG(8));
 }
 
-void SegaSequencePlayer::s_drawTileSetCustom(const uint8 *pos) {
+void SegaSequencePlayer::s_drawTileSetCustomCoords(const uint8 *pos) {
 	DrawObject *w = &_drawObjects[ARG(0)];
 	_renderer->fillRectWithTiles(w->addr, ARG(2), ARG(4), w->width, w->height, w->nTblVal, true);
 }
@@ -454,8 +524,8 @@ void SegaSequencePlayer::s_clearSprites(const uint8*) {
 	_animator->clearSprites();
 }
 
-void SegaSequencePlayer::s_27(const uint8 *pos) {
-
+void SegaSequencePlayer::s_moveSprites2(const uint8 *pos) {
+	_animator->moveSprites2(ARG(0), ARG(2), S_ARG(4), S_ARG(6));
 }
 
 void SegaSequencePlayer::s_moveSprites(const uint8 *pos) {
@@ -467,66 +537,74 @@ void SegaSequencePlayer::s_moveMorphSprite(const uint8 *pos) {
 }
 
 void SegaSequencePlayer::s_unpauseCD(const uint8 *pos) {
-	// Do nothing. We don't support this in our AudioCD API. The original will use s_playCD() to seek to a track,
-	// wait for the seek to finish and then pause. It then used this opcode to actually start the playback. Since
-	// s_playCD() and s_unpauseCD() always seem to be called on the same frame we can just start the playback
-	// normally. The only difference to the playback of songs is that there is no looping.
+	// We don't support this in our AudioCD API. The original will use s_playCD() to seek to a track,
+	// wait for the seek to finish and then pause. It then uses this opcode to actually start the playback.
+	// Since s_playCD() and s_unpauseCD() are not always called on the same frame I emulate the original
+	// behavior like this.
+	if (_newTrack != -1)
+		_vm->snd_playSong(_newTrack, false);
+	_newTrack = -1;
 }
 
-void SegaSequencePlayer::s_enableWaterDeepAnimations(const uint8 *pos) {
+void SegaSequencePlayer::s_toggleWaterDeepAnimations(const uint8 *pos) {
 	_waterdeepScene = ARG(0);
 	_waterdeepSceneTimer = 0;
 }
 
-void SegaSequencePlayer::s_32(const uint8 *pos) {
+void SegaSequencePlayer::s_assignSpeechAnimGraphics(const uint8 *pos) {
 	if (ARG(0) == 100) {
-		_varUnkX1 = ARG(2);
+		_speechAnimType = ARG(2);
 	} else {
 		assert(ARG(0) < 6);
-		_varUnkX2[ARG(0) * 2] = ARG(2);
-		_varUnkX2[ARG(0) * 2 + 1] = ARG(4);
+		_speechAnimDrawOps[ARG(0) * 2] = ARG(2);
+		_speechAnimDrawOps[ARG(0) * 2 + 1] = ARG(4);
 	}
 }
 
-void SegaSequencePlayer::s_setUpdate2(const uint8 *pos) {
-	_update2 = ARG(0);
-	_unkSEQ2 = 0;
-	if (_update2)
-		update3();
+void SegaSequencePlayer::s_toggleSpeechAnimation(const uint8 *pos) {
+	_playSpeechAnimation = ARG(0);
+	_speechAnimTimer = 0;
+	if (_playSpeechAnimation)
+		updateSpeechAnimGraphics(0);
 }
 
-void SegaSequencePlayer::s_orbEffect(const uint8*) {
-	_renderer->memsetVRAM(0x2AA0, 0, 22528);
+void SegaSequencePlayer::s_orbZoomOutEffect(const uint8*) {
+	_renderer->memsetVRAM(0x2AA0, 0, 0x5800);
 	DrawObject *d = &_drawObjects[16];
-	memset(_tempBuffer, 0, 22528);
-	memcpy(_tempBuffer + 128, d->tileData, (d->width * d->height) << 5);
+	memset(_scaleSrcBuffer, 0, 0x5800);
+	memcpy(_scaleSrcBuffer + 128, d->tileData, (d->width * d->height) << 5);
 	_renderer->fillRectWithTiles(0, 4, 0, 32, 22, 0x2155, true, true);
 
-	memset(&_tempBuffer[0xF600], 0, 512 * sizeof(uint8));
-	uint8 *dst2 = &_tempBuffer[0xF600 + (7 << 4) + 6];
+	memset(_scaleStampMap, 0, 0x100 * sizeof(uint16));
+	uint16 *dst2 = &_scaleStampMap[(7 << 4) + 6];
 	uint16 t = 1;
 	for (int h = 0; h < 9; ++h) {
-		uint8 *dst = dst2;
-		for (int w = 0; w < 10; ++w) {
-			*dst++ = t & 0xFF;
-			*dst++ = t++ >> 4;;
-		}
-		dst2 += 32;
+		uint16 *dst = dst2;
+		for (int w = 0; w < 10; ++w)
+			*dst++ = t++;
+		dst2 += 16;
 	}
-	int z = 512;
+
+	int step = 512;
 	for (int i = 0; i < 90; ++i) {
-		uint16 *dst = (uint16*)(&_tempBuffer[0x6A00]);
-		uint16 a = 0x58000 - z * 128;
-		uint16 b = 0x59000 - z * 88;
-		for (int ii = 0; ii < 90; ++ii) {
-			*dst++ = a >> 8;
-			*dst++ = b >> 8;
-			*dst++ = z;
+		uint32 nextFrame = _vm->_system->getMillis() + 64;
+		uint16 *dst = (uint16*)_scaleTraceVectors;
+		uint32 xtr = 0x58000 - step * 128;
+		uint32 ytr = 0x59000 - step * 88;
+		for (int ii = 0; ii < 176; ++ii) {
+			*dst++ = xtr >> 8;
+			*dst++ = ytr >> 8;
+			*dst++ = step;
 			*dst++ = 0;
-			b += z;
+			ytr += step;
 		}
-		_renderer->loadToVRAM(&_tempBuffer[0x6A00], 22528, 0x2AA0);
-		z += 16;
+		memset(_scaleOutBuffer, 0, 0x5800);
+		_screen->sega_gfxScale(_scaleOutBuffer, 256, 176, 21, _scaleSrcBuffer, _scaleStampMap, _scaleTraceVectors);
+		_renderer->loadToVRAM(_scaleOutBuffer, 0x5800, 0x2AA0);
+		_renderer->render(0);
+		_screen->updateScreen();
+		_vm->delayUntil(nextFrame);
+		step += 16;
 	}
 }
 
@@ -536,8 +614,12 @@ void SegaSequencePlayer::s_stopCD(const uint8*) {
 
 void SegaSequencePlayer::s_playCD(const uint8 *pos) {
 	int track = _cdaTracks[ARG(0)];
+
+	// The original seeks to the requested CD track here and pauses it.
+	// The actual playback is then triggered via s_unpauseCD().
 	if (track)
-		_vm->snd_playSong(track, false);
+		_newTrack = track;
+	_vm->snd_stopSound();
 
 	if (_waitFlag) {
 		while (!(_vm->shouldQuit() || _vm->skipFlag()))
@@ -545,12 +627,33 @@ void SegaSequencePlayer::s_playCD(const uint8 *pos) {
 	}
 }
 
-void SegaSequencePlayer::s_displayText(const uint8 *pos) {
+void SegaSequencePlayer::s_displayTextEn(const uint8 *pos) {
+	if (_vm->gameFlags().lang == Common::JA_JPN)
+		return;
+
+	const char *str = (const char*)pos;
+	_vm->_txt->clearDim(2);
+
+	if (_playingID >= 55) {
+		_screen->setFontStyles(_screen->_currentFont, Font::kStyleForceTwoByte | Font::kStyleFat);
+		_vm->_txt->printMessageAtPos(str, 0, 0, -1, 0xEE);
+		_screen->setFontStyles(_screen->_currentFont, Font::kStyleFat);
+	} else {
+		int x = 0;
+		int y = 0;
+
+		if (_playingID >= 53) {
+			x = 152 - (_screen->getTextWidth(str) >> 1);
+			y = 16;
+		}
+
+		_vm->_txt->printMessageAtPos(str, x, y, -1, 0xEE);
+	}
 
 }
 
 void SegaSequencePlayer::s_loadCustomPalettes(const uint8 *pos) {
-	Common::SeekableReadStreamEndian *in = _res->getEndianAwareResourceStream(0);
+	Common::SeekableReadStreamEndian *in = _res->resStreamEndianAware(0);
 	in->seek(ARG(0) << 5);
 	_screen->sega_loadCustomPaletteData(in);
 	delete in;
@@ -563,15 +666,4 @@ void SegaSequencePlayer::s_playSoundEffect(const uint8 *pos) {
 #undef S_ARG
 #undef ARG
 
-const uint8 SegaSequencePlayer::_cdaTracks[60] = {
-	0x00, 0x0d, 0x0f, 0x11, 0x12, 0x13, 0x14, 0x15,
-	0x00, 0x1d, 0x1e, 0x00, 0x0e, 0x16, 0x37, 0x38,
-	0x22, 0x00, 0x23, 0x24, 0x25, 0x1c, 0x18, 0x10,
-	0x1f, 0x17, 0x00, 0x1b, 0x21, 0x00, 0x00, 0x00,
-	0x30, 0x31, 0x26, 0x27, 0x28, 0x29, 0x35, 0x36,
-	0x33, 0x34, 0x2c, 0x2b, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00
-};
-
 } // End of namespace Kyra
diff --git a/engines/kyra/sequence/seqplayer_eob_segacd.h b/engines/kyra/sequence/seqplayer_eob_segacd.h
index 4c4770dc4c..30b6c6b88b 100644
--- a/engines/kyra/sequence/seqplayer_eob_segacd.h
+++ b/engines/kyra/sequence/seqplayer_eob_segacd.h
@@ -47,8 +47,8 @@ private:
 	void setHScrollTimers(uint16 destA, int incrA, int delayA, uint16 destB, int incrB, int delayB);
 	void updateScrollTimers();
 	void animateWaterdeepScene();
-	void update2();
-	void update3();
+	void updateSpeechAnimations();
+	void updateSpeechAnimGraphics(int animDrawOp);
 
 	struct TileSet {
 		const uint16 *data;
@@ -60,8 +60,8 @@ private:
 
 	struct ScrollTimer {
 		ScrollTimer() : _offsCur(0), _offsDest(0), _incr(0), _delay(0), _timer(0) {}
-		uint16 _offsCur;
-		uint16 _offsDest;
+		int16 _offsCur;
+		int16 _offsDest;
 		int16 _incr;
 		int16 _delay;
 		int16 _timer;
@@ -81,15 +81,21 @@ private:
 		uint16 addr;
 	};
 
-	bool _var1;
 	uint16 _waterdeepScene;
-	uint16 _update2;
-	uint16 _varUnkX1;
-	uint16 _varUnkX2[12];
+	uint16 _playSpeechAnimation;
+	uint16 _speechAnimType;
+	uint16 _speechAnimDrawOps[14];
 
 	bool _waitFlag;
-	int _waterdeepSceneTimer, _unkSEQ2;
-	uint8 *_tempBuffer;
+	int _waterdeepSceneTimer, _speechAnimTimer;
+	uint16 _speechAnimNo, _speechAnimFrame;
+	int _playingID;
+	int _newTrack;
+
+	uint8 *_scaleSrcBuffer;
+	uint8 *_scaleOutBuffer;
+	uint16 *_scaleStampMap;
+	uint16 *_scaleTraceVectors;
 
 	uint32 _debugResyncCnt;
 
@@ -101,9 +107,6 @@ private:
 	SegaAnimator *_animator;
 	SegaCDResource *_res;
 
-	const uint16 *_wdDsX;
-	const uint8 *_wdDsY;
-
 private:
 	class SQOpcode : public Common::Functor1Mem<const uint8*, void, SegaSequencePlayer> {
 	public:
@@ -125,13 +128,13 @@ private:
 	void s_initDrawObject(const uint8 *pos);
 	void s_drawTileSet(const uint8 *pos);
 	void s_loadTileDataSingle(const uint8 *pos);
-	void s_3(const uint8 *pos);
-	void s_4(const uint8 *pos);
+	void s_drawTileSetCustom(const uint8 *pos);
+	void s_drawTileSetCustomTopToBottom(const uint8 *pos);
 	void s_fillRect(const uint8 *pos);
-	void s_6(const uint8 *pos);
-	void s_7(const uint8 *pos);
-	void s_8(const uint8 *pos);
-	void s_9_dispText(const uint8 *pos);
+	void s_void(const uint8*) {}
+	void s_initSprite(const uint8 *pos);
+	void s_removeSprite(const uint8 *pos);
+	void s_displayTextJp(const uint8 *pos);
 	void s_fadeToNeutral(const uint8 *pos);
 	void s_fadeToBlack(const uint8 *pos);
 	void s_fadeToNeutral2(const uint8 *pos);
@@ -140,31 +143,36 @@ private:
 	void s_vScroll(const uint8 *pos);
 	void s_hScroll(const uint8 *pos);
 	void s_paletteOps(const uint8 *pos);
-	void s_initSprite(const uint8 *pos);
+	void s_initSpriteCustomCoords(const uint8 *pos);
 	void s_fillRectWithPattern(const uint8 *pos);
-	void s_loadTileDataMult(const uint8 *pos);
-	void s_21(const uint8 *pos);
-	void s_22(const uint8 *pos);
-	void s_initSprite2(const uint8 *pos);
-	void s_drawTileSetCustom(const uint8 *pos);
+	void s_loadTileDataSeries(const uint8 *pos);
+	void s_drawTileSetSeries(const uint8 *pos);
+	void s_initSpriteSeries(const uint8 *pos);
+	void s_initSpriteCustom(const uint8 *pos);
+	void s_drawTileSetCustomCoords(const uint8 *pos);
 	void s_waitForPaletteFade(const uint8*);
 	void s_clearSprites(const uint8*);
-	void s_27(const uint8 *pos);
+	void s_moveSprites2(const uint8 *pos);
 	void s_moveSprites(const uint8 *pos);
 	void s_moveMorphSprite(const uint8 *pos);
 	void s_unpauseCD(const uint8 *pos);
-	void s_enableWaterDeepAnimations(const uint8 *pos);
-	void s_32(const uint8 *pos);
-	void s_setUpdate2(const uint8 *pos);
-	void s_orbEffect(const uint8*);
+	void s_toggleWaterDeepAnimations(const uint8 *pos);
+	void s_assignSpeechAnimGraphics(const uint8 *pos);
+	void s_toggleSpeechAnimation(const uint8 *pos);
+	void s_orbZoomOutEffect(const uint8*);
 	void s_stopCD(const uint8*);
 	void s_playCD(const uint8 *pos);
-	void s_displayText(const uint8 *pos);
+	void s_displayTextEn(const uint8 *pos);
 	void s_loadCustomPalettes(const uint8 *pos);
 	void s_playSoundEffect(const uint8 *pos);
 
 private:
-	static const uint8 _cdaTracks[60];
+	const uint8 *_cdaTracks;
+	const uint8 *_wdAnimSprites;
+	const uint8 *_speechAnimData;
+	const uint16 *_wdDsX;
+	const uint8 *_wdDsY;
+	const uint16 *_patternTables[6];
 };
 
 } // End of namespace Kyra
diff --git a/engines/kyra/sequence/seqplayer_lok.cpp b/engines/kyra/sequence/seqplayer_lok.cpp
index cc74440973..2ce2e9da7f 100644
--- a/engines/kyra/sequence/seqplayer_lok.cpp
+++ b/engines/kyra/sequence/seqplayer_lok.cpp
@@ -375,7 +375,7 @@ void SeqPlayer::s1_copyRegionSpecial() {
 		_screen->copyRegion(152, 56, 152, 56, 48, 48, 2, 0);
 		break;
 	case 4: {
-		_screen->_charWidth = -2;
+		_screen->_charSpacing = -2;
 		const int x = (Screen::SCREEN_W - _screen->getTextWidth(copyStr)) / 2;
 		const int y = 179;
 		_screen->setTextColorMap(colorMap);
diff --git a/engines/kyra/sequence/sequences_eob.cpp b/engines/kyra/sequence/sequences_eob.cpp
index a3724f3a9e..4da7b0aba4 100644
--- a/engines/kyra/sequence/sequences_eob.cpp
+++ b/engines/kyra/sequence/sequences_eob.cpp
@@ -2135,6 +2135,7 @@ void EoBAmigaFinalePlayer::playDialogue(int line, bool withAnim) {
 int EoBEngine::mainMenu() {
 	int menuChoice = _menuChoiceInit;
 	_menuChoiceInit = 0;
+	int resXtr = 0;
 	Screen::FontId of = _screen->_currentFont;
 
 	while (menuChoice >= 0 && !shouldQuit()) {
@@ -2151,32 +2152,58 @@ int EoBEngine::mainMenu() {
 					_screen->loadPalette(_ttlCfg->palFiles[i].filename, _screen->getPalette(0));
 			}
 
-			_screen->loadEoBBitmap(_ttlCfg->bmpFile, _cgaMappingDefault, 5, 3, _ttlCfg->page);
+			if (_ttlCfg->bmpFile[0])
+				_screen->loadEoBBitmap(_ttlCfg->bmpFile, _cgaMappingDefault, 5, 3, _ttlCfg->page);
 
 			if (_ttlCfg->fade)
 				_screen->fadeFromBlack(10);
 			else
 				_screen->setScreenPalette(_screen->getPalette(0));
 
-			_screen->_curPage = 2;
-			of = _screen->setFont(Screen::FID_6_FNT);
-			Common::String versionString(Common::String::format("ScummVM %s", gScummVMVersion));
-			_screen->printText(versionString.c_str(), 280 - versionString.size() * 6, 153 + _ttlCfg->versionStrYOffs, _screen->getPagePixel(2, 0, 0), 0);
-			_screen->setFont(of);
-			_screen->fillRect(0, 159 + _ttlCfg->versionStrYOffs, 319, 199, _screen->getPagePixel(2, 0, 0));
+			if (_flags.platform == Common::kPlatformSegaCD) {
 
-			gui_drawBox(_ttlCfg->menu1X, _ttlCfg->menu1Y, _ttlCfg->menu1W, _ttlCfg->menu1H, _ttlCfg->menu1col1, _ttlCfg->menu1col2, _ttlCfg->menu1col3);
-			gui_drawBox(_ttlCfg->menu2X, _ttlCfg->menu2Y, _ttlCfg->menu2W, _ttlCfg->menu2H, _ttlCfg->menu2col1, _ttlCfg->menu2col2, _ttlCfg->menu2col3);
+				// load playfield etc.
+				/*_screen->sega_getAnimator()->clearSprites();
+				_screen->sega_getRenderer()->setupWindowPlane(0, 0, SegaRenderer::kWinToLeft, SegaRenderer::kWinToTop);
+				_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 40, 28, 0x2000);
+				_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 28, 0x2000);
+				_screen->sega_selectPalette(6, 1, false);
+				_screen->sega_selectPalette(7, 3, true);*/
+				_txt->clearDim(3);
+				//_screen->sega_fadeToNeutral(0);
 
-			_screen->_curPage = 0;
-			_screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK);
-			_screen->updateScreen();
+				
+
+				_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 19, 40, 8, 0);
+				_screen->sega_getRenderer()->fillRectWithTiles(1, 7, 20, 26, 5, 0x461, true);
+				_screen->sega_getRenderer()->fillRectWithTiles(1, 6, 21, 1, 5, 0);
+				_screen->sega_getRenderer()->fillRectWithTiles(1, 6, 25, 26, 1, 0);
+				if (_flags.lang != Common::JA_JPN)
+					_screen->setFontStyles(_screen->_currentFont, Font::kStyleForceTwoByte | Font::kStyleFat);
+			} else {
+				_screen->_curPage = 2;
+				of = _screen->setFont(Screen::FID_6_FNT);
+				Common::String versionString(Common::String::format("ScummVM %s", gScummVMVersion));
+				_screen->printText(versionString.c_str(), 280 - versionString.size() * 6, 153 + _ttlCfg->versionStrYOffs, _screen->getPagePixel(2, 0, 0), 0);
+				_screen->setFont(of);
+				_screen->fillRect(0, 159 + _ttlCfg->versionStrYOffs, 319, 199, _screen->getPagePixel(2, 0, 0));
+
+				gui_drawBox(_ttlCfg->menu1X, _ttlCfg->menu1Y, _ttlCfg->menu1W, _ttlCfg->menu1H, _ttlCfg->menu1col1, _ttlCfg->menu1col2, _ttlCfg->menu1col3);
+				gui_drawBox(_ttlCfg->menu2X, _ttlCfg->menu2Y, _ttlCfg->menu2W, _ttlCfg->menu2H, _ttlCfg->menu2col1, _ttlCfg->menu2col2, _ttlCfg->menu2col3);
+
+				_screen->_curPage = 0;
+				_screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK);
+				_screen->updateScreen();
+			}
 
 			_allowImport = true;
 			menuChoice = mainMenuLoop();
 			_allowImport = false;
-			}
 
+			if (_flags.platform == Common::kPlatformSegaCD && _flags.lang != Common::JA_JPN)
+				_screen->setFontStyles(_screen->_currentFont, Font::kStyleFat);
+
+			}
 			break;
 
 		case 1:
@@ -2186,7 +2213,7 @@ int EoBEngine::mainMenu() {
 
 		case 2:
 			// create new party
-			if (_flags.platform == Common::kPlatformPC98) {
+			if (_flags.platform == Common::kPlatformPC98 || _flags.platform == Common::kPlatformSegaCD) {
 				_sound->selectAudioResourceSet(kMusicIntro);
 				_sound->loadSoundFile(0);
 				_screen->hideMouse();
@@ -2200,12 +2227,13 @@ int EoBEngine::mainMenu() {
 				_eventList.clear();
 			}
 
-			menuChoice = shouldQuit() ? -5 : -2;
+			menuChoice = shouldQuit() ? -5 : resXtr - 2;
 			break;
 
 		case 3:
-			// quit
-			menuChoice = -5;
+			// Create default party for SegaCD - Quit for all other platforms
+			menuChoice = (_flags.platform == Common::kPlatformSegaCD) ? 2 : -5;
+			resXtr = -2;
 			break;
 
 		case 4:
@@ -2246,10 +2274,10 @@ int EoBEngine::mainMenuLoop() {
 
 void EoBEngine::seq_playIntro(int part) {
 	if (_flags.platform == Common::kPlatformSegaCD) {
-		if (part != kOnlyCredits)
+		if (part == kOnlyCredits)
 			seq_segaOpeningCredits();
 		else
-			seq_segaPlaySequence(53, 53);
+			seq_segaPlaySequence(53, true);
 	} else {
 		EoBIntroPlayer(this, _screen).start((EoBIntroPlayer::IntroPart)part);
 	}
@@ -2259,7 +2287,10 @@ void EoBEngine::seq_playFinale() {
 	if (_flags.platform == Common::kPlatformPC98) {
 		EoBPC98FinalePlayer(this, _screen).start(_xdth);
 		return;
-	} 
+	} else if (_flags.platform == Common::kPlatformSegaCD) {
+		seq_segaPlaySequence(_xdth ? 55 : 56, true);
+		return;
+	}
 
 	Common::SeekableReadStream *s = _res->createReadStream("TEXT.DAT");
 	_screen->loadFileDataToPage(s, 5, 32000);
@@ -2383,7 +2414,7 @@ void EoBEngine::seq_segaOpeningCredits() {
 	_screen->sega_getRenderer()->loadToVRAM(scrollTable, 0x400, 0xD800);
 
 	_sres->loadContainer("CREDIT");
-	Common::SeekableReadStreamEndian *in = _sres->getEndianAwareResourceStream(1);
+	Common::SeekableReadStreamEndian *in = _sres->resStreamEndianAware(1);
 	_screen->sega_getRenderer()->loadToVRAM(in, 32, true);
 	delete in;
 
@@ -2400,7 +2431,7 @@ void EoBEngine::seq_segaOpeningCredits() {
 		_screen->sega_getRenderer()->loadToVRAM(scrollTable, 0x400, 0xD800);
 		_screen->sega_selectPalette(i == 3 ? 59 : 50, 0, true);
 
-		in = _sres->getEndianAwareResourceStream(i);
+		in = _sres->resStreamEndianAware(i);
 		_screen->sega_getRenderer()->loadToVRAM(in, 32, true);
 		delete in;
 
@@ -2447,7 +2478,7 @@ void EoBEngine::seq_segaOpeningCredits() {
 	_screen->sega_getRenderer()->setPitch(64);
 	_screen->sega_selectPalette(0, 0);
 
-	in = _sres->getEndianAwareResourceStream(8);
+	in = _sres->resStreamEndianAware(8);
 	_screen->sega_getRenderer()->loadToVRAM(in, 32, true);
 	delete in;
 
@@ -2474,8 +2505,8 @@ void EoBEngine::seq_segaOpeningCredits() {
 	_screen->updateScreen();
 }
 
-void EoBEngine::seq_segaSetupSequence(int id) {
-	if (_flags.platform != Common::kPlatformSegaCD || id == -1)
+void EoBEngine::seq_segaSetupSequence(int sequenceId) {
+	if (_flags.platform != Common::kPlatformSegaCD || sequenceId == -1)
 		return;
 	
 	_screen->sega_fadeToBlack(1);
@@ -2486,21 +2517,22 @@ void EoBEngine::seq_segaSetupSequence(int id) {
 		gui_drawCharPortraitWithStats(i);
 	}
 
-	_screen->sega_getRenderer()->setupWindowPlane(0, (id == 53 || id == 54) ? 23 : 18, SegaRenderer::kWinToRight, SegaRenderer::kWinToBottom);
+	_screen->sega_getRenderer()->setupWindowPlane(0, (sequenceId == 53 || sequenceId == 54) ? 23 : 18, SegaRenderer::kWinToRight, SegaRenderer::kWinToBottom);
 	_screen->sega_getRenderer()->memsetVRAM(0xD840, 0xEE, 512);
 	_screen->sega_getAnimator()->clearSprites();
 	_screen->setScreenDim(2);
 
 }
 
-bool EoBEngine::seq_segaPlaySequence(int sequenceId, int setupID) {
+bool EoBEngine::seq_segaPlaySequence(int sequenceId, bool init) {
 	if (_flags.platform != Common::kPlatformSegaCD)
 		return true;
 
 	_allowSkip = true;
 	resetSkipFlag();
 
-	seq_segaSetupSequence(setupID);
+	if (init)
+		seq_segaSetupSequence(sequenceId);
 
 	_allowSkip = false;
 	resetSkipFlag();
diff --git a/engines/kyra/sequence/sequences_hof.cpp b/engines/kyra/sequence/sequences_hof.cpp
index a5e44cf1bc..3235df6d85 100644
--- a/engines/kyra/sequence/sequences_hof.cpp
+++ b/engines/kyra/sequence/sequences_hof.cpp
@@ -496,7 +496,7 @@ int SeqPlayer_HOF::play(SequenceID firstScene, SequenceID loopStartScene) {
 			_loopStartScene -= kSequenceLoLDemoScene1;
 		_lastScene = kSequenceLoLDemoScene6 - kSequenceLoLDemoScene1;
 		_target = kLoLDemo;
-		_screen->_charWidth = 0;
+		_screen->_charSpacing = 0;
 	} else if (firstScene >= kSequenceHoFDemoVirgin) {
 		incompatibleData = (_vm->game() != GI_KYRA2 || !_vm->gameFlags().isDemo || _vm->gameFlags().isTalkie);
 		_firstScene -= kSequenceHoFDemoVirgin;
@@ -504,12 +504,12 @@ int SeqPlayer_HOF::play(SequenceID firstScene, SequenceID loopStartScene) {
 			_loopStartScene -= kSequenceHoFDemoVirgin;
 		_lastScene = kSequenceHoFDemoFisher - kSequenceHoFDemoVirgin;
 		_target = kHoFDemo;
-		_screen->_charWidth = -2;
+		_screen->_charSpacing = -2;
 	} else {
 		_isFinale = _preventLooping = firstScene > kSequenceZanfaun;
 		incompatibleData = (_vm->game() != GI_KYRA2 || (_vm->gameFlags().isDemo && (!_vm->gameFlags().isTalkie || _isFinale)));
 		_target = kHoF;
-		_screen->_charWidth = -2;
+		_screen->_charSpacing = -2;
 		if (_isFinale) {
 			soundSet = kMusicFinale;
 			_lastScene = kSequenceFrash;
@@ -622,7 +622,7 @@ void SeqPlayer_HOF::runLoop() {
 		delayTicks(75);
 
 	_screen->setCurPage(oldPage);
-	_screen->_charWidth = 0;
+	_screen->_charSpacing = 0;
 	_screen->showMouse();
 }
 
@@ -1486,7 +1486,7 @@ void SeqPlayer_HOF::playHoFTalkieCredits() {
 	_screen->updateScreen();
 	_screen->fadeFromBlack();
 
-	_screen->_charWidth = -2;
+	_screen->_charSpacing = -2;
 	uint8 *dataPtr = new uint8[0xAFD];
 	memcpy(dataPtr, talkieCredits, talkieCreditsSize);
 	_vm->staticres()->unloadId(k2SeqplayCredits);
diff --git a/engines/kyra/sequence/sequences_lok.cpp b/engines/kyra/sequence/sequences_lok.cpp
index dd36d5dbff..2da6a50c81 100644
--- a/engines/kyra/sequence/sequences_lok.cpp
+++ b/engines/kyra/sequence/sequences_lok.cpp
@@ -315,7 +315,7 @@ bool KyraEngine_LoK::seq_introMalcolmTree() {
 bool KyraEngine_LoK::seq_introKallakWriting() {
 	_seq->makeHandShapes();
 	_screen->setAnimBlockPtr(5060);
-	_screen->_charWidth = -2;
+	_screen->_charSpacing = -2;
 	_screen->clearPage(3);
 	const bool skipped = _seq->playSequence(_seq_KallakWriting, true);
 	_seq->freeHandShapes();
@@ -1192,7 +1192,7 @@ void KyraEngine_LoK::seq_playEnding() {
 	_eventList.clear();
 
 	if (_flags.platform == Common::kPlatformAmiga) {
-		_screen->_charWidth = -2;
+		_screen->_charSpacing = -2;
 		_screen->setCurPage(2);
 
 		_screen->getPalette(2).clear();
@@ -1238,7 +1238,7 @@ void KyraEngine_LoK::seq_playCredits() {
 	_screen->setCurPage(0);
 	_screen->clearCurPage();
 	_screen->setTextColorMap(colorMap);
-	_screen->_charWidth = -1;
+	_screen->_charSpacing = -1;
 
 	// we only need this for the FM-TOWNS version
 	if (_flags.platform == Common::kPlatformFMTowns && _configMusic == 1)
diff --git a/engines/kyra/sequence/sequences_lol.cpp b/engines/kyra/sequence/sequences_lol.cpp
index a4c79bbc53..f67f7a62a2 100644
--- a/engines/kyra/sequence/sequences_lol.cpp
+++ b/engines/kyra/sequence/sequences_lol.cpp
@@ -1193,7 +1193,7 @@ void LoLEngine::showCredits() {
 	_screen->hideMouse();
 
 	static const uint8 colorMap[] = { 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x6F, 0x6F, 0x6D };
-	_screen->_charWidth = 0;
+	_screen->_charSpacing = 0;
 
 	_screen->loadBitmap("ROOM.CPS", 2, 2, &_screen->getPalette(0));
 
@@ -1206,7 +1206,7 @@ void LoLEngine::showCredits() {
 
 	_screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK);
 
-	_screen->_charOffset = 0;
+	_screen->_lineSpacing = 0;
 
 	char *credits = 0;
 
diff --git a/engines/kyra/sound/sound_segacd_eob.cpp b/engines/kyra/sound/sound_segacd_eob.cpp
index 52c660cb42..c7d2feed63 100644
--- a/engines/kyra/sound/sound_segacd_eob.cpp
+++ b/engines/kyra/sound/sound_segacd_eob.cpp
@@ -76,10 +76,10 @@ void SoundSegaCD_EoB::playTrack(uint8 track) {
 	if (!_musicEnabled || !_ready)
 		return;
 
-	int loop = track >> 7;
+	int loop = track >> 6;
 	track &= 0x7F;
 
-	g_system->getAudioCDManager()->play(track - 1, loop, 0, 0);
+	g_system->getAudioCDManager()->play(track - 1, loop - 1, 0, 0);
 	g_system->getAudioCDManager()->update();
 }
 
@@ -103,9 +103,9 @@ void SoundSegaCD_EoB::updateVolumeSettings() {
 	if (/*!_driver ||*/ !_ready)
 		return;
 
-	bool mute = false;
-	if (ConfMan.hasKey("mute"))
-		mute = ConfMan.getBool("mute");
+	//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")));
diff --git a/engines/kyra/text/text.cpp b/engines/kyra/text/text.cpp
index 36af67e6aa..e7b5b0a479 100644
--- a/engines/kyra/text/text.cpp
+++ b/engines/kyra/text/text.cpp
@@ -44,9 +44,9 @@ void TextDisplayer::setTalkCoords(uint16 y) {
 }
 
 int TextDisplayer::getCenterStringX(const char *str, int x1, int x2) {
-	_screen->_charWidth = -2;
+	_screen->_charSpacing = -2;
 	int strWidth = _screen->getTextWidth(str);
-	_screen->_charWidth = 0;
+	_screen->_charSpacing = 0;
 	int w = x2 - x1 + 1;
 	return x1 + (w - strWidth) / 2;
 }
@@ -54,7 +54,7 @@ int TextDisplayer::getCenterStringX(const char *str, int x1, int x2) {
 int TextDisplayer::getCharLength(const char *str, int len) {
 	int charsCount = 0;
 	if (*str) {
-		_screen->_charWidth = -2;
+		_screen->_charSpacing = -2;
 		int i = 0;
 		while (i <= len && *str) {
 			uint c = *str++;
@@ -66,7 +66,7 @@ int TextDisplayer::getCharLength(const char *str, int len) {
 			i += _screen->getCharWidth(c);
 			++charsCount;
 		}
-		_screen->_charWidth = 0;
+		_screen->_charSpacing = 0;
 	}
 	return charsCount;
 }
@@ -99,17 +99,17 @@ char *TextDisplayer::preprocessString(const char *str) {
 	}
 	p = _talkBuffer;
 	Screen::FontId curFont = _screen->setFont(Screen::FID_8_FNT);
-	_screen->_charWidth = -2;
+	_screen->_charSpacing = -2;
 	int textWidth = _screen->getTextWidth(p);
-	_screen->_charWidth = 0;
+	_screen->_charSpacing = 0;
 	if (textWidth > 176) {
 		if (textWidth > 352) {
 			int count = getCharLength(p, textWidth / 3);
 			int offs = dropCRIntoString(p, count);
 			p += count + offs;
-			_screen->_charWidth = -2;
+			_screen->_charSpacing = -2;
 			textWidth = _screen->getTextWidth(p);
-			_screen->_charWidth = 0;
+			_screen->_charSpacing = 0;
 			count = getCharLength(p, textWidth / 2);
 			dropCRIntoString(p, count);
 		} else {
@@ -144,14 +144,14 @@ int TextDisplayer::buildMessageSubstrings(const char *str) {
 
 int TextDisplayer::getWidestLineWidth(int linesCount) {
 	int maxWidth = 0;
-	_screen->_charWidth = -2;
+	_screen->_charSpacing = -2;
 	for (int l = 0; l < linesCount; ++l) {
 		int w = _screen->getTextWidth(&_talkSubstrings[l * TALK_SUBSTRING_LEN]);
 		if (maxWidth < w) {
 			maxWidth = w;
 		}
 	}
-	_screen->_charWidth = 0;
+	_screen->_charSpacing = 0;
 	return maxWidth;
 }
 
@@ -207,9 +207,9 @@ void TextDisplayer::printText(const char *str, int x, int y, uint8 c0, uint8 c1,
 	uint8 colorMap[] = { 0, 15, 12, 12 };
 	colorMap[3] = c1;
 	_screen->setTextColor(colorMap, 0, 3);
-	_screen->_charWidth = -2;
+	_screen->_charSpacing = -2;
 	_screen->printText(str, x, y, c0, c2);
-	_screen->_charWidth = 0;
+	_screen->_charSpacing = 0;
 }
 
 void TextDisplayer::printCharacterText(const char *text, int8 charNum, int charX) {
diff --git a/engines/kyra/text/text_eob_segacd.cpp b/engines/kyra/text/text_eob_segacd.cpp
index ad872b1049..19ddb11699 100644
--- a/engines/kyra/text/text_eob_segacd.cpp
+++ b/engines/kyra/text/text_eob_segacd.cpp
@@ -29,7 +29,7 @@
 
 namespace Kyra {
 
-TextDisplayer_SegaCD::TextDisplayer_SegaCD(EoBEngine *engine, Screen_EoB *scr) : TextDisplayer_rpg(engine, scr), _renderer(scr->sega_getRenderer()), _curDim(0) {
+TextDisplayer_SegaCD::TextDisplayer_SegaCD(EoBEngine *engine, Screen_EoB *scr) : TextDisplayer_rpg(engine, scr), _screen(scr), _renderer(scr->sega_getRenderer()), _curDim(0) {
 	assert(_renderer);
 }
 
@@ -37,22 +37,42 @@ TextDisplayer_SegaCD::~TextDisplayer_SegaCD() {
 
 }
 
+void TextDisplayer_SegaCD::printMessageAtPos(const char *str, int x, int y, int textColor, int shadowColor) {
+	const ScreenDim *s = &_dimTable[_curDim];
+	if (x == -1)
+		x = s->sx;
+	if (y == -1)
+		y = s->sy;
+	if (textColor == -1)
+		textColor = s->unk8;
+	if (shadowColor == -1)
+		shadowColor = 0;
+
+	_screen->setTextMarginRight(s->w);
+	_screen->printShadedText(str, x, y, textColor, 0, shadowColor, s->w >> 3);
+
+	if (s->unkE) {
+		for (int i = 0; i < s->h >> 3; ++i)
+			_screen->sega_loadTextBufferToVRAM(i * (s->w >> 3), (s->unkC & 0x7FF) << 5, (s->w * s->h) >> 1);
+	} else {
+		_screen->sega_loadTextBufferToVRAM(0, (s->unkC & 0x7FF) << 5, (s->w * s->h) >> 1);
+	}	
+}
+
 int TextDisplayer_SegaCD::clearDim(int dim) {
 	int res = _curDim;
 	_curDim = dim;
 	const ScreenDim *s = &_dimTable[dim];
-	uint32 size = (s->w * s->h) >> 1;
-	uint8 *buf = new uint8[size];
-	memset(buf, s->unkA, size);
-	_renderer->loadToVRAM(buf, size, (s->unkC & 0x7FF) << 5);
-	delete[] buf;
+	_renderer->memsetVRAM((s->unkC & 0x7FF) << 5, s->unkA, (s->w * s->h) >> 1);
+	_screen->sega_clearTextBuffer(s->unkA);
 	return res;
 }
 
-const ScreenDim TextDisplayer_SegaCD::_dimTable[3] = {
+const ScreenDim TextDisplayer_SegaCD::_dimTable[4] = {
 	{ 0x0001, 0x0017, 0x0118, 0x0018, 0xff, 0x44, 0x2597, 0x0000 },
 	{ 0x0012, 0x0009, 0x00a0, 0x0080, 0xff, 0x00, 0x0153, 0x0028 },
-	{ 0x0001, 0x0014, 0x0130, 0x0030, 0xff, 0xee, 0xe51c, 0x0000 }
+	{ 0x0001, 0x0014, 0x0130, 0x0030, 0xff, 0xee, 0xe51c, 0x0000 },
+	{ 0x0001, 0x0017, 0x0118, 0x0018, 0xff, 0x00, 0x0461, 0x0000 }
 };
 
 } // End of namespace Kyra
diff --git a/engines/kyra/text/text_eob_segacd.h b/engines/kyra/text/text_eob_segacd.h
index d140b01a18..e8a6f1c7b4 100644
--- a/engines/kyra/text/text_eob_segacd.h
+++ b/engines/kyra/text/text_eob_segacd.h
@@ -42,6 +42,7 @@ public:
 	void printDialogueText(int stringId, const char *pageBreakString);
 	void printDialogueText(const char *str, bool wait = false);
 	void printMessage(const char *str, int textColor = -1, ...);*/
+	void printMessageAtPos(const char *str, int x = -1, int y = -1, int textColor = -1, int shadowColor = -1) override;
 
 	int clearDim(int dim) override;
 	//void clearCurDim() override;
@@ -56,10 +57,11 @@ public:
 	//int lineCount() const { return _lineCount; }
 
 private:
+	Screen_EoB *_screen;
 	SegaRenderer *_renderer;
 	int _curDim;
 
-	static const ScreenDim _dimTable[3];
+	static const ScreenDim _dimTable[4];
 };
 
 } // End of namespace Kyra
diff --git a/engines/kyra/text/text_hof.cpp b/engines/kyra/text/text_hof.cpp
index 2d6332a0a2..cc3f517c63 100644
--- a/engines/kyra/text/text_hof.cpp
+++ b/engines/kyra/text/text_hof.cpp
@@ -91,9 +91,9 @@ char *TextDisplayer_HoF::preprocessString(const char *str) {
 
 	p = _talkBuffer;
 	Screen::FontId curFont = _screen->setFont(Screen::FID_8_FNT);
-	_screen->_charWidth = -2;
+	_screen->_charSpacing = -2;
 	int textWidth = _screen->getTextWidth(p);
-	_screen->_charWidth = 0;
+	_screen->_charSpacing = 0;
 
 	int maxTextWidth = (_vm->language() == 0) ? 176 : 240;
 
@@ -102,9 +102,9 @@ char *TextDisplayer_HoF::preprocessString(const char *str) {
 			int count = getCharLength(p, textWidth / 3);
 			int offs = dropCRIntoString(p, count);
 			p += count + offs;
-			_screen->_charWidth = -2;
+			_screen->_charSpacing = -2;
 			textWidth = _screen->getTextWidth(p);
-			_screen->_charWidth = 0;
+			_screen->_charSpacing = 0;
 			count = getCharLength(p, textWidth / 2);
 			dropCRIntoString(p, count);
 		} else {
diff --git a/engines/kyra/text/text_mr.cpp b/engines/kyra/text/text_mr.cpp
index 700aed984a..1787cc58c5 100644
--- a/engines/kyra/text/text_mr.cpp
+++ b/engines/kyra/text/text_mr.cpp
@@ -45,7 +45,7 @@ char *TextDisplayer_MR::preprocessString(const char *str) {
 
 	p = _talkBuffer;
 	Screen::FontId curFont = _screen->setFont(Screen::FID_8_FNT);
-	_screen->_charWidth = -2;
+	_screen->_charSpacing = -2;
 
 	const int maxTextWidth = (_vm->language() == 0) ? 176 : 240;
 	int textWidth = _screen->getTextWidth(p);
@@ -137,9 +137,9 @@ void TextDisplayer_MR::printText(const char *str, int x, int y, uint8 c0, uint8
 	uint8 colorMap[] = { 0, 255, 240, 240 };
 	colorMap[3] = c1;
 	_screen->setTextColor(colorMap, 0, 3);
-	_screen->_charWidth = -2;
+	_screen->_charSpacing = -2;
 	_screen->printText(str, x, y, c0, c2);
-	_screen->_charWidth = 0;
+	_screen->_charSpacing = 0;
 }
 
 void TextDisplayer_MR::restoreScreen() {
diff --git a/engines/kyra/text/text_rpg.cpp b/engines/kyra/text/text_rpg.cpp
index ce4bf868c2..d76326050b 100644
--- a/engines/kyra/text/text_rpg.cpp
+++ b/engines/kyra/text/text_rpg.cpp
@@ -153,7 +153,7 @@ void TextDisplayer_rpg::displayText(char *str, ...) {
 	int sjisOffs = (sjisTextMode || _vm->game() != GI_LOL) ? 8 : 9;
 	Screen::FontId of = (_vm->game() == GI_EOB2 && _vm->gameFlags().platform == Common::kPlatformFMTowns) ? _screen->setFont(Screen::FID_8_FNT) : _screen->_currentFont;
 
-	uint16 charsPerLine = (sd->w << 3) / (_screen->getFontWidth() + _screen->_charWidth);
+	uint16 charsPerLine = (sd->w << 3) / (_screen->getFontWidth() + _screen->_charSpacing);
 
 	while (c) {
 		char a = tolower((unsigned char)_ctrl[1]);
@@ -191,7 +191,7 @@ void TextDisplayer_rpg::displayText(char *str, ...) {
 			}
 		}
 
-		uint16 dv = _textDimData[sdx].column / (_screen->getFontWidth() + _screen->_charWidth);
+		uint16 dv = _textDimData[sdx].column / (_screen->getFontWidth() + _screen->_charSpacing);
 
 		switch (c - 1) {
 		case 0:
@@ -218,11 +218,11 @@ void TextDisplayer_rpg::displayText(char *str, ...) {
 
 		case 8:
 			printLine(_currentLine);
-			dv = _textDimData[sdx].column / (_screen->getFontWidth() + _screen->_charWidth);
+			dv = _textDimData[sdx].column / (_screen->getFontWidth() + _screen->_charSpacing);
 			dv = ((dv + 8) & 0xFFF8) - 1;
 			if (dv >= charsPerLine)
 				dv = 0;
-			_textDimData[sdx].column = (_screen->getFontWidth() + _screen->_charWidth) * dv;
+			_textDimData[sdx].column = (_screen->getFontWidth() + _screen->_charSpacing) * dv;
 			break;
 
 		case 12:
@@ -325,8 +325,8 @@ void TextDisplayer_rpg::printLine(char *str) {
 	bool sjisTextMode = _pc98TextMode && (sdx == 3 || sdx == 4 || sdx == 5 || sdx == 15) ? true : false;
 	int sjisOffs = (sjisTextMode || _vm->game() != GI_LOL) ? 8 : 9;
 
-	int fh = (_screen->_currentFont == Screen::FID_SJIS_TEXTMODE_FNT) ? 9 : (_screen->getFontHeight() + _screen->_charOffset);
-	int lines = (sd->h - _screen->_charOffset) / fh;
+	int fh = (_screen->_currentFont == Screen::FID_SJIS_TEXTMODE_FNT) ? 9 : (_screen->getFontHeight() + _screen->_lineSpacing);
+	int lines = (sd->h - _screen->_lineSpacing) / fh;
 
 	while (_textDimData[sdx].line >= lines) {
 		if ((lines - _waitButtonSpace) <= _lineCount && _allowPageBreak) {
diff --git a/engines/kyra/text/text_rpg.h b/engines/kyra/text/text_rpg.h
index 1fe66f75b7..715e567013 100644
--- a/engines/kyra/text/text_rpg.h
+++ b/engines/kyra/text/text_rpg.h
@@ -42,6 +42,7 @@ public:
 	void printDialogueText(int stringId, const char *pageBreakString);
 	void printDialogueText(const char *str, bool wait = false);
 	void printMessage(const char *str, int textColor = -1, ...);
+	virtual void printMessageAtPos(const char *str, int x = -1, int y = -1, int textColor = -1, int shadowColor = -1) {}
 
 	virtual int clearDim(int dim);
 	void clearCurDim();


Commit: 73650a0f54d58b50b4f30d5f81e824eb597ca4d0
    https://github.com/scummvm/scummvm/commit/73650a0f54d58b50b4f30d5f81e824eb597ca4d0
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:09+02:00

Commit Message:
KYRA: (EOB/SegaCD) - fix main menu

Changed paths:
    engines/kyra/engine/eob.cpp
    engines/kyra/graphics/screen_eob.h
    engines/kyra/graphics/screen_eob_segacd.cpp
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/resource/resource_segacd.cpp
    engines/kyra/resource/resource_segacd.h
    engines/kyra/resource/staticres_eob.cpp
    engines/kyra/sequence/seqplayer_eob_segacd.cpp
    engines/kyra/sequence/seqplayer_eob_segacd.h
    engines/kyra/sequence/sequences_eob.cpp
    engines/kyra/sound/sound_segacd_eob.cpp
    engines/kyra/text/text_eob_segacd.cpp
    engines/kyra/text/text_eob_segacd.h


diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index 7bf285e61b..9e231cabdc 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -90,8 +90,9 @@ Common::Error EoBEngine::init() {
 	if (_flags.platform == Common::kPlatformPC98) {
 		_screen->modifyScreenDim(28, 0x0A, 0xA4, 0x15, 0x18);
 		_screen->modifyScreenDim(12, 0x01, 0x04, 0x14, 0x9A);
-	//} else if (_flags.platform == Common::kPlatformSegaCD) {
-		//_screen->modifyScreenDim(28, 0x01, 0x17, 0x23, 0x18);
+	} else if (_flags.platform == Common::kPlatformSegaCD) {
+		_screen->modifyScreenDim(27, 0x00, 0x02, 0x11, 0x03);
+		_screen->modifyScreenDim(28, 0x07, 0xA0, 0x17, 0x24);
 	} else {
 		_screen->modifyScreenDim(12, 0x01, 0x04, 0x14, 0xA0);
 	}
diff --git a/engines/kyra/graphics/screen_eob.h b/engines/kyra/graphics/screen_eob.h
index fcfa807d16..0ba09f7df5 100644
--- a/engines/kyra/graphics/screen_eob.h
+++ b/engines/kyra/graphics/screen_eob.h
@@ -128,8 +128,10 @@ public:
 	void sega_fadeToNeutral(int delay) { sega_fadePalette(delay, 0); }
 	void sega_paletteOps(int16 opPal, int16 par1, int16 par2);
 	void sega_clearTextBuffer(uint8 col);
+	void sega_drawTextBox(int pW, int pH, int x, int y, int w, int h, uint8 color1, uint8 color2);
 	void sega_loadTextBufferToVRAM(uint16 srcOffset, uint16 addr, int size);
 	void sega_gfxScale(uint8 *out, uint16 w, uint16 h, uint16 pitch, const uint8 *in, const uint16 *stampMap, const uint16 *traceVectors);
+	void sega_drawClippedLine(uint8 *dst, int pW, int pH, int x, int y, int w, int h, uint8 color);
 
 	SegaRenderer *sega_getRenderer() const { return _segaRenderer; }
 	SegaAnimator *sega_getAnimator() const { return _segaAnimator; }
diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index 2f347a492b..7e982ffc25 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -208,6 +208,18 @@ void Screen_EoB::sega_clearTextBuffer(uint8 col) {
 	memset(_textRenderBuffer, col, _textRenderBufferSize);
 }
 
+void Screen_EoB::sega_drawTextBox(int pW, int pH, int x, int y, int w, int h, uint8 color1, uint8 color2) {
+	sega_drawClippedLine(_textRenderBuffer, 26, 5, x, y, w, 1, color1);
+	sega_drawClippedLine(_textRenderBuffer, 26, 5, x, y + h - 1, w, 1, color1);
+	sega_drawClippedLine(_textRenderBuffer, 26, 5, x, y, 1, h, color1);
+	sega_drawClippedLine(_textRenderBuffer, 26, 5, x + w - 1, y, 1, h, color1);
+	sega_drawClippedLine(_textRenderBuffer, 26, 5, x + 1, y + 1, w - 2, 1, color2);
+	sega_drawClippedLine(_textRenderBuffer, 26, 5, x + 1, y + h - 2, w - 2, 1, color2);
+	sega_drawClippedLine(_textRenderBuffer, 26, 5, x + 1, y + 1, 1, h - 2, color2);
+	sega_drawClippedLine(_textRenderBuffer, 26, 5, x + w - 2, y + 1, 1, h - 2, color2);
+	
+}
+
 void Screen_EoB::sega_loadTextBufferToVRAM(uint16 srcOffset, uint16 addr, int size) {
 	_segaRenderer->loadToVRAM(_textRenderBuffer + srcOffset, size, addr);
 }
@@ -252,6 +264,34 @@ void Screen_EoB::sega_gfxScale(uint8 *out, uint16 w, uint16 h, uint16 pitch, con
 	}
 }
 
+void Screen_EoB::sega_drawClippedLine(uint8 *dst, int pW, int pH, int x, int y, int w, int h, uint8 color) {
+	uint8 p = (x & 1) ? 0x0F : 0xF0;
+	color &= p;
+	p = ~p;
+
+	dst += ((((y >> 3) * pW + (x >> 3)) << 5) + ((y & 7) << 2) + ((x & 7) >> 1));
+
+	while (h--) {
+		uint8 *dst2 = dst;
+		uint8 p2 = p;
+		uint8 col2 = color;
+		for (int i = x; i < x + w; ++i) {
+			*dst = (*dst & p) | color;
+			p = ~p;
+			color = (color << 4) | (color >> 4);
+			if (i & 1)
+				dst++;
+			if ((i & 7) == 7)
+				dst += 28;
+		}
+		dst = dst2 + 4;
+		color = col2;
+		p = p2;
+		if ((++y & 7) == 0)
+			dst = dst + (pW << 5) - 32;
+	}
+}
+
 #if SEGA_PERFORMANCE
 #define mRenderLineFragment(hFlip, oddStart, oddEnd, useMask, dst, mask, src, start, end, pal) \
 { \
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 446b675766..264b11d885 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -71,6 +71,15 @@ void EoBCoreEngine::gui_drawPlayField(bool refresh) {
 			_screen->getPalette(7).copy(_screen->getPalette(1), 0, 32);
 		}
 	}
+
+	// load playfield etc.
+	/*_screen->sega_getAnimator()->clearSprites();
+	_screen->sega_getRenderer()->setupWindowPlane(0, 0, SegaRenderer::kWinToLeft, SegaRenderer::kWinToTop);
+	_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 40, 28, 0x2000);
+	_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 28, 0x2000);
+	_screen->sega_selectPalette(6, 1, false);
+	_screen->sega_selectPalette(7, 3, true);*/
+	//_screen->sega_fadeToNeutral(0);
 }
 
 void EoBCoreEngine::gui_restorePlayField() {
@@ -2010,13 +2019,13 @@ void GUI_EoB::simpleMenu_setup(int sd, int maxItem, const char *const *strings,
 
 	for (int i = 0; i < _menuNumItems; i++) {
 		int item = simpleMenu_getMenuItem(i, menuItemsMask, itemOffset);
-		int ty = y + i * (lineSpacing + _screen->getFontHeight());
+		int ty = i * (lineSpacing + _screen->getFontHeight());
 		if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
-			_vm->_txt->printMessageAtPos(strings[item], x, ty, item == v ? 0x55 : 0xff, 0x11);
+			_vm->_txt->printMessageAtPos(strings[item], 4, 2 + ty, item == v ? 0x55 : 0xff, 0x11);
 		} else {
-			_screen->printShadedText(strings[item], x, ty, (_vm->_configRenderMode == Common::kRenderCGA) ? 1 : _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+			_screen->printShadedText(strings[item], x, y + ty, (_vm->_configRenderMode == Common::kRenderCGA) ? 1 : _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
 			if (item == v)
-				_screen->printText(strings[item], x, ty, _vm->guiSettings()->colors.guiColorLightRed, 0);
+				_screen->printText(strings[item], x, y + ty, _vm->guiSettings()->colors.guiColorLightRed, 0);
 		}
 	}
 
@@ -2071,8 +2080,8 @@ int GUI_EoB::simpleMenu_process(int sd, const char *const *strings, void *b, int
 
 	if (newItem != currentItem) {
 		if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
-			_vm->_txt->printMessageAtPos(strings[simpleMenu_getMenuItem(currentItem, menuItemsMask, itemOffset)], x, y + currentItem * lineH, 0xFF, 0x11);
-			_vm->_txt->printMessageAtPos(strings[simpleMenu_getMenuItem(newItem, menuItemsMask, itemOffset)], x, y + newItem * lineH, 0x55, 0x11);
+			_vm->_txt->printMessageAtPos(strings[simpleMenu_getMenuItem(currentItem, menuItemsMask, itemOffset)], 4, 2 + currentItem * lineH, 0xFF, 0x11);
+			_vm->_txt->printMessageAtPos(strings[simpleMenu_getMenuItem(newItem, menuItemsMask, itemOffset)], 4, 2 + newItem * lineH, 0x55, 0x11);
 			_screen->sega_getRenderer()->render(0);
 		} else {
 			_screen->printText(strings[simpleMenu_getMenuItem(currentItem, menuItemsMask, itemOffset)], x, y + currentItem * lineH, (_vm->_configRenderMode == Common::kRenderCGA) ? 1 : _vm->guiSettings()->colors.guiColorWhite, 0);
diff --git a/engines/kyra/resource/resource_segacd.cpp b/engines/kyra/resource/resource_segacd.cpp
index 9a9861ef07..f006638bcd 100644
--- a/engines/kyra/resource/resource_segacd.cpp
+++ b/engines/kyra/resource/resource_segacd.cpp
@@ -20,6 +20,8 @@
  *
  */
 
+#ifdef ENABLE_EOB
+
 #include "kyra/resource/resource.h"
 #include "kyra/resource/resource_intern.h"
 #include "kyra/resource/resource_segacd.h"
@@ -120,3 +122,5 @@ uint8 *SegaCDResource::resData(int resID, uint32 *resLen) {
 }
 
 } // End of namespace Kyra
+
+#endif // ENABLE_EOB
diff --git a/engines/kyra/resource/resource_segacd.h b/engines/kyra/resource/resource_segacd.h
index be87dd9d10..1b9b1fbe90 100644
--- a/engines/kyra/resource/resource_segacd.h
+++ b/engines/kyra/resource/resource_segacd.h
@@ -20,6 +20,8 @@
  *
  */
 
+#ifdef ENABLE_EOB
+
 #ifndef KYRA_RESOURCE_SEGACD_H
 #define KYRA_RESOURCE_SEGACD_H
 
@@ -64,3 +66,4 @@ private:
 } // End of namespace Kyra
 
 #endif
+#endif // ENABLE_EOB
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index af0b277d6e..1c5b623cd4 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -1454,7 +1454,7 @@ const EoBEngine::TitleScreenConfig EoBEngine::_titleConfig[5] = {
 		false,
 		77, 161, 173, 29, 1, 2, 12,
 		76, 160, 175, 31, 1, 2, -1,
-		-8
+		41
 	}
 };
 
diff --git a/engines/kyra/sequence/seqplayer_eob_segacd.cpp b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
index cc2d83850b..68c4390aab 100644
--- a/engines/kyra/sequence/seqplayer_eob_segacd.cpp
+++ b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
@@ -20,6 +20,8 @@
  *
  */
 
+#ifdef ENABLE_EOB
+
 #include "kyra/engine/eob.h"
 #include "kyra/graphics/screen_eob.h"
 #include "kyra/graphics/screen_eob_segacd.h"
@@ -68,7 +70,7 @@ SegaSequencePlayer::SegaSequencePlayer(EoBEngine *vm, Screen_EoB *screen, SegaCD
 	SQOPC(s_toggleWaterDeepAnimations);
 	SQOPC(s_assignSpeechAnimGraphics);
 	SQOPC(s_toggleSpeechAnimation);
-	SQOPC(s_orbZoomOutEffect);
+	SQOPC(s_orbZoomEffect);
 	SQOPC(s_stopCD);
 	SQOPC(s_playCD);
 	SQOPC(s_displayTextEn);
@@ -568,7 +570,7 @@ void SegaSequencePlayer::s_toggleSpeechAnimation(const uint8 *pos) {
 		updateSpeechAnimGraphics(0);
 }
 
-void SegaSequencePlayer::s_orbZoomOutEffect(const uint8*) {
+void SegaSequencePlayer::s_orbZoomEffect(const uint8*) {
 	_renderer->memsetVRAM(0x2AA0, 0, 0x5800);
 	DrawObject *d = &_drawObjects[16];
 	memset(_scaleSrcBuffer, 0, 0x5800);
@@ -667,3 +669,5 @@ void SegaSequencePlayer::s_playSoundEffect(const uint8 *pos) {
 #undef ARG
 
 } // End of namespace Kyra
+
+#endif // ENABLE_EOB
diff --git a/engines/kyra/sequence/seqplayer_eob_segacd.h b/engines/kyra/sequence/seqplayer_eob_segacd.h
index 30b6c6b88b..5ad9521cd4 100644
--- a/engines/kyra/sequence/seqplayer_eob_segacd.h
+++ b/engines/kyra/sequence/seqplayer_eob_segacd.h
@@ -20,6 +20,8 @@
  *
  */
 
+#ifdef ENABLE_EOB
+
 #ifndef KYRA_SEQPLAYER_SEGACD_H
 #define KYRA_SEQPLAYER_SEGACD_H
 
@@ -159,7 +161,7 @@ private:
 	void s_toggleWaterDeepAnimations(const uint8 *pos);
 	void s_assignSpeechAnimGraphics(const uint8 *pos);
 	void s_toggleSpeechAnimation(const uint8 *pos);
-	void s_orbZoomOutEffect(const uint8*);
+	void s_orbZoomEffect(const uint8*);
 	void s_stopCD(const uint8*);
 	void s_playCD(const uint8 *pos);
 	void s_displayTextEn(const uint8 *pos);
@@ -178,3 +180,4 @@ private:
 } // End of namespace Kyra
 
 #endif
+#endif // ENABLE_EOB
diff --git a/engines/kyra/sequence/sequences_eob.cpp b/engines/kyra/sequence/sequences_eob.cpp
index 4da7b0aba4..6e7a3eacc2 100644
--- a/engines/kyra/sequence/sequences_eob.cpp
+++ b/engines/kyra/sequence/sequences_eob.cpp
@@ -2160,30 +2160,21 @@ int EoBEngine::mainMenu() {
 			else
 				_screen->setScreenPalette(_screen->getPalette(0));
 
-			if (_flags.platform == Common::kPlatformSegaCD) {
+			Common::String versionString(Common::String::format("ScummVM %s", gScummVMVersion));
 
-				// load playfield etc.
-				/*_screen->sega_getAnimator()->clearSprites();
-				_screen->sega_getRenderer()->setupWindowPlane(0, 0, SegaRenderer::kWinToLeft, SegaRenderer::kWinToTop);
-				_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 40, 28, 0x2000);
-				_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 28, 0x2000);
-				_screen->sega_selectPalette(6, 1, false);
-				_screen->sega_selectPalette(7, 3, true);*/
+			if (_flags.platform == Common::kPlatformSegaCD) {
 				_txt->clearDim(3);
-				//_screen->sega_fadeToNeutral(0);
-
-				
-
+				_screen->sega_drawTextBox(26, 5, 0, 0, 208, 40, 0x11, 0xDD);
 				_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 19, 40, 8, 0);
 				_screen->sega_getRenderer()->fillRectWithTiles(1, 7, 20, 26, 5, 0x461, true);
+				_screen->sega_getRenderer()->fillRectWithTiles(1, 7, 25, 25, 1, 0x4E3, true);
 				_screen->sega_getRenderer()->fillRectWithTiles(1, 6, 21, 1, 5, 0);
-				_screen->sega_getRenderer()->fillRectWithTiles(1, 6, 25, 26, 1, 0);
-				if (_flags.lang != Common::JA_JPN)
-					_screen->setFontStyles(_screen->_currentFont, Font::kStyleForceTwoByte | Font::kStyleFat);
+				_screen->setFontStyles(_screen->_currentFont, Font::kStyleNarrow);
+				_txt->printMessageAtPos(versionString.c_str(), 200 - versionString.size() * 8, _ttlCfg->versionStrYOffs, 0x88);
+				_screen->setFontStyles(_screen->_currentFont, _flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat);
 			} else {
 				_screen->_curPage = 2;
 				of = _screen->setFont(Screen::FID_6_FNT);
-				Common::String versionString(Common::String::format("ScummVM %s", gScummVMVersion));
 				_screen->printText(versionString.c_str(), 280 - versionString.size() * 6, 153 + _ttlCfg->versionStrYOffs, _screen->getPagePixel(2, 0, 0), 0);
 				_screen->setFont(of);
 				_screen->fillRect(0, 159 + _ttlCfg->versionStrYOffs, 319, 199, _screen->getPagePixel(2, 0, 0));
diff --git a/engines/kyra/sound/sound_segacd_eob.cpp b/engines/kyra/sound/sound_segacd_eob.cpp
index c7d2feed63..6115e90fa8 100644
--- a/engines/kyra/sound/sound_segacd_eob.cpp
+++ b/engines/kyra/sound/sound_segacd_eob.cpp
@@ -24,7 +24,6 @@
 
 #include "kyra/sound/sound_intern.h"
 #include "kyra/resource/resource.h"
-//#include "kyra/sound/drivers/mlalf98.h"
 #include "common/config-manager.h"
 #include "backends/audiocd/audiocd.h"
 
@@ -113,4 +112,4 @@ void SoundSegaCD_EoB::updateVolumeSettings() {
 
 } // End of namespace Kyra
 
-#endif
+#endif // ENABLE_EOB
diff --git a/engines/kyra/text/text_eob_segacd.cpp b/engines/kyra/text/text_eob_segacd.cpp
index 19ddb11699..e000a83170 100644
--- a/engines/kyra/text/text_eob_segacd.cpp
+++ b/engines/kyra/text/text_eob_segacd.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#if defined(ENABLE_EOB) || defined(ENABLE_LOL)
+#ifdef ENABLE_EOB
 
 #include "kyra/engine/eob.h"
 #include "kyra/graphics/screen_eob.h"
@@ -72,9 +72,9 @@ const ScreenDim TextDisplayer_SegaCD::_dimTable[4] = {
 	{ 0x0001, 0x0017, 0x0118, 0x0018, 0xff, 0x44, 0x2597, 0x0000 },
 	{ 0x0012, 0x0009, 0x00a0, 0x0080, 0xff, 0x00, 0x0153, 0x0028 },
 	{ 0x0001, 0x0014, 0x0130, 0x0030, 0xff, 0xee, 0xe51c, 0x0000 },
-	{ 0x0001, 0x0017, 0x0118, 0x0018, 0xff, 0x00, 0x0461, 0x0000 }
+	{ 0x0001, 0x0017, 0x00D0, 0x0030, 0xff, 0x00, 0x0461, 0x0000 }
 };
 
 } // End of namespace Kyra
 
-#endif // (ENABLE_EOB || ENABLE_LOL)
+#endif // ENABLE_EOB
diff --git a/engines/kyra/text/text_eob_segacd.h b/engines/kyra/text/text_eob_segacd.h
index e8a6f1c7b4..dd5a35762b 100644
--- a/engines/kyra/text/text_eob_segacd.h
+++ b/engines/kyra/text/text_eob_segacd.h
@@ -20,7 +20,7 @@
  *
  */
 
-#if defined(ENABLE_EOB) || defined(ENABLE_LOL)
+#ifdef ENABLE_EOB
 
 #ifndef KYRA_TEXT_EOB_SEGACD_H
 #define KYRA_TEXT_EOB_SEGACD_H
@@ -68,4 +68,4 @@ private:
 
 #endif
 
-#endif // ENABLE_EOB || ENABLE_LOL
+#endif // ENABLE_EOB


Commit: 18ae0015302689e602ff3cc8ab6aabe3b90f21e8
    https://github.com/scummvm/scummvm/commit/18ae0015302689e602ff3cc8ab6aabe3b90f21e8
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:09+02:00

Commit Message:
KYRA: (EOB/SegaCD) - add sprite converter

(this converts the SegaCD sprites into the usual format)

Changed paths:
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/graphics/screen_eob.h
    engines/kyra/graphics/screen_eob_segacd.cpp


diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index 9e231cabdc..3822574761 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -126,21 +126,12 @@ Common::Error EoBEngine::init() {
 	////////////////////////////////
 	////////////////////////////////
 	_sres->loadContainer("ITEM");
+	_itemIconShapes = new const uint8*[_numItemIconShapes];
+	memset(_itemIconShapes, 0, _numItemIconShapes * sizeof(uint8*));
 	uint32 tsize;
 	uint8 *in = _sres->resData(0, &tsize);
-	_screen->sega_getRenderer()->loadToVRAM(in, 128, 0xFF80);
+	_screen->sega_encodeSpriteShapes(_itemIconShapes, in, _numItemIconShapes, 16, 16, 3);
 	delete[] in;
-	int hw = 5;
-	_screen->sega_getAnimator()->initSprite(0, 0, 0, 0xE7FC, 5);
-	_screen->sega_getAnimator()->update();
-	_screen->sega_getRenderer()->render(2, true);
-	_screen->sega_getAnimator()->clearSprites();
-	_screen->sega_getAnimator()->update();
-	int cp = _screen->setCurPage(2);
-	_itemIconShapes = new const uint8*[_numItemIconShapes];
-	memset(_itemIconShapes, 0, _numItemIconShapes * sizeof(uint8*));
-	_itemIconShapes[0] = _screen->encodeShape(0, 0, ((hw & 3) + 1), ((hw >> 2) + 1) << 3);
-	_screen->setCurPage(cp);
 	/////////////////////7
 	//////////////////////
 	////////////////
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index a3b3ebf503..d3f8032333 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -675,9 +675,7 @@ Common::Error EoBCoreEngine::go() {
 	_screen->setFont(_flags.platform == Common::kPlatformPC98 ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT);
 	//loadItemsAndDecorationsShapes();
 
-
 	_screen->setMouseCursor(0, 0, _itemIconShapes[0]);
-	/*setHandItem(0);*/
 
 	// Import original save game files (especially the "Quick Start Party")
 	if (ConfMan.getBool("importOrigSaves")) {
diff --git a/engines/kyra/graphics/screen_eob.h b/engines/kyra/graphics/screen_eob.h
index 0ba09f7df5..85ea35c44b 100644
--- a/engines/kyra/graphics/screen_eob.h
+++ b/engines/kyra/graphics/screen_eob.h
@@ -132,6 +132,7 @@ public:
 	void sega_loadTextBufferToVRAM(uint16 srcOffset, uint16 addr, int size);
 	void sega_gfxScale(uint8 *out, uint16 w, uint16 h, uint16 pitch, const uint8 *in, const uint16 *stampMap, const uint16 *traceVectors);
 	void sega_drawClippedLine(uint8 *dst, int pW, int pH, int x, int y, int w, int h, uint8 color);
+	void sega_encodeSpriteShapes(const uint8 **dst, const uint8 *src, int numShapes, int w, int h, int pal);
 
 	SegaRenderer *sega_getRenderer() const { return _segaRenderer; }
 	SegaAnimator *sega_getAnimator() const { return _segaAnimator; }
diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index 7e982ffc25..671af27d62 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -292,6 +292,35 @@ void Screen_EoB::sega_drawClippedLine(uint8 *dst, int pW, int pH, int x, int y,
 	}
 }
 
+void Screen_EoB::sega_encodeSpriteShapes(const uint8 **dst, const uint8 *src, int numShapes, int w, int h, int pal) {
+	int spriteSize = (w * h) >> 1;
+	_segaRenderer->loadToVRAM(src, numShapes * spriteSize, 0);
+	int hw = (((h >> 3) - 1) << 2) | ((w >> 3) - 1);
+
+	int cp = setCurPage(2);
+
+	for (int l = 0, s = 0; s < numShapes; l = s) {
+		for (int i = s; i < numShapes; ++i) {
+			_segaAnimator->initSprite(s % 80, ((s % 80) * w) % SCREEN_W, ((s % 80) / (SCREEN_W / w)) * h, ((pal << 13) | (i * (w >> 3) * (h >> 3))), hw);
+			if (((++s) % 80) == 0)
+				break;
+		}
+		
+		_segaAnimator->update();
+		_segaRenderer->render(2, 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);
+
+		clearPage(2);
+	}
+
+	_segaAnimator->clearSprites();
+	_segaAnimator->update();
+	_segaRenderer->memsetVRAM(0, 0, numShapes * spriteSize);
+	setCurPage(cp);
+}
+
 #if SEGA_PERFORMANCE
 #define mRenderLineFragment(hFlip, oddStart, oddEnd, useMask, dst, mask, src, start, end, pal) \
 { \


Commit: a26d08466c334e1bc2eb174c245c4c6cf29d9e79
    https://github.com/scummvm/scummvm/commit/a26d08466c334e1bc2eb174c245c4c6cf29d9e79
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:09+02:00

Commit Message:
KYRA: (EOB/SegaCD) - implement default party creation

Changed paths:
    engines/kyra/engine/chargen.cpp


diff --git a/engines/kyra/engine/chargen.cpp b/engines/kyra/engine/chargen.cpp
index b7bc24d965..ae925beb99 100644
--- a/engines/kyra/engine/chargen.cpp
+++ b/engines/kyra/engine/chargen.cpp
@@ -46,9 +46,9 @@ public:
 	bool start(EoBCharacter *characters, uint8 ***faceShapes, bool defaultParty);
 
 private:
+	void init(bool defaultParty);
 	bool createCustomParty(uint8 ***faceShapes);
 	void createDefaultParty();
-	void init();
 	void initButtonsFromList(int first, int numButtons);
 	void initButton(int index, int x, int y, int w, int h, int keyCode);
 	void checkForCompleteParty();
@@ -196,6 +196,8 @@ bool CharacterGenerator::start(EoBCharacter *characters, uint8 ***faceShapes, bo
 	_vm->snd_stopSound();
 	_vm->delay(_vm->_tickLength);
 
+	init(defaultParty);
+
 	if (defaultParty)
 		createDefaultParty();
 	else if (!createCustomParty(faceShapes))
@@ -213,9 +215,63 @@ bool CharacterGenerator::start(EoBCharacter *characters, uint8 ***faceShapes, bo
 	return true;
 }
 
-bool CharacterGenerator::createCustomParty(uint8 ***faceShapes) {
-	init();
+void CharacterGenerator::init(bool defaultParty) {
+	if (_faceShapes) {
+		for (int i = 0; i < 44; i++)
+			delete[] _faceShapes[i];
+		delete[] _faceShapes;
+	}
+
+	_faceShapes = new uint8 *[44];
+	if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
+		uint8 *in = _vm->resource()->fileData("FACE", 0);
+		_screen->sega_encodeSpriteShapes((const uint8**)_faceShapes, in, 44, 32, 32, 3);
+		delete[] in;
+	} else {
+		_screen->loadShapeSetBitmap("CHARGENA", 5, 3);
+		for (int i = 0; i < 44; i++)
+			_faceShapes[i] = _screen->encodeShape((i % 10) << 2, (i / 10) << 5, 4, 32, true, _vm->_cgaMappingDefault);
+	}
+	_screen->_curPage = 0;
+
+	if (_vm->gameFlags().platform == Common::kPlatformAmiga || (_vm->game() == GI_EOB1 && _vm->gameFlags().platform == Common::kPlatformPC98))
+		_screen->fadeToBlack(32);
+
+	// If we start with a default party we only need the face shapes
+	if (defaultParty)
+		return;
+
+	_screen->loadEoBBitmap((_vm->game() == GI_EOB1 && _vm->_flags.lang == Common::ES_ESP) ? "CCARGEN" : "CHARGEN", _vm->_cgaMappingDefault, 5, 3, 0);
+	_screen->selectPC98Palette(4, _screen->getPalette(0));
+
+	if (_vm->gameFlags().platform == Common::kPlatformAmiga || (_vm->game() == GI_EOB1 && _vm->gameFlags().platform == Common::kPlatformPC98))
+		_screen->fadeFromBlack(32);
+
+	_screen->loadShapeSetBitmap((_vm->game() == GI_EOB1 && _vm->_flags.lang == Common::ES_ESP) ? "CCARGENB" : "CHARGENB", 5, 3);
+	if (_chargenMagicShapes) {
+		for (int i = 0; i < 10; i++)
+			delete[] _chargenMagicShapes[i];
+		delete[] _chargenMagicShapes;
+	}
+
+	_chargenMagicShapes = new uint8 * [10];
+	for (int i = 0; i < 10; i++)
+		_chargenMagicShapes[i] = _screen->encodeShape(i << 2, 0, 4, 32, true, _vm->_cgaMappingDefault);
+
+	for (int i = 0; i < 17; i++) {
+		const CreatePartyModButton *c = &_chargenModButtons[i];
+		_chargenButtonLabels[i] = c->labelW ? _screen->encodeShape(c->encodeLabelX, c->encodeLabelY, c->labelW, c->labelH, true, _vm->_cgaMappingDefault) : 0;
+	}
 
+	_screen->convertPage(3, 2, _vm->_cgaMappingDefault);
+	_screen->_curPage = 0;
+	_screen->convertToHiColor(2);
+	_screen->shadeRect(142, 63, 306, 193, 4);
+	_screen->copyRegion(144, 64, 0, 0, 180, 128, 0, 2, Screen::CR_NO_P_CHECK);
+	_screen->updateScreen();
+}
+
+bool CharacterGenerator::createCustomParty(uint8 ***faceShapes) {
 	_screen->setScreenDim(2);
 
 	checkForCompleteParty();
@@ -285,52 +341,24 @@ bool CharacterGenerator::createCustomParty(uint8 ***faceShapes) {
 void CharacterGenerator::createDefaultParty() {
 	assert(_chargenDefaultNames);
 	assert(_chargenDefaultStats);
-}
-
-void CharacterGenerator::init() {
-	_screen->loadShapeSetBitmap("CHARGENA", 5, 3);
-	if (_faceShapes) {
-		for (int i = 0; i < 44; i++)
-			delete[] _faceShapes[i];
-		delete[] _faceShapes;
+	const uint8 *pos = _chargenDefaultStats;
+	for (int i = 0; i < 4; ++i) {
+		EoBCharacter &c = _characters[i];
+		c.raceSex = *pos++;
+		c.cClass = *pos++;
+		c.alignment = *pos++;
+		c.portrait = *pos++;
+		c.faceShape = _faceShapes[c.portrait];
+		c.strengthCur = *pos++;
+		c.intelligenceCur = *pos++;
+		c.wisdomCur = *pos++;
+		c.dexterityCur = *pos++;
+		c.constitutionCur = *pos++;
+		c.charismaCur = *pos++;
+		c.armorClass = *pos++;
+		c.hitPointsCur = *pos++;
+		Common::strlcpy(c.name, _chargenDefaultNames[i], 11);
 	}
-
-	_faceShapes = new uint8*[44];
-	for (int i = 0; i < 44; i++)
-		_faceShapes[i] = _screen->encodeShape((i % 10) << 2, (i / 10) << 5, 4, 32, true, _vm->_cgaMappingDefault);
-	_screen->_curPage = 0;
-
-	if (_vm->gameFlags().platform == Common::kPlatformAmiga || (_vm->game() == GI_EOB1 && _vm->gameFlags().platform == Common::kPlatformPC98))
-		_screen->fadeToBlack(32);
-
-	_screen->loadEoBBitmap((_vm->game() == GI_EOB1 && _vm->_flags.lang == Common::ES_ESP) ? "CCARGEN" : "CHARGEN", _vm->_cgaMappingDefault, 5, 3, 0);
-	_screen->selectPC98Palette(4, _screen->getPalette(0));
-
-	if (_vm->gameFlags().platform == Common::kPlatformAmiga || (_vm->game() == GI_EOB1 && _vm->gameFlags().platform == Common::kPlatformPC98))
-		_screen->fadeFromBlack(32);
-
-	_screen->loadShapeSetBitmap((_vm->game() == GI_EOB1 && _vm->_flags.lang == Common::ES_ESP) ? "CCARGENB" : "CHARGENB", 5, 3);
-	if (_chargenMagicShapes) {
-		for (int i = 0; i < 10; i++)
-			delete[] _chargenMagicShapes[i];
-		delete[] _chargenMagicShapes;
-	}
-
-	_chargenMagicShapes = new uint8*[10];
-	for (int i = 0; i < 10; i++)
-		_chargenMagicShapes[i] = _screen->encodeShape(i << 2, 0, 4, 32, true, _vm->_cgaMappingDefault);
-
-	for (int i = 0; i < 17; i++) {
-		const CreatePartyModButton *c = &_chargenModButtons[i];
-		_chargenButtonLabels[i] = c->labelW ? _screen->encodeShape(c->encodeLabelX, c->encodeLabelY, c->labelW, c->labelH, true, _vm->_cgaMappingDefault) : 0;
-	}
-
-	_screen->convertPage(3, 2, _vm->_cgaMappingDefault);
-	_screen->_curPage = 0;
-	_screen->convertToHiColor(2);
-	_screen->shadeRect(142, 63, 306, 193, 4);
-	_screen->copyRegion(144, 64, 0, 0, 180, 128, 0, 2, Screen::CR_NO_P_CHECK);
-	_screen->updateScreen();
 }
 
 void CharacterGenerator::initButtonsFromList(int first, int numButtons) {


Commit: c2c7cfff9ba6f5825b17f501cf7e2bd50fd14e16
    https://github.com/scummvm/scummvm/commit/c2c7cfff9ba6f5825b17f501cf7e2bd50fd14e16
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:10+02:00

Commit Message:
KYRA: (EOB/SegaCD) - implement specific shape handling

Changed paths:
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eob.h
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h


diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index 3822574761..f93938f01c 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -122,24 +122,21 @@ Common::Error EoBEngine::init() {
 		assert(_txt);
 	}
 
-	//////////////////////////////77
-	////////////////////////////////
-	////////////////////////////////
+	return Common::kNoError;
+}
+
+void EoBEngine::loadItemsAndDecorationsShapes() {
+	if (_flags.platform != Common::kPlatformSegaCD) {
+		EoBCoreEngine::loadItemsAndDecorationsShapes();
+		return;
+	}
+
 	_sres->loadContainer("ITEM");
-	_itemIconShapes = new const uint8*[_numItemIconShapes];
+	_itemIconShapes = new const uint8 *[_numItemIconShapes];
 	memset(_itemIconShapes, 0, _numItemIconShapes * sizeof(uint8*));
-	uint32 tsize;
-	uint8 *in = _sres->resData(0, &tsize);
+	uint8 *in = _sres->resData(0);
 	_screen->sega_encodeSpriteShapes(_itemIconShapes, in, _numItemIconShapes, 16, 16, 3);
 	delete[] in;
-	/////////////////////7
-	//////////////////////
-	////////////////
-
-
-
-
-	return Common::kNoError;
 }
 
 void EoBEngine::startupNew() {
diff --git a/engines/kyra/engine/eob.h b/engines/kyra/engine/eob.h
index bdc06a2ea2..6f9790fe6c 100644
--- a/engines/kyra/engine/eob.h
+++ b/engines/kyra/engine/eob.h
@@ -75,6 +75,9 @@ private:
 	static const TitleScreenConfig _titleConfig[5];
 	const TitleScreenConfig *_ttlCfg;
 
+	// Init
+	void loadItemsAndDecorationsShapes() override;
+
 	// Main loop
 	void startupNew() override;
 	void startupLoad() override;
@@ -91,10 +94,11 @@ private:
 	void seq_xdeath() override;
 
 	void seq_segaOpeningCredits();
+	void seq_segaFinalCredits();
 	void seq_segaSetupSequence(int sequenceId);
+	void seq_segaRestoreAfterSequence();
 	bool seq_segaPlaySequence(int sequenceId, bool init = false);
 
-
 	const char *const *_finBonusStrings;
 	SegaSequencePlayer *_seqPlayer;
 	bool _xdth;
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index d3f8032333..d9514c43a2 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -673,7 +673,7 @@ Common::Error EoBCoreEngine::go() {
 	static_cast<Debugger_EoB *>(getDebugger())->initialize();
 	_txt->removePageBreakFlag();
 	_screen->setFont(_flags.platform == Common::kPlatformPC98 ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT);
-	//loadItemsAndDecorationsShapes();
+	loadItemsAndDecorationsShapes();
 
 	_screen->setMouseCursor(0, 0, _itemIconShapes[0]);
 
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index 9435716ca6..0429990f5c 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -273,7 +273,7 @@ protected:
 	bool _playFinale;
 
 	//Init, config
-	void loadItemsAndDecorationsShapes();
+	virtual void loadItemsAndDecorationsShapes();
 	void releaseItemsAndDecorationsShapes();
 
 	void initButtonData();


Commit: 78c6b8fb92564ef6cd1b153c2a86062aa3c19ff2
    https://github.com/scummvm/scummvm/commit/78c6b8fb92564ef6cd1b153c2a86062aa3c19ff2
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:10+02:00

Commit Message:
KYRA: (EOB/SegaCD) - cleanup

Changed paths:
    engines/kyra/graphics/screen_eob_segacd.cpp
    engines/kyra/graphics/screen_eob_segacd.h
    engines/kyra/resource/resource_segacd.cpp
    engines/kyra/resource/resource_segacd.h
    engines/kyra/sequence/seqplayer_eob_segacd.cpp
    engines/kyra/sequence/seqplayer_eob_segacd.h


diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index 671af27d62..ee241003df 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -1182,6 +1182,56 @@ const uint8 *SegaCDFont::getGlyphData(uint16 c, uint8 &charWidth, uint8 &charHei
 
 #undef mRenderLineFragment
 
+ScrollManager::ScrollManager(SegaRenderer *renderer) :_renderer(renderer) {
+	_vScrollTimers = new ScrollTimer[2];
+	assert(_vScrollTimers);
+	_hScrollTimers = new ScrollTimer[2];
+	assert(_hScrollTimers);
+}
+
+ScrollManager::~ScrollManager() {
+	delete[] _vScrollTimers;
+	delete[] _hScrollTimers;
+}
+
+void ScrollManager::setVScrollTimers(uint16 destA, int incrA, int delayA, uint16 destB, int incrB, int delayB) {
+	_vScrollTimers[0]._offsDest = destA;
+	_vScrollTimers[0]._incr = incrA;
+	_vScrollTimers[0]._timer = _vScrollTimers[0]._delay = delayA;
+	_vScrollTimers[1]._offsDest = destB;
+	_vScrollTimers[1]._incr = incrB;
+	_vScrollTimers[1]._timer = _vScrollTimers[1]._delay = delayB;
+}
+
+void ScrollManager::setHScrollTimers(uint16 destA, int incrA, int delayA, uint16 destB, int incrB, int delayB) {
+	_hScrollTimers[0]._offsDest = destA;
+	_hScrollTimers[0]._incr = incrA;
+	_hScrollTimers[0]._timer = _hScrollTimers[0]._delay = delayA;
+	_hScrollTimers[1]._offsDest = destB;
+	_hScrollTimers[1]._incr = incrB;
+	_hScrollTimers[1]._timer = _hScrollTimers[1]._delay = delayB;
+}
+
+void ScrollManager::updateScrollTimers() {
+	for (int i = 0; i < 4; ++i) {
+		ScrollTimer &t = i < 2 ? _vScrollTimers[i] : _hScrollTimers[i - 2];
+		if (t._delay == 0 && t._offsCur != t._offsDest)
+			t._offsCur = t._offsDest;
+		if (t._offsCur == t._offsDest)
+			continue;
+		if (--t._timer)
+			continue;
+
+		t._offsCur += t._incr;
+		t._timer = t._delay;
+	}
+
+	_renderer->writeVSRAMValue(0, _vScrollTimers[0]._offsCur);
+	_renderer->writeVSRAMValue(2, _vScrollTimers[1]._offsCur);
+	uint16 hscr[2] = { (uint16)_hScrollTimers[0]._offsCur, (uint16)_hScrollTimers[1]._offsCur };
+	_renderer->loadToVRAM(hscr, 4, 0xD800);
+}
+
 } // End of namespace Kyra
 
 #endif // ENABLE_EOB
diff --git a/engines/kyra/graphics/screen_eob_segacd.h b/engines/kyra/graphics/screen_eob_segacd.h
index 194597e31b..69edf61fe2 100644
--- a/engines/kyra/graphics/screen_eob_segacd.h
+++ b/engines/kyra/graphics/screen_eob_segacd.h
@@ -182,6 +182,30 @@ private:
 	bool _needUpdate;
 };
 
+class ScrollManager {
+public:
+	ScrollManager(SegaRenderer *renderer);
+	~ScrollManager();
+
+	void setVScrollTimers(uint16 destA, int incrA, int delayA, uint16 destB, int incrB, int delayB);
+	void setHScrollTimers(uint16 destA, int incrA, int delayA, uint16 destB, int incrB, int delayB);
+	void updateScrollTimers();
+
+private:
+	struct ScrollTimer {
+		ScrollTimer() : _offsCur(0), _offsDest(0), _incr(0), _delay(0), _timer(0) {}
+		int16 _offsCur;
+		int16 _offsDest;
+		int16 _incr;
+		int16 _delay;
+		int16 _timer;
+	};
+
+	ScrollTimer *_vScrollTimers;
+	ScrollTimer *_hScrollTimers;
+	SegaRenderer *_renderer;
+};
+
 } // End of namespace Kyra
 
 #endif // ENABLE_EOB
diff --git a/engines/kyra/resource/resource_segacd.cpp b/engines/kyra/resource/resource_segacd.cpp
index f006638bcd..e8ef4522f8 100644
--- a/engines/kyra/resource/resource_segacd.cpp
+++ b/engines/kyra/resource/resource_segacd.cpp
@@ -29,7 +29,7 @@
 
 namespace Kyra {
 
-SegaCDResource::SegaCDResource(Resource *res) : _res(res), _str(0), _resTable(0), _numResources(0) {
+SegaCDResource::SegaCDResource(Resource *res) : _res(res), _str(0), _resTable(0), _numResources(0), _curOffset(0), _curSize(0) {
 }
 
 SegaCDResource::~SegaCDResource() {
@@ -37,6 +37,9 @@ SegaCDResource::~SegaCDResource() {
 }
 
 bool SegaCDResource::loadContainer(const Common::String &filename, uint32 offset, uint32 size) {
+	if (_curFile.equals(filename) && _curOffset == offset && _curSize == size)
+		return true;
+
 	unloadContainer();
 
 	_str = _res->createEndianAwareReadStream(filename);
@@ -77,6 +80,10 @@ bool SegaCDResource::loadContainer(const Common::String &filename, uint32 offset
 		_resTable[i]._len = next - _resTable[i]._offset;
 	}
 
+	_curFile = filename;
+	_curOffset = offset;
+	_curSize = size;
+
 	return true;
 }
 
diff --git a/engines/kyra/resource/resource_segacd.h b/engines/kyra/resource/resource_segacd.h
index 1b9b1fbe90..9c7a214256 100644
--- a/engines/kyra/resource/resource_segacd.h
+++ b/engines/kyra/resource/resource_segacd.h
@@ -47,7 +47,7 @@ public:
 	Common::SeekableReadStreamEndian *resStreamEndianAware(int resID);
 	Common::SeekableReadStream *resStream(int resID);
 
-	uint8 *resData(int resID, uint32 *resLen);
+	uint8 *resData(int resID, uint32 *resLen = 0);
 
 private:
 	Resource *_res;
@@ -61,6 +61,9 @@ private:
 	TableEntry *_resTable;
 	int _numResources;
 	Common::SeekableReadStreamEndian *_str;
+	Common::String _curFile;
+	uint32 _curOffset;
+	uint32 _curSize;
 };
 
 } // End of namespace Kyra
diff --git a/engines/kyra/sequence/seqplayer_eob_segacd.cpp b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
index 68c4390aab..8e98231cf7 100644
--- a/engines/kyra/sequence/seqplayer_eob_segacd.cpp
+++ b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
@@ -78,10 +78,8 @@ SegaSequencePlayer::SegaSequencePlayer(EoBEngine *vm, Screen_EoB *screen, SegaCD
 	SQOPC(s_playSoundEffect);
 #undef SQOPC
 
-	_vScrollTimers = new ScrollTimer[2];
-	assert(_vScrollTimers);
-	_hScrollTimers = new ScrollTimer[2];
-	assert(_hScrollTimers);
+	_scrollManager = new ScrollManager(_renderer);
+	assert(_scrollManager);
 	_tileSets = new TileSet[100];
 	assert(_tileSets);
 	memset(_tileSets, 0, 100 * sizeof(TileSet));
@@ -117,8 +115,7 @@ SegaSequencePlayer::SegaSequencePlayer(EoBEngine *vm, Screen_EoB *screen, SegaCD
 SegaSequencePlayer::~SegaSequencePlayer() {
 	delete[] _drawObjects;
 	delete[] _tileSets;
-	delete[] _vScrollTimers;
-	delete[] _hScrollTimers;
+	delete[] _scrollManager;
 	delete[] _scaleSrcBuffer;
 	delete[] _scaleOutBuffer;
 	delete[] _scaleStampMap;
@@ -131,8 +128,8 @@ SegaSequencePlayer::~SegaSequencePlayer() {
 bool SegaSequencePlayer::play(int id) {
 	_screen->sega_fadeToBlack(2);
 	_animator->clearSprites();
-	setVScrollTimers(0, 1, 0, 0, 1, 0);
-	setHScrollTimers(0, 1, 0, 0, 1, 0);
+	_scrollManager->setVScrollTimers(0, 1, 0, 0, 1, 0);
+	_scrollManager->setHScrollTimers(0, 1, 0, 0, 1, 0);
 	_vm->_txt->clearDim(2);
 
 	_renderer->fillRectWithTiles(2, 0, 0, 40, 28, 0xE6C2);
@@ -266,7 +263,7 @@ void SegaSequencePlayer::run(const uint8 *data) {
 		if (_playSpeechAnimation)
 			updateSpeechAnimations();
 
-		updateScrollTimers();
+		_scrollManager->updateScrollTimers();
 		_animator->update();
 		_renderer->render(0);
 		_screen->sega_updatePaletteFaders(-1);
@@ -275,7 +272,7 @@ void SegaSequencePlayer::run(const uint8 *data) {
 		uint32 now = _vm->_system->getMillis();
 		int diff = now - (frameStart + 16);
 		if (diff < 0)
-			_vm->delay(frameStart + 16 - now);
+			_vm->delay((uint32)-diff);
 		else if (diff) {
 			frameCounter += diff;
 			// This will be triggered with higher values whenever there is a palette fading and the code waits for it
@@ -296,44 +293,6 @@ void SegaSequencePlayer::run(const uint8 *data) {
 	}
 }
 
-void SegaSequencePlayer::setVScrollTimers(uint16 destA, int incrA, int delayA, uint16 destB, int incrB, int delayB) {
-	_vScrollTimers[0]._offsDest = destA;
-	_vScrollTimers[0]._incr = incrA;
-	_vScrollTimers[0]._timer = _vScrollTimers[0]._delay = delayA;
-	_vScrollTimers[1]._offsDest = destB;
-	_vScrollTimers[1]._incr = incrB;
-	_vScrollTimers[1]._timer = _vScrollTimers[1]._delay = delayB;
-}
-
-void SegaSequencePlayer::setHScrollTimers(uint16 destA, int incrA, int delayA, uint16 destB, int incrB, int delayB) {
-	_hScrollTimers[0]._offsDest = destA;
-	_hScrollTimers[0]._incr = incrA;
-	_hScrollTimers[0]._timer = _hScrollTimers[0]._delay = delayA;
-	_hScrollTimers[1]._offsDest = destB;
-	_hScrollTimers[1]._incr = incrB;
-	_hScrollTimers[1]._timer = _hScrollTimers[1]._delay = delayB;
-}
-
-void SegaSequencePlayer::updateScrollTimers() {
-	for (int i = 0; i < 4; ++i) {
-		ScrollTimer &t = i < 2 ? _vScrollTimers[i] : _hScrollTimers[i - 2];
-		if (t._delay == 0 && t._offsCur != t._offsDest)
-			t._offsCur = t._offsDest;
-		if (t._offsCur == t._offsDest)
-			continue;
-		if (--t._timer)
-			continue;
-
-		t._offsCur += t._incr;
-		t._timer = t._delay;
-	}
-
-	_renderer->writeVSRAMValue(0, _vScrollTimers[0]._offsCur);
-	_renderer->writeVSRAMValue(2, _vScrollTimers[1]._offsCur);
-	uint16 hscr[2] = { (uint16)_hScrollTimers[0]._offsCur, (uint16)_hScrollTimers[1]._offsCur };
-	_renderer->loadToVRAM(hscr, 4, 0xD800);
-}
-
 void SegaSequencePlayer::animateWaterdeepScene() {
 	if (--_waterdeepSceneTimer > 0)
 		return;
@@ -472,11 +431,11 @@ void SegaSequencePlayer::s_setPalette(const uint8 *pos) {
 }
 
 void SegaSequencePlayer::s_vScroll(const uint8 *pos) {
-	setVScrollTimers(ARG(0), S_ARG(2), ARG(4), ARG(6), S_ARG(8), ARG(10));
+	_scrollManager->setVScrollTimers(ARG(0), S_ARG(2), ARG(4), ARG(6), S_ARG(8), ARG(10));
 }
 
 void SegaSequencePlayer::s_hScroll(const uint8 *pos) {
-	setHScrollTimers(ARG(0), S_ARG(2), ARG(4), ARG(6), S_ARG(8), ARG(10));
+	_scrollManager->setHScrollTimers(ARG(0), S_ARG(2), ARG(4), ARG(6), S_ARG(8), ARG(10));
 }
 
 void SegaSequencePlayer::s_paletteOps(const uint8 *pos) {
diff --git a/engines/kyra/sequence/seqplayer_eob_segacd.h b/engines/kyra/sequence/seqplayer_eob_segacd.h
index 5ad9521cd4..b2651c02e7 100644
--- a/engines/kyra/sequence/seqplayer_eob_segacd.h
+++ b/engines/kyra/sequence/seqplayer_eob_segacd.h
@@ -45,9 +45,7 @@ public:
 
 private:
 	void run(const uint8 *data);
-	void setVScrollTimers(uint16 destA, int incrA, int delayA, uint16 destB, int incrB, int delayB);
-	void setHScrollTimers(uint16 destA, int incrA, int delayA, uint16 destB, int incrB, int delayB);
-	void updateScrollTimers();
+
 	void animateWaterdeepScene();
 	void updateSpeechAnimations();
 	void updateSpeechAnimGraphics(int animDrawOp);
@@ -60,18 +58,6 @@ private:
 
 	TileSet *_tileSets;
 
-	struct ScrollTimer {
-		ScrollTimer() : _offsCur(0), _offsDest(0), _incr(0), _delay(0), _timer(0) {}
-		int16 _offsCur;
-		int16 _offsDest;
-		int16 _incr;
-		int16 _delay;
-		int16 _timer;
-	};
-
-	ScrollTimer *_vScrollTimers;
-	ScrollTimer *_hScrollTimers;
-
 	struct DrawObject {
 		uint16 agg;
 		const uint16 *tileData;
@@ -108,6 +94,7 @@ private:
 	SegaRenderer *_renderer;
 	SegaAnimator *_animator;
 	SegaCDResource *_res;
+	ScrollManager *_scrollManager;
 
 private:
 	class SQOpcode : public Common::Functor1Mem<const uint8*, void, SegaSequencePlayer> {


Commit: b46ab198f71550bb31c13f7be600979ab2676350
    https://github.com/scummvm/scummvm/commit/b46ab198f71550bb31c13f7be600979ab2676350
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:10+02:00

Commit Message:
KYRA: (EOB/SegaCD) - fix warning

Changed paths:
    engines/kyra/engine/chargen.cpp
    engines/kyra/engine/eobcommon.h


diff --git a/engines/kyra/engine/chargen.cpp b/engines/kyra/engine/chargen.cpp
index ae925beb99..a13ef7bb87 100644
--- a/engines/kyra/engine/chargen.cpp
+++ b/engines/kyra/engine/chargen.cpp
@@ -43,11 +43,11 @@ public:
 	CharacterGenerator(EoBCoreEngine *vm, Screen_EoB *screen);
 	~CharacterGenerator();
 
-	bool start(EoBCharacter *characters, uint8 ***faceShapes, bool defaultParty);
+	bool start(EoBCharacter *characters, const uint8 ***faceShapes, bool defaultParty);
 
 private:
 	void init(bool defaultParty);
-	bool createCustomParty(uint8 ***faceShapes);
+	bool createCustomParty(const uint8 ***faceShapes);
 	void createDefaultParty();
 	void initButtonsFromList(int first, int numButtons);
 	void initButton(int index, int x, int y, int w, int h, int keyCode);
@@ -121,7 +121,7 @@ private:
 	static const int16 _raceModifiers[];
 
 	EoBCharacter *_characters;
-	uint8 **_faceShapes;
+	const uint8 **_faceShapes;
 
 	EoBCoreEngine *_vm;
 	Screen_EoB *_screen;
@@ -184,7 +184,7 @@ CharacterGenerator::~CharacterGenerator() {
 	_screen->clearPage(2);
 }
 
-bool CharacterGenerator::start(EoBCharacter *characters, uint8 ***faceShapes, bool defaultParty) {
+bool CharacterGenerator::start(EoBCharacter *characters, const uint8 ***faceShapes, bool defaultParty) {
 	if (!characters || !faceShapes) {
 		warning("CharacterGenerator::start: Called without character data");
 		return true;
@@ -222,10 +222,10 @@ void CharacterGenerator::init(bool defaultParty) {
 		delete[] _faceShapes;
 	}
 
-	_faceShapes = new uint8 *[44];
+	_faceShapes = new const uint8 *[44];
 	if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
 		uint8 *in = _vm->resource()->fileData("FACE", 0);
-		_screen->sega_encodeSpriteShapes((const uint8**)_faceShapes, in, 44, 32, 32, 3);
+		_screen->sega_encodeSpriteShapes(_faceShapes, in, 44, 32, 32, 3);
 		delete[] in;
 	} else {
 		_screen->loadShapeSetBitmap("CHARGENA", 5, 3);
@@ -271,7 +271,7 @@ void CharacterGenerator::init(bool defaultParty) {
 	_screen->updateScreen();
 }
 
-bool CharacterGenerator::createCustomParty(uint8 ***faceShapes) {
+bool CharacterGenerator::createCustomParty(const uint8 ***faceShapes) {
 	_screen->setScreenDim(2);
 
 	checkForCompleteParty();
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index 0429990f5c..ad2ccf98c5 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -107,7 +107,7 @@ struct EoBCharacter {
 	uint8 food;
 	uint8 level[3];
 	uint32 experience[3];
-	uint8 *faceShape;
+	const uint8 *faceShape;
 
 	int8 mageSpells[80];
 	int8 clericSpells[80];
@@ -331,7 +331,7 @@ protected:
 	bool startCharacterGeneration(bool defaultParty);
 	bool startPartyTransfer();
 
-	uint8 **_faceShapes;
+	const uint8 **_faceShapes;
 
 	static const int8 _characterClassType[];
 	static const uint8 _hpIncrPerLevel[];


Commit: a657d022284433a5020692e39fa2dcb20f48097f
    https://github.com/scummvm/scummvm/commit/a657d022284433a5020692e39fa2dcb20f48097f
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:10+02:00

Commit Message:
KYRA: (EOB/SegaCD) - implement ending credits and stats screen

Changed paths:
    engines/kyra/engine/eob.h
    engines/kyra/graphics/screen.h
    engines/kyra/graphics/screen_eob.cpp
    engines/kyra/graphics/screen_eob_segacd.cpp
    engines/kyra/graphics/screen_eob_segacd.h
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/sequence/seqplayer_eob_segacd.cpp
    engines/kyra/sequence/sequences_eob.cpp
    engines/kyra/text/text_eob_segacd.cpp
    engines/kyra/text/text_eob_segacd.h
    engines/kyra/text/text_rpg.h


diff --git a/engines/kyra/engine/eob.h b/engines/kyra/engine/eob.h
index 6f9790fe6c..d940575b36 100644
--- a/engines/kyra/engine/eob.h
+++ b/engines/kyra/engine/eob.h
@@ -95,6 +95,8 @@ private:
 
 	void seq_segaOpeningCredits();
 	void seq_segaFinalCredits();
+	void seq_segaShowStats();
+
 	void seq_segaSetupSequence(int sequenceId);
 	void seq_segaRestoreAfterSequence();
 	bool seq_segaPlaySequence(int sequenceId, bool init = false);
diff --git a/engines/kyra/graphics/screen.h b/engines/kyra/graphics/screen.h
index 359f7f78da..871256a4f8 100644
--- a/engines/kyra/graphics/screen.h
+++ b/engines/kyra/graphics/screen.h
@@ -125,8 +125,8 @@ public:
 		kStyleNone			=	0,
 		kStyleLeftShadow	=	1	<<	0,
 		kStyleFat			=	1	<<	1,
-		kStyleNarrow		=	1	<<	2,
-		kStyleVariant		=	1	<<	3,
+		kStyleNarrow1		=	1	<<	2,
+		kStyleNarrow2		=	1	<<	3,
 		kStyleForceTwoByte	=	1	<<	4,
 		kStyleFixedWidth	=	1	<<	5
 	};
diff --git a/engines/kyra/graphics/screen_eob.cpp b/engines/kyra/graphics/screen_eob.cpp
index f09c8bd93e..43e59b0329 100644
--- a/engines/kyra/graphics/screen_eob.cpp
+++ b/engines/kyra/graphics/screen_eob.cpp
@@ -1517,7 +1517,7 @@ bool Screen_EoB::loadFont(FontId fontId, const char *filename) {
 		fnt = new AmigaDOSFont(_vm->resource(), _vm->game() == GI_EOB2 && _vm->gameFlags().lang == Common::DE_DEU);
 	} else if (_isSegaCD) {
 		fnt = new SegaCDFont(_vm->staticres()->loadRawDataBe16(kEoB1Ascii2SjisTable1, temp), _vm->staticres()->loadRawDataBe16(kEoB1Ascii2SjisTable2, temp),
-			_vm->staticres()->loadRawData(kEoB1CharWidthTable1, temp), _vm->staticres()->loadRawData(kEoB1CharWidthTable1, temp), _vm->staticres()->loadRawData(kEoB1CharWidthTable1, temp));
+			_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);
diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index ee241003df..3dff8a09e9 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -422,6 +422,7 @@ void SegaRenderer::setupPlaneAB(int pixelWidth, int pixelHeigth) {
 			_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;
 	}
 }
@@ -433,6 +434,7 @@ void SegaRenderer::setupWindowPlane(int blockX, int blockY, int horizontalMode,
 		_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;
 }
 
@@ -655,50 +657,53 @@ void SegaRenderer::render(int destPageNum, bool spritesOnly) {
 
 void SegaRenderer::renderPlanePart(int plane, uint8 *dstBuffer, int x1, int y1, int x2, int y2) {
 	SegaPlane *p = &_planes[plane];
-	uint16 *ntbl = p->nameTable + y1 * _pitch + x1;
 	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;
-			renderPlaneTile(dst, x, ntbl, vScrollTableIndex, hScrollTableIndex, _pitch);
+			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 + x1], 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 + x1], vscrPxStart, vscrPxEnd, hScrollTableIndex, _pitch);
+			}
 			dst += 8;
 		}
-		ntbl += _pitch;
 		dst = dst2 + (_screenW << 3);
 	}
 }
 
-void SegaRenderer::renderPlaneTile(uint8 *dst, int destX, const uint16 *nameTable, int vScrollTableIndex, int hScrollTableIndex, uint16 pitch) {
-	uint16 vscrNt = 0;
-	uint16 vscrPx = 0;
-
-	if (vScrollTableIndex != -1) {
-		vscrNt = _vsram[vScrollTableIndex] & 0x3FF;
-		vscrPx = vscrNt & 7;
-		vscrNt >>= 3;
-	}
-
-	for (int bY = vscrPx; bY < vscrPx + 8; ++bY) {
+void SegaRenderer::renderPlaneTile(uint8 *dst, int destX, const uint16 *nameTable, 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 (bY == 8) {
-			nameTable += pitch;
-			if (hScrollTableIndex != -1 && _hScrollMode == kHScroll8PixelRows)
-				hScrollTableIndex += 16;
-		}
-
 		if (hScrollTableIndex != -1) {
 			hscrNt = (-_hScrollTable[hScrollTableIndex]) & 0x3FF;
 			hscrPx = hscrNt & 7;
 			hscrNt >>= 3;
 		}
 
-		const uint16 *pNt = &nameTable[vscrNt * pitch + ((destX + hscrNt) % pitch)];
+		const uint16 *pNt = &nameTable[(destX + hscrNt) % pitch];
 		if (pNt < (const uint16*)(&_vram[0x10000])) {
 			uint16 nt = *pNt;
 			uint16 pal = ((nt >> 13) & 3) << 4;
@@ -716,7 +721,7 @@ void SegaRenderer::renderPlaneTile(uint8 *dst, int destX, const uint16 *nameTabl
 
 		if (hscrPx) {
 			dst += (8 - hscrPx);
-			pNt = &nameTable[vscrNt * pitch + ((destX + hscrNt + 1) % pitch)];
+			pNt = &nameTable[(destX + hscrNt + 1) % pitch];
 			if (pNt < (const uint16*)(&_vram[0x10000])) {
 				uint16 nt = *pNt;
 				uint16 pal = ((nt >> 13) & 3) << 4;
@@ -1050,7 +1055,7 @@ void SegaCDFont::setStyles(int styles) {
 	_forceTwoByte = (styles & kStyleForceTwoByte);
 	_data = (styles & kStyleFat) ? _buffer + 131072 : _buffer;
 	_fixedWidth = (styles & kStyleFixedWidth);
-	_style = (styles & kStyleNarrow) ? 1 : (styles & kStyleVariant ? 2 : 0);
+	_style = (styles & kStyleNarrow1) ? 1 : (styles & kStyleNarrow2 ? 2 : 0);
 }
 
 void SegaCDFont::drawChar(uint16 c, byte *dst, int pitch, int xOffs, int yOffs) const {
diff --git a/engines/kyra/graphics/screen_eob_segacd.h b/engines/kyra/graphics/screen_eob_segacd.h
index 69edf61fe2..83f43b71f2 100644
--- a/engines/kyra/graphics/screen_eob_segacd.h
+++ b/engines/kyra/graphics/screen_eob_segacd.h
@@ -88,7 +88,7 @@ public:
 
 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 vScrollTableIndex, int hScrollTableIndex, uint16 pitch);
+	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);
@@ -109,9 +109,9 @@ private:
 	void clearPrioChain();
 
 	struct SegaPlane {
-		SegaPlane() : blockX(0), blockY(0), w(0), h(0), nameTable(0), nameTableSize(0) {}
+		SegaPlane() : blockX(0), blockY(0), w(0), h(0), mod(0), nameTable(0), nameTableSize(0) {}
 		int blockX, blockY;
-		uint16 w, h;
+		uint16 w, h, mod;
 		uint16 *nameTable;
 		uint16 nameTableSize;
 	};
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 264b11d885..7bfd8c1f62 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -2021,7 +2021,7 @@ void GUI_EoB::simpleMenu_setup(int sd, int maxItem, const char *const *strings,
 		int item = simpleMenu_getMenuItem(i, menuItemsMask, itemOffset);
 		int ty = i * (lineSpacing + _screen->getFontHeight());
 		if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
-			_vm->_txt->printMessageAtPos(strings[item], 4, 2 + ty, item == v ? 0x55 : 0xff, 0x11);
+			_vm->_txt->printShadowedText(strings[item], 4, 2 + ty, item == v ? 0x55 : 0xff, 0x11);
 		} else {
 			_screen->printShadedText(strings[item], x, y + ty, (_vm->_configRenderMode == Common::kRenderCGA) ? 1 : _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
 			if (item == v)
@@ -2080,8 +2080,8 @@ int GUI_EoB::simpleMenu_process(int sd, const char *const *strings, void *b, int
 
 	if (newItem != currentItem) {
 		if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
-			_vm->_txt->printMessageAtPos(strings[simpleMenu_getMenuItem(currentItem, menuItemsMask, itemOffset)], 4, 2 + currentItem * lineH, 0xFF, 0x11);
-			_vm->_txt->printMessageAtPos(strings[simpleMenu_getMenuItem(newItem, menuItemsMask, itemOffset)], 4, 2 + newItem * lineH, 0x55, 0x11);
+			_vm->_txt->printShadowedText(strings[simpleMenu_getMenuItem(currentItem, menuItemsMask, itemOffset)], 4, 2 + currentItem * lineH, 0xFF, 0x11);
+			_vm->_txt->printShadowedText(strings[simpleMenu_getMenuItem(newItem, menuItemsMask, itemOffset)], 4, 2 + newItem * lineH, 0x55, 0x11);
 			_screen->sega_getRenderer()->render(0);
 		} else {
 			_screen->printText(strings[simpleMenu_getMenuItem(currentItem, menuItemsMask, itemOffset)], x, y + currentItem * lineH, (_vm->_configRenderMode == Common::kRenderCGA) ? 1 : _vm->guiSettings()->colors.guiColorWhite, 0);
diff --git a/engines/kyra/sequence/seqplayer_eob_segacd.cpp b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
index 8e98231cf7..a707cc73b8 100644
--- a/engines/kyra/sequence/seqplayer_eob_segacd.cpp
+++ b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
@@ -115,7 +115,6 @@ SegaSequencePlayer::SegaSequencePlayer(EoBEngine *vm, Screen_EoB *screen, SegaCD
 SegaSequencePlayer::~SegaSequencePlayer() {
 	delete[] _drawObjects;
 	delete[] _tileSets;
-	delete[] _scrollManager;
 	delete[] _scaleSrcBuffer;
 	delete[] _scaleOutBuffer;
 	delete[] _scaleStampMap;
@@ -123,6 +122,8 @@ SegaSequencePlayer::~SegaSequencePlayer() {
 
 	for (Common::Array<SQOpcode*>::iterator i = _opcodes.begin(); i != _opcodes.end(); ++i)
 		delete (*i);
+
+	delete _scrollManager;
 }
 
 bool SegaSequencePlayer::play(int id) {
@@ -212,10 +213,14 @@ bool SegaSequencePlayer::play(int id) {
 	debugC(3, kDebugLevelSequence, "Total millis out of sync: %d", _debugResyncCnt);
 
 	if (_vm->shouldQuit() || _vm->skipFlag()) {
-		_vm->snd_stopSound();
+		if (!(_playingID == 55 || _playingID == 56))
+			_vm->snd_stopSound();
 		_screen->sega_fadeToBlack(5);
 	}
 
+	_scrollManager->setVScrollTimers(0, 1, 0, 0, 1, 0);
+	_scrollManager->setHScrollTimers(0, 1, 0, 0, 1, 0);
+
 	_vm->_allowSkip = false;
 	_vm->resetSkipFlag();
 
@@ -407,7 +412,7 @@ void SegaSequencePlayer::s_displayTextJp(const uint8 *pos) {
 		y = 16;
 	}
 
-	_vm->_txt->printMessageAtPos(str, x, y, -1, 0xEE);
+	_vm->_txt->printShadowedText(str, x, y, -1, 0xEE);
 }
 
 void SegaSequencePlayer::s_fadeToNeutral(const uint8 *pos) {
@@ -597,7 +602,7 @@ void SegaSequencePlayer::s_displayTextEn(const uint8 *pos) {
 
 	if (_playingID >= 55) {
 		_screen->setFontStyles(_screen->_currentFont, Font::kStyleForceTwoByte | Font::kStyleFat);
-		_vm->_txt->printMessageAtPos(str, 0, 0, -1, 0xEE);
+		_vm->_txt->printShadowedText(str, 0, 0, -1, 0xEE);
 		_screen->setFontStyles(_screen->_currentFont, Font::kStyleFat);
 	} else {
 		int x = 0;
@@ -608,9 +613,8 @@ void SegaSequencePlayer::s_displayTextEn(const uint8 *pos) {
 			y = 16;
 		}
 
-		_vm->_txt->printMessageAtPos(str, x, y, -1, 0xEE);
+		_vm->_txt->printShadowedText(str, x, y, -1, 0xEE);
 	}
-
 }
 
 void SegaSequencePlayer::s_loadCustomPalettes(const uint8 *pos) {
diff --git a/engines/kyra/sequence/sequences_eob.cpp b/engines/kyra/sequence/sequences_eob.cpp
index 6e7a3eacc2..a8f2f3c7ae 100644
--- a/engines/kyra/sequence/sequences_eob.cpp
+++ b/engines/kyra/sequence/sequences_eob.cpp
@@ -2169,8 +2169,8 @@ int EoBEngine::mainMenu() {
 				_screen->sega_getRenderer()->fillRectWithTiles(1, 7, 20, 26, 5, 0x461, true);
 				_screen->sega_getRenderer()->fillRectWithTiles(1, 7, 25, 25, 1, 0x4E3, true);
 				_screen->sega_getRenderer()->fillRectWithTiles(1, 6, 21, 1, 5, 0);
-				_screen->setFontStyles(_screen->_currentFont, Font::kStyleNarrow);
-				_txt->printMessageAtPos(versionString.c_str(), 200 - versionString.size() * 8, _ttlCfg->versionStrYOffs, 0x88);
+				_screen->setFontStyles(_screen->_currentFont, Font::kStyleNarrow1);
+				_txt->printShadowedText(versionString.c_str(), 200 - versionString.size() * 8, _ttlCfg->versionStrYOffs, 0x88);
 				_screen->setFontStyles(_screen->_currentFont, _flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat);
 			} else {
 				_screen->_curPage = 2;
@@ -2268,7 +2268,8 @@ void EoBEngine::seq_playIntro(int part) {
 		if (part == kOnlyCredits)
 			seq_segaOpeningCredits();
 		else
-			seq_segaPlaySequence(53, true);
+			seq_playFinale();
+			//seq_segaPlaySequence(53, true);
 	} else {
 		EoBIntroPlayer(this, _screen).start((EoBIntroPlayer::IntroPart)part);
 	}
@@ -2280,6 +2281,9 @@ void EoBEngine::seq_playFinale() {
 		return;
 	} else if (_flags.platform == Common::kPlatformSegaCD) {
 		seq_segaPlaySequence(_xdth ? 55 : 56, true);
+		seq_segaFinalCredits();
+		seq_segaShowStats();
+		snd_stopSound();
 		return;
 	}
 
@@ -2390,27 +2394,28 @@ void EoBEngine::seq_xdeath() {
 void EoBEngine::seq_segaOpeningCredits() {
 	uint16 *scrollTable = new uint16[0x200];
 	memset(scrollTable, 0, 0x200 * sizeof(uint16));
+	SegaRenderer *r = _screen->sega_getRenderer();
 
-	_screen->sega_getRenderer()->setPitch(128);
-	_screen->sega_getRenderer()->setPlaneTableLocation(SegaRenderer::kPlaneA, 0xE000);
-	_screen->sega_getRenderer()->setupPlaneAB(1024, 256);
-	_screen->sega_getRenderer()->setHScrollMode(SegaRenderer::kHScroll1PixelRows);
+	r->setPitch(128);
+	r->setPlaneTableLocation(SegaRenderer::kPlaneA, 0xE000);
+	r->setupPlaneAB(1024, 256);
+	r->setHScrollMode(SegaRenderer::kHScroll1PixelRows);
 	
-	_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 40, 28, 0);
-	_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 128, 28, 1);
-	_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 28, 1, true);
+	r->fillRectWithTiles(0, 0, 0, 40, 28, 0);
+	r->fillRectWithTiles(1, 0, 0, 128, 28, 1);
+	r->fillRectWithTiles(1, 0, 0, 40, 28, 1, true);
 	_screen->sega_selectPalette(7, 3, false);
 
 	updateScrollState(scrollTable, 320);
-	_screen->sega_getRenderer()->loadToVRAM(scrollTable, 0x400, 0xD800);
+	r->loadToVRAM(scrollTable, 0x400, 0xD800);
 
 	_sres->loadContainer("CREDIT");
 	Common::SeekableReadStreamEndian *in = _sres->resStreamEndianAware(1);
-	_screen->sega_getRenderer()->loadToVRAM(in, 32, true);
+	r->loadToVRAM(in, 32, true);
 	delete in;
 
 	_screen->sega_selectPalette(50, 0, 0);
-	_screen->sega_getRenderer()->render(0);
+	r->render(0);
 
 	_allowSkip = true;
 	resetSkipFlag();
@@ -2419,14 +2424,14 @@ void EoBEngine::seq_segaOpeningCredits() {
 
 	for (int i = 0; i < 8 && !(shouldQuit() || skipFlag()); ++i) {
 		updateScrollState(scrollTable, 320);
-		_screen->sega_getRenderer()->loadToVRAM(scrollTable, 0x400, 0xD800);
+		r->loadToVRAM(scrollTable, 0x400, 0xD800);
 		_screen->sega_selectPalette(i == 3 ? 59 : 50, 0, true);
 
 		in = _sres->resStreamEndianAware(i);
-		_screen->sega_getRenderer()->loadToVRAM(in, 32, true);
+		r->loadToVRAM(in, 32, true);
 		delete in;
 
-		_screen->sega_getRenderer()->render(0);
+		r->render(0);
 		_screen->updateScreen();
 
 		_screen->sega_paletteOps(6, 0, 0);
@@ -2435,8 +2440,8 @@ void EoBEngine::seq_segaOpeningCredits() {
 		for (int ii = 9730; ii > 0 && !(shouldQuit() || skipFlag()); ii -= mod) {
 			uint32 end = _system->getMillis() + 16;
 			updateScrollState(scrollTable, ii / 30);
-			_screen->sega_getRenderer()->loadToVRAM(scrollTable, 0x400, 0xD800);
-			_screen->sega_getRenderer()->render(0);
+			r->loadToVRAM(scrollTable, 0x400, 0xD800);
+			r->render(0);
 			_screen->updateScreen();
 			mod--;
 			delayUntil(end);
@@ -2445,14 +2450,14 @@ void EoBEngine::seq_segaOpeningCredits() {
 		delay(3000);
 
 		if (i == 7)
-			_screen->sega_getRenderer()->fillRectWithTiles(1, 40, 0, 88, 28, 0, false);
+			r->fillRectWithTiles(1, 40, 0, 88, 28, 0, false);
 
 		mod = -1;
 		for (int ii = 0; ii <= 3240 && !(shouldQuit() || skipFlag()); ii += mod) {
 			uint32 end = _system->getMillis() + 16;
 			updateScrollState(scrollTable, ii / 10);
-			_screen->sega_getRenderer()->loadToVRAM(scrollTable, 0x400, 0xD800);
-			_screen->sega_getRenderer()->render(0);
+			r->loadToVRAM(scrollTable, 0x400, 0xD800);
+			r->render(0);
 			_screen->updateScreen();
 			mod++;
 			delayUntil(end);
@@ -2462,46 +2467,242 @@ void EoBEngine::seq_segaOpeningCredits() {
 	}
 
 	_screen->sega_fadeToBlack(0);
-	_screen->sega_getRenderer()->setPlaneTableLocation(SegaRenderer::kPlaneA, 0xC000);
-	_screen->sega_getRenderer()->setupPlaneAB(512, 512);
-	_screen->sega_getRenderer()->setHScrollMode(SegaRenderer::kHScrollFullScreen);
-	_screen->sega_getRenderer()->memsetVRAM(0xD800, 0, 0x400);
-	_screen->sega_getRenderer()->setPitch(64);
+	r->setPlaneTableLocation(SegaRenderer::kPlaneA, 0xC000);
+	r->setupPlaneAB(512, 512);
+	r->setHScrollMode(SegaRenderer::kHScrollFullScreen);
+	r->memsetVRAM(0xD800, 0, 0x400);
+	r->setPitch(64);
 	_screen->sega_selectPalette(0, 0);
 
 	in = _sres->resStreamEndianAware(8);
-	_screen->sega_getRenderer()->loadToVRAM(in, 32, true);
+	r->loadToVRAM(in, 32, true);
 	delete in;
 
-	_screen->sega_getRenderer()->memsetVRAM(0x8C20, 0xCC, 0x700);
+	r->memsetVRAM(0x8C20, 0xCC, 0x700);
 
 	for (int y = 0; y < 28; y += 4) {
 		for (int x = 0; x < 40; x += 4)
-			_screen->sega_getRenderer()->fillRectWithTiles(0, x, y, 8, 7, 0x461, true);
+			r->fillRectWithTiles(0, x, y, 8, 7, 0x461, true);
 	}
-	_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 28, 1, true);
-	_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 40, 28, 0);
-	_screen->sega_getRenderer()->render(0);
+	r->fillRectWithTiles(1, 0, 0, 40, 28, 1, true);
+	r->fillRectWithTiles(0, 0, 0, 40, 28, 0);
+	r->render(0);
 	_screen->sega_fadeToNeutral(3);
 
-	while (!(shouldQuit() || skipFlag())) {
+	while (!(shouldQuit() || skipFlag()))
 		delay(20);
-	}
 
 	_allowSkip = false;
 	resetSkipFlag();
 
-	_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 19, 40, 9, 1);
-	_screen->sega_getRenderer()->render(0);
+	r->fillRectWithTiles(1, 0, 19, 40, 9, 1);
+	r->render(0);
 	_screen->updateScreen();
 }
 
+void EoBEngine::seq_segaFinalCredits() {
+	if (shouldQuit())
+		return;
+
+	int temp = 0;
+	const uint8 *grid = _staticres->loadRawData(kEoB1CreditsTileGrid, temp);
+	const char *const *strings = _staticres->loadStrings(kEoB1CreditsStrings2, temp);
+	SegaRenderer *r = _screen->sega_getRenderer();
+	_screen->sega_fadeToBlack(0);
+	_screen->sega_selectPalette(7, 3, true);
+	_txt->clearDim(4);
+
+	r->setupPlaneAB(512, 256);
+	r->fillRectWithTiles(0, 0, 0, 40, 28, 0);
+	r->fillRectWithTiles(1, 0, 0, 40, 32, 0);
+	r->fillRectWithTiles(1, 5, 0, 30, 32, 0x600A, true);
+	r->fillRectWithTiles(0, 0, 0, 40, 5, 0x6001);
+	r->fillRectWithTiles(0, 0, 5, 40, 1, 0x6002);
+	r->fillRectWithTiles(0, 0, 22, 40, 1, 0x6003);
+	r->fillRectWithTiles(0, 0, 23, 40, 5, 0x6001);
+	r->memsetVRAM(32, 0xCC, 32);
+	r->loadToVRAM(grid, 64, 64);
+	r->memsetVRAM(0x140, 0, 0x7800);
+	r->render(0);
+
+	delay(320);
+
+	_screen->sega_fadeToNeutral(1);
+	ScrollManager *scrMan = new ScrollManager(r);
+	scrMan->setVScrollTimers(0, 1, 0, 4730, 1, 2);
+
+	int skipLines = 0;
+	int curStr = 0;
+	int ln = 30;
+
+	_allowSkip = true;
+	resetSkipFlag();
+
+	for (bool loop = true; loop; loop = !(shouldQuit() || skipFlag())) {
+		for (int i = 0; i < 32; ++i) {
+			uint32 del = _system->getMillis() + 16;
+			scrMan->updateScrollTimers();
+			r->render(0);
+			_screen->updateScreen();
+			delayUntil(del);
+		}
+
+		_screen->sega_clearTextBuffer(0);
+
+		if (!skipLines) {
+			const char *pos = strings[curStr];
+			char c = *pos;
+			if (c == '/') {
+				if (*++pos == 'E')
+					break;
+				skipLines = (int)(*pos - '0');
+				curStr++;
+			} else {
+
+				int styles = _flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat;
+				//int extraSpacing = 6;
+
+				if (c == '<') {
+					styles |= Font::kStyleNarrow1;
+					//extraSpacing = 4;
+					pos++;
+				}
+				if (c == ';')
+					pos++;
+
+				_screen->setFontStyles(_screen->_currentFont, styles);
+				_txt->printShadowedText(pos, 120 - (_screen->getTextWidth(pos) >> 1), 0, 0xFF, 0xCC, true);
+				curStr++;
+			}
+		} else {
+			skipLines--;
+		}
+
+		_screen->sega_loadTextBufferToVRAM(0, (ln * 30 + 10) << 5, 1920);
+
+		ln += 2;
+		if (ln == 32)
+			ln = 0;
+	}
+
+	_screen->sega_fadeToBlack(1);
+
+	_screen->setFontStyles(_screen->_currentFont, _flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleFat);
+	r->setupPlaneAB(512, 512);
+	scrMan->setVScrollTimers(0, 1, 0, 0, 1, 0);
+	scrMan->updateScrollTimers();
+	delete scrMan;
+
+	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);
+
+	_screen->sega_fadeToNeutral(3);
+
+	while (!(shouldQuit() || skipFlag()))
+		delay(20);
+
+	_allowSkip = false;
+	resetSkipFlag();
+
+	_screen->sega_fadeToBlack(3);
+}
+
+void EoBEngine::seq_segaShowStats() {
+	if (shouldQuit())
+		return;
+
+	SegaRenderer *r = _screen->sega_getRenderer();
+	_txt->clearDim(5);
+
+	int styles = _flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat;
+	_screen->setFontStyles(_screen->_currentFont, styles);
+
+	_txt->printShadowedText(_finBonusStrings[2], 90, 8, 0xFF, 0x00, false);
+
+	styles |= Font::kStyleNarrow2;
+	_screen->setFontStyles(_screen->_currentFont, styles);
+
+	_txt->printShadowedText(_finBonusStrings[3], 48, 28, 0xFF, 0x00, false);
+	_txt->printShadowedText(_finBonusStrings[4], 48, 40, 0xFF, 0x00, false);
+	_txt->printShadowedText(_finBonusStrings[5], 48, 52, 0xFF, 0x00, false);
+	_txt->printShadowedText(_finBonusStrings[6], 48, 64, 0xFF, 0x00, false);
+	_txt->printShadowedText(_finBonusStrings[7], 48, 76, 0xFF, 0x00, false);
+	_txt->printShadowedText(_finBonusStrings[8], 48, 88, 0xFF, 0x00, false);
+
+	styles &= ~(Font::kStyleNarrow2);
+	_screen->setFontStyles(_screen->_currentFont, styles);
+
+	////
+	////
+	uint32 totalPlayTicks = 0;
+	uint32 totalEnemiesKilled = 0;
+	uint32 totalSteps = 0;
+	uint32 partyArrows = 0;
+	uint32 numMaps = 0;
+	/////
+	////
+
+	uint32 specialSearches = 0;
+	for (int i = 1; i <= 12; ++i) {
+		if (checkScriptFlags(1 << i))
+			++specialSearches;
+	}
+
+	_txt->printShadowedText(Common::String::format("%u:%02u:%02u", totalPlayTicks / 216000, totalPlayTicks / 3600, totalPlayTicks / 60).c_str(), 148, 28, 0xFF, 0x00, false);
+	_txt->printShadowedText(Common::String::format("%u", totalEnemiesKilled).c_str(), 148, 40, 0xFF, 0x00, false);
+	_txt->printShadowedText(Common::String::format("%u", totalSteps).c_str(), 148, 52, 0xFF, 0x00, false);
+	_txt->printShadowedText(Common::String::format("%u(%u%%)", partyArrows, partyArrows * 100 / 26).c_str(), 148, 64, 0xFF, 0x00, false);
+	_txt->printShadowedText(Common::String::format("%u(%u%%)", numMaps, numMaps * 100 / 12).c_str(), 148, 76, 0xFF, 0x00, false);
+	_txt->printShadowedText(Common::String::format("%u(%u%%)", specialSearches, specialSearches * 100 / 12).c_str(), 148, 88, 0xFF, 0x00, false);
+
+	if (checkScriptFlags(0x1FFE)) {
+		const char pwgen[] = "A15BZFQ3CDXYEKNM279GHIUSJLR84P6T";
+		const uint8 pwAdd[5] = { 0, 13, 3, 7, 0 };
+		char password[7] = "\0\0\0\0\0\0";
+
+		uint8 v = 0;
+		for (int i = 0; i < 5; i++) {
+			password[i] = pwgen[(_characters[i].hitPointsCur + pwAdd[i]) & 0x1F];
+			v = (v + (uint8)password[i]) & 0x1F;
+		}
+		password[5] = pwgen[v];
+
+		_txt->printShadowedText(_finBonusStrings[0], 30, 108, 0x22, 0x00, false);
+		_txt->printShadowedText(_finBonusStrings[1], 30, 132, 0x22, 0x00, false);
+		_txt->printShadowedText(password, 140, 156, 0xFF, 0x00, false);
+	}
+
+	_screen->sega_loadTextBufferToVRAM(0, 32, 28160);
+	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);
+
+	// 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...
+	_screen->sega_selectPalette(36, 2);
+	_screen->sega_fadeToNeutral(3);
+
+	resetSkipFlag();
+	_allowSkip = false;
+
+	while (!(shouldQuit() || skipFlag()))
+		delay(20);
+
+	_allowSkip = false;
+	resetSkipFlag();
+
+	_screen->setFontStyles(_screen->_currentFont, _flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleFat);
+	_screen->sega_fadeToBlack(3);
+}
+
 void EoBEngine::seq_segaSetupSequence(int sequenceId) {
 	if (_flags.platform != Common::kPlatformSegaCD || sequenceId == -1)
 		return;
 	
 	_screen->sega_fadeToBlack(1);
-	//gfxM45(0);
 	for (int i = 0; i < 6; i++) {
 		_characters[i].damageTaken = 0;
 		_characters[i].slotStatus[0] = _characters[i].slotStatus[1] = 0;
@@ -2512,7 +2713,17 @@ void EoBEngine::seq_segaSetupSequence(int sequenceId) {
 	_screen->sega_getRenderer()->memsetVRAM(0xD840, 0xEE, 512);
 	_screen->sega_getAnimator()->clearSprites();
 	_screen->setScreenDim(2);
+}
 
+void EoBEngine::seq_segaRestoreAfterSequence() {
+	SegaRenderer *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->fillRectWithTiles(0, 0, 0, 40, 28, 0x2000);
+	r->fillRectWithTiles(1, 0, 0, 40, 28, 0x2000);
+	r->render(0);
 }
 
 bool EoBEngine::seq_segaPlaySequence(int sequenceId, bool init) {
@@ -2528,7 +2739,11 @@ bool EoBEngine::seq_segaPlaySequence(int sequenceId, bool init) {
 	_allowSkip = false;
 	resetSkipFlag();
 
-	return _seqPlayer->play(sequenceId);
+	if (!_seqPlayer->play(sequenceId))
+		return false;
+
+	seq_segaRestoreAfterSequence();
+	return true;
 }
 
 #undef updateScrollState
diff --git a/engines/kyra/text/text_eob_segacd.cpp b/engines/kyra/text/text_eob_segacd.cpp
index e000a83170..8895db1ea2 100644
--- a/engines/kyra/text/text_eob_segacd.cpp
+++ b/engines/kyra/text/text_eob_segacd.cpp
@@ -37,7 +37,7 @@ TextDisplayer_SegaCD::~TextDisplayer_SegaCD() {
 
 }
 
-void TextDisplayer_SegaCD::printMessageAtPos(const char *str, int x, int y, int textColor, int shadowColor) {
+void TextDisplayer_SegaCD::printShadowedText(const char *str, int x, int y, int textColor, int shadowColor, bool noScreenUpdate) {
 	const ScreenDim *s = &_dimTable[_curDim];
 	if (x == -1)
 		x = s->sx;
@@ -51,6 +51,9 @@ void TextDisplayer_SegaCD::printMessageAtPos(const char *str, int x, int y, int
 	_screen->setTextMarginRight(s->w);
 	_screen->printShadedText(str, x, y, textColor, 0, shadowColor, s->w >> 3);
 
+	if (noScreenUpdate)
+		return;
+
 	if (s->unkE) {
 		for (int i = 0; i < s->h >> 3; ++i)
 			_screen->sega_loadTextBufferToVRAM(i * (s->w >> 3), (s->unkC & 0x7FF) << 5, (s->w * s->h) >> 1);
@@ -68,11 +71,13 @@ int TextDisplayer_SegaCD::clearDim(int dim) {
 	return res;
 }
 
-const ScreenDim TextDisplayer_SegaCD::_dimTable[4] = {
+const ScreenDim TextDisplayer_SegaCD::_dimTable[6] = {
 	{ 0x0001, 0x0017, 0x0118, 0x0018, 0xff, 0x44, 0x2597, 0x0000 },
 	{ 0x0012, 0x0009, 0x00a0, 0x0080, 0xff, 0x00, 0x0153, 0x0028 },
 	{ 0x0001, 0x0014, 0x0130, 0x0030, 0xff, 0xee, 0xe51c, 0x0000 },
-	{ 0x0001, 0x0017, 0x00D0, 0x0030, 0xff, 0x00, 0x0461, 0x0000 }
+	{ 0x0001, 0x0017, 0x00D0, 0x0030, 0xff, 0x00, 0x0461, 0x0000 },
+	{ 0x0000, 0x0000, 0x00F0, 0x0100, 0xff, 0x00, 0x600A, 0x0000 },
+	{ 0x0000, 0x0000, 0x0140, 0x00B0, 0xff, 0x11, 0x4001, 0x0000 }
 };
 
 } // End of namespace Kyra
diff --git a/engines/kyra/text/text_eob_segacd.h b/engines/kyra/text/text_eob_segacd.h
index dd5a35762b..ac75eba26b 100644
--- a/engines/kyra/text/text_eob_segacd.h
+++ b/engines/kyra/text/text_eob_segacd.h
@@ -42,7 +42,7 @@ public:
 	void printDialogueText(int stringId, const char *pageBreakString);
 	void printDialogueText(const char *str, bool wait = false);
 	void printMessage(const char *str, int textColor = -1, ...);*/
-	void printMessageAtPos(const char *str, int x = -1, int y = -1, int textColor = -1, int shadowColor = -1) override;
+	void printShadowedText(const char *str, int x = -1, int y = -1, int textColor = -1, int shadowColor = -1, bool noScreenUpdate = false) override;
 
 	int clearDim(int dim) override;
 	//void clearCurDim() override;
@@ -61,7 +61,7 @@ private:
 	SegaRenderer *_renderer;
 	int _curDim;
 
-	static const ScreenDim _dimTable[4];
+	static const ScreenDim _dimTable[6];
 };
 
 } // End of namespace Kyra
diff --git a/engines/kyra/text/text_rpg.h b/engines/kyra/text/text_rpg.h
index 715e567013..a43aa9bcf5 100644
--- a/engines/kyra/text/text_rpg.h
+++ b/engines/kyra/text/text_rpg.h
@@ -42,7 +42,7 @@ public:
 	void printDialogueText(int stringId, const char *pageBreakString);
 	void printDialogueText(const char *str, bool wait = false);
 	void printMessage(const char *str, int textColor = -1, ...);
-	virtual void printMessageAtPos(const char *str, int x = -1, int y = -1, int textColor = -1, int shadowColor = -1) {}
+	virtual void printShadowedText(const char *str, int x = -1, int y = -1, int textColor = -1, int shadowColor = -1, bool noScreenUpdate = false) {}
 
 	virtual int clearDim(int dim);
 	void clearCurDim();


Commit: ec2d4644f746ba48158248663c412358e511a33c
    https://github.com/scummvm/scummvm/commit/ec2d4644f746ba48158248663c412358e511a33c
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:10+02:00

Commit Message:
KYRA: (EOB/SegaCD) - fix item data init

Changed paths:
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eob.h
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h
    engines/kyra/engine/items_eob.cpp
    engines/kyra/engine/kyra_rpg.h
    engines/kyra/resource/resource_segacd.cpp
    engines/kyra/resource/resource_segacd.h
    engines/kyra/resource/staticres_eob.cpp
    engines/kyra/sequence/seqplayer_eob_segacd.cpp
    engines/kyra/sequence/sequences_eob.cpp


diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index f93938f01c..b62a5ce29b 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -38,8 +38,8 @@ EoBEngine::EoBEngine(OSystem *system, const GameFlags &flags)
 	_menuChoiceInit = 4;
 
 	_turnUndeadString = 0;
-	_itemNamesPC98 = 0;
-	_numItemNamesPC98 = 0;
+	_itemNamesStatic = 0;
+	_numItemNamesStatic = 0;
 	_finBonusStrings = _npcStrings[1] = _npcStrings[2] = 0;
 	_npcStrings[3] = _npcStrings[4] = _npcStrings[5] = _npcStrings[6] = 0;
 	_npcStrings[7] = _npcStrings[8] = _npcStrings[9] = _npcStrings[10] = 0;
@@ -125,18 +125,35 @@ Common::Error EoBEngine::init() {
 	return Common::kNoError;
 }
 
+#define loadSpritesAndEncodeToShapes(resID, shapeBuffer, numShapes, width, height) \
+	shapeBuffer = new const uint8 *[numShapes]; \
+	memset(shapeBuffer, 0, numShapes * sizeof(uint8*)); \
+	in = _sres->resData(resID); \
+	_screen->sega_encodeSpriteShapes(shapeBuffer, in, numShapes, width, height, 3); \
+	delete[] in
+
 void EoBEngine::loadItemsAndDecorationsShapes() {
 	if (_flags.platform != Common::kPlatformSegaCD) {
 		EoBCoreEngine::loadItemsAndDecorationsShapes();
 		return;
 	}
 
+	uint8 *in = 0;
 	_sres->loadContainer("ITEM");
-	_itemIconShapes = new const uint8 *[_numItemIconShapes];
-	memset(_itemIconShapes, 0, _numItemIconShapes * sizeof(uint8*));
-	uint8 *in = _sres->resData(0);
-	_screen->sega_encodeSpriteShapes(_itemIconShapes, in, _numItemIconShapes, 16, 16, 3);
-	delete[] in;
+	loadSpritesAndEncodeToShapes(0, _itemIconShapes, _numItemIconShapes, 16, 16);
+	loadSpritesAndEncodeToShapes(14, _blueItemIconShapes, _numItemIconShapes, 16, 16);
+	loadSpritesAndEncodeToShapes(13, _xtraItemIconShapes, 3, 16, 16);
+}
+
+#undef loadSpritesAndEncodeToShapes
+
+Common::SeekableReadStreamEndian *EoBEngine::getItemDefinitionFile(int index) {
+	assert(index == 0 || index == 1);
+	if (_flags.platform != Common::kPlatformSegaCD)
+		return EoBCoreEngine::getItemDefinitionFile(index);
+
+	_sres->loadContainer("ITEMDAT");
+	return _sres->resStreamEndian(index);
 }
 
 void EoBEngine::startupNew() {
diff --git a/engines/kyra/engine/eob.h b/engines/kyra/engine/eob.h
index d940575b36..33489c0419 100644
--- a/engines/kyra/engine/eob.h
+++ b/engines/kyra/engine/eob.h
@@ -77,6 +77,7 @@ private:
 
 	// Init
 	void loadItemsAndDecorationsShapes() override;
+	Common::SeekableReadStreamEndian *getItemDefinitionFile(int index) override;
 
 	// Main loop
 	void startupNew() override;
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index d9514c43a2..ad7939654d 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -63,7 +63,7 @@ EoBCoreEngine::EoBCoreEngine(OSystem *system, const GameFlags &flags) : KyraRpgE
 	_vmpFilePattern = "%s.VMP";
 
 	_largeItemShapes = _smallItemShapes = _thrownItemShapes = _spellShapes = _firebeamShapes = 0;
-	_itemIconShapes = _amigaBlueItemIconShapes = _wallOfForceShapes = _teleporterShapes = _sparkShapes = _compassShapes = 0;
+	_itemIconShapes = _blueItemIconShapes = _xtraItemIconShapes = _wallOfForceShapes = _teleporterShapes = _sparkShapes = _compassShapes = 0;
 	_redSplatShape = _greenSplatShape = _deadCharShape = _disabledCharGrid = 0;
 	_blackBoxSmallGrid = _weaponSlotGrid = _blackBoxWideGrid = _lightningColumnShape = 0;
 
@@ -84,9 +84,9 @@ EoBCoreEngine::EoBCoreEngine(OSystem *system, const GameFlags &flags) : KyraRpgE
 	_items = 0;
 	_itemTypes = 0;
 	_itemNames = 0;
-	_itemNamesPC98 = 0;
+	_itemNamesStatic = 0;
 	_itemInHand = -1;
-	_numItems = _numItemNames = _numItemNamesPC98 = 0;
+	_numItems = _numItemNames = _numItemNamesStatic = 0;
 
 	_castScrollSlot = 0;
 	_currentSub = 0;
@@ -684,7 +684,7 @@ Common::Error EoBCoreEngine::go() {
 		//ConfMan.flushToDisk();
 	}
 
-	//loadItemDefs();
+	loadItemDefs();
 	int action = 0;
 
 	for (bool repeatLoop = true; repeatLoop; repeatLoop ^= true) {
@@ -934,11 +934,11 @@ void EoBCoreEngine::loadItemsAndDecorationsShapes() {
 		
 		if (_flags.platform == Common::kPlatformAmiga) {
 			const uint8 offsY = (_flags.gameID == GI_EOB1) ? 80 : 96;
-			_amigaBlueItemIconShapes = new const uint8*[_numItemIconShapes];
+			_blueItemIconShapes = new const uint8*[_numItemIconShapes];
 			for (int i = 0; i < _numItemIconShapes; i++) {
 				int bx = (i % 0x14) << 1;
 				int by = (i / 0x14) << 4;
-				_amigaBlueItemIconShapes[i] = _screen->getPagePixel(2, (bx << 3) + 8, by + offsY + 8) ? _screen->encodeShape(bx, by + offsY, 2, 0x10, false, 0) : _screen->encodeShape(bx, by, 2, 0x10, false, 0);
+				_blueItemIconShapes[i] = _screen->getPagePixel(2, (bx << 3) + 8, by + offsY + 8) ? _screen->encodeShape(bx, by + offsY, 2, 0x10, false, 0) : _screen->encodeShape(bx, by, 2, 0x10, false, 0);
 			}
 		}
 	}
@@ -996,84 +996,29 @@ void EoBCoreEngine::loadItemsAndDecorationsShapes() {
 	}
 }
 
+#define releaseShpArr(shapes, num) \
+if (shapes) { \
+	for (int iii = 0; iii < num; iii++) { \
+		if (shapes[iii]) \
+			delete[] shapes[iii]; \
+	} \
+} \
+shapes = 0
+
 void EoBCoreEngine::releaseItemsAndDecorationsShapes() {
 	if (_flags.platform != Common::kPlatformFMTowns || _flags.gameID != GI_EOB2) {
-		if (_largeItemShapes) {
-			for (int i = 0; i < _numLargeItemShapes; i++) {
-				if (_largeItemShapes[i])
-					delete[] _largeItemShapes[i];
-			}
-		}
-
-		if (_smallItemShapes) {
-			for (int i = 0; i < _numSmallItemShapes; i++) {
-				if (_smallItemShapes[i])
-					delete[] _smallItemShapes[i];
-			}
-		}
-
-		if (_thrownItemShapes) {
-			for (int i = 0; i < _numThrownItemShapes; i++) {
-				if (_thrownItemShapes[i])
-					delete[] _thrownItemShapes[i];
-			}
-		}
-
-		if (_spellShapes) {
-			for (int i = 0; i < 4; i++) {
-				if (_spellShapes[i])
-					delete[] _spellShapes[i];
-			}
-		}
-
-		if (_itemIconShapes) {
-			for (int i = 0; i < _numItemIconShapes; i++) {
-				if (_itemIconShapes[i])
-					delete[] _itemIconShapes[i];
-			}
-		}
-
-		if (_amigaBlueItemIconShapes) {
-			for (int i = 0; i < _numItemIconShapes; i++) {
-				if (_amigaBlueItemIconShapes[i])
-					delete[] _amigaBlueItemIconShapes[i];
-			}
-		}
-
-		if (_sparkShapes) {
-			for (int i = 0; i < 3; i++) {
-				if (_sparkShapes[i])
-					delete[] _sparkShapes[i];
-			}
-		}
-
-		if (_wallOfForceShapes) {
-			for (int i = 0; i < 6; i++) {
-				if (_wallOfForceShapes[i])
-					delete[] _wallOfForceShapes[i];
-			}
-		}
-
-		if (_teleporterShapes) {
-			for (int i = 0; i < 6; i++) {
-				if (_teleporterShapes[i])
-					delete[] _teleporterShapes[i];
-			}
-		}
-
-		if (_compassShapes) {
-			for (int i = 0; i < 12; i++) {
-				if (_compassShapes[i])
-					delete[] _compassShapes[i];
-			}
-		}
-
-		if (_firebeamShapes) {
-			for (int i = 0; i < 3; i++) {
-				if (_firebeamShapes[i])
-					delete[] _firebeamShapes[i];
-			}
-		}
+		releaseShpArr(_largeItemShapes, _numLargeItemShapes);
+		releaseShpArr(_smallItemShapes, _numSmallItemShapes);
+		releaseShpArr(_thrownItemShapes, _numThrownItemShapes);
+		releaseShpArr(_spellShapes, 4);
+		releaseShpArr(_itemIconShapes, _numItemIconShapes);
+		releaseShpArr(_blueItemIconShapes, _numItemIconShapes);
+		releaseShpArr(_xtraItemIconShapes, 3);
+		releaseShpArr(_sparkShapes, 3);
+		releaseShpArr(_wallOfForceShapes, 6);
+		releaseShpArr(_teleporterShapes, 6);
+		releaseShpArr(_compassShapes, 12);
+		releaseShpArr(_firebeamShapes, 3);
 
 		delete[] _redSplatShape;
 		delete[] _greenSplatShape;
@@ -1090,7 +1035,8 @@ void EoBCoreEngine::releaseItemsAndDecorationsShapes() {
 	delete[] _thrownItemShapes;
 	delete[] _spellShapes;
 	delete[] _itemIconShapes;
-	delete[] _amigaBlueItemIconShapes;
+	delete[] _blueItemIconShapes;
+	delete[] _xtraItemIconShapes;
 	delete[] _sparkShapes;
 	delete[] _wallOfForceShapes;
 	delete[] _teleporterShapes;
@@ -1098,33 +1044,17 @@ void EoBCoreEngine::releaseItemsAndDecorationsShapes() {
 	delete[] _firebeamShapes;
 
 	for (int i = 0; i < 3; ++i) {
-		if (_largeItemShapesScl[i]) {
-			for (int ii = 0; ii < _numLargeItemShapes; ++ii) {
-				if (_largeItemShapesScl[i][ii])
-					delete[] _largeItemShapesScl[i][ii];
-			}
-		}
-
-		if (_smallItemShapesScl[i]) {
-			for (int ii = 0; ii < _numSmallItemShapes; ++ii) {
-				if (_smallItemShapesScl[i][ii])
-					delete[] _smallItemShapesScl[i][ii];
-			}
-		}
-
-		if (_thrownItemShapesScl[i]) {
-			for (int ii = 0; ii < _numThrownItemShapes; ++ii) {
-				if (_thrownItemShapesScl[i][ii])
-					delete[] _thrownItemShapesScl[i][ii];
-			}
-		}
-
+		releaseShpArr(_largeItemShapesScl[i], _numLargeItemShapes);
+		releaseShpArr(_smallItemShapesScl[i], _numSmallItemShapes);
+		releaseShpArr(_thrownItemShapesScl[i], _numThrownItemShapes);
 		delete[] _largeItemShapesScl[i];
 		delete[] _smallItemShapesScl[i];
 		delete[] _thrownItemShapesScl[i];
 	}
 }
 
+#undef releaseShpArr
+
 void EoBCoreEngine::setHandItem(Item itemIndex) {
 	if (itemIndex == -1) {
 		if (_flags.platform == Common::kPlatformFMTowns)
@@ -1141,10 +1071,24 @@ void EoBCoreEngine::setHandItem(Item itemIndex) {
 	int icon = _items[_itemInHand].icon;
 	const uint8 *shp = _itemIconShapes[icon];
 	const uint8 *ovl = 0;
+	bool applyBluePal = ((_partyEffectFlags & 2) && (_items[_itemInHand].flags & 0x80)) ? true : false;
+
+	if (_xtraItemIconShapes) {
+		bool applyBluePalC = applyBluePal;
+		applyBluePal = false;
+		if (_items[_itemInHand].nameUnid == 23)
+			shp = _xtraItemIconShapes[0];
+		else if (_items[_itemInHand].nameUnid == 97)
+			shp = _xtraItemIconShapes[1];
+		else if (_items[_itemInHand].nameId == 39)
+			shp = _xtraItemIconShapes[2];
+		else
+			applyBluePal = applyBluePalC;
+	}
 
-	if (icon && (_items[_itemInHand].flags & 0x80) && (_partyEffectFlags & 2)) {
-		if (_amigaBlueItemIconShapes)
-			shp = _amigaBlueItemIconShapes[icon];
+	if (icon && applyBluePal) {
+		if (_blueItemIconShapes)
+			shp = _blueItemIconShapes[icon];
 		else
 			ovl = _flags.gameID == GI_EOB1 ? ((_configRenderMode == Common::kRenderCGA) ? _itemsOverlayCGA : &_itemsOverlay[icon << 4]) : _screen->generateShapeOverlay(shp, _lightBlueFadingTable);
 	}
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index ad2ccf98c5..6df3f59b68 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -291,6 +291,8 @@ protected:
 	const uint8 **_largeItemShapesScl[3];
 	const uint8 **_smallItemShapesScl[3];
 	const uint8 **_thrownItemShapesScl[3];
+	const uint8 **_blueItemIconShapes;
+	const uint8 **_xtraItemIconShapes;
 	const int _numLargeItemShapes;
 	const int _numSmallItemShapes;
 	const int _numThrownItemShapes;
@@ -428,6 +430,7 @@ protected:
 	bool _loading;
 
 	// Items
+	virtual Common::SeekableReadStreamEndian *getItemDefinitionFile(int index);
 	void loadItemDefs();
 	Item duplicateItem(Item itemIndex);
 	void setItemPosition(Item *itemQueue, int block, Item item, int pos);
@@ -464,8 +467,8 @@ protected:
 	EoBItemType *_itemTypes;
 	char **_itemNames;
 	uint16 _numItemNames;
-	int _numItemNamesPC98;
-	const char * const *_itemNamesPC98;
+	int _numItemNamesStatic;
+	const char * const *_itemNamesStatic;
 	uint32 _partyEffectFlags;
 	Item _lastUsedItem;
 
diff --git a/engines/kyra/engine/items_eob.cpp b/engines/kyra/engine/items_eob.cpp
index e784e2d3f9..4df903d726 100644
--- a/engines/kyra/engine/items_eob.cpp
+++ b/engines/kyra/engine/items_eob.cpp
@@ -28,14 +28,22 @@
 
 namespace Kyra {
 
+Common::SeekableReadStreamEndian *EoBCoreEngine::getItemDefinitionFile(int index) {
+	assert(index == 0 || index == 1);
+	return _res->createEndianAwareReadStream(index ? "itemtype.dat" : "item.dat");
+}
+
 void EoBCoreEngine::loadItemDefs() {
-	Common::SeekableReadStreamEndian *s = _res->createEndianAwareReadStream("item.dat");
+	Common::SeekableReadStreamEndian *s = getItemDefinitionFile(0);
 	memset(_items, 0, sizeof(EoBItem) * 600);
 	_numItems = s->readUint16();
 
 	for (int i = 0; i < 600; i++)
 		_items[i].block = -1;
 
+	if (_flags.platform == Common::kPlatformSegaCD)
+		_items[498].block = _items[499].block = -2;
+
 	for (int i = 0; i < _numItems; i++) {
 		_items[i].nameUnid = s->readByte();
 		_items[i].nameId = s->readByte();
@@ -50,11 +58,11 @@ void EoBCoreEngine::loadItemDefs() {
 		_items[i].value = s->readSByte();
 	}
 
-	if (_itemNamesPC98) {
-		_numItemNames = _numItemNamesPC98;
+	if (_itemNamesStatic) {
+		_numItemNames = _numItemNamesStatic;
 		for (int i = 0; i < _numItemNames; i++) {
-			assert(strlen(_itemNamesPC98[i]) < 35);
-			Common::strlcpy(_itemNames[i], _itemNamesPC98[i], 34);
+			assert(strlen(_itemNamesStatic[i]) < 35);
+			Common::strlcpy(_itemNames[i], _itemNamesStatic[i], 34);
 		}
 	} else {
 		_numItemNames = s->readUint16();
@@ -64,7 +72,7 @@ void EoBCoreEngine::loadItemDefs() {
 
 	delete s;
 
-	s = _res->createEndianAwareReadStream("itemtype.dat");
+	s = getItemDefinitionFile(1);
 	uint16 numTypes = s->readUint16();
 
 	delete[] _itemTypes;
@@ -481,9 +489,22 @@ void EoBCoreEngine::drawItemIconShape(int pageNum, Item itemId, int x, int y) {
 	const uint8 *ovl = 0;
 	const uint8 *shp = _itemIconShapes[icn];
 
+	if (_xtraItemIconShapes) {
+		bool applyBluePalC = applyBluePal;
+		applyBluePal = false;
+		if (_items[itemId].nameUnid == 23)
+			shp = _xtraItemIconShapes[0];
+		else if (_items[itemId].nameUnid == 97)
+			shp = _xtraItemIconShapes[1];
+		else if (_items[itemId].nameId == 39)
+			shp = _xtraItemIconShapes[2];
+		else
+			applyBluePal = applyBluePalC;
+	}
+
 	if (applyBluePal) {
-		if (_amigaBlueItemIconShapes) {
-			shp = _amigaBlueItemIconShapes[icn];
+		if (_blueItemIconShapes) {
+			shp = _blueItemIconShapes[icn];
 		} else if (_flags.gameID == GI_EOB1) {
 			ovl = (_configRenderMode == Common::kRenderCGA) ? _itemsOverlayCGA : &_itemsOverlay[icn << 4];
 		} else {
diff --git a/engines/kyra/engine/kyra_rpg.h b/engines/kyra/engine/kyra_rpg.h
index 63d42e38e8..88c7c58aa3 100644
--- a/engines/kyra/engine/kyra_rpg.h
+++ b/engines/kyra/engine/kyra_rpg.h
@@ -143,7 +143,6 @@ protected:
 	void initStaticResource();
 
 	const uint8 **_itemIconShapes;
-	const uint8 **_amigaBlueItemIconShapes;
 
 	// Main loop
 	virtual void update() = 0;
diff --git a/engines/kyra/resource/resource_segacd.cpp b/engines/kyra/resource/resource_segacd.cpp
index e8ef4522f8..303f6b429a 100644
--- a/engines/kyra/resource/resource_segacd.cpp
+++ b/engines/kyra/resource/resource_segacd.cpp
@@ -95,7 +95,7 @@ void SegaCDResource::unloadContainer() {
 	_str = 0;
 }
 
-Common::SeekableReadStreamEndian *SegaCDResource::resStreamEndianAware(int resID) {
+Common::SeekableReadStreamEndian *SegaCDResource::resStreamEndian(int resID) {
 	if (!_str || !_resTable || resID >= _numResources)
 		return 0;
 
diff --git a/engines/kyra/resource/resource_segacd.h b/engines/kyra/resource/resource_segacd.h
index 9c7a214256..18ded013ee 100644
--- a/engines/kyra/resource/resource_segacd.h
+++ b/engines/kyra/resource/resource_segacd.h
@@ -44,7 +44,7 @@ public:
 	bool loadContainer(const Common::String &filename, uint32 offset = 0, uint32 size = 0);
 	void unloadContainer();
 
-	Common::SeekableReadStreamEndian *resStreamEndianAware(int resID);
+	Common::SeekableReadStreamEndian *resStreamEndian(int resID);
 	Common::SeekableReadStream *resStream(int resID);
 
 	uint8 *resData(int resID, uint32 *resLen = 0);
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index 1c5b623cd4..44787e2aae 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -1184,7 +1184,7 @@ void EoBEngine::initStaticResource() {
 	for (int i = 0; i < 5; i++)
 		_cgaMappingLevel[i] = _staticres->loadRawData(kEoB1CgaMappingLevel0 + i, temp);
 
-	_itemNamesPC98 = _staticres->loadStrings(kEoB1ItemNames, _numItemNamesPC98);
+	_itemNamesStatic = _staticres->loadStrings(kEoB1ItemNames, _numItemNamesStatic);
 
 	_turnUndeadString = _staticres->loadStrings(kEoB1TurnUndeadString, temp);
 
diff --git a/engines/kyra/sequence/seqplayer_eob_segacd.cpp b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
index a707cc73b8..ca87fbed3f 100644
--- a/engines/kyra/sequence/seqplayer_eob_segacd.cpp
+++ b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
@@ -157,7 +157,7 @@ bool SegaSequencePlayer::play(int id) {
 		if (!_res->loadContainer("VISUAL", offset, size))
 			return false;
 
-		Common::SeekableReadStreamEndian *in = _res->resStreamEndianAware(0);
+		Common::SeekableReadStreamEndian *in = _res->resStreamEndian(0);
 		if (!in)
 			return false;
 		_screen->sega_loadCustomPaletteData(in);
@@ -167,7 +167,7 @@ bool SegaSequencePlayer::play(int id) {
 		_screen->sega_selectPalette(32, 1, false);
 		_screen->sega_selectPalette(30, 3, false);
 
-		in = _res->resStreamEndianAware(2);
+		in = _res->resStreamEndian(2);
 		if (!in)
 			return false;
 		uint32 len = in->size();
@@ -175,7 +175,7 @@ bool SegaSequencePlayer::play(int id) {
 		in->read(tileData, len);
 		delete in;
 
-		in = _res->resStreamEndianAware(1);
+		in = _res->resStreamEndian(1);
 		if (!in)
 			return false;
 		memset(_tileSets, 0, 100 * sizeof(TileSet));
@@ -189,7 +189,7 @@ bool SegaSequencePlayer::play(int id) {
 		}
 		delete in;
 
-		in = _res->resStreamEndianAware(3);
+		in = _res->resStreamEndian(3);
 		if (!in)
 			return false;
 		len = in->size();
@@ -618,7 +618,7 @@ void SegaSequencePlayer::s_displayTextEn(const uint8 *pos) {
 }
 
 void SegaSequencePlayer::s_loadCustomPalettes(const uint8 *pos) {
-	Common::SeekableReadStreamEndian *in = _res->resStreamEndianAware(0);
+	Common::SeekableReadStreamEndian *in = _res->resStreamEndian(0);
 	in->seek(ARG(0) << 5);
 	_screen->sega_loadCustomPaletteData(in);
 	delete in;
diff --git a/engines/kyra/sequence/sequences_eob.cpp b/engines/kyra/sequence/sequences_eob.cpp
index a8f2f3c7ae..3244f9a2e8 100644
--- a/engines/kyra/sequence/sequences_eob.cpp
+++ b/engines/kyra/sequence/sequences_eob.cpp
@@ -2410,7 +2410,7 @@ void EoBEngine::seq_segaOpeningCredits() {
 	r->loadToVRAM(scrollTable, 0x400, 0xD800);
 
 	_sres->loadContainer("CREDIT");
-	Common::SeekableReadStreamEndian *in = _sres->resStreamEndianAware(1);
+	Common::SeekableReadStreamEndian *in = _sres->resStreamEndian(1);
 	r->loadToVRAM(in, 32, true);
 	delete in;
 
@@ -2427,7 +2427,7 @@ void EoBEngine::seq_segaOpeningCredits() {
 		r->loadToVRAM(scrollTable, 0x400, 0xD800);
 		_screen->sega_selectPalette(i == 3 ? 59 : 50, 0, true);
 
-		in = _sres->resStreamEndianAware(i);
+		in = _sres->resStreamEndian(i);
 		r->loadToVRAM(in, 32, true);
 		delete in;
 
@@ -2474,7 +2474,7 @@ void EoBEngine::seq_segaOpeningCredits() {
 	r->setPitch(64);
 	_screen->sega_selectPalette(0, 0);
 
-	in = _sres->resStreamEndianAware(8);
+	in = _sres->resStreamEndian(8);
 	r->loadToVRAM(in, 32, true);
 	delete in;
 


Commit: 08fea39d4ac633e64d4fb6c398f1b6dbca78c575
    https://github.com/scummvm/scummvm/commit/08fea39d4ac633e64d4fb6c398f1b6dbca78c575
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:10+02:00

Commit Message:
KYRA: (EOB2/Amiga) - fix minor color glitch

(sometimes distorted colors when skipping intro/outro)

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 cbcd1c559e..68e754c485 100644
--- a/engines/kyra/sequence/sequences_darkmoon.cpp
+++ b/engines/kyra/sequence/sequences_darkmoon.cpp
@@ -1453,11 +1453,16 @@ void DarkmoonSequenceHelper::printText(int index, int color) {
 }
 
 void DarkmoonSequenceHelper::fadeText() {
-	int rate = _vm->skipFlag() || _vm->shouldQuit() ? 16 : 8;
-	if (_vm->gameFlags().platform == Common::kPlatformAmiga)
-		_screen->fadeTextColor(_palettes[0], 31, rate);
-	else if (_vm->_configRenderMode != Common::kRenderEGA)
-		_screen->fadeTextColor(_palettes[0], 255, rate);
+	uint8 col = _vm->gameFlags().platform == Common::kPlatformAmiga ? 31 : 255;
+
+	if (_vm->skipFlag() || _vm->shouldQuit()) {
+		_screen->clearCurDim();
+		_screen->setPaletteIndex(col, 0, 0, 0);
+		return;
+	}
+
+	if (_vm->_configRenderMode != Common::kRenderEGA)
+		_screen->fadeTextColor(_palettes[0], col, 8);
 	
 	memset(_textColor, 0, 3);
 	_screen->clearCurDim();


Commit: fbc75a6df5a3b117186269f79baca9029770025e
    https://github.com/scummvm/scummvm/commit/fbc75a6df5a3b117186269f79baca9029770025e
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:11+02:00

Commit Message:
KYRA: (EOB/PC98) - minor ending sequence tweak

(skip flag behavior)

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


diff --git a/engines/kyra/sequence/sequences_eob.cpp b/engines/kyra/sequence/sequences_eob.cpp
index 3244f9a2e8..535fa457b4 100644
--- a/engines/kyra/sequence/sequences_eob.cpp
+++ b/engines/kyra/sequence/sequences_eob.cpp
@@ -1634,13 +1634,17 @@ void EoBPC98FinalePlayer::credits() {
 }
 
 void EoBPC98FinalePlayer::bonus() {
-	if (_vm->shouldQuit() || _vm->skipFlag())
+	if (_vm->shouldQuit())
 		return;
 
-	wait(300);
 	if (!_vm->checkScriptFlags(0x1FFE))
 		return;
 
+	if (_vm->skipFlag())
+		_vm->_eventList.clear();
+	else
+		wait(300);
+
 	fadeToBlack(9, 3);
 	_screen->setCurPage(0);
 	_screen->clearCurPage();


Commit: 4cb11adf9fe372ddcdb8167e57b7a65bf7384a4e
    https://github.com/scummvm/scummvm/commit/4cb11adf9fe372ddcdb8167e57b7a65bf7384a4e
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:12+02:00

Commit Message:
KYRA: (EOB/LOL) - keymap init code reduction

Changed paths:
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/kyra_rpg.cpp
    engines/kyra/engine/kyra_rpg.h
    engines/kyra/engine/lol.cpp


diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index ad7939654d..1b75386e6f 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -339,118 +339,33 @@ EoBCoreEngine::~EoBCoreEngine() {
 	delete _txt;
 	_txt = 0;
 }
-
+ 
 Common::KeymapArray EoBCoreEngine::initKeymaps(const Common::String &gameId) {
-	Common::Keymap *const engineKeyMap = new Common::Keymap(Common::Keymap::kKeymapTypeGame, kKeymapName, "Eye of the Beholder");
-
-	Common::Action *act;
-
-	act = new Common::Action("LCLK", _("Interact via Left Click"));
-	act->setLeftClickEvent();
-	act->addDefaultInputMapping("MOUSE_LEFT");
-	act->addDefaultInputMapping("JOY_A");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("RCLK", _("Interact via Right Click"));
-	act->setRightClickEvent();
-	act->addDefaultInputMapping("MOUSE_RIGHT");
-	act->addDefaultInputMapping("JOY_B");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("MVF", _("Move Forward"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_UP));
-	act->addDefaultInputMapping("UP");
-	act->addDefaultInputMapping("JOY_UP");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("MVB", _("Move Back"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_DOWN));
-	act->addDefaultInputMapping("DOWN");
-	act->addDefaultInputMapping("JOY_DOWN");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("MVL", _("Move Left"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_LEFT));
-	act->addDefaultInputMapping("LEFT");
-	act->addDefaultInputMapping("JOY_LEFT_TRIGGER");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("MVR", _("Move Right"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_RIGHT));
-	act->addDefaultInputMapping("RIGHT");
-	act->addDefaultInputMapping("JOY_RIGHT_TRIGGER");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("TL", _("Turn Left"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_HOME));
-	act->addDefaultInputMapping("HOME");
-	act->addDefaultInputMapping("JOY_LEFT");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("TR", _("Turn Right"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_PAGEUP));
-	act->addDefaultInputMapping("PAGEUP");
-	act->addDefaultInputMapping("JOY_RIGHT");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("INV", _("Open/Close Inventory"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_i, 'i'));
-	act->addDefaultInputMapping("i");
-	act->addDefaultInputMapping("JOY_X");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("SCE", _("Switch Inventory/Character screen"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_p, 'p'));
-	act->addDefaultInputMapping("p");
-	act->addDefaultInputMapping("JOY_Y");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("CMP", _("Camp"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_c, 'c'));
-	act->addDefaultInputMapping("c");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("CSP", _("Cast Spell"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_SPACE, ' '));
-	act->addDefaultInputMapping("SPACE");
-	act->addDefaultInputMapping("JOY_LEFT_SHOULDER");
-	engineKeyMap->addAction(act);
-
+	Common::Keymap *const keyMap = new Common::Keymap(Common::Keymap::kKeymapTypeGame, kKeymapName, "Eye of the Beholder");
+
+	addKeymapAction(keyMap, "LCLK", _("Interact via Left Click)"), &Common::Action::setLeftClickEvent, "MOUSE_LEFT", "JOY_A");
+	addKeymapAction(keyMap, "RCLK", _("Interact via Right Click)"), &Common::Action::setRightClickEvent, "MOUSE_RIGHT", "JOY_B");
+	addKeymapAction(keyMap, "MVF", _("Move Forward"), Common::KeyState(Common::KEYCODE_UP), "UP", "JOY_UP");
+	addKeymapAction(keyMap, "MVB", _("Move Back"), Common::KeyState(Common::KEYCODE_DOWN), "DOWN", "JOY_DOWN");
+	addKeymapAction(keyMap, "MVL", _("Move Left"), Common::KeyState(Common::KEYCODE_LEFT), "LEFT", "JOY_LEFT_TRIGGER");
+	addKeymapAction(keyMap, "MVR", _("Move Right"), Common::KeyState(Common::KEYCODE_RIGHT), "RIGHT", "JOY_RIGHT_TRIGGER");
+	addKeymapAction(keyMap, "TL", _("Turn Left"), Common::KeyState(Common::KEYCODE_HOME), "HOME", "JOY_LEFT");
+	addKeymapAction(keyMap, "TR", _("Turn Right"), Common::KeyState(Common::KEYCODE_PAGEUP), "PAGEUP", "JOY_RIGHT");
+	addKeymapAction(keyMap, "INV", _("Open/Close Inventory"), Common::KeyState(Common::KEYCODE_i, 'i'), "i", "JOY_X");
+	addKeymapAction(keyMap, "SCE", _("Switch Inventory/Character screen"), Common::KeyState(Common::KEYCODE_p, 'p'), "p", "JOY_Y");
+	addKeymapAction(keyMap, "CMP", _("Camp"), Common::KeyState(Common::KEYCODE_c, 'c'), "c", "");
+	addKeymapAction(keyMap, "CSP", _("Cast Spell"), Common::KeyState(Common::KEYCODE_SPACE, ' '), "SPACE", "JOY_LEFT_SHOULDER");
 	// TODO: Spell cursor, but this needs more thought, since different
 	// game versions use different keycodes.
-	act = new Common::Action("SL1", _("Spell Level 1"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_1, '1'));
-	act->addDefaultInputMapping("1");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("SL2", _("Spell Level 2"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_2, '2'));
-	act->addDefaultInputMapping("2");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("SL3", _("Spell Level 3"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_3, '2'));
-	act->addDefaultInputMapping("3");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("SL4", _("Spell Level 4"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_4, '4'));
-	act->addDefaultInputMapping("4");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("SL5", _("Spell Level 5"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_5, '5'));
-	act->addDefaultInputMapping("5");
-	engineKeyMap->addAction(act);
-
-	if (gameId == "eob2") {
-		act = new Common::Action("SL6", _("Spell Level 6"));
-		act->setKeyEvent(Common::KeyState(Common::KEYCODE_6, '6'));
-		act->addDefaultInputMapping("6");
-		engineKeyMap->addAction(act);
-	}
-
-	return Common::Keymap::arrayOf(engineKeyMap);
+	addKeymapAction(keyMap, "SL1", _("Spell Level 1"), Common::KeyState(Common::KEYCODE_1, '1'), "1", "");
+	addKeymapAction(keyMap, "SL2", _("Spell Level 2"), Common::KeyState(Common::KEYCODE_2, '2'), "2", "");
+	addKeymapAction(keyMap, "SL3", _("Spell Level 3"), Common::KeyState(Common::KEYCODE_3, '3'), "3", "");
+	addKeymapAction(keyMap, "SL4", _("Spell Level 4"), Common::KeyState(Common::KEYCODE_4, '4'), "4", "");
+	addKeymapAction(keyMap, "SL5", _("Spell Level 5"), Common::KeyState(Common::KEYCODE_5, '5'), "5", "");
+	if (gameId == "eob2")
+		addKeymapAction(keyMap, "SL6", _("Spell Level 6"), Common::KeyState(Common::KEYCODE_6, '6'), "6", "");
+
+	return Common::Keymap::arrayOf(keyMap);
 }
 
 Common::Error EoBCoreEngine::init() {
diff --git a/engines/kyra/engine/kyra_rpg.cpp b/engines/kyra/engine/kyra_rpg.cpp
index 73d334891a..c5104f4de0 100644
--- a/engines/kyra/engine/kyra_rpg.cpp
+++ b/engines/kyra/engine/kyra_rpg.cpp
@@ -25,6 +25,10 @@
 #include "kyra/engine/kyra_rpg.h"
 #include "kyra/sound/sound.h"
 
+#include "backends/keymapper/keymap.h"
+#include "backends/keymapper/action.h"
+
+#include "common/func.h"
 #include "common/system.h"
 
 namespace Kyra {
@@ -220,6 +224,22 @@ Common::Error KyraRpgEngine::init() {
 	return Common::kNoError;
 }
 
+void KyraRpgEngine::addKeymapAction(Common::Keymap *const keyMap, const char *actionId, const Common::String &actionDesc, const Common::Functor0Mem<void, Common::Action>::FuncType setEventProc, const Common::String &mapping1, const Common::String &mapping2) {
+	Common::Action *act = new Common::Action(actionId, actionDesc);
+	Common::Functor0Mem<void, Common::Action>(act, setEventProc)();
+	act->addDefaultInputMapping(mapping1);
+	act->addDefaultInputMapping(mapping2);
+	keyMap->addAction(act);
+}
+
+void KyraRpgEngine::addKeymapAction(Common::Keymap *const keyMap, const char *actionId, const Common::String &actionDesc, Common::KeyState eventKeyState, const Common::String &mapping1, const Common::String &mapping2) {
+	Common::Action *act = new Common::Action(actionId, actionDesc);
+	act->setKeyEvent(eventKeyState);
+	act->addDefaultInputMapping(mapping1);
+	act->addDefaultInputMapping(mapping2);
+	keyMap->addAction(act);
+}
+
 bool KyraRpgEngine::posWithinRect(int posX, int posY, int x1, int y1, int x2, int y2) {
 	if (posX < x1 || posX > x2 || posY < y1 || posY > y2)
 		return false;
diff --git a/engines/kyra/engine/kyra_rpg.h b/engines/kyra/engine/kyra_rpg.h
index 88c7c58aa3..80b83e4bfe 100644
--- a/engines/kyra/engine/kyra_rpg.h
+++ b/engines/kyra/engine/kyra_rpg.h
@@ -30,6 +30,13 @@
 #include "kyra/gui/gui_eob.h"
 #include "kyra/text/text_lol.h"
 
+#include "common/keyboard.h"
+#include "backends/keymapper/action.h"
+
+namespace {
+	class Action;
+}
+
 namespace Kyra {
 
 struct LevelDecorationProperty {
@@ -142,6 +149,9 @@ protected:
 	// Init
 	void initStaticResource();
 
+	static void addKeymapAction(Common::Keymap *const keyMap, const char *actionId, const Common::String &actionDesc, const Common::Functor0Mem<void, Common::Action>::FuncType setEventProc, const Common::String &mapping1, const Common::String &mapping2);
+	static void addKeymapAction(Common::Keymap *const keyMap, const char *actionId, const Common::String &actionDesc, Common::KeyState eventKeyState, const Common::String &mapping1, const Common::String &mapping2);
+
 	const uint8 **_itemIconShapes;
 
 	// Main loop
diff --git a/engines/kyra/engine/lol.cpp b/engines/kyra/engine/lol.cpp
index 4611e0cec2..2738fe7339 100644
--- a/engines/kyra/engine/lol.cpp
+++ b/engines/kyra/engine/lol.cpp
@@ -461,92 +461,24 @@ Common::Error LoLEngine::init() {
 }
 
 Common::KeymapArray LoLEngine::initKeymaps() {
-	Common::Keymap *engineKeyMap = new Common::Keymap(Common::Keymap::kKeymapTypeGame, kKeymapName, "Lands of Lore");
-
-	Common::Action *act;
-
-	act = new Common::Action("LCLK", _("Interact via Left Click"));
-	act->setLeftClickEvent();
-	act->addDefaultInputMapping("MOUSE_LEFT");
-	act->addDefaultInputMapping("JOY_A");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("RCLK", _("Interact via Right Click"));
-	act->setRightClickEvent();
-	act->addDefaultInputMapping("MOUSE_RIGHT");
-	act->addDefaultInputMapping("JOY_B");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("AT1", _("Attack 1"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_F1, Common::ASCII_F1));
-	act->addDefaultInputMapping("F1");
-	act->addDefaultInputMapping("JOY_X");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("AT2", _("Attack 2"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_F2, Common::ASCII_F2));
-	act->addDefaultInputMapping("F2");
-	act->addDefaultInputMapping("JOY_Y");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("AT3", _("Attack 3"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_F3, Common::ASCII_F3));
-	act->addDefaultInputMapping("F3");
-	act->addDefaultInputMapping("JOY_LEFT_SHOULDER");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("MVF", _("Move Forward"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_UP));
-	act->addDefaultInputMapping("UP");
-	act->addDefaultInputMapping("JOY_UP");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("MVB", _("Move Back"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_DOWN));
-	act->addDefaultInputMapping("DOWN");
-	act->addDefaultInputMapping("JOY_DOWN");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("SLL", _("Slide Left"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_LEFT));
-	act->addDefaultInputMapping("LEFT");
-	act->addDefaultInputMapping("JOY_LEFT_TRIGGER");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("SLR", _("Slide Right"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_RIGHT));
-	act->addDefaultInputMapping("RIGHT");
-	act->addDefaultInputMapping("JOY_RIGHT_TRIGGER");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("TL", _("Turn Left"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_HOME));
-	act->addDefaultInputMapping("HOME");
-	act->addDefaultInputMapping("JOY_LEFT");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("TR", _("Turn Right"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_PAGEUP));
-	act->addDefaultInputMapping("PAGEUP");
-	act->addDefaultInputMapping("JOY_RIGHT");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("RST", _("Rest"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_r, 'r'));
-	act->addDefaultInputMapping("r");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("OPT", _("Options"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_o, 'o'));
-	act->addDefaultInputMapping("o");
-	engineKeyMap->addAction(act);
-
-	act = new Common::Action("SPL", _("Choose Spell"));
-	act->setKeyEvent(Common::KeyState(Common::KEYCODE_SLASH, '/'));
-	act->addDefaultInputMapping("SLASH");
-	engineKeyMap->addAction(act);
-
-	return Common::Keymap::arrayOf(engineKeyMap);
+	Common::Keymap *keyMap = new Common::Keymap(Common::Keymap::kKeymapTypeGame, kKeymapName, "Lands of Lore");
+
+	addKeymapAction(keyMap, "LCLK", _("Interact via Left Click)"), &Common::Action::setLeftClickEvent, "MOUSE_LEFT", "JOY_A");
+	addKeymapAction(keyMap, "RCLK", _("Interact via Right Click)"), &Common::Action::setRightClickEvent, "MOUSE_RIGHT", "JOY_B");
+	addKeymapAction(keyMap, "AT1", _("Attack 1"), Common::KeyState(Common::KEYCODE_F1, Common::ASCII_F1), "F1", "JOY_X");
+	addKeymapAction(keyMap, "AT2", _("Attack 2"), Common::KeyState(Common::KEYCODE_F2, Common::ASCII_F2), "F2", "JOY_Y");
+	addKeymapAction(keyMap, "AT3", _("Attack 3"), Common::KeyState(Common::KEYCODE_F3, Common::ASCII_F3), "F3", "JOY_LEFT_SHOULDER");
+	addKeymapAction(keyMap, "MVF", _("Move Forward"), Common::KeyState(Common::KEYCODE_UP), "UP", "JOY_UP");
+	addKeymapAction(keyMap, "MVB", _("Move Back"), Common::KeyState(Common::KEYCODE_DOWN), "DOWN", "JOY_DOWN");
+	addKeymapAction(keyMap, "SLL", _("Slide Left"), Common::KeyState(Common::KEYCODE_LEFT), "LEFT", "JOY_LEFT_TRIGGER");
+	addKeymapAction(keyMap, "SLR", _("Slide Right"), Common::KeyState(Common::KEYCODE_RIGHT), "RIGHT", "JOY_RIGHT_TRIGGER");
+	addKeymapAction(keyMap, "TL", _("Turn Left"), Common::KeyState(Common::KEYCODE_HOME), "HOME", "JOY_LEFT");
+	addKeymapAction(keyMap, "TR", _("Turn Right"), Common::KeyState(Common::KEYCODE_PAGEUP), "PAGEUP", "JOY_RIGHT");
+	addKeymapAction(keyMap, "RST", _("Rest"), Common::KeyState(Common::KEYCODE_r, 'r'), "r", "");
+	addKeymapAction(keyMap, "OPT", _("Options"), Common::KeyState(Common::KEYCODE_o, 'o'), "o", "");
+	addKeymapAction(keyMap, "SPL", _("Choose Spell"), Common::KeyState(Common::KEYCODE_SLASH, '/'), "SLASH", "");
+
+	return Common::Keymap::arrayOf(keyMap);
 }
 
 void LoLEngine::pauseEngineIntern(bool pause) {


Commit: 5da41b3e110ef72f9f7e0626065c1e68767fe8fb
    https://github.com/scummvm/scummvm/commit/5da41b3e110ef72f9f7e0626065c1e68767fe8fb
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:12+02:00

Commit Message:
KYRA: (EOB/SegaCD) - fix level loading

The first level will now load up when selecting 'start default party' and it is possible to walk around a bit. The Gui (playfield, compass, inventory) is still completely broken, but at least the level blocks and some of the decoration shapes are drawn correctly. Monsters will be drawn, but their anim frames are mixed up, so they will always face the wrong direction and cannot visibly attack.

Changed paths:
    engines/kyra/engine/chargen.cpp
    engines/kyra/engine/darkmoon.cpp
    engines/kyra/engine/darkmoon.h
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eob.h
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h
    engines/kyra/engine/items_eob.cpp
    engines/kyra/engine/kyra_rpg.cpp
    engines/kyra/engine/kyra_rpg.h
    engines/kyra/engine/lol.cpp
    engines/kyra/engine/lol.h
    engines/kyra/engine/scene_eob.cpp
    engines/kyra/engine/scene_lol.cpp
    engines/kyra/engine/scene_rpg.cpp
    engines/kyra/engine/sprites_eob.cpp
    engines/kyra/engine/timer_eob.cpp
    engines/kyra/engine/timer_rpg.cpp
    engines/kyra/graphics/screen.cpp
    engines/kyra/graphics/screen.h
    engines/kyra/graphics/screen_eob.cpp
    engines/kyra/graphics/screen_eob.h
    engines/kyra/graphics/screen_eob_segacd.cpp
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/gui/saveload_eob.cpp
    engines/kyra/resource/resource.cpp
    engines/kyra/resource/resource.h
    engines/kyra/resource/staticres_eob.cpp
    engines/kyra/resource/staticres_rpg.cpp
    engines/kyra/sequence/sequences_eob.cpp


diff --git a/engines/kyra/engine/chargen.cpp b/engines/kyra/engine/chargen.cpp
index a13ef7bb87..727eec167a 100644
--- a/engines/kyra/engine/chargen.cpp
+++ b/engines/kyra/engine/chargen.cpp
@@ -225,7 +225,7 @@ void CharacterGenerator::init(bool defaultParty) {
 	_faceShapes = new const uint8 *[44];
 	if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
 		uint8 *in = _vm->resource()->fileData("FACE", 0);
-		_screen->sega_encodeSpriteShapes(_faceShapes, in, 44, 32, 32, 3);
+		_screen->sega_encodeShapesFromSprites(_faceShapes, in, 44, 32, 32, 3);
 		delete[] in;
 	} else {
 		_screen->loadShapeSetBitmap("CHARGENA", 5, 3);
diff --git a/engines/kyra/engine/darkmoon.cpp b/engines/kyra/engine/darkmoon.cpp
index 900956cf49..3403f8136b 100644
--- a/engines/kyra/engine/darkmoon.cpp
+++ b/engines/kyra/engine/darkmoon.cpp
@@ -160,6 +160,40 @@ void DarkMoonEngine::updateUsedCharacterHandItem(int charIndex, int slot) {
 	}
 }
 
+void DarkMoonEngine::loadMonsterShapes(const char *filename, int monsterIndex, bool hasDecorations, int encodeTableIndex) {
+	if (_flags.platform != Common::kPlatformFMTowns) {
+		EoBCoreEngine::loadMonsterShapes(filename, monsterIndex, hasDecorations, encodeTableIndex);
+		return;
+	}
+
+	Common::String tmp = Common::String::format("%s.MNT", filename);
+	Common::SeekableReadStream *s = _res->createReadStream(tmp);
+	if (!s)
+		error("Screen_EoB::loadMonsterShapes(): Failed to load file '%s'", tmp.c_str());
+
+	for (int i = 0; i < 6; i++)
+		_monsterShapes[monsterIndex + i] = loadFMTownsShape(s);
+
+	for (int i = 0; i < 6; i++) {
+		for (int ii = 0; ii < 2; ii++)
+			s->read(_monsterPalettes[(monsterIndex >= 18 ? i + 6 : i) * 2 + ii], 16);
+	}
+
+	if (hasDecorations)
+		loadMonsterDecoration(s, monsterIndex);
+
+	delete s;
+}
+
+uint8 *DarkMoonEngine::loadFMTownsShape(Common::SeekableReadStream *stream) {
+	uint32 size = stream->readUint32LE();
+	uint8 *shape = new uint8[size];
+	stream->read(shape, size);
+	if (shape[0] == 1)
+		shape[0]++;
+	return shape;
+}
+
 void DarkMoonEngine::generateMonsterPalettes(const char *file, int16 monsterIndex) {
 	if (_flags.platform == Common::kPlatformAmiga)
 		return;
@@ -232,7 +266,7 @@ void DarkMoonEngine::loadMonsterDecoration(Common::SeekableReadStream *stream, i
 
 	if (_flags.platform == Common::kPlatformFMTowns) {
 		while (!activeDecorations.empty()) {
-			activeDecorations.front()->shp = loadTownsShape(stream);
+			activeDecorations.front()->shp = loadFMTownsShape(stream);
 			activeDecorations.pop_front();
 		}
 	}
@@ -392,6 +426,19 @@ bool DarkMoonEngine::killMonsterExtra(EoBMonsterInPlay *m) {
 	return true;
 }
 
+void DarkMoonEngine::loadVcnData(const char *file, const uint8 *cgaMapping) {
+	if (file)
+		strcpy(_lastBlockDataFile, file);
+	delete[] _vcnBlocks;
+
+	if (_flags.platform == Common::kPlatformFMTowns) {
+		Common::String fn = Common::String::format(_vcnFilePattern.c_str(), _lastBlockDataFile);
+		_vcnBlocks = _res->fileData(fn.c_str(), 0);
+	} else {
+		EoBCoreEngine::loadVcnData(file, cgaMapping);
+	}
+}
+
 const uint8 *DarkMoonEngine::loadDoorShapes(const char *filename, int doorIndex, const uint8 *shapeDefs) {
 	_screen->loadShapeSetBitmap(filename, 3, 3);
 	for (int i = 0; i < 3; i++) {
diff --git a/engines/kyra/engine/darkmoon.h b/engines/kyra/engine/darkmoon.h
index 88fb42ef88..b7949cd129 100644
--- a/engines/kyra/engine/darkmoon.h
+++ b/engines/kyra/engine/darkmoon.h
@@ -92,6 +92,8 @@ private:
 	void updateUsedCharacterHandItem(int charIndex, int slot) override;
 
 	// Monsters
+	void loadMonsterShapes(const char *filename, int monsterIndex, bool hasDecorations, int encodeTableIndex) override;
+	uint8 *loadFMTownsShape(Common::SeekableReadStream *stream);
 	void generateMonsterPalettes(const char *file, int16 monsterIndex) override;
 	void loadMonsterDecoration(Common::SeekableReadStream *stream, int16 monsterIndex) override;
 	const uint8 *loadMonsterProperties(const uint8 *data) override;
@@ -99,6 +101,7 @@ private:
 	bool killMonsterExtra(EoBMonsterInPlay *m) override;
 
 	// Level
+	void loadVcnData(const char *file, const uint8 *cgaMapping) override;
 	void loadDoorShapes(int doorType1, int shapeId1, int doorType2, int shapeId2) override {}
 	const uint8 *loadDoorShapes(const char *filename, int doorIndex, const uint8 *shapeDefs) override;
 	void drawDoorIntern(int type, int, int x, int y, int w, int wall, int mDim, int16, int16) override;
diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index b62a5ce29b..9bd9264c45 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -47,6 +47,7 @@ EoBEngine::EoBEngine(OSystem *system, const GameFlags &flags)
 	_dscDoorScaleMult4 = _dscDoorScaleMult5 = _dscDoorScaleMult6 = _dscDoorY3 = 0;
 	_dscDoorY4 = _dscDoorY5 = _dscDoorY6 = _dscDoorY7 = _doorShapeEncodeDefs = 0;
 	_doorSwitchShapeEncodeDefs = _doorSwitchCoords = 0;
+	_doorShapesSrc = _doorSwitchShapesSrc = 0;
 	_dscDoorCoordsExt = 0;
 	_useMainMenuGUISettings = false;
 	_ttlCfg = 0;
@@ -54,9 +55,12 @@ EoBEngine::EoBEngine(OSystem *system, const GameFlags &flags)
 
 	_seqPlayer = 0;
 	_sres = 0;
+	_levelCurTrack = 0;
 }
 
 EoBEngine::~EoBEngine() {
+	delete[] _doorShapesSrc;
+	delete[] _doorSwitchShapesSrc;
 	delete[] _itemsOverlay;
 	delete _seqPlayer;
 	delete _sres;
@@ -129,7 +133,7 @@ Common::Error EoBEngine::init() {
 	shapeBuffer = new const uint8 *[numShapes]; \
 	memset(shapeBuffer, 0, numShapes * sizeof(uint8*)); \
 	in = _sres->resData(resID); \
-	_screen->sega_encodeSpriteShapes(shapeBuffer, in, numShapes, width, height, 3); \
+	_screen->sega_encodeShapesFromSprites(shapeBuffer, in, numShapes, width, height, 3); \
 	delete[] in
 
 void EoBEngine::loadItemsAndDecorationsShapes() {
@@ -160,6 +164,16 @@ void EoBEngine::startupNew() {
 	_sound->selectAudioResourceSet(kMusicIngame);
 	_sound->loadSoundFile(0);
 	_screen->selectPC98Palette(0, _screen->getPalette(0));
+	if (_flags.platform == Common::kPlatformSegaCD) {
+		_screen->sega_selectPalette(4, 0);
+		_screen->sega_selectPalette(6, 1);
+		_screen->sega_selectPalette(8, 2);
+		_screen->sega_selectPalette(7, 3);
+
+		_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 40, 28, 0x2000);
+		_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 28, 0x2000);
+		_txt->clearDim(0);
+	}
 	_currentLevel = 1;
 	_currentSub = 0;
 	loadLevel(1, 0);
@@ -413,6 +427,56 @@ void EoBEngine::updateUsedCharacterHandItem(int charIndex, int slot) {
 	}
 }
 
+void EoBEngine::loadMonsterShapes(const char *filename, int monsterIndex, bool hasDecorations, int encodeTableIndex) {
+	if (_flags.platform != Common::kPlatformSegaCD) {
+		EoBCoreEngine::loadMonsterShapes(filename, monsterIndex, hasDecorations, encodeTableIndex);
+		return;
+	}
+
+	static const uint8 lvlEncodeTableIndex[] = {
+		0x00, 0x00, 0x00, 0x01, 0x03, 0x02, 0x04, 0x05, 0x06, 0x06, 0x07, 0x06, 0x10,
+		0x0e, 0x0c, 0x08, 0x0f, 0x14, 0x12, 0x09, 0x0b, 0x0a, 0x13, 0x11, 0x15, 0x0d
+	};
+
+	_sres->loadContainer(Common::String::format("L%d", _currentLevel));
+	uint8 *data = _sres->resData(monsterIndex >> 4, 0);
+	const uint8 *pos = data;
+
+	int size = 0;
+	const uint8 *enc = _staticres->loadRawData(kEoBBaseEncodeMonsterDefs00 + lvlEncodeTableIndex[(_currentLevel << 1) + (monsterIndex >> 4)], size);
+	size >>= 1;
+	assert(size <= 18);
+
+	for (int i = 0; i < size; i++) {
+		_monsterShapes[monsterIndex + i] = _screen->sega_encodeShape(pos, enc[0], enc[1], 2);
+		pos += ((enc[0] * enc[1]) >> 1);
+		enc += 2;
+	}
+
+	delete[] data;
+
+#if 0
+	// DEBUG: display all the just encoded monster shapes on screen
+	setLevelPalettes(_currentLevel);
+	_screen->sega_fadeToNeutral(0);
+	_screen->clearPage(0);
+	uint16 x = 0;
+	uint8 y = 0;
+	uint8 hmax = 0;
+	for (int i = 0; i < size; i++) {
+		if (x + (_monsterShapes[monsterIndex + i][2] << 3) > 320) {
+			y += hmax;
+			x = hmax = 0;
+		}
+		hmax = MAX(_monsterShapes[monsterIndex + i][1], hmax);
+		_screen->drawShape(0, _monsterShapes[monsterIndex + i], x, y);
+		_screen->updateScreen();
+		x += _monsterShapes[monsterIndex + i][2] << 3;
+	}
+	_screen->updateScreen();
+#endif
+}
+
 void EoBEngine::replaceMonster(int unit, uint16 block, int pos, int dir, int type, int shpIndex, int mode, int h2, int randItem, int fixedItem) {
 	if (_levelBlockProperties[block].flags & 7)
 		return;
@@ -450,32 +514,111 @@ void EoBEngine::updateScriptTimersExtra() {
 	}
 }
 
-void EoBEngine::loadDoorShapes(int doorType1, int shapeId1, int doorType2, int shapeId2) {
-	_screen->loadShapeSetBitmap("DOOR", 5, 3);
-	_screen->_curPage = 2;
+void EoBEngine::readLevelFileData(int level) {
+	if (_flags.platform != Common::kPlatformSegaCD) {
+		EoBCoreEngine::readLevelFileData(level);
+		return;
+	}
+	_sres->loadContainer(Common::String::format("L%d", level));
+	Common::SeekableReadStream *s = _sres->resStream(7);
+	_screen->loadFileDataToPage(s, 5, 15000);
+	delete s;
+}
 
-	if (doorType1 != 0xFF) {
-		for (int i = 0; i < 3; i++) {
-			const uint8 *enc = &_doorShapeEncodeDefs[(doorType1 * 3 + i) << 2];
-			_doorShapes[shapeId1 + i] = _screen->encodeShape(enc[0], enc[1], enc[2], enc[3], false, _cgaLevelMappingIndex ? _cgaMappingLevel[_cgaLevelMappingIndex[_currentLevel - 1]] : 0);
-			enc = &_doorSwitchShapeEncodeDefs[(doorType1 * 3 + i) << 2];
-			_doorSwitches[shapeId1 + i].shp = _screen->encodeShape(enc[0], enc[1], enc[2], enc[3], false, _cgaLevelMappingIndex ? _cgaMappingLevel[_cgaLevelMappingIndex[_currentLevel - 1]] : 0);
-			_doorSwitches[shapeId1 + i].x = _doorSwitchCoords[doorType1 * 6 + i * 2];
-			_doorSwitches[shapeId1 + i].y = _doorSwitchCoords[doorType1 * 6 + i * 2 + 1];
-		}
+void EoBEngine::loadVcnData(const char *file, const uint8 *cgaMapping) {
+	if (file)
+		strcpy(_lastBlockDataFile, file);
+	delete[] _vcnBlocks;
+
+	Common::String fn = Common::String::format(_vcnFilePattern.c_str(), _lastBlockDataFile);
+	if (_flags.platform == Common::kPlatformAmiga) {
+		Common::SeekableReadStream *in = _res->createReadStream(fn);
+		uint32 vcnSize = in->readUint16LE() * (_vcnSrcBitsPerPixel << 3);
+		_vcnBlocks = new uint8[vcnSize];
+		_screen->getPalette(1).loadAmigaPalette(*in, 1, 5);
+		in->seek(22, SEEK_CUR);
+		in->read(_vcnBlocks, vcnSize);
+		delete in;
+	} else if (_flags.platform == Common::kPlatformPC98) {
+		_vcnBlocks = _res->fileData(fn.c_str(), 0);
+	} else if (_flags.platform == Common::kPlatformSegaCD) {
+		_sres->loadContainer(Common::String::format("L%d", _currentLevel));
+		_vcnBlocks = _sres->resData(5, 0);
+	} else {
+		EoBCoreEngine::loadVcnData(file, cgaMapping);
 	}
+}
+
+Common::SeekableReadStreamEndian *EoBEngine::getVmpData(const char *file) {
+	if (_flags.platform != Common::kPlatformSegaCD)
+		return EoBCoreEngine::getVmpData(file);
+	_sres->loadContainer(Common::String::format("L%d", _currentLevel));
+	return _sres->resStreamEndian(3);
+}
+
+const uint8 *EoBEngine::getBlockFileData(int level) {
+	if (_flags.platform != Common::kPlatformSegaCD)
+		return EoBCoreEngine::getBlockFileData(level);
+	_sres->loadContainer(Common::String::format("L%d", level));
+	Common::SeekableReadStream *s = _sres->resStream(6);
+	_screen->loadFileDataToPage(s, 15, s->size());
+	delete s;
+	return _screen->getCPagePtr(15);
+}
+
+Common::SeekableReadStreamEndian *EoBEngine::getDecDefinitions(const char *decFile) {
+	if (_flags.platform != Common::kPlatformSegaCD)
+		return EoBCoreEngine::getDecDefinitions(decFile);
+	_sres->loadContainer(Common::String::format("L%d", _currentLevel));
+	return _sres->resStreamEndian(4);
+}
+
+void EoBEngine::loadDecShapesToPage3(const char *shpFile) {
+	if (_flags.platform != Common::kPlatformSegaCD)
+		return EoBCoreEngine::loadDecShapesToPage3(shpFile);
+	_sres->loadContainer(Common::String::format("L%d", _currentLevel));
+	Common::SeekableReadStream *s = _sres->resStream(2);
+	_screen->loadFileDataToPage(s, 3, s->size());
+	_dcrShpDataPos = _screen->getCPagePtr(3);
+	delete s;
+}
+
+void EoBEngine::loadDoorShapes(int doorType1, int shapeId1, int doorType2, int shapeId2) {
+	static const uint8 lvlIndex[13] = { 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x04 };
+	const int doorType[2] = { doorType1, doorType2 };
+	const int shapeId[2] = { shapeId1, shapeId2 };
+
+	if (_flags.platform == Common::kPlatformSegaCD) {
+		_sres->loadContainer(Common::String::format("L%d", _currentLevel));
+		Common::SeekableReadStreamEndian *in = _sres->resStreamEndian(8);
+		_screen->loadFileDataToPage(in, 2, in->size());
+		delete in;
+	} else {
+		_screen->loadShapeSetBitmap("DOOR", 5, 3);
+		_screen->_curPage = 2;
+	}
+
+	for (int a = 0; a < 2; ++a) {
+		if (doorType[a] == 0xFF)
+			continue;
 
-	if (doorType2 != 0xFF) {
 		for (int i = 0; i < 3; i++) {
-			const uint8 *enc = &_doorShapeEncodeDefs[(doorType2 * 3 + i) << 2];
-			_doorShapes[shapeId2 + i] = _screen->encodeShape(enc[0], enc[1], enc[2], enc[3], false, _cgaLevelMappingIndex ? _cgaMappingLevel[_cgaLevelMappingIndex[_currentLevel - 1]] : 0);
-			enc = &_doorSwitchShapeEncodeDefs[(doorType2 * 3 + i) << 2];
-			_doorSwitches[shapeId2 + i].shp = _screen->encodeShape(enc[0], enc[1], enc[2], enc[3], false, _cgaLevelMappingIndex ? _cgaMappingLevel[_cgaLevelMappingIndex[_currentLevel - 1]] : 0);
-			_doorSwitches[shapeId2 + i].x = _doorSwitchCoords[doorType2 * 6 + i * 2];
-			_doorSwitches[shapeId2 + i].y = _doorSwitchCoords[doorType2 * 6 + i * 2 + 1];
+			if (_flags.platform == Common::kPlatformSegaCD) {
+				int offs = lvlIndex[_currentLevel] * 6 + shapeId[a] + i;
+				const uint8 *enc = &_doorShapeEncodeDefs[offs << 2];
+				_doorShapes[shapeId[a] + i] = _screen->sega_encodeShape(_doorShapesSrc[offs], enc[0] << 3, enc[1] << 3, 0);
+				enc = &_doorSwitchShapeEncodeDefs[(offs << 2) - shapeId[a]];
+				_doorSwitches[shapeId[a] + i].shp = _screen->sega_encodeShape(_doorSwitchShapesSrc[offs], enc[0] << 3, enc[1] << 3, 0);
+			} else {
+				const uint8 *enc = &_doorShapeEncodeDefs[(doorType[a] * 3 + i) << 2];
+				_doorShapes[shapeId[a] + i] = _screen->encodeShape(enc[0], enc[1], enc[2], enc[3], false, _cgaLevelMappingIndex ? _cgaMappingLevel[_cgaLevelMappingIndex[_currentLevel - 1]] : 0);
+				enc = &_doorSwitchShapeEncodeDefs[(doorType[a] * 3 + i) << 2];
+				_doorSwitches[shapeId[a] + i].shp = _screen->encodeShape(enc[0], enc[1], enc[2], enc[3], false, _cgaLevelMappingIndex ? _cgaMappingLevel[_cgaLevelMappingIndex[_currentLevel - 1]] : 0);
+			}
+			_doorSwitches[shapeId[a] + i].x = _doorSwitchCoords[doorType[a] * 6 + i * 2];
+			_doorSwitches[shapeId[a] + i].y = _doorSwitchCoords[doorType[a] * 6 + i * 2 + 1];
 		}
 	}
-
 	_screen->_curPage = 0;
 }
 
@@ -550,6 +693,17 @@ void EoBEngine::drawDoorIntern(int type, int index, int x, int y, int w, int wal
 	}
 }
 
+void EoBEngine::setLevelPalettes(int level) {
+	if (_flags.platform != Common::kPlatformSegaCD)
+		return;
+
+	static const uint8 palette0[13] = { 0x04, 0x10, 0x11, 0x12, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31 };
+	static const uint8 palette2[13] = { 0x08, 0x08, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d };
+
+	_screen->sega_selectPalette(palette0[level], 0);
+	_screen->sega_selectPalette(palette2[level], 2);
+}
+
 void EoBEngine::turnUndeadAuto() {
 	if (_currentLevel != 2 && _currentLevel != 7)
 		return;
@@ -628,6 +782,21 @@ void EoBEngine::snd_loadAmigaSounds(int level, int) {
 	_amigaCurSoundFile = level;
 }
 
+void EoBEngine::snd_updateLevelScore() {
+	if (_flags.platform != Common::kPlatformSegaCD || _currentLevel != 5)
+		return;
+
+	int x = _currentBlock & 0x1F;
+	int y = (_currentBlock >> 5) & 0x1F;
+
+	int track = (x >= 14 && x < 20 && y >= 7 && y < 15) ? (x == 14 && y == 14 ? 6 : 12) : (x == 17 && y == 6 ? 12 : 6);
+	if (track == _levelCurTrack)
+		return;
+
+	_levelCurTrack = track;
+	snd_playSong(track);
+}
+
 bool EoBEngine::checkPartyStatusExtra() {
 	_screen->copyPage(0, 10);
 	int cd = _screen->curDimIndex();
@@ -684,6 +853,19 @@ void EoBEngine::healParty() {
 	}
 }
 
+void EoBEngine::gui_drawPlayField(bool refresh) {
+	if (_flags.platform != Common::kPlatformSegaCD) {
+		EoBCoreEngine::gui_drawPlayField(refresh);
+		return;
+	}
+
+	uint8 *data = _res->fileData("PLAYFLD", 0);
+
+	delete[] data;
+
+	_screen->sega_fadeToNeutral(0);
+}
+
 const KyraRpgGUISettings *EoBEngine::guiSettings() const {
 	if (_flags.platform == Common::kPlatformAmiga)
 		return _useMainMenuGUISettings ? &_guiSettingsAmigaMainMenu : &_guiSettingsAmiga;
diff --git a/engines/kyra/engine/eob.h b/engines/kyra/engine/eob.h
index 33489c0419..d8fbefa8d9 100644
--- a/engines/kyra/engine/eob.h
+++ b/engines/kyra/engine/eob.h
@@ -121,14 +121,22 @@ private:
 	void updateUsedCharacterHandItem(int charIndex, int slot) override;
 
 	// Monsters
+	void loadMonsterShapes(const char *filename, int monsterIndex, bool hasDecorations, int encodeTableIndex) override;
 	void replaceMonster(int unit, uint16 block, int d, int dir, int type, int shpIndex, int mode, int h2, int randItem, int fixedItem) override;
 	bool killMonsterExtra(EoBMonsterInPlay *m) override;
 	void updateScriptTimersExtra() override;
 
 	// Level
+	void readLevelFileData(int level) override;
+	void loadVcnData(const char *file, const uint8 *cgaMapping) override;
+	Common::SeekableReadStreamEndian *getVmpData(const char *file) override;
+	const uint8 *getBlockFileData(int level) override;
+	Common::SeekableReadStreamEndian *getDecDefinitions(const char *decFile) override;
+	void loadDecShapesToPage3(const char *shpFile) override;
 	const uint8 *loadDoorShapes(const char *filename, int doorIndex, const uint8 *shapeDefs) override { return 0; }
 	void loadDoorShapes(int doorType1, int shapeId1, int doorType2, int shapeId2) override;
 	void drawDoorIntern(int type, int index, int x, int y, int w, int wall, int mDim, int16 y1, int16 y2) override;
+	void setLevelPalettes(int level) override;
 
 	const int16 *_dscDoorCoordsExt;
 	const uint8 *_dscDoorScaleMult4;
@@ -144,6 +152,9 @@ private:
 	const uint8 *_doorSwitchShapeEncodeDefs;
 	const uint8 *_doorSwitchCoords;
 
+	const uint8 *const *_doorShapesSrc;
+	const uint8 *const *_doorSwitchShapesSrc;
+
 	// Fight
 	static const uint8 _monsterAcHitChanceTbl1[];
 	static const uint8 _monsterAcHitChanceTbl2[];
@@ -156,6 +167,9 @@ private:
 
 	// Sound
 	void snd_loadAmigaSounds(int level, int) override;
+	void snd_updateLevelScore() override;
+
+	int _levelCurTrack;
 
 	// Misc
 	bool checkPartyStatusExtra() override;
@@ -166,6 +180,7 @@ private:
 	SegaCDResource *_sres;
 
 	// GUI
+	void gui_drawPlayField(bool refresh) override;
 	const KyraRpgGUISettings *guiSettings() const override;
 	void useMainMenuGUISettings(bool toggle) override { _useMainMenuGUISettings = toggle; }
 
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 1b75386e6f..1b3a3b58c4 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -188,6 +188,8 @@ 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;
+	_invFont3 = Screen::FID_8_FNT;
 	_transferStringsScummVM = 0;
 	_buttonDefs = 0;
 	_npcPreset = 0;
@@ -225,6 +227,7 @@ EoBCoreEngine::EoBCoreEngine(OSystem *system, const GameFlags &flags) : KyraRpgE
 	_inventorySlotsX = _slotValidationFlags = _encodeMonsterShpTable = 0;
 	_cgaMappingDefault = _cgaMappingAlt = _cgaMappingInv = _cgaLevelMappingIndex = _cgaMappingItemsL = _cgaMappingItemsS = _cgaMappingThrown = _cgaMappingIcons = _cgaMappingDeco = 0;
 	_amigaLevelSoundList1 = _amigaLevelSoundList2 = 0;
+	_dcrShpDataPos = 0;
 	_amigaSoundMap = 0;
 	_amigaCurSoundFile = -1;
 	_prefMenuPlatformOffset = 0;
@@ -574,13 +577,16 @@ void EoBCoreEngine::loadFonts() {
 	}
 
 	if (_flags.platform == Common::kPlatformFMTowns) {
-			_screen->loadFont(Screen::FID_SJIS_SMALL_FNT, "FONT.DMP");
+		_screen->loadFont(Screen::FID_SJIS_SMALL_FNT, "FONT.DMP");
 	} else if (_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;
 	} else if (_flags.platform == Common::kPlatformSegaCD) {
 		_screen->loadFont(Screen::FID_8_FNT, "FONTK12");
 		//_screen->loadFont(Screen::FID_6_FNT, "FONT8SH");
 		_screen->setFontStyles(Screen::FID_8_FNT, _flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleFat);
+		_invFont1 = _invFont2 = _conFont = Screen::FID_8_FNT;
 	}
 }
 
@@ -592,11 +598,13 @@ Common::Error EoBCoreEngine::go() {
 
 	_screen->setMouseCursor(0, 0, _itemIconShapes[0]);
 
-	// Import original save game files (especially the "Quick Start Party")
+	// 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")) {
-		//importOriginalSaveFile(-1);
-		//ConfMan.setBool("importOrigSaves", false);
-		//ConfMan.flushToDisk();
+		if (_flags.platform != Common::kPlatformSegaCD)
+			importOriginalSaveFile(-1);
+		ConfMan.setBool("importOrigSaves", false);
+		ConfMan.flushToDisk();
 	}
 
 	loadItemDefs();
@@ -702,8 +710,7 @@ void EoBCoreEngine::runLoop() {
 	_envAudioTimer = _system->getMillis() + (rollDice(1, 10, 3) * 18 * _tickLength);
 	_flashShapeTimer = 0;
 	_drawSceneTimer = _system->getMillis();
-
-	_screen->setFont(_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT);
+	_screen->setFont(_conFont);
 	_screen->setScreenDim(7);
 
 	_runFlag = true;
@@ -728,7 +735,8 @@ void EoBCoreEngine::runLoop() {
 			snd_processEnvironmentalSoundEffect(_flags.gameID == GI_EOB1 ? 30 : (rollDice(1, 2, -1) ? 27 : 28), _currentBlock + rollDice(1, 12, -1));
 		}
 
-		updateEnvironmentalSfx(0);
+		snd_updateLevelScore();
+		snd_updateEnvironmentalSfx(0);
 		turnUndeadAuto();
 	}
 }
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index 6df3f59b68..a49cad5161 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -496,9 +496,8 @@ protected:
 	const char *const *_ascii2SjisTables2;
 
 	// Monsters
-	void loadMonsterShapes(const char *filename, int monsterIndex, bool hasDecorations, int encodeTableIndex);
+	virtual void loadMonsterShapes(const char *filename, int monsterIndex, bool hasDecorations, int encodeTableIndex);
 	void releaseMonsterShapes(int first, int num);
-	uint8 *loadTownsShape(Common::SeekableReadStream *stream);
 	virtual void generateMonsterPalettes(const char *file, int16 monsterIndex) {}
 	virtual void loadMonsterDecoration(Common::SeekableReadStream *stream, int16 monsterIndex) {}
 	virtual const uint8 *loadMonsterProperties(const uint8 *data) { return 0; }
@@ -594,21 +593,25 @@ protected:
 
 	// Level
 	void loadLevel(int level, int sub);
-	void readLevelFileData(int level);
+	virtual void readLevelFileData(int level);
 	Common::String initLevelData(int sub);
 	void addLevelItems() override;
-	void loadVcnData(const char *file, const uint8 *cgaMapping);
+	virtual void loadVcnData(const char *file, const uint8 *cgaMapping);
+	virtual Common::SeekableReadStreamEndian *getVmpData(const char *file);
 	void loadBlockProperties(const char *mazFile) override;
-	const uint8 *getBlockFileData(int levelIndex = 0) override;
-	Common::String getBlockFileName(int levelIndex, int sub);
+	virtual const uint8 *getBlockFileData(int levelIndex) override;
 	const uint8 *getBlockFileData(const char *mazFile);
+	Common::String getBlockFileName(int levelIndex, int sub);
 	void loadDecorations(const char *cpsFile, const char *decFile);
+	virtual Common::SeekableReadStreamEndian *getDecDefinitions(const char *decFile);
+	virtual void loadDecShapesToPage3(const char *shpFile);
 	void assignWallsAndDecorations(int wallIndex, int vmpIndex, int decDataIndex, int specialType, int flags);
 	void releaseDecorations();
 	void releaseDoorShapes();
 	void toggleWallState(int wall, int flags);
 	virtual void loadDoorShapes(int doorType1, int shapeId1, int doorType2, int shapeId2) = 0;
 	virtual const uint8 *loadDoorShapes(const char *filename, int doorIndex, const uint8 *shapeDefs) = 0;
+	virtual void setLevelPalettes(int level) {}
 
 	void drawScene(int refresh) override;
 	void drawSceneShapes(int start = 0) override;
@@ -633,6 +636,7 @@ protected:
 
 	EoBRect8 *_levelDecorationRects;
 	SpriteDecoration *_doorSwitches;
+	const uint8 *_dcrShpDataPos;
 
 	int8 _currentSub;
 	Common::String _curGfxFile;
@@ -689,7 +693,7 @@ protected:
 	uint8 _scriptTimersMode;
 
 	// Gui
-	void gui_drawPlayField(bool refresh);
+	virtual void gui_drawPlayField(bool refresh);
 	void gui_restorePlayField();
 	void gui_drawAllCharPortraitsWithStats();
 	void gui_drawCharPortraitWithStats(int index);
@@ -782,6 +786,10 @@ protected:
 
 	const uint16 *_inventorySlotsX;
 	const uint8 *_inventorySlotsY;
+	Screen::FontId _invFont1;
+	Screen::FontId _invFont2;
+	Screen::FontId _invFont3;
+	Screen::FontId _conFont;
 	const uint8 **_compassShapes;
 	uint8 _charExchangeSwap;
 	bool _configHpBarGraphs;
@@ -1199,6 +1207,7 @@ protected:
 	void snd_stopSound();
 	void snd_fadeOut(int del = 160);
 	virtual void snd_loadAmigaSounds(int level, int sub) = 0;
+	virtual void snd_updateLevelScore() {}
 
 	const char **_amigaSoundMap;
 	const char *const *_amigaLevelSoundList1;
diff --git a/engines/kyra/engine/items_eob.cpp b/engines/kyra/engine/items_eob.cpp
index 4df903d726..7cf95c893b 100644
--- a/engines/kyra/engine/items_eob.cpp
+++ b/engines/kyra/engine/items_eob.cpp
@@ -733,7 +733,7 @@ void EoBCoreEngine::endObjectFlight(EoBFlyingObject *fo) {
 	if (fo->enable == 1) {
 		_items[fo->item].pos &= 3;
 		runLevelScript(fo->curBlock, 4);
-		updateEnvironmentalSfx(18);
+		snd_updateEnvironmentalSfx(18);
 	}
 	memset(fo, 0, sizeof(EoBFlyingObject));
 }
diff --git a/engines/kyra/engine/kyra_rpg.cpp b/engines/kyra/engine/kyra_rpg.cpp
index c5104f4de0..b23fa5a866 100644
--- a/engines/kyra/engine/kyra_rpg.cpp
+++ b/engines/kyra/engine/kyra_rpg.cpp
@@ -54,6 +54,7 @@ KyraRpgEngine::KyraRpgEngine(OSystem *system, const GameFlags &flags) : KyraEngi
 	_vcnBpp = flags.useHiColorMode ? 2 : 1;
 	_vcnSrcBitsPerPixel = (flags.platform == Common::kPlatformAmiga) ? 5 : (_vcnBpp == 2 ? 8 : 4);
 	_vcnDrawLine = 0;
+	_vmpVisOffs = (flags.platform == Common::kPlatformSegaCD) ? _vmpOffsetsSegaCD : _vmpOffsetsDefault;
 
 	_vmpPtr = 0;
 	_blockBrightness = _wllVcnOffset = _wllVcnOffset2 = _wllVcnRmdOffset = 0;
@@ -169,7 +170,7 @@ Common::Error KyraRpgEngine::init() {
 
 	_levelDecorationProperties = new LevelDecorationProperty[100];
 	memset(_levelDecorationProperties, 0, 100 * sizeof(LevelDecorationProperty));
-	_levelDecorationShapes = new uint8*[400];
+	_levelDecorationShapes = new const uint8*[400];
 	memset(_levelDecorationShapes, 0, 400 * sizeof(uint8 *));
 	_levelBlockProperties = new LevelBlockProperty[1025];
 	memset(_levelBlockProperties, 0, 1025 * sizeof(LevelBlockProperty));
@@ -407,7 +408,7 @@ bool KyraRpgEngine::snd_processEnvironmentalSoundEffect(int soundId, int block)
 	return true;
 }
 
-void KyraRpgEngine::updateEnvironmentalSfx(int soundId) {
+void KyraRpgEngine::snd_updateEnvironmentalSfx(int soundId) {
 	snd_processEnvironmentalSoundEffect(soundId, _currentBlock);
 }
 
diff --git a/engines/kyra/engine/kyra_rpg.h b/engines/kyra/engine/kyra_rpg.h
index 80b83e4bfe..95e78bf651 100644
--- a/engines/kyra/engine/kyra_rpg.h
+++ b/engines/kyra/engine/kyra_rpg.h
@@ -156,7 +156,7 @@ protected:
 
 	// Main loop
 	virtual void update() = 0;
-	void updateEnvironmentalSfx(int soundId);
+	void snd_updateEnvironmentalSfx(int soundId);
 
 	// timers
 	void setupTimers() override = 0;
@@ -269,7 +269,7 @@ protected:
 	LevelDecorationProperty *_levelDecorationData;
 	uint16 _levelDecorationDataSize;
 	LevelDecorationProperty *_levelDecorationProperties;
-	uint8 **_levelDecorationShapes;
+	const uint8 **_levelDecorationShapes;
 	uint16 _decorationCount;
 	int16 _mappedDecorationsCount;
 	uint16 *_vmpPtr;
@@ -333,6 +333,10 @@ protected:
 	const uint8 *_dscDoorFrameIndex1;
 	const uint8 *_dscDoorFrameIndex2;
 
+	const uint16 *_vmpVisOffs;
+	static const uint16 _vmpOffsetsDefault[9];
+	static const uint16 _vmpOffsetsSegaCD[9];
+
 	// Script
 	virtual void runLevelScript(int block, int flags) = 0;
 
diff --git a/engines/kyra/engine/lol.cpp b/engines/kyra/engine/lol.cpp
index 2738fe7339..2db62f61f3 100644
--- a/engines/kyra/engine/lol.cpp
+++ b/engines/kyra/engine/lol.cpp
@@ -878,7 +878,7 @@ void LoLEngine::runLoop() {
 		if (_sceneUpdateRequired)
 			gui_drawScene(0);
 		else
-			updateEnvironmentalSfx(0);
+			snd_updateEnvironmentalSfx(0);
 
 		if (_partyDamageFlags != -1) {
 			checkForPartyDeath();
diff --git a/engines/kyra/engine/lol.h b/engines/kyra/engine/lol.h
index 50c7247460..cff94db138 100644
--- a/engines/kyra/engine/lol.h
+++ b/engines/kyra/engine/lol.h
@@ -986,7 +986,7 @@ private:
 	int _lvlShapeIndex;
 	bool _partyAwake;
 
-	uint8 *_specialGuiShape;
+	const uint8 *_specialGuiShape;
 	uint16 _specialGuiShapeX;
 	uint16 _specialGuiShapeY;
 	uint16 _specialGuiShapeMirrorFlag;
diff --git a/engines/kyra/engine/scene_eob.cpp b/engines/kyra/engine/scene_eob.cpp
index 64207b7532..18bef2d262 100644
--- a/engines/kyra/engine/scene_eob.cpp
+++ b/engines/kyra/engine/scene_eob.cpp
@@ -95,7 +95,8 @@ void EoBCoreEngine::loadLevel(int level, int sub) {
 		_levelBlockProperties[0x035C].assignedObjects = 0x0E8D;
 
 	loadVcnData(gfxFile.c_str(), _cgaLevelMappingIndex ? _cgaMappingLevel[_cgaLevelMappingIndex[level - 1]] : 0);
-	_screen->loadEoBBitmap("INVENT", _cgaMappingInv, 5, 3, 2);
+	if (_flags.platform != Common::kPlatformSegaCD)
+		_screen->loadEoBBitmap("INVENT", _cgaMappingInv, 5, 3, 2);
 	if (_flags.platform == Common::kPlatformAmiga && _flags.gameID == GI_EOB1)
 		_screen->getPalette(0).copy(_screen->getPalette(1), 1, 5, 1);
 
@@ -162,9 +163,9 @@ Common::String EoBCoreEngine::initLevelData(int sub) {
 		loadBlockProperties((const char *)pos);
 		pos += slen;
 
-		Common::SeekableReadStreamEndian *s = _res->createEndianAwareReadStream(Common::String::format(_vmpFilePattern.c_str(), (const char *)pos));
+		Common::SeekableReadStreamEndian *s = getVmpData((const char*)pos);		
 		assert(s);
-		uint16 size = (_flags.platform == Common::kPlatformFMTowns) ? 2916 : s->readUint16();
+		uint16 size = (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformSegaCD) ? 2916 : s->readUint16();
 		delete[] _vmpPtr;
 		_vmpPtr = new uint16[size];
 		if (_flags.gameID == GI_EOB1) {
@@ -196,6 +197,8 @@ Common::String EoBCoreEngine::initLevelData(int sub) {
 		if (_flags.gameID == GI_EOB2 || (_flags.platform != Common::kPlatformAmiga && _configRenderMode != Common::kRenderCGA && _configRenderMode != Common::kRenderEGA))
 			_screen->loadPalette(tmpStr.c_str(), _screen->getPalette(_flags.platform == Common::kPlatformAmiga ? 6 : 0));
 
+		setLevelPalettes(_currentLevel);
+
 		if (_flags.platform == Common::kPlatformFMTowns) {
 			uint16 *src = (uint16*)_screen->getPalette(0).getData();
 			_screen->createFadeTable16bit(src, (uint16*)_greenFadingTable, 4, 75);
@@ -204,11 +207,7 @@ Common::String EoBCoreEngine::initLevelData(int sub) {
 			_screen->createFadeTable16bit(src, (uint16*)_lightBlueFadingTable, 11, 125);
 			_screen->createFadeTable16bit(src, (uint16*)_greyFadingTable, 0, 85);
 			_screen->setScreenPalette(_screen->getPalette(0));
-		} else if (_flags.platform == Common::kPlatformAmiga) {
-			// Amiga versions don't have shape shading
-		} else if (_flags.gameID == GI_EOB1 && _flags.platform == Common::kPlatformPC98) {
-		
-		} else if (_configRenderMode != Common::kRenderCGA) {
+		} else if (_configRenderMode != Common::kRenderCGA && _flags.platform != Common::kPlatformAmiga && _flags.platform != Common::kPlatformSegaCD && !(_flags.gameID == GI_EOB1 && _flags.platform == Common::kPlatformPC98)) {
 			Palette backupPal(256);
 			backupPal.copy(_screen->getPalette(0), 224, 32, 224);
 			_screen->getPalette(0).fill(224, 32, 0x3F);
@@ -318,30 +317,8 @@ void EoBCoreEngine::addLevelItems() {
 }
 
 void EoBCoreEngine::loadVcnData(const char *file, const uint8 *cgaMapping) {	
-	if (file)
-		strcpy(_lastBlockDataFile, file);
-
-	delete[] _vcnBlocks;
 	uint32 vcnSize = 0;
-
 	Common::String fn = Common::String::format(_vcnFilePattern.c_str(), _lastBlockDataFile);
-
-	if (_flags.platform == Common::kPlatformFMTowns) {
-		_vcnBlocks = _res->fileData(fn.c_str(), &vcnSize);
-		return;
-	} else if (_flags.gameID == GI_EOB1 && (_flags.platform == Common::kPlatformAmiga || _flags.platform == Common::kPlatformPC98)) {
-		Common::SeekableReadStream *in = _res->createReadStream(fn);
-		vcnSize = _flags.platform == Common::kPlatformPC98 ? in->size() : in->readUint16LE() * (_vcnSrcBitsPerPixel << 3);
-		_vcnBlocks = new uint8[vcnSize];			
-		if (_flags.platform == Common::kPlatformAmiga) {
-			_screen->getPalette(1).loadAmigaPalette(*in, 1, 5);
-			in->seek(22, SEEK_CUR);
-		}
-		in->read(_vcnBlocks, vcnSize);
-		delete in;
-		return;
-	}
-
 	_screen->loadBitmap(fn.c_str(), 3, 3, 0, true);
 
 	const uint8 *pos = _screen->getCPagePtr(3);
@@ -381,6 +358,10 @@ void EoBCoreEngine::loadVcnData(const char *file, const uint8 *cgaMapping) {
 	}
 }
 
+Common::SeekableReadStreamEndian *EoBCoreEngine::getVmpData(const char *file) {
+	return _res->createEndianAwareReadStream(Common::String::format(_vmpFilePattern.c_str(), file));
+}
+
 void EoBCoreEngine::loadBlockProperties(const char *mazFile) {
 	memset(_levelBlockProperties, 0, 1024 * sizeof(LevelBlockProperty));
 	const uint8 *p = getBlockFileData(mazFile) + 6;
@@ -425,14 +406,23 @@ Common::String EoBCoreEngine::getBlockFileName(int levelIndex, int sub) {
 
 const uint8 *EoBCoreEngine::getBlockFileData(const char *mazFile) {
 	_curBlockFile = mazFile;
-	return getBlockFileData();
+	return getBlockFileData(_currentLevel);
+}
+
+
+Common::SeekableReadStreamEndian *EoBCoreEngine::getDecDefinitions(const char *decFile) {
+	return _res->createEndianAwareReadStream(decFile, Resource::kForceLE);
+}
+
+void EoBCoreEngine::loadDecShapesToPage3(const char *shpFile) {
+	_screen->loadShapeSetBitmap(shpFile, 5, 3);
 }
 
 void EoBCoreEngine::loadDecorations(const char *cpsFile, const char *decFile) {
-	_screen->loadShapeSetBitmap(cpsFile, 5, 3);
-	Common::SeekableReadStream *s = _res->createReadStream(decFile);
+	loadDecShapesToPage3(cpsFile);
+	Common::SeekableReadStreamEndian *s = getDecDefinitions(decFile);
 
-	_levelDecorationDataSize = s->readUint16LE();
+	_levelDecorationDataSize = s->readUint16();
 	delete[] _levelDecorationData;
 	_levelDecorationData = new LevelDecorationProperty[_levelDecorationDataSize];
 	memset(_levelDecorationData, 0, _levelDecorationDataSize * sizeof(LevelDecorationProperty));
@@ -447,20 +437,20 @@ void EoBCoreEngine::loadDecorations(const char *cpsFile, const char *decFile) {
 		l->next = s->readByte();
 		l->flags = s->readByte();
 		for (int ii = 0; ii < 10; ii++)
-			l->shapeX[ii] = s->readSint16LE();
+			l->shapeX[ii] = s->readSint16();
 		for (int ii = 0; ii < 10; ii++)
-			l->shapeY[ii] = s->readSint16LE();
+			l->shapeY[ii] = s->readSint16();
 	}
 
-	int len = s->readUint16LE();
+	int len = s->readUint16();
 	delete[] _levelDecorationRects;
 	_levelDecorationRects = new EoBRect8[len];
 	for (int i = 0; i < len; i++) {
 		EoBRect8 *l = &_levelDecorationRects[i];
-		l->x = s->readUint16LE();
-		l->y = s->readUint16LE();
-		l->w = s->readUint16LE();
-		l->h = s->readUint16LE();
+		l->x = s->readUint16();
+		l->y = s->readUint16();
+		l->w = s->readUint16();
+		l->h = s->readUint16();
 	}
 
 	delete s;
@@ -499,7 +489,12 @@ void EoBCoreEngine::assignWallsAndDecorations(int wallIndex, int vmpIndex, int d
 			if (r->w == 0 || r->h == 0)
 				error("Error trying to make decoration %d (x: %d, y: %d, w: %d, h: %d)", decIndex, r->x, r->y, r->w, r->h);
 
-			_levelDecorationShapes[t] = _screen->encodeShape(r->x, r->y, r->w, r->h, false, _cgaLevelMappingIndex ? _cgaMappingLevel[_cgaLevelMappingIndex[_currentLevel - 1]] : 0);
+			if (_flags.platform == Common::kPlatformSegaCD) {
+				_levelDecorationShapes[t] = _screen->sega_encodeShape(_dcrShpDataPos, r->w << 3, r->h, 0);
+				_dcrShpDataPos += ((r->w << 2) * r->h);
+			} else {
+				_levelDecorationShapes[t] = _screen->encodeShape(r->x, r->y, r->w, r->h, false, _cgaLevelMappingIndex ? _cgaMappingLevel[_cgaLevelMappingIndex[_currentLevel - 1]] : 0);
+			}
 		}
 
 		decIndex = _levelDecorationProperties[_mappedDecorationsCount++].next;
@@ -520,6 +515,7 @@ void EoBCoreEngine::releaseDecorations() {
 		}
 	}
 	_mappedDecorationsCount = 0;
+	_dcrShpDataPos = 0;
 }
 
 void EoBCoreEngine::releaseDoorShapes() {
@@ -577,7 +573,7 @@ void EoBCoreEngine::drawScene(int refresh) {
 	if (refresh && !_partyResting)
 		_screen->copyRegion(0, 0, 0, 0, 176, 120, 2, 0, Screen::CR_NO_P_CHECK);
 
-	updateEnvironmentalSfx(0);
+	snd_updateEnvironmentalSfx(0);
 
 	if (!_dialogueField && refresh && !_updateFlags)
 		gui_drawCompass(false);
@@ -642,7 +638,7 @@ void EoBCoreEngine::drawDecorations(int index) {
 			int16 d = *_dscWallMapping[s];
 			int8 l = _wllShapeMap[_visibleBlocks[index]->walls[d]];
 
-			uint8 *shapeData = 0;
+			const uint8 *shapeData = 0;
 
 			int x = 0;
 
diff --git a/engines/kyra/engine/scene_lol.cpp b/engines/kyra/engine/scene_lol.cpp
index 93ff588ece..01e5064214 100644
--- a/engines/kyra/engine/scene_lol.cpp
+++ b/engines/kyra/engine/scene_lol.cpp
@@ -1225,7 +1225,7 @@ void LoLEngine::drawScene(int pageNum) {
 		SWAP(_sceneDrawPage1, _sceneDrawPage2);
 	}
 
-	updateEnvironmentalSfx(0);
+	snd_updateEnvironmentalSfx(0);
 	gui_drawCompass();
 
 	_sceneUpdateRequired = false;
@@ -1452,7 +1452,7 @@ void LoLEngine::drawDecorations(int index) {
 		uint8 d = (_currentDirection + _dscWalls[s]) & 3;
 		int8 l = _wllShapeMap[_visibleBlocks[index]->walls[d]];
 
-		uint8 *shapeData = 0;
+		const uint8 *shapeData = 0;
 
 		int x = 0;
 		int y = 0;
diff --git a/engines/kyra/engine/scene_rpg.cpp b/engines/kyra/engine/scene_rpg.cpp
index 8dc8e184e7..a4a4d351f4 100644
--- a/engines/kyra/engine/scene_rpg.cpp
+++ b/engines/kyra/engine/scene_rpg.cpp
@@ -161,110 +161,110 @@ void KyraRpgEngine::generateBlockDrawingBuffer() {
 
 	uint8 t = _visibleBlocks[0]->walls[_sceneDrawVarRight];
 	if (t)
-		generateVmpTileData(-2, 3, t, 102, 3, 5);
+		generateVmpTileData(-2, 3, t, _vmpVisOffs[0], 3, 5);
 
 	t = _visibleBlocks[6]->walls[_sceneDrawVarLeft];
 	if (t)
-		generateVmpTileDataFlipped(21, 3, t, 102, 3, 5);
+		generateVmpTileDataFlipped(21, 3, t, _vmpVisOffs[0], 3, 5);
 
 	t = _visibleBlocks[1]->walls[_sceneDrawVarRight];
 	uint8 t2 = _visibleBlocks[2]->walls[_sceneDrawVarDown];
 
 	if (hasWall(t) && !(_wllWallFlags[t2] & 8))
-		generateVmpTileData(2, 3, t, 102, 3, 5);
+		generateVmpTileData(2, 3, t, _vmpVisOffs[0], 3, 5);
 	else if (t && (_wllWallFlags[t2] & 8))
-		generateVmpTileData(2, 3, t2, 102, 3, 5);
+		generateVmpTileData(2, 3, t2, _vmpVisOffs[0], 3, 5);
 
 	t = _visibleBlocks[5]->walls[_sceneDrawVarLeft];
 	t2 = _visibleBlocks[4]->walls[_sceneDrawVarDown];
 
 	if (hasWall(t) && !(_wllWallFlags[t2] & 8))
-		generateVmpTileDataFlipped(17, 3, t, 102, 3, 5);
+		generateVmpTileDataFlipped(17, 3, t, _vmpVisOffs[0], 3, 5);
 	else if (t && (_wllWallFlags[t2] & 8))
-		generateVmpTileDataFlipped(17, 3, t2, 102, 3, 5);
+		generateVmpTileDataFlipped(17, 3, t2, _vmpVisOffs[0], 3, 5);
 
 	t = _visibleBlocks[2]->walls[_sceneDrawVarRight];
 	if (t)
-		generateVmpTileData(8, 3, t, 97, 1, 5);
+		generateVmpTileData(8, 3, t, _vmpVisOffs[1], 1, 5);
 
 	t = _visibleBlocks[4]->walls[_sceneDrawVarLeft];
 	if (t)
-		generateVmpTileDataFlipped(13, 3, t, 97, 1, 5);
+		generateVmpTileDataFlipped(13, 3, t, _vmpVisOffs[1], 1, 5);
 
 	t = _visibleBlocks[1]->walls[_sceneDrawVarDown];
 	if (hasWall(t))
-		generateVmpTileData(-4, 3, t, 129, 6, 5);
+		generateVmpTileData(-4, 3, t, _vmpVisOffs[2], 6, 5);
 
 	t = _visibleBlocks[5]->walls[_sceneDrawVarDown];
 	if (hasWall(t))
-		generateVmpTileData(20, 3, t, 129, 6, 5);
+		generateVmpTileData(20, 3, t, _vmpVisOffs[2], 6, 5);
 
 	t = _visibleBlocks[2]->walls[_sceneDrawVarDown];
 	if (hasWall(t))
-		generateVmpTileData(2, 3, t, 129, 6, 5);
+		generateVmpTileData(2, 3, t, _vmpVisOffs[2], 6, 5);
 
 	t = _visibleBlocks[4]->walls[_sceneDrawVarDown];
 	if (hasWall(t))
-		generateVmpTileData(14, 3, t, 129, 6, 5);
+		generateVmpTileData(14, 3, t, _vmpVisOffs[2], 6, 5);
 
 	t = _visibleBlocks[3]->walls[_sceneDrawVarDown];
 	if (t)
-		generateVmpTileData(8, 3, t, 129, 6, 5);
+		generateVmpTileData(8, 3, t, _vmpVisOffs[2], 6, 5);
 
 	t = _visibleBlocks[7]->walls[_sceneDrawVarRight];
 	if (t)
-		generateVmpTileData(0, 3, t, 117, 2, 6);
+		generateVmpTileData(0, 3, t, _vmpVisOffs[3], 2, 6);
 
 	t = _visibleBlocks[11]->walls[_sceneDrawVarLeft];
 	if (t)
-		generateVmpTileDataFlipped(20, 3, t, 117, 2, 6);
+		generateVmpTileDataFlipped(20, 3, t, _vmpVisOffs[3], 2, 6);
 
 	t = _visibleBlocks[8]->walls[_sceneDrawVarRight];
 	if (t)
-		generateVmpTileData(6, 2, t, 81, 2, 8);
+		generateVmpTileData(6, 2, t, _vmpVisOffs[4], 2, 8);
 
 	t = _visibleBlocks[10]->walls[_sceneDrawVarLeft];
 	if (t)
-		generateVmpTileDataFlipped(14, 2, t, 81, 2, 8);
+		generateVmpTileDataFlipped(14, 2, t, _vmpVisOffs[4], 2, 8);
 
 	t = _visibleBlocks[8]->walls[_sceneDrawVarDown];
 	if (hasWall(t))
-		generateVmpTileData(-4, 2, t, 159, 10, 8);
+		generateVmpTileData(-4, 2, t, _vmpVisOffs[5], 10, 8);
 
 	t = _visibleBlocks[10]->walls[_sceneDrawVarDown];
 	if (hasWall(t))
-		generateVmpTileData(16, 2, t, 159, 10, 8);
+		generateVmpTileData(16, 2, t, _vmpVisOffs[5], 10, 8);
 
 	t = _visibleBlocks[9]->walls[_sceneDrawVarDown];
 	if (t)
-		generateVmpTileData(6, 2, t, 159, 10, 8);
+		generateVmpTileData(6, 2, t, _vmpVisOffs[5], 10, 8);
 
 	t = _visibleBlocks[12]->walls[_sceneDrawVarRight];
 	if (t)
-		generateVmpTileData(3, 1, t, 45, 3, 12);
+		generateVmpTileData(3, 1, t, _vmpVisOffs[6], 3, 12);
 
 	t = _visibleBlocks[14]->walls[_sceneDrawVarLeft];
 	if (t)
-		generateVmpTileDataFlipped(16, 1, t, 45, 3, 12);
+		generateVmpTileDataFlipped(16, 1, t, _vmpVisOffs[6], 3, 12);
 
 	t = _visibleBlocks[12]->walls[_sceneDrawVarDown];
 	if (!(_wllWallFlags[t] & 8))
-		generateVmpTileData(-13, 1, t, 239, 16, 12);
+		generateVmpTileData(-13, 1, t, _vmpVisOffs[7], 16, 12);
 
 	t = _visibleBlocks[14]->walls[_sceneDrawVarDown];
 	if (!(_wllWallFlags[t] & 8))
-		generateVmpTileData(19, 1, t, 239, 16, 12);
+		generateVmpTileData(19, 1, t, _vmpVisOffs[7], 16, 12);
 
 	t = _visibleBlocks[13]->walls[_sceneDrawVarDown];
 	if (t)
-		generateVmpTileData(3, 1, t, 239, 16, 12);
+		generateVmpTileData(3, 1, t, _vmpVisOffs[7], 16, 12);
 
 	t = _visibleBlocks[15]->walls[_sceneDrawVarRight];
 	t2 = _visibleBlocks[17]->walls[_sceneDrawVarLeft];
 	if (t)
-		generateVmpTileData(0, 0, t, 0, 3, 15);
+		generateVmpTileData(0, 0, t, _vmpVisOffs[8], 3, 15);
 	if (t2)
-		generateVmpTileDataFlipped(19, 0, t2, 0, 3, 15);
+		generateVmpTileDataFlipped(19, 0, t2, _vmpVisOffs[8], 3, 15);
 }
 
 void KyraRpgEngine::generateVmpTileData(int16 startBlockX, uint8 startBlockY, uint8 vmpMapIndex, int16 vmpOffset, uint8 numBlocksX, uint8 numBlocksY) {
@@ -288,24 +288,20 @@ void KyraRpgEngine::generateVmpTileDataFlipped(int16 startBlockX, uint8 startBlo
 	if (!_wllVmpMap[vmpMapIndex])
 		return;
 
-	uint16 *vmp = &_vmpPtr[(_wllVmpMap[vmpMapIndex] - 1) * 431 + vmpOffset + 330];
+	uint16 *vmp = &_vmpPtr[(_wllVmpMap[vmpMapIndex] - 1) * 431 + vmpOffset + 330] + (numBlocksX - 1);
+	uint16 *bl = &_blockDrawingBuffer[startBlockY * 22 + startBlockX];
 
 	for (int i = 0; i < numBlocksY; i++) {
+		uint16 *bl2 = bl;
+		uint16 *vmpb = vmp;
 		for (int ii = 0; ii < numBlocksX; ii++) {
-			if ((startBlockX + ii) < 0 || (startBlockX + ii) > 21)
-				continue;
-
-			uint16 v = vmp[i * numBlocksX + (numBlocksX - 1 - ii)];
-			if (!v)
-				continue;
-
-			if (v & 0x4000)
-				v -= 0x4000;
-			else
-				v |= 0x4000;
-
-			_blockDrawingBuffer[(startBlockY + i) * 22 + startBlockX + ii] = v;
+			if ((startBlockX + ii >= 0) && (startBlockX + ii < 22) && *vmp)
+				*bl = (*vmp & 0x4000) ? *vmp - 0x4000 : *vmp | 0x4000;
+			bl++;
+			vmp--;
 		}
+		bl = bl2 + 22;
+		vmp = vmpb + numBlocksX;
 	}
 }
 
@@ -319,7 +315,6 @@ void KyraRpgEngine::assignVisibleBlocks(int block, int direction) {
 	for (int i = 0; i < 18; i++) {
 		uint16 t = (block + _dscBlockIndex[direction * 18 + i]) & 0x3FF;
 		_visibleBlockIndex[i] = t;
-
 		_visibleBlocks[i] = &_levelBlockProperties[t];
 		_lvlShapeLeftRight[i] = _lvlShapeLeftRight[18 + i] = -1;
 	}
@@ -686,9 +681,9 @@ void KyraRpgEngine::openCloseDoor(int block, int openClose) {
 			if (_flags.gameID == GI_LOL) {
 				snd_processEnvironmentalSoundEffect(snd + 28, _currentBlock);
 				if (!checkSceneUpdateNeed(block))
-					updateEnvironmentalSfx(0);
+					snd_updateEnvironmentalSfx(0);
 			} else {
-				updateEnvironmentalSfx(snd);
+				snd_updateEnvironmentalSfx(snd);
 			}
 		}
 
diff --git a/engines/kyra/engine/sprites_eob.cpp b/engines/kyra/engine/sprites_eob.cpp
index 845d9d233f..0e456a21d1 100644
--- a/engines/kyra/engine/sprites_eob.cpp
+++ b/engines/kyra/engine/sprites_eob.cpp
@@ -33,40 +33,21 @@
 namespace Kyra {
 
 void EoBCoreEngine::loadMonsterShapes(const char *filename, int monsterIndex, bool hasDecorations, int encodeTableIndex) {
-	if (_flags.platform == Common::kPlatformFMTowns) {
-		Common::String tmp = Common::String::format("%s.MNT", filename);
-		Common::SeekableReadStream *s = _res->createReadStream(tmp);
-		if (!s)
-			error("Screen_EoB::loadMonsterShapes(): Failed to load file '%s'", tmp.c_str());
-
-		for (int i = 0; i < 6; i++)
-			_monsterShapes[monsterIndex + i] = loadTownsShape(s);
-
-		for (int i = 0; i < 6; i++) {
-			for (int ii = 0; ii < 2; ii++)
-				s->read(_monsterPalettes[(monsterIndex >= 18 ? i + 6 : i) * 2 + ii], 16);
-		}
+	_screen->loadShapeSetBitmap(filename, 3, 3);
+	const uint16 *enc = &_encodeMonsterShpTable[encodeTableIndex << 2];
 
-		if (hasDecorations)
-			loadMonsterDecoration(s, monsterIndex);
+	for (int i = 0; i < 6; i++, enc += 4)
+		_monsterShapes[monsterIndex + i] = _screen->encodeShape(enc[0], enc[1], enc[2], enc[3], false, _cgaMappingDefault);
 
-		delete s;
-	} else {
-		_screen->loadShapeSetBitmap(filename, 3, 3);
-		const uint16 *enc = &_encodeMonsterShpTable[encodeTableIndex << 2];
+	generateMonsterPalettes(filename, monsterIndex);
 
-		for (int i = 0; i < 6; i++, enc += 4)
-			_monsterShapes[monsterIndex + i] = _screen->encodeShape(enc[0], enc[1], enc[2], enc[3], false, _cgaMappingDefault);
-
-		generateMonsterPalettes(filename, monsterIndex);
-
-		if (hasDecorations) {
-			Common::SeekableReadStream *s = _res->createReadStream(Common::String::format("%s.DCR", filename));
-			if (s)
-				loadMonsterDecoration(s, monsterIndex);
-			delete s;
-		}
+	if (hasDecorations) {
+		Common::SeekableReadStream *s = _res->createReadStream(Common::String::format("%s.DCR", filename));
+		if (s)
+			loadMonsterDecoration(s, monsterIndex);
+		delete s;
 	}
+
 	_screen->_curPage = 0;
 }
 
@@ -79,15 +60,6 @@ void EoBCoreEngine::releaseMonsterShapes(int first, int num) {
 	}
 }
 
-uint8 *EoBCoreEngine::loadTownsShape(Common::SeekableReadStream *stream) {
-	uint32 size = stream->readUint32LE();
-	uint8 *shape= new uint8[size];
-	stream->read(shape, size);
-	if (shape[0] == 1)
-		shape[0]++;
-	return shape;
-}
-
 const uint8 *EoBCoreEngine::loadActiveMonsterData(const uint8 *data, int level) {
 	for (uint8 p = *data++; p != 0xFF; p = *data++) {
 		uint8 v = *data++;
@@ -362,7 +334,7 @@ void EoBCoreEngine::drawBlockItems(int index) {
 	uint8 w = _visibleBlocks[index]->walls[_sceneDrawVarDown];
 	uint8 flg = (index == 16) ? 0x80 : _wllWallFlags[w];
 
-	if (_wllVmpMap[w] && !(flg & 0x80))
+	//if (_wllVmpMap[w] && !(flg & 0x80))
 		return;
 
 	uint16 o2 = o = _items[o].next;
@@ -488,6 +460,19 @@ void EoBCoreEngine::drawMonsters(int index) {
 		int shpIndex = d->shpIndex ? 18 : 0;
 		int palIndex = d->palette ? ((((shpIndex == 18) ? subFrame + 5 : subFrame - 1) << 1) + (d->palette - 1)) : -1;
 
+		if (_flags.platform == Common::kPlatformSegaCD) {
+			if (d->curAttackFrame == -1)
+				subFrame = 5;
+			else if (f == 3)
+				subFrame = 2;
+			else if (f == -3)
+				subFrame = 4;
+			else if (f == 4)
+				subFrame = 3;
+			else if (f == -4)
+				subFrame = -3;
+		}
+
 		const uint8 *shp = _screen->scaleShape(_monsterShapes[subFrame + shpIndex - 1], blockDistance);
 
 		int v30 = (subFrame == 1 || subFrame > 3) ? 1 : 0;
@@ -1025,17 +1010,17 @@ bool EoBCoreEngine::updateMonsterTryCloseAttack(EoBMonsterInPlay *m, int block)
 		if (facing) {
 			disableSysTimer(2);
 			if (m->type == 4)
-				updateEnvironmentalSfx(_monsterProps[m->type].sound1);
+				snd_updateEnvironmentalSfx(_monsterProps[m->type].sound1);
 			m->curAttackFrame = -2;
 			_flashShapeTimer = 0;
 			drawScene(1);
 			m->curAttackFrame = -1;
 			if (m->type != 4)
-				updateEnvironmentalSfx(_monsterProps[m->type].sound1);
+				snd_updateEnvironmentalSfx(_monsterProps[m->type].sound1);
 			_flashShapeTimer = _system->getMillis() + 8 * _tickLength;
 			drawScene(1);
 		} else {
-			updateEnvironmentalSfx(_monsterProps[m->type].sound1);
+			snd_updateEnvironmentalSfx(_monsterProps[m->type].sound1);
 		}
 
 		monsterCloseAttack(m);
diff --git a/engines/kyra/engine/timer_eob.cpp b/engines/kyra/engine/timer_eob.cpp
index f12f986152..c14ee5f09e 100644
--- a/engines/kyra/engine/timer_eob.cpp
+++ b/engines/kyra/engine/timer_eob.cpp
@@ -305,7 +305,7 @@ void EoBCoreEngine::timerSpecialCharacterUpdate(int timerNum) {
 		}
 
 		int od = _screen->curDimIndex();
-		Screen::FontId of = _screen->setFont(_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT);
+		Screen::FontId of = _screen->setFont(_conFont);
 		_screen->setScreenDim(7);
 
 		switch (evt) {
diff --git a/engines/kyra/engine/timer_rpg.cpp b/engines/kyra/engine/timer_rpg.cpp
index 572829eb64..3ad3947d80 100644
--- a/engines/kyra/engine/timer_rpg.cpp
+++ b/engines/kyra/engine/timer_rpg.cpp
@@ -73,11 +73,11 @@ void KyraRpgEngine::timerProcessDoors(int timerNum) {
 			if (!(_updateFlags & 1)) {
 				snd_processEnvironmentalSoundEffect(snd + 28, b);
 				if (!checkSceneUpdateNeed(b))
-					updateEnvironmentalSfx(0);
+					snd_updateEnvironmentalSfx(0);
 			}
 		} else {
 			checkSceneUpdateNeed(b);
-			updateEnvironmentalSfx(snd);
+			snd_updateEnvironmentalSfx(snd);
 		}
 
 		if (flg & 0x30)
diff --git a/engines/kyra/graphics/screen.cpp b/engines/kyra/graphics/screen.cpp
index d8ca112e1d..460e7bbe89 100644
--- a/engines/kyra/graphics/screen.cpp
+++ b/engines/kyra/graphics/screen.cpp
@@ -63,7 +63,7 @@ Screen::Screen(KyraEngine_v1 *vm, OSystem *system, const ScreenDim *dimTable, co
 	_textRenderBufferSize = 0;
 
 	_useHiColorScreen = _vm->gameFlags().useHiColorMode;
-	_use256ColorMode = true;
+	_useShapeShading = true;
 	_screenPageSize = SCREEN_PAGE_SIZE;
 	_16bitPalette = 0;
 	_16bitConversionPalette = 0;
@@ -194,7 +194,6 @@ bool Screen::init() {
 	// We allow 256 color palettes in EGA mode, since original EOB II code does the same and requires it
 	const int numColors = _use16ColorMode ? 16 : (_isAmiga ? 32 : (_renderMode == Common::kRenderCGA ? 4 : 256));
 	const int numColorsInternal = _useAmigaExtraColors ? 64 : numColors;
-	_use256ColorMode = (_bytesPerPixel != 2 && !_isAmiga && !_use16ColorMode && _renderMode != Common::kRenderCGA && _renderMode != Common::kRenderEGA);
 
 	_dualPaletteModeSplitY = 0;
 
diff --git a/engines/kyra/graphics/screen.h b/engines/kyra/graphics/screen.h
index 871256a4f8..71dd24635a 100644
--- a/engines/kyra/graphics/screen.h
+++ b/engines/kyra/graphics/screen.h
@@ -624,7 +624,7 @@ protected:
 	bool _useOverlays;
 	bool _useSJIS;
 	bool _use16ColorMode;
-	bool _use256ColorMode;
+	bool _useShapeShading;
 	bool _4bitPixelPacking;
 	bool _useHiResEGADithering;
 	bool _useHiColorScreen;
diff --git a/engines/kyra/graphics/screen_eob.cpp b/engines/kyra/graphics/screen_eob.cpp
index 43e59b0329..29d82e2333 100644
--- a/engines/kyra/graphics/screen_eob.cpp
+++ b/engines/kyra/graphics/screen_eob.cpp
@@ -131,6 +131,8 @@ bool Screen_EoB::init() {
 			memset(_segaCustomPalettes, 0, 128 * sizeof(uint16));
 		}
 
+		_useShapeShading = (_bytesPerPixel != 2 && !_isAmiga && !_isSegaCD && !_use16ColorMode && _renderMode != Common::kRenderCGA && _renderMode != Common::kRenderEGA) || _useHiResEGADithering;
+
 		static const char *cpsExt[] = { "CPS", "EGA", "SHP", "BIN" };
 		int ci = 0;
 		if (_vm->game() == GI_EOB1) {
@@ -1581,7 +1583,7 @@ void Screen_EoB::drawShapeSetPixel(uint8 *dst, uint8 col) {
 	if (_bytesPerPixel == 2) {
 		*(uint16*)dst = _16bitPalette[(_dsShapeFadingLevel << 8) + col];
 		return;
-	} else if (_use256ColorMode || _useHiResEGADithering) {
+	} else if (_useShapeShading) {
 		if (_dsBackgroundFading) {
 			if (_dsShapeFadingLevel) {
 				col = *dst;
diff --git a/engines/kyra/graphics/screen_eob.h b/engines/kyra/graphics/screen_eob.h
index 85ea35c44b..4f252c0d5e 100644
--- a/engines/kyra/graphics/screen_eob.h
+++ b/engines/kyra/graphics/screen_eob.h
@@ -132,7 +132,8 @@ public:
 	void sega_loadTextBufferToVRAM(uint16 srcOffset, uint16 addr, int size);
 	void sega_gfxScale(uint8 *out, uint16 w, uint16 h, uint16 pitch, const uint8 *in, const uint16 *stampMap, const uint16 *traceVectors);
 	void sega_drawClippedLine(uint8 *dst, int pW, int pH, int x, int y, int w, int h, uint8 color);
-	void sega_encodeSpriteShapes(const uint8 **dst, const uint8 *src, int numShapes, int w, int h, int pal);
+	uint8 *sega_encodeShape(const uint8 *src, int w, int h, int pal);
+	void sega_encodeShapesFromSprites(const uint8 **dst, const uint8 *src, int numShapes, int w, int h, int pal);
 
 	SegaRenderer *sega_getRenderer() const { return _segaRenderer; }
 	SegaAnimator *sega_getAnimator() const { return _segaAnimator; }
diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index 3dff8a09e9..8e3be9ee01 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -225,10 +225,7 @@ void Screen_EoB::sega_loadTextBufferToVRAM(uint16 srcOffset, uint16 addr, int si
 }
 
 void Screen_EoB::sega_gfxScale(uint8 *out, uint16 w, uint16 h, uint16 pitch, const uint8 *in, const uint16 *stampMap, const uint16 *traceVectors) {
-	/* Implement only the required functions:
-	 - no support for stamp size other than 0
-	 - no support for rotation/flipping
-	 */
+	// Implement only the required functions. No support for stamp size other than 0 or for rotation/flipping.
 	while (h--) {
 		uint32 xt = *traceVectors++ << 8;
 		uint32 yt = *traceVectors++ << 8;
@@ -292,12 +289,37 @@ void Screen_EoB::sega_drawClippedLine(uint8 *dst, int pW, int pH, int x, int y,
 	}
 }
 
-void Screen_EoB::sega_encodeSpriteShapes(const uint8 **dst, const uint8 *src, int numShapes, int w, int h, int pal) {
+uint8 *Screen_EoB::sega_encodeShape(const uint8 *src, int w, int h, int pal) {
+	uint8 *shp = new uint8[(w >> 1) * h + 20];
+	uint8 *dst = shp;
+	*dst++ = 2;
+	*dst++ = h;
+	*dst++ = w >> 3;
+	*dst++ = h;
+	*dst++ = 0;
+
+	for (int i = 1; i < 16; i++)
+		*dst++ = (pal << 4) | i;
+
+	const uint8 *pos = src;
+	for (int i = 0; i < h; ++i) {
+		const uint8 *pos2 = pos;
+		for (int ii = 0; ii < (w >> 1); ++ii) {
+			*dst++ = *pos;
+			pos += h;
+		}
+		pos = pos2 + 1;
+	}
+
+	return shp;
+}
+
+void Screen_EoB::sega_encodeShapesFromSprites(const uint8 **dst, const uint8 *src, int numShapes, int w, int h, int pal) {
 	int spriteSize = (w * h) >> 1;
 	_segaRenderer->loadToVRAM(src, numShapes * spriteSize, 0);
 	int hw = (((h >> 3) - 1) << 2) | ((w >> 3) - 1);
 
-	int cp = setCurPage(2);
+	int cp = setCurPage(7);
 
 	for (int l = 0, s = 0; s < numShapes; l = s) {
 		for (int i = s; i < numShapes; ++i) {
@@ -307,12 +329,12 @@ void Screen_EoB::sega_encodeSpriteShapes(const uint8 **dst, const uint8 *src, in
 		}
 		
 		_segaAnimator->update();
-		_segaRenderer->render(2, true);
+		_segaRenderer->render(7, 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);
 
-		clearPage(2);
+		clearPage(7);
 	}
 
 	_segaAnimator->clearSprites();
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 7bfd8c1f62..73932bcf49 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -118,14 +118,14 @@ void EoBCoreEngine::gui_drawCharPortraitWithStats(int index) {
 		_screen->copyRegion(240, 168, x2, y2 + 24, 64, 26, 2, 2, Screen::CR_NO_P_CHECK);
 		int cp = _screen->setCurPage(2);
 
-		Screen::FontId cf = _screen->setFont(_flags.use16ColorMode ? Screen::FID_SJIS_SMALL_FNT : Screen::FID_6_FNT);
+		Screen::FontId cf = _screen->setFont(_invFont1);
 
 		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->setFont(Screen::FID_6_FNT);
+		_screen->setFont(_invFont2);
 
 		gui_drawFaceShape(index);
 		gui_drawWeaponSlot(index, 0);
@@ -152,9 +152,9 @@ void EoBCoreEngine::gui_drawCharPortraitWithStats(int index) {
 		_screen->copyRegion(176, 0, 0, 0, 144, 168, 2, 2, Screen::CR_NO_P_CHECK);
 		_screen->_curPage = 2;
 		gui_drawFaceShape(index);
-		Screen::FontId cf = _screen->setFont(_flags.use16ColorMode ? Screen::FID_SJIS_SMALL_FNT : Screen::FID_6_FNT);
+		Screen::FontId cf = _screen->setFont(_invFont1);
 		_screen->printShadedText(c->name, 219, 6, txtCol2, 0, guiSettings()->colors.guiColorBlack);
-		_screen->setFont(Screen::FID_6_FNT);
+		_screen->setFont(_invFont2);
 		gui_drawHitpoints(index);
 		gui_drawFoodStatusGraph(index);
 
@@ -162,7 +162,7 @@ void EoBCoreEngine::gui_drawCharPortraitWithStats(int index) {
 			int statusTxtY = 158;
 			if (_flags.lang == Common::JA_JPN) {
 				statusTxtY = 157;
-				_screen->setFont(_flags.platform == Common::kPlatformFMTowns ? Screen::FID_8_FNT : Screen::FID_SJIS_FNT);
+				_screen->setFont(_invFont3);
 			}
 
 			if (c->hitPointsCur == -10)
@@ -178,7 +178,7 @@ void EoBCoreEngine::gui_drawCharPortraitWithStats(int index) {
 			else if (c->flags & 8)
 				_screen->printShadedText(_characterGuiStringsSt[6], 232, statusTxtY, guiSettings()->colors.guiColorLightRed, 0, guiSettings()->colors.guiColorBlack);
 
-			_screen->setFont(Screen::FID_6_FNT);
+			_screen->setFont(_invFont2);
 
 			for (int i = 0; i < 27; i++)
 				gui_drawInventoryItem(i, 0, 2);
@@ -430,10 +430,9 @@ void EoBCoreEngine::gui_drawHitpoints(int index) {
 		if (bgCur <= 10)
 			col = guiSettings()->colors.guiColorDarkRed;
 
-		if (!_currentControlMode)
+		if (!_currentControlMode && _flags.platform != Common::kPlatformSegaCD)
 			_screen->printText(_characterGuiStringsHp[0], x - 13, y - 1, guiSettings()->colors.guiColorBlack, 0);
 
-
 		gui_drawHorizontalBarGraph(x, y, w, h, bgCur, bgMax, col, guiSettings()->colors.barGraph);
 
 	} else {
@@ -582,7 +581,7 @@ void EoBCoreEngine::gui_drawInventoryItem(int slot, int redraw, int pageNum) {
 }
 
 void EoBCoreEngine::gui_drawCompass(bool force) {
-	if (_currentDirection == _compassDirection && !force)
+	//if (_currentDirection == _compassDirection && !force)
 		return;
 
 	static const uint8 shpX[2][3] = { { 0x70, 0x4D, 0x95 }, { 0x72, 0x4F, 0x97 } };
diff --git a/engines/kyra/gui/saveload_eob.cpp b/engines/kyra/gui/saveload_eob.cpp
index 1dc726fdc1..4598919623 100644
--- a/engines/kyra/gui/saveload_eob.cpp
+++ b/engines/kyra/gui/saveload_eob.cpp
@@ -846,7 +846,7 @@ Common::String EoBCoreEngine::readOriginalSaveFile(Common::String &file) {
 			_screen->decodeFrame4(cmpData, l->wallsXorData, 4096);
 		}
 		_curBlockFile = getBlockFileName(i + 1, 0);
-		const uint8 *p = getBlockFileData();
+		const uint8 *p = getBlockFileData(i + 1);
 		uint16 len = READ_LE_UINT16(p + 4);
 		p += 6;
 
@@ -1261,7 +1261,7 @@ bool EoBCoreEngine::saveAsOriginalSaveFile(int slot) {
 
 		Common::String curBlockFile = _curBlockFile;
 		_curBlockFile = getBlockFileName(i + 1, 0);
-		const uint8 *p = getBlockFileData();
+		const uint8 *p = getBlockFileData(i + 1);
 		_curBlockFile = curBlockFile;
 		uint16 len = READ_LE_UINT16(p + 4);
 		p += 6;
diff --git a/engines/kyra/resource/resource.cpp b/engines/kyra/resource/resource.cpp
index 1423a6412c..86afa6d1c3 100644
--- a/engines/kyra/resource/resource.cpp
+++ b/engines/kyra/resource/resource.cpp
@@ -322,9 +322,9 @@ Common::SeekableReadStream *Resource::createReadStream(const Common::String &fil
 	return _files.createReadStreamForMember(file);
 }
 
-Common::SeekableReadStreamEndian *Resource::createEndianAwareReadStream(const Common::String &file) {
+Common::SeekableReadStreamEndian *Resource::createEndianAwareReadStream(const Common::String &file, int endianness) {
 	Common::SeekableReadStream *stream = _files.createReadStreamForMember(file);
-	return stream ? new EndianAwareStreamWrapper(stream, _bigEndianPlatForm) : 0;
+	return stream ? new EndianAwareStreamWrapper(stream, (endianness == kForceBE) ? true : (endianness == kForceLE ? false : _bigEndianPlatForm)) : 0;
 }
 
 Common::Archive *Resource::loadArchive(const Common::String &name, Common::ArchiveMemberPtr member) {
diff --git a/engines/kyra/resource/resource.h b/engines/kyra/resource/resource.h
index 1bf5a313ee..dfd7ac9fd7 100644
--- a/engines/kyra/resource/resource.h
+++ b/engines/kyra/resource/resource.h
@@ -74,7 +74,14 @@ public:
 	uint32 getFileSize(const char *file);
 	uint8 *fileData(const char *file, uint32 *size);
 	Common::SeekableReadStream *createReadStream(const Common::String &file);
-	Common::SeekableReadStreamEndian *createEndianAwareReadStream(const Common::String &file);
+
+	enum Endianness {
+		kPlatformEndianness = 0,
+		kForceLE,
+		kForceBE
+	};
+
+	Common::SeekableReadStreamEndian *createEndianAwareReadStream(const Common::String &file, int endianness = kPlatformEndianness);
 
 	bool loadFileToBuf(const char *file, void *buf, uint32 maxSize);
 protected:
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index 44787e2aae..b45b3a69e0 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -1241,6 +1241,31 @@ void EoBEngine::initStaticResource() {
 		_sound->initAudioResourceInfo(kMusicFinale, &finale);
 	}
 
+	// Build offset tables for door shapes encoding
+	if (_flags.platform == Common::kPlatformSegaCD) {
+		const uint8 *shp = _screen->getCPagePtr(2);
+		const uint8 *e1 = _doorShapeEncodeDefs;
+		const uint8 *e2 = _doorSwitchShapeEncodeDefs;
+		const uint8 **doorShapesSrc = new const uint8*[30];
+		const uint8 **doorSwitchShapesSrc = new const uint8*[30];
+
+		for (int i = 0; i < 5; ++i) {
+			for (int ii = 0; ii < 6; ++ii) {
+				doorShapesSrc[i * 6 + ii] = shp;
+				shp += ((e1[0] * e1[1]) << 5);
+				e1 += 4;
+			}
+			for (int ii = 0; ii < 3; ++ii) {
+				doorSwitchShapesSrc[i * 6 + ii] = doorSwitchShapesSrc[i * 6 + 3 + ii] = shp;
+				shp += ((e2[0] * e2[1]) << 5);
+				e2 += 4;
+			}
+		}
+
+		_doorShapesSrc = doorShapesSrc;
+		_doorSwitchShapesSrc = doorSwitchShapesSrc;
+	}
+
 	_monsterAcHitChanceTable1 = _monsterAcHitChanceTbl1;
 	_monsterAcHitChanceTable2 = _monsterAcHitChanceTbl2;
 
diff --git a/engines/kyra/resource/staticres_rpg.cpp b/engines/kyra/resource/staticres_rpg.cpp
index d0156e3340..0c7b536fa4 100644
--- a/engines/kyra/resource/staticres_rpg.cpp
+++ b/engines/kyra/resource/staticres_rpg.cpp
@@ -74,6 +74,10 @@ void StaticResource::freeRawDataBe32(void *&ptr, int &size) {
 
 const uint8 KyraRpgEngine::_dropItemDirIndex[] = { 0, 1, 2, 3, 1, 3, 0, 2, 3, 2, 1, 0, 2, 0, 3, 1 };
 
+const uint16 KyraRpgEngine::_vmpOffsetsDefault[9] = { 102, 97, 129, 117, 81, 159, 45, 239, 0 };
+
+const uint16 KyraRpgEngine::_vmpOffsetsSegaCD[9] = { 0, 15, 20, 50, 62, 78, 158, 194, 386 };
+
 void KyraRpgEngine::initStaticResource() {
 	int temp;
 	_dscShapeX = (const int16 *)_staticres->loadRawDataBe16(kRpgCommonDscX, temp);
diff --git a/engines/kyra/sequence/sequences_eob.cpp b/engines/kyra/sequence/sequences_eob.cpp
index 535fa457b4..d83cc5f63f 100644
--- a/engines/kyra/sequence/sequences_eob.cpp
+++ b/engines/kyra/sequence/sequences_eob.cpp
@@ -2272,8 +2272,7 @@ void EoBEngine::seq_playIntro(int part) {
 		if (part == kOnlyCredits)
 			seq_segaOpeningCredits();
 		else
-			seq_playFinale();
-			//seq_segaPlaySequence(53, true);
+			seq_segaPlaySequence(53, true);
 	} else {
 		EoBIntroPlayer(this, _screen).start((EoBIntroPlayer::IntroPart)part);
 	}


Commit: b59d2018738800647fecf1b3e24641b7c25f1dd9
    https://github.com/scummvm/scummvm/commit/b59d2018738800647fecf1b3e24641b7c25f1dd9
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:12+02:00

Commit Message:
KYRA: (EOB/SegaCD) - fix decorations and monster animations

Changed paths:
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eob.h
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h
    engines/kyra/engine/scene_eob.cpp
    engines/kyra/engine/sprites_eob.cpp
    engines/kyra/graphics/screen_eob.h
    engines/kyra/graphics/screen_eob_segacd.cpp
    engines/kyra/gui/saveload_eob.cpp


diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index 9bd9264c45..11388f1d40 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -56,6 +56,7 @@ EoBEngine::EoBEngine(OSystem *system, const GameFlags &flags)
 	_seqPlayer = 0;
 	_sres = 0;
 	_levelCurTrack = 0;
+	_dcrResCur = -1;
 }
 
 EoBEngine::~EoBEngine() {
@@ -448,7 +449,7 @@ void EoBEngine::loadMonsterShapes(const char *filename, int monsterIndex, bool h
 	assert(size <= 18);
 
 	for (int i = 0; i < size; i++) {
-		_monsterShapes[monsterIndex + i] = _screen->sega_encodeShape(pos, enc[0], enc[1], 2);
+		_monsterShapes[monsterIndex + i] = _screen->sega_convertShape(pos, enc[0], enc[1], 2);
 		pos += ((enc[0] * enc[1]) >> 1);
 		enc += 2;
 	}
@@ -563,6 +564,7 @@ const uint8 *EoBEngine::getBlockFileData(int level) {
 	Common::SeekableReadStream *s = _sres->resStream(6);
 	_screen->loadFileDataToPage(s, 15, s->size());
 	delete s;
+	_dcrResCur = -1;
 	return _screen->getCPagePtr(15);
 }
 
@@ -574,13 +576,18 @@ Common::SeekableReadStreamEndian *EoBEngine::getDecDefinitions(const char *decFi
 }
 
 void EoBEngine::loadDecShapesToPage3(const char *shpFile) {
-	if (_flags.platform != Common::kPlatformSegaCD)
-		return EoBCoreEngine::loadDecShapesToPage3(shpFile);
-	_sres->loadContainer(Common::String::format("L%d", _currentLevel));
-	Common::SeekableReadStream *s = _sres->resStream(2);
-	_screen->loadFileDataToPage(s, 3, s->size());
-	_dcrShpDataPos = _screen->getCPagePtr(3);
-	delete s;
+	if (_flags.platform != Common::kPlatformSegaCD) {
+		EoBCoreEngine::loadDecShapesToPage3(shpFile);
+		return;
+	}
+	if (_dcrResCur != _currentLevel) {
+		_sres->loadContainer(Common::String::format("L%d", _currentLevel));
+		Common::SeekableReadStream *s = _sres->resStream(2);
+		_screen->loadFileDataToPage(s, 3, s->size());
+		_dcrShpDataPos = _screen->getCPagePtr(3);
+		_dcrResCur = _currentLevel;
+		delete s;
+	}
 }
 
 void EoBEngine::loadDoorShapes(int doorType1, int shapeId1, int doorType2, int shapeId2) {
@@ -606,9 +613,9 @@ void EoBEngine::loadDoorShapes(int doorType1, int shapeId1, int doorType2, int s
 			if (_flags.platform == Common::kPlatformSegaCD) {
 				int offs = lvlIndex[_currentLevel] * 6 + shapeId[a] + i;
 				const uint8 *enc = &_doorShapeEncodeDefs[offs << 2];
-				_doorShapes[shapeId[a] + i] = _screen->sega_encodeShape(_doorShapesSrc[offs], enc[0] << 3, enc[1] << 3, 0);
+				_doorShapes[shapeId[a] + i] = _screen->sega_convertShape(_doorShapesSrc[offs], enc[0] << 3, enc[1] << 3, 0);
 				enc = &_doorSwitchShapeEncodeDefs[(offs << 2) - shapeId[a]];
-				_doorSwitches[shapeId[a] + i].shp = _screen->sega_encodeShape(_doorSwitchShapesSrc[offs], enc[0] << 3, enc[1] << 3, 0);
+				_doorSwitches[shapeId[a] + i].shp = _screen->sega_convertShape(_doorSwitchShapesSrc[offs], enc[0] << 3, enc[1] << 3, 0);
 			} else {
 				const uint8 *enc = &_doorShapeEncodeDefs[(doorType[a] * 3 + i) << 2];
 				_doorShapes[shapeId[a] + i] = _screen->encodeShape(enc[0], enc[1], enc[2], enc[3], false, _cgaLevelMappingIndex ? _cgaMappingLevel[_cgaLevelMappingIndex[_currentLevel - 1]] : 0);
diff --git a/engines/kyra/engine/eob.h b/engines/kyra/engine/eob.h
index d8fbefa8d9..691d1c4104 100644
--- a/engines/kyra/engine/eob.h
+++ b/engines/kyra/engine/eob.h
@@ -155,6 +155,8 @@ private:
 	const uint8 *const *_doorShapesSrc;
 	const uint8 *const *_doorSwitchShapesSrc;
 
+	int _dcrResCur;
+
 	// Fight
 	static const uint8 _monsterAcHitChanceTbl1[];
 	static const uint8 _monsterAcHitChanceTbl2[];
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 1b3a3b58c4..8fb273494b 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -56,8 +56,10 @@ EoBCoreEngine::EoBCoreEngine(OSystem *system, const GameFlags &flags) : KyraRpgE
 
 	_enableHiResDithering = false;
 
+	_tickLength = 55;
 	_envAudioTimer = 0;
 	_flashShapeTimer = 0;
+	_flashShapeTimerIntv = (_flags.platform == Common::kPlatformSegaCD ? 16 : _tickLength) * 8;
 	_drawSceneTimer = 0;
 	_vcnFilePattern = "%s.VCN";
 	_vmpFilePattern = "%s.VMP";
@@ -372,10 +374,6 @@ Common::KeymapArray EoBCoreEngine::initKeymaps(const Common::String &gameId) {
 }
 
 Common::Error EoBCoreEngine::init() {
-	// In EOB the timer proc is directly invoked via interrupt 0x1C, 18.2 times per second.
-	// This makes a tick length of 54.94.
-	_tickLength = 55;
-
 	if (ConfMan.hasKey("render_mode"))
 		_configRenderMode = Common::parseRenderMode(ConfMan.get("render_mode"));
 
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index a49cad5161..81833c0fc7 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -202,6 +202,8 @@ struct EoBMonsterInPlay {
 	int8 mode;
 	int8 stray;
 	int8 curAttackFrame;
+	uint8 animProgress;
+	uint8 animType;
 	int8 spellStatusLeft;
 	int16 hitPointsMax;
 	int16 hitPointsCur;
@@ -646,6 +648,7 @@ protected:
 
 	uint32 _drawSceneTimer;
 	uint32 _flashShapeTimer;
+	uint32 _flashShapeTimerIntv;
 	uint32 _envAudioTimer;
 	uint16 _teleporterPulse;
 
diff --git a/engines/kyra/engine/scene_eob.cpp b/engines/kyra/engine/scene_eob.cpp
index 18bef2d262..63855adf08 100644
--- a/engines/kyra/engine/scene_eob.cpp
+++ b/engines/kyra/engine/scene_eob.cpp
@@ -490,7 +490,7 @@ void EoBCoreEngine::assignWallsAndDecorations(int wallIndex, int vmpIndex, int d
 				error("Error trying to make decoration %d (x: %d, y: %d, w: %d, h: %d)", decIndex, r->x, r->y, r->w, r->h);
 
 			if (_flags.platform == Common::kPlatformSegaCD) {
-				_levelDecorationShapes[t] = _screen->sega_encodeShape(_dcrShpDataPos, r->w << 3, r->h, 0);
+				_levelDecorationShapes[t] = _screen->sega_convertShape(_dcrShpDataPos, r->w << 3, r->h, 0);
 				_dcrShpDataPos += ((r->w << 2) * r->h);
 			} else {
 				_levelDecorationShapes[t] = _screen->encodeShape(r->x, r->y, r->w, r->h, false, _cgaLevelMappingIndex ? _cgaMappingLevel[_cgaLevelMappingIndex[_currentLevel - 1]] : 0);
@@ -582,7 +582,7 @@ void EoBCoreEngine::drawScene(int refresh) {
 		_screen->updateScreen();
 
 	if (_sceneDefaultUpdate) {
-		_sceneDefaultUpdate = false;
+		_sceneDefaultUpdate = 0;
 		_drawSceneTimer = _system->getMillis() + 4 * _tickLength;
 	}
 
diff --git a/engines/kyra/engine/sprites_eob.cpp b/engines/kyra/engine/sprites_eob.cpp
index 0e456a21d1..a8075c49bf 100644
--- a/engines/kyra/engine/sprites_eob.cpp
+++ b/engines/kyra/engine/sprites_eob.cpp
@@ -61,8 +61,13 @@ void EoBCoreEngine::releaseMonsterShapes(int first, int num) {
 }
 
 const uint8 *EoBCoreEngine::loadActiveMonsterData(const uint8 *data, int level) {
+	static const uint8 intervals[4] = { 35, 30, 25, 0 };
 	for (uint8 p = *data++; p != 0xFF; p = *data++) {
 		uint8 v = *data++;
+		if (_flags.platform == Common::kPlatformSegaCD) {
+			assert(v < ARRAYSIZE(intervals));
+			v = intervals[v];
+		}
 		_timer->setCountdown(0x20 + (p << 1), v);
 		_timer->setCountdown(0x21 + (p << 1), v);
 	}
@@ -459,75 +464,91 @@ void EoBCoreEngine::drawMonsters(int index) {
 		int subFrame = ABS(f);
 		int shpIndex = d->shpIndex ? 18 : 0;
 		int palIndex = d->palette ? ((((shpIndex == 18) ? subFrame + 5 : subFrame - 1) << 1) + (d->palette - 1)) : -1;
+		int shpOffs = subFrame + shpIndex - 1;
+		int xAdd2 = 0;
+		int yAdd2 = 0;
+
+		int v30 = (subFrame == 1 || subFrame > 3) ? 1 : 0;
+		int posOffs = (d->pos == 4) ? 4 : _dscItemPosIndex[cDirOffs + d->pos];
+		int posIndex = (index * 5 + posOffs) << 1;
 
 		if (_flags.platform == Common::kPlatformSegaCD) {
-			if (d->curAttackFrame == -1)
+			if (d->curAttackFrame < 0)
 				subFrame = 5;
-			else if (f == 3)
-				subFrame = 2;
-			else if (f == -3)
-				subFrame = 4;
-			else if (f == 4)
-				subFrame = 3;
-			else if (f == -4)
-				subFrame = -3;
+			else if (subFrame >= 3)
+				subFrame--;
+
+			if (d->animType != subFrame) {
+				d->animType = subFrame;
+				d->animProgress = 0;
+			}
+		} else if (d->curAttackFrame < 0) {
+			d->curAttackFrame++;
 		}
 
-		const uint8 *shp = _screen->scaleShape(_monsterShapes[subFrame + shpIndex - 1], blockDistance);
+		for (int numFrames = 1; numFrames; --numFrames) {
+			if (_flags.platform == Common::kPlatformSegaCD) {
+				int temp = 0;
+				const uint8 *frm = _staticres->loadRawData(kEoB1MonsterAnimFrames00 + d->type * 5 + subFrame - 1, temp) + ((d->animProgress++) << 2);
+				shpOffs = shpIndex + (frm[0] & 0x3F);
+				numFrames += ((frm[0] >> 6) & 1);
+				xAdd2 = (int8)frm[1];
+				yAdd2 = (int8)frm[2];
+				if (frm[4] == 0xFE)
+					d->animProgress = 0;
+				else if (frm[4] == 0xFF)
+					d->curAttackFrame = 0;
+			}
 
-		int v30 = (subFrame == 1 || subFrame > 3) ? 1 : 0;
-		int v1e = (d->pos == 4) ? 4 : _dscItemPosIndex[cDirOffs + d->pos];
-		int posIndex = (index * 5 + v1e) << 1;
+			const uint8 *shp = _screen->scaleShape(_monsterShapes[shpOffs], blockDistance);
 
-		int x = _dscShapeCoords[posIndex] + 88;
-		int y = _dscShapeCoords[posIndex + 1] + 127;
+			int x = _dscShapeCoords[posIndex] + 88;
+			int y = _dscShapeCoords[posIndex + 1] + 127;
 
-		if (p->u30 == 1) {
-			if (v30) {
-				if (_flags.gameID == GI_EOB2)
-					posIndex = ((posIndex >> 1) - v1e) << 1;
-				y = _dscShapeCoords[posIndex + 1] + 127 + yAdd[blockDistance + ((v1e == 4 || _flags.gameID == GI_EOB1) ? 0 : 3)];
-			} else {
-				if (_flags.gameID == GI_EOB2)
-					posIndex = ((posIndex >> 1) - v1e + 4) << 1;
-				x = _dscShapeCoords[posIndex] + 88;
+			if (p->u30 == 1) {
+				if (v30) {
+					if (_flags.gameID == GI_EOB2)
+						posIndex = ((posIndex >> 1) - posOffs) << 1;
+					y = _dscShapeCoords[posIndex + 1] + 127 + yAdd[blockDistance + ((posOffs == 4 || _flags.gameID == GI_EOB1) ? 0 : 3)];
+				} else {
+					if (_flags.gameID == GI_EOB2)
+						posIndex = ((posIndex >> 1) - posOffs + 4) << 1;
+					x = _dscShapeCoords[posIndex] + 88;
+				}
 			}
-		}
 
-		int w = shp[2] << 3;
-		int h = shp[1];
+			int w = shp[2] << 3;
+			int h = shp[1];
 
-		x = x - (w >> 1) + (d->idleAnimState >> 4);
-		y = y - h + (d->idleAnimState & 0x0F);
+			x = x - (w >> 1) + (d->idleAnimState >> 4) + xAdd2;
+			y = y - h + (d->idleAnimState & 0x0F) + yAdd2;
 
-		drawMonsterShape(shp, x, y, f >= 0 ? 0 : 1, d->flags, palIndex);
+			drawMonsterShape(shp, x, y, f >= 0 ? 0 : 1, d->flags, palIndex);
 
-		if (_flags.gameID == GI_EOB1) {
-			_screen->setShapeFadingLevel(0);
-			continue;
-		}
+			if (_flags.gameID == GI_EOB2) {
+				for (int ii = 0; ii < 3; ii++) {
+					if (!p->decorations[ii])
+						continue;
 
-		for (int ii = 0; ii < 3; ii++) {
-			if (!p->decorations[ii])
-				continue;
+					SpriteDecoration *dcr = &_monsterDecorations[(p->decorations[ii] - 1) * 6 + subFrame + shpIndex - 1];
 
-			SpriteDecoration *dcr = &_monsterDecorations[(p->decorations[ii] - 1) * 6 + subFrame + shpIndex - 1];
+					if (!dcr->shp)
+						continue;
 
-			if (!dcr->shp)
-				continue;
+					shp = _screen->scaleShape(dcr->shp, blockDistance);
+					int dx = dcr->x;
+					int dy = dcr->y;
 
-			shp = _screen->scaleShape(dcr->shp, blockDistance);
-			int dx = dcr->x;
-			int dy = dcr->y;
+					for (int iii = 0; iii < blockDistance; iii++) {
+						dx = (dx << 1) / 3;
+						dy = (dy << 1) / 3;
+					}
 
-			for (int iii = 0; iii < blockDistance; iii++) {
-				dx = (dx << 1) / 3;
-				dy = (dy << 1) / 3;
+					drawMonsterShape(shp, x + ((f < 0) ? (w - dx - (shp[2] << 3)) : dx), y + dy, f >= 0 ? 0 : 1, d->flags, -1);
+				}
 			}
-
-			drawMonsterShape(shp, x + ((f < 0) ? (w - dx - (shp[2] << 3)) : dx), y + dy, f >= 0 ? 0 : 1, d->flags, -1);
+			_screen->setShapeFadingLevel(0);
 		}
-		_screen->setShapeFadingLevel(0);
 	}
 }
 
@@ -1009,16 +1030,19 @@ bool EoBCoreEngine::updateMonsterTryCloseAttack(EoBMonsterInPlay *m, int block)
 
 		if (facing) {
 			disableSysTimer(2);
-			if (m->type == 4)
+			if ((_flags.platform == Common::kPlatformSegaCD) == (m->type != 4))
 				snd_updateEnvironmentalSfx(_monsterProps[m->type].sound1);
-			m->curAttackFrame = -2;
+
 			_flashShapeTimer = 0;
-			drawScene(1);
-			m->curAttackFrame = -1;
-			if (m->type != 4)
-				snd_updateEnvironmentalSfx(_monsterProps[m->type].sound1);
-			_flashShapeTimer = _system->getMillis() + 8 * _tickLength;
-			drawScene(1);
+			m->curAttackFrame = -2;
+
+			for (int i = 0; i < 16 && m->curAttackFrame < 0; ++i) {
+				if (m->type != 4 && m->curAttackFrame == -1)
+					snd_updateEnvironmentalSfx(_monsterProps[m->type].sound1);
+				drawScene(1);
+				_flashShapeTimer = _system->getMillis() + _flashShapeTimerIntv;
+			}
+
 		} else {
 			snd_updateEnvironmentalSfx(_monsterProps[m->type].sound1);
 		}
@@ -1030,7 +1054,7 @@ bool EoBCoreEngine::updateMonsterTryCloseAttack(EoBMonsterInPlay *m, int block)
 			m->animStep ^= 1;
 			_sceneUpdateRequired = 1;
 			enableSysTimer(2);
-			_flashShapeTimer = _system->getMillis() + 8 * _tickLength;
+			_flashShapeTimer = _system->getMillis() + _flashShapeTimerIntv;
 		}
 	} else {
 		int b = m->block;
diff --git a/engines/kyra/graphics/screen_eob.h b/engines/kyra/graphics/screen_eob.h
index 4f252c0d5e..dde516a7d6 100644
--- a/engines/kyra/graphics/screen_eob.h
+++ b/engines/kyra/graphics/screen_eob.h
@@ -132,7 +132,7 @@ public:
 	void sega_loadTextBufferToVRAM(uint16 srcOffset, uint16 addr, int size);
 	void sega_gfxScale(uint8 *out, uint16 w, uint16 h, uint16 pitch, const uint8 *in, const uint16 *stampMap, const uint16 *traceVectors);
 	void sega_drawClippedLine(uint8 *dst, int pW, int pH, int x, int y, int w, int h, uint8 color);
-	uint8 *sega_encodeShape(const uint8 *src, int w, int h, int pal);
+	uint8 *sega_convertShape(const uint8 *src, int w, int h, int pal);
 	void sega_encodeShapesFromSprites(const uint8 **dst, const uint8 *src, int numShapes, int w, int h, int pal);
 
 	SegaRenderer *sega_getRenderer() const { return _segaRenderer; }
diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index 8e3be9ee01..125502ccde 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -289,7 +289,7 @@ void Screen_EoB::sega_drawClippedLine(uint8 *dst, int pW, int pH, int x, int y,
 	}
 }
 
-uint8 *Screen_EoB::sega_encodeShape(const uint8 *src, int w, int h, int pal) {
+uint8 *Screen_EoB::sega_convertShape(const uint8 *src, int w, int h, int pal) {
 	uint8 *shp = new uint8[(w >> 1) * h + 20];
 	uint8 *dst = shp;
 	*dst++ = 2;
diff --git a/engines/kyra/gui/saveload_eob.cpp b/engines/kyra/gui/saveload_eob.cpp
index 4598919623..d7645005d5 100644
--- a/engines/kyra/gui/saveload_eob.cpp
+++ b/engines/kyra/gui/saveload_eob.cpp
@@ -249,6 +249,8 @@ Common::Error EoBCoreEngine::loadGameState(int slot) {
 			m->directionChanged = in.readByte();
 			m->stepsTillRemoteAttack = in.readByte();
 			m->sub = in.readByte();
+			m->animType = 0;
+			m->animProgress = 0;
 		}
 
 		for (int ii = 0; ii < _numFlyingObjects; ii++) {


Commit: edc0c5dc9126d839689d6f2b4ff17e256a3bb3ad
    https://github.com/scummvm/scummvm/commit/edc0c5dc9126d839689d6f2b4ff17e256a3bb3ad
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:12+02:00

Commit Message:
AUDIO: (FM-TOWNS) - fix pcm rate

Changed paths:
    audio/softsynth/fmtowns_pc98/towns_audio.cpp


diff --git a/audio/softsynth/fmtowns_pc98/towns_audio.cpp b/audio/softsynth/fmtowns_pc98/towns_audio.cpp
index 49961dee32..17fec43c0a 100644
--- a/audio/softsynth/fmtowns_pc98/towns_audio.cpp
+++ b/audio/softsynth/fmtowns_pc98/towns_audio.cpp
@@ -253,8 +253,8 @@ private:
 	uint8 _outputMute[16];
 	bool _updateOutputVol;
 
-	const uint32 _tickLength;
-	const uint32 _envDuration;
+	const uint32 _intRate;
+	const uint32 _extRate;
 	uint32 _timer;
 
 	uint16 _musicVolume;
@@ -277,10 +277,9 @@ private:
 
 TownsAudioInterfaceInternal::TownsAudioInterfaceInternal(Audio::Mixer *mixer, TownsAudioInterface *owner, TownsAudioInterfacePluginDriver *driver, bool externalMutex) :
 	TownsPC98_FmSynth(mixer, kTypeTowns), _fmInstruments(0), _pcmInstruments(0), _pcmChan(0), _waveTables(0), _waveTablesTotalDataSize(0),
-	_tickLength(0x08), _envDuration(0x30), _timer(0), _drv(driver), _drvOwner(owner), _externalMutex(externalMutex), _pcmSfxChanMask(0), _outputVolumeFlags(0),
+	_intRate(72), _extRate(384), _timer(0), _drv(driver), _drvOwner(owner), _externalMutex(externalMutex), _pcmSfxChanMask(0), _outputVolumeFlags(0),
 	_fmChanPlaying(0), _musicVolume(Audio::Mixer::kMaxMixerVolume), _sfxVolume(Audio::Mixer::kMaxMixerVolume),
 	_numReservedChannels(0), _numWaveTables(0), _updateOutputVol(false), _ready(false) {
-
 #define INTCB(x) &TownsAudioInterfaceInternal::intf_##x
 	static const TownsAudioIntfCallback intfCb[] = {
 		// 0
@@ -540,9 +539,9 @@ void TownsAudioInterfaceInternal::nextTickEx(int32 *buffer, uint32 bufferSize) {
 		updateOutputVolumeInternal();
 
 	for (uint32 i = 0; i < bufferSize; i++) {
-		_timer += _tickLength;
-		while (_timer >= _envDuration) {
-			_timer -= _envDuration;
+		_timer += _intRate;
+		while (_timer >= _extRate) {
+			_timer -= _extRate;
 
 			for (int ii = 0; ii < 8; ii++)
 				_pcmChan[ii].updateOutput();


Commit: 151e3e79ca546331f654d68357b1f5408ec46342
    https://github.com/scummvm/scummvm/commit/151e3e79ca546331f654d68357b1f5408ec46342
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:13+02:00

Commit Message:
KYRA: (EOB/SegaCD) - implement item shape handling

Changed paths:
    engines/kyra/engine/darkmoon.cpp
    engines/kyra/engine/darkmoon.h
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eob.h
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/sprites_eob.cpp
    engines/kyra/graphics/screen_eob.h
    engines/kyra/graphics/screen_eob_segacd.cpp


diff --git a/engines/kyra/engine/darkmoon.cpp b/engines/kyra/engine/darkmoon.cpp
index 3403f8136b..46b74f2a46 100644
--- a/engines/kyra/engine/darkmoon.cpp
+++ b/engines/kyra/engine/darkmoon.cpp
@@ -77,6 +77,60 @@ Common::Error DarkMoonEngine::init() {
 	return Common::kNoError;
 }
 
+
+void DarkMoonEngine::loadItemsAndDecorationsShapes() {
+	if (_flags.platform != Common::kPlatformFMTowns) {
+		EoBCoreEngine::loadItemsAndDecorationsShapes();
+		return;
+	}
+
+	releaseItemsAndDecorationsShapes();
+	int size = 0;
+
+	_largeItemShapes = new const uint8 *[_numLargeItemShapes];
+	for (int i = 0; i < _numLargeItemShapes; i++)
+		_largeItemShapes[i] = _staticres->loadRawData(kEoB2LargeItemsShapeData00 + i, size);
+	_smallItemShapes = new const uint8 *[_numSmallItemShapes];
+	for (int i = 0; i < _numSmallItemShapes; i++)
+		_smallItemShapes[i] = _staticres->loadRawData(kEoB2SmallItemsShapeData00 + i, size);
+	_thrownItemShapes = new const uint8 *[_numThrownItemShapes];
+	for (int i = 0; i < _numThrownItemShapes; i++)
+		_thrownItemShapes[i] = _staticres->loadRawData(kEoB2ThrownShapeData00 + i, size);
+	_spellShapes = new const uint8 *[4];
+	for (int i = 0; i < 4; i++)
+		_spellShapes[i] = _staticres->loadRawData(kEoB2SpellShapeData00 + i, size);
+	_firebeamShapes = new const uint8 *[3];
+	for (int i = 0; i < 3; i++)
+		_firebeamShapes[i] = _staticres->loadRawData(kEoB2FirebeamShapeData00 + i, size);
+	_redSplatShape = _staticres->loadRawData(kEoB2RedSplatShapeData, size);
+	_greenSplatShape = _staticres->loadRawData(kEoB2GreenSplatShapeData, size);
+	_itemIconShapes = new const uint8 *[_numItemIconShapes];
+	for (int i = 0; i < _numItemIconShapes; i++)
+		_itemIconShapes[i] = _staticres->loadRawData(kEoB2ItemIconShapeData00 + i, size);
+
+	_teleporterShapes = new const uint8 *[6];
+	_sparkShapes = new const uint8 *[3];
+	_compassShapes = new const uint8 *[12];
+	if (_flags.gameID == GI_EOB2)
+		_wallOfForceShapes = new const uint8 *[6];
+
+	_lightningColumnShape = _staticres->loadRawData(kEoB2LightningColumnShapeData, size);
+	for (int i = 0; i < 6; i++)
+		_wallOfForceShapes[i] = _staticres->loadRawData(kEoB2WallOfForceShapeData00 + i, size);
+	for (int i = 0; i < 6; i++)
+		_teleporterShapes[i] = _staticres->loadRawData(kEoB2TeleporterShapeData00 + i, size);
+	for (int i = 0; i < 3; i++)
+		_sparkShapes[i] = _staticres->loadRawData(kEoB2SparkShapeData00 + i, size);
+	for (int i = 0; i < 12; i++)
+		_compassShapes[i] = _staticres->loadRawData(kEoB2CompassShapeData00 + i, size);
+
+	_deadCharShape = _staticres->loadRawData(kEoB2DeadCharShapeData, size);
+	_disabledCharGrid = _staticres->loadRawData(kEoB2DisabledCharGridShapeData, size);
+	_blackBoxSmallGrid = _staticres->loadRawData(kEoB2SmallGridShapeData, size);
+	_weaponSlotGrid = _staticres->loadRawData(kEoB2WeaponSlotGridShapeData, size);
+	_blackBoxWideGrid = _staticres->loadRawData(kEoB2WideGridShapeData, size);
+}
+
 void DarkMoonEngine::startupNew() {
 	_sound->selectAudioResourceSet(kMusicIngame);
 	_currentLevel = 4;
diff --git a/engines/kyra/engine/darkmoon.h b/engines/kyra/engine/darkmoon.h
index b7949cd129..d4871ec9ef 100644
--- a/engines/kyra/engine/darkmoon.h
+++ b/engines/kyra/engine/darkmoon.h
@@ -52,10 +52,11 @@ public:
 	~DarkMoonEngine() override;
 
 private:
-	// Init / Release
+	// Init
 	Common::Error init() override;
 	void initStaticResource();
 	void initSpells() override;
+	void loadItemsAndDecorationsShapes() override;
 
 	// Main Menu
 	int mainMenu() override;
diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index 11388f1d40..39bc998597 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -137,19 +137,42 @@ Common::Error EoBEngine::init() {
 	_screen->sega_encodeShapesFromSprites(shapeBuffer, in, numShapes, width, height, 3); \
 	delete[] in
 
+#define loadAndEncodeShapes(resID, resOffset, shapeBuffer, numShapes, width, height, size) \
+	shapeBuffer = new const uint8 *[numShapes]; \
+	memset(shapeBuffer, 0, numShapes * sizeof(uint8*)); \
+	in = _sres->resData(resID); \
+	for (int ii = 0; ii < numShapes; ++ii) \
+		shapeBuffer[ii] = _screen->sega_convertShape(in + resOffset + ii * size, width, height, 3); \
+	delete[] in
+
 void EoBEngine::loadItemsAndDecorationsShapes() {
 	if (_flags.platform != Common::kPlatformSegaCD) {
 		EoBCoreEngine::loadItemsAndDecorationsShapes();
 		return;
 	}
 
-	uint8 *in = 0;
+	releaseItemsAndDecorationsShapes();
 	_sres->loadContainer("ITEM");
+	uint8 *in = 0;
+
 	loadSpritesAndEncodeToShapes(0, _itemIconShapes, _numItemIconShapes, 16, 16);
 	loadSpritesAndEncodeToShapes(14, _blueItemIconShapes, _numItemIconShapes, 16, 16);
 	loadSpritesAndEncodeToShapes(13, _xtraItemIconShapes, 3, 16, 16);
+
+	loadAndEncodeShapes(1, 0, _smallItemShapes, _numSmallItemShapes, 32, 24, 768);
+	loadAndEncodeShapes(2, 0, _largeItemShapes, _numLargeItemShapes, 64, 24, 1472);
+	loadAndEncodeShapes(11, 0, _thrownItemShapes, _numThrownItemShapes, 32, 24, 768);
+	int offset1 = 0, offset2 = 0;
+	for (int i = 0; i < 3; ++i) {
+		offset1 += (0x180 / (i + 1));
+		offset2 += (0x300 / (i + 1));
+		loadAndEncodeShapes(1, offset1, _smallItemShapesScl[i], _numSmallItemShapes, (3 - i) << 3, 16 - ((i >> 1) << 3), 768);
+		loadAndEncodeShapes(2, offset2, _largeItemShapesScl[i], _numLargeItemShapes, (6 - 2 * i) << 3, 16 - ((i >> 1) << 3), 1472);
+		loadAndEncodeShapes(11, offset1, _thrownItemShapesScl[i], _numThrownItemShapes, (3 - i) << 3, 16 - ((i >> 1) << 3), 768);
+	}
 }
 
+#undef loadAndEncodeShapes
 #undef loadSpritesAndEncodeToShapes
 
 Common::SeekableReadStreamEndian *EoBEngine::getItemDefinitionFile(int index) {
@@ -613,9 +636,9 @@ void EoBEngine::loadDoorShapes(int doorType1, int shapeId1, int doorType2, int s
 			if (_flags.platform == Common::kPlatformSegaCD) {
 				int offs = lvlIndex[_currentLevel] * 6 + shapeId[a] + i;
 				const uint8 *enc = &_doorShapeEncodeDefs[offs << 2];
-				_doorShapes[shapeId[a] + i] = _screen->sega_convertShape(_doorShapesSrc[offs], enc[0] << 3, enc[1] << 3, 0);
+				_doorShapes[shapeId[a] + i] = _screen->sega_convertShape(_doorShapesSrc[offs], enc[0] << 3, enc[1] << 3, 0, enc[2] - enc[3]);
 				enc = &_doorSwitchShapeEncodeDefs[(offs << 2) - shapeId[a]];
-				_doorSwitches[shapeId[a] + i].shp = _screen->sega_convertShape(_doorSwitchShapesSrc[offs], enc[0] << 3, enc[1] << 3, 0);
+				_doorSwitches[shapeId[a] + i].shp = _screen->sega_convertShape(_doorSwitchShapesSrc[offs], enc[0] << 3, enc[1] << 3, 0, enc[2] - enc[3]);
 			} else {
 				const uint8 *enc = &_doorShapeEncodeDefs[(doorType[a] * 3 + i) << 2];
 				_doorShapes[shapeId[a] + i] = _screen->encodeShape(enc[0], enc[1], enc[2], enc[3], false, _cgaLevelMappingIndex ? _cgaMappingLevel[_cgaLevelMappingIndex[_currentLevel - 1]] : 0);
@@ -644,7 +667,7 @@ void EoBEngine::drawDoorIntern(int type, int index, int x, int y, int w, int wal
 	case 4:
 	case 5:
 	case 6:
-		y = _dscDoorY7[mDim] - shp[1];
+		y = _dscDoorY7[mDim] - shp[3];
 		d1 = _dscDoorCoordsExt[index << 1] >> 3;
 		d2 = _dscDoorCoordsExt[(index << 1) + 1] >> 3;
 		if (_shpDmX1 > d1)
@@ -664,7 +687,7 @@ void EoBEngine::drawDoorIntern(int type, int index, int x, int y, int w, int wal
 	case 7:
 	case 8:
 	case 9:
-		y = _dscDoorY3[mDim] - _doorShapes[shapeIndex + 3][1];
+		y = _dscDoorY3[mDim] - _doorShapes[shapeIndex + 3][3];
 		d1 = x - (_doorShapes[shapeIndex + 3][2] << 2);
 		x -= (shp[2] << 2);
 		drawBlockObject(0, 2, _doorShapes[shapeIndex + 3], d1, y, 5);
@@ -689,7 +712,7 @@ void EoBEngine::drawDoorIntern(int type, int index, int x, int y, int w, int wal
 		break;
 
 	default:
-		y = (_currentLevel == 12 ? _dscDoorY6[mDim] : _dscDoorY1[mDim]) - shp[1];
+		y = (_currentLevel == 12 ? _dscDoorY6[mDim] : _dscDoorY1[mDim]) - shp[3];
 		x -= (shp[2] << 2);
 		y -= (wall >= 30 ? _dscDoorScaleMult2[mDim] : (wall - _dscDoorScaleOffs[wall]) * _dscDoorScaleMult1[mDim]);
 		drawBlockObject(0, 2, shp, x, y, 5);
diff --git a/engines/kyra/engine/eob.h b/engines/kyra/engine/eob.h
index 691d1c4104..1b5e48c046 100644
--- a/engines/kyra/engine/eob.h
+++ b/engines/kyra/engine/eob.h
@@ -43,10 +43,12 @@ public:
 	~EoBEngine() override;
 
 private:
-	// Init / Release
+	// Init
 	Common::Error init() override;
 	void initStaticResource();
 	void initSpells() override;
+	void loadItemsAndDecorationsShapes() override;
+	Common::SeekableReadStreamEndian *getItemDefinitionFile(int index) override;
 
 	// Main Menu
 	int mainMenu() override;
@@ -75,10 +77,6 @@ private:
 	static const TitleScreenConfig _titleConfig[5];
 	const TitleScreenConfig *_ttlCfg;
 
-	// Init
-	void loadItemsAndDecorationsShapes() override;
-	Common::SeekableReadStreamEndian *getItemDefinitionFile(int index) override;
-
 	// Main loop
 	void startupNew() override;
 	void startupLoad() override;
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 8fb273494b..6d929cb1c7 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -772,38 +772,28 @@ void EoBCoreEngine::loadItemsAndDecorationsShapes() {
 	int size = 0;
 
 	_largeItemShapes = new const uint8*[_numLargeItemShapes];
-	if (_flags.platform == Common::kPlatformFMTowns && _flags.gameID == GI_EOB2) {
-		for (int i = 0; i < _numLargeItemShapes; i++)
-			_largeItemShapes[i] = _staticres->loadRawData(kEoB2LargeItemsShapeData00 + i, size);
-	} else {
-		_screen->loadShapeSetBitmap("ITEML1", 5, 3);
-		for (int i = 0; i < _numLargeItemShapes; i++)
-			_largeItemShapes[i] = _screen->encodeShape((i / div) << 3, (i % div) * mul, 8, 24, false, _cgaMappingItemsL);
+	_screen->loadShapeSetBitmap("ITEML1", 5, 3);
+	for (int i = 0; i < _numLargeItemShapes; i++)
+		_largeItemShapes[i] = _screen->encodeShape((i / div) << 3, (i % div) * mul, 8, 24, false, _cgaMappingItemsL);
 
-		if (_flags.gameID == GI_EOB1) {
-			for (int c = 0; c < 3; ++c) {
-				_largeItemShapesScl[c] = new const uint8*[_numLargeItemShapes];
-				for (int i = 0; i < _numLargeItemShapes; i++)
-					_largeItemShapesScl[c][i] = _screen->encodeShape((i / div) << 3, (i % div) * mul + 24 + (c << 4), 6 - 2 * c, 16 - ((c >> 1) << 3), false, _cgaMappingItemsL);
-			}
+	if (_flags.gameID == GI_EOB1) {
+		for (int c = 0; c < 3; ++c) {
+			_largeItemShapesScl[c] = new const uint8*[_numLargeItemShapes];
+			for (int i = 0; i < _numLargeItemShapes; i++)
+				_largeItemShapesScl[c][i] = _screen->encodeShape((i / div) << 3, (i % div) * mul + 24 + (c << 4), 6 - 2 * c, 16 - ((c >> 1) << 3), false, _cgaMappingItemsL);
 		}
 	}
 
 	_smallItemShapes = new const uint8*[_numSmallItemShapes];
-	if (_flags.platform == Common::kPlatformFMTowns && _flags.gameID == GI_EOB2) {
-		for (int i = 0; i < _numSmallItemShapes; i++)
-			_smallItemShapes[i] = _staticres->loadRawData(kEoB2SmallItemsShapeData00 + i, size);
-	} else {
-		_screen->loadShapeSetBitmap("ITEMS1", 5, 3);
-		for (int i = 0; i < _numSmallItemShapes; i++)
-			_smallItemShapes[i] = _screen->encodeShape((i / div) << 2, (i % div) * mul, 4, 24, false, _cgaMappingItemsS);
+	_screen->loadShapeSetBitmap("ITEMS1", 5, 3);
+	for (int i = 0; i < _numSmallItemShapes; i++)
+		_smallItemShapes[i] = _screen->encodeShape((i / div) << 2, (i % div) * mul, 4, 24, false, _cgaMappingItemsS);
 
-		if (_flags.gameID == GI_EOB1) {
-			for (int c = 0; c < 3; ++c) {
-				_smallItemShapesScl[c] = new const uint8*[_numSmallItemShapes];
-				for (int i = 0; i < _numSmallItemShapes; i++)
-					_smallItemShapesScl[c][i] = _screen->encodeShape((i / div) << 2, (i % div) * mul + 24 + (c << 4), 3 - c, 16 - ((c >> 1) << 3), false, _cgaMappingItemsS);
-			}
+	if (_flags.gameID == GI_EOB1) {
+		for (int c = 0; c < 3; ++c) {
+			_smallItemShapesScl[c] = new const uint8*[_numSmallItemShapes];
+			for (int i = 0; i < _numSmallItemShapes; i++)
+				_smallItemShapesScl[c][i] = _screen->encodeShape((i / div) << 2, (i % div) * mul + 24 + (c << 4), 3 - c, 16 - ((c >> 1) << 3), false, _cgaMappingItemsS);
 		}
 	}
 
@@ -812,55 +802,40 @@ void EoBCoreEngine::loadItemsAndDecorationsShapes() {
 		_spellShapes = new const uint8*[4];
 	_firebeamShapes = new const uint8*[3];
 
-	if (_flags.platform == Common::kPlatformFMTowns && _flags.gameID == GI_EOB2) {
-		for (int i = 0; i < _numThrownItemShapes; i++)
-			_thrownItemShapes[i] = _staticres->loadRawData(kEoB2ThrownShapeData00 + i, size);
-		for (int i = 0; i < 4; i++)
-			_spellShapes[i] = _staticres->loadRawData(kEoB2SpellShapeData00 + i, size);
-		for (int i = 0; i < 3; i++)
-			_firebeamShapes[i] = _staticres->loadRawData(kEoB2FirebeamShapeData00 + i, size);
-		_redSplatShape = _staticres->loadRawData(kEoB2RedSplatShapeData, size);
-		_greenSplatShape = _staticres->loadRawData(kEoB2GreenSplatShapeData, size);
-	} else {
-		_screen->loadShapeSetBitmap("THROWN", 5, 3);
-		for (int i = 0; i < _numThrownItemShapes; i++)
-			_thrownItemShapes[i] = _screen->encodeShape((i / div) << 2, (i % div) * mul, 4, 24, false, _cgaMappingThrown);
+	_screen->loadShapeSetBitmap("THROWN", 5, 3);
+	for (int i = 0; i < _numThrownItemShapes; i++)
+		_thrownItemShapes[i] = _screen->encodeShape((i / div) << 2, (i % div) * mul, 4, 24, false, _cgaMappingThrown);
 
-		if (_flags.gameID == GI_EOB1) {
-			for (int c = 0; c < 3; ++c) {
-				_thrownItemShapesScl[c] = new const uint8*[_numThrownItemShapes];
-				for (int i = 0; i < _numThrownItemShapes; i++)
-					_thrownItemShapesScl[c][i] = _screen->encodeShape((i / div) << 2, (i % div) * mul + 24 + (c << 4), 3 - c, 16 - ((c >> 1) << 3), false, _cgaMappingThrown);
-			}
-		} else {
-			for (int i = 0; i < 4; i++)
-				_spellShapes[i] = _screen->encodeShape(8, i << 5, 6, 32, false, _cgaMappingThrown);
+	if (_flags.gameID == GI_EOB1) {
+		for (int c = 0; c < 3; ++c) {
+			_thrownItemShapesScl[c] = new const uint8*[_numThrownItemShapes];
+			for (int i = 0; i < _numThrownItemShapes; i++)
+				_thrownItemShapesScl[c][i] = _screen->encodeShape((i / div) << 2, (i % div) * mul + 24 + (c << 4), 3 - c, 16 - ((c >> 1) << 3), false, _cgaMappingThrown);
 		}
-
-		_firebeamShapes[0] = _screen->encodeShape(16, 0, 4, 24, false, _cgaMappingThrown);
-		_firebeamShapes[1] = _screen->encodeShape(16, 24, 4, 24, false, _cgaMappingThrown);
-		_firebeamShapes[2] = _screen->encodeShape(16, 48, 3, 24, false, _cgaMappingThrown);
-		_redSplatShape = _screen->encodeShape(16, _flags.gameID == GI_EOB1 ? 144 : 72, 5, 24, false, _cgaMappingThrown);
-		_greenSplatShape = _screen->encodeShape(16, _flags.gameID == GI_EOB1 ? 168 : 96, 5, 16, false, _cgaMappingThrown);
+	} else {
+		for (int i = 0; i < 4; i++)
+			_spellShapes[i] = _screen->encodeShape(8, i << 5, 6, 32, false, _cgaMappingThrown);
 	}
 
+	_firebeamShapes[0] = _screen->encodeShape(16, 0, 4, 24, false, _cgaMappingThrown);
+	_firebeamShapes[1] = _screen->encodeShape(16, 24, 4, 24, false, _cgaMappingThrown);
+	_firebeamShapes[2] = _screen->encodeShape(16, 48, 3, 24, false, _cgaMappingThrown);
+	_redSplatShape = _screen->encodeShape(16, _flags.gameID == GI_EOB1 ? 144 : 72, 5, 24, false, _cgaMappingThrown);
+	_greenSplatShape = _screen->encodeShape(16, _flags.gameID == GI_EOB1 ? 168 : 96, 5, 16, false, _cgaMappingThrown);
+
+
 	_itemIconShapes = new const uint8*[_numItemIconShapes];
-	if (_flags.platform == Common::kPlatformFMTowns && _flags.gameID == GI_EOB2) {
-		for (int i = 0; i < _numItemIconShapes; i++)
-			_itemIconShapes[i] = _staticres->loadRawData(kEoB2ItemIconShapeData00 + i, size);
-	} else {
-		_screen->loadShapeSetBitmap("ITEMICN", 5, 3);
-		for (int i = 0; i < _numItemIconShapes; i++)
-			_itemIconShapes[i] = _screen->encodeShape((i % 0x14) << 1, (i / 0x14) << 4, 2, 0x10, false, _cgaMappingIcons);
+	_screen->loadShapeSetBitmap("ITEMICN", 5, 3);
+	for (int i = 0; i < _numItemIconShapes; i++)
+		_itemIconShapes[i] = _screen->encodeShape((i % 0x14) << 1, (i / 0x14) << 4, 2, 0x10, false, _cgaMappingIcons);
 		
-		if (_flags.platform == Common::kPlatformAmiga) {
-			const uint8 offsY = (_flags.gameID == GI_EOB1) ? 80 : 96;
-			_blueItemIconShapes = new const uint8*[_numItemIconShapes];
-			for (int i = 0; i < _numItemIconShapes; i++) {
-				int bx = (i % 0x14) << 1;
-				int by = (i / 0x14) << 4;
-				_blueItemIconShapes[i] = _screen->getPagePixel(2, (bx << 3) + 8, by + offsY + 8) ? _screen->encodeShape(bx, by + offsY, 2, 0x10, false, 0) : _screen->encodeShape(bx, by, 2, 0x10, false, 0);
-			}
+	if (_flags.platform == Common::kPlatformAmiga) {
+		const uint8 offsY = (_flags.gameID == GI_EOB1) ? 80 : 96;
+		_blueItemIconShapes = new const uint8*[_numItemIconShapes];
+		for (int i = 0; i < _numItemIconShapes; i++) {
+			int bx = (i % 0x14) << 1;
+			int by = (i / 0x14) << 4;
+			_blueItemIconShapes[i] = _screen->getPagePixel(2, (bx << 3) + 8, by + offsY + 8) ? _screen->encodeShape(bx, by + offsY, 2, 0x10, false, 0) : _screen->encodeShape(bx, by, 2, 0x10, false, 0);
 		}
 	}
 
@@ -870,50 +845,31 @@ void EoBCoreEngine::loadItemsAndDecorationsShapes() {
 	if (_flags.gameID == GI_EOB2)
 		_wallOfForceShapes = new const uint8*[6];
 
-	if (_flags.platform == Common::kPlatformFMTowns && _flags.gameID == GI_EOB2) {
-		_lightningColumnShape = _staticres->loadRawData(kEoB2LightningColumnShapeData, size);
-		for (int i = 0; i < 6; i++)
-			_wallOfForceShapes[i] = _staticres->loadRawData(kEoB2WallOfForceShapeData00 + i, size);
+	_screen->loadShapeSetBitmap("DECORATE", 5, 3);
+	if (_flags.gameID == GI_EOB2) {
+		_lightningColumnShape = _screen->encodeShape(18, 88, 4, 64);
 		for (int i = 0; i < 6; i++)
-			_teleporterShapes[i] = _staticres->loadRawData(kEoB2TeleporterShapeData00 + i, size);
-		for (int i = 0; i < 3; i++)
-			_sparkShapes[i] = _staticres->loadRawData(kEoB2SparkShapeData00 + i, size);
-		for (int i = 0; i < 12; i++)
-			_compassShapes[i] = _staticres->loadRawData(kEoB2CompassShapeData00 + i, size);
-
-		_deadCharShape = _staticres->loadRawData(kEoB2DeadCharShapeData, size);
-		_disabledCharGrid = _staticres->loadRawData(kEoB2DisabledCharGridShapeData, size);
-		_blackBoxSmallGrid = _staticres->loadRawData(kEoB2SmallGridShapeData, size);
-		_weaponSlotGrid = _staticres->loadRawData(kEoB2WeaponSlotGridShapeData, size);
-		_blackBoxWideGrid = _staticres->loadRawData(kEoB2WideGridShapeData, size);
-
-	} else {
-		_screen->loadShapeSetBitmap("DECORATE", 5, 3);
-		if (_flags.gameID == GI_EOB2) {
-			_lightningColumnShape = _screen->encodeShape(18, 88, 4, 64);
-			for (int i = 0; i < 6; i++)
-				_wallOfForceShapes[i] = _screen->encodeShape(_wallOfForceShapeDefs[(i << 2)], _wallOfForceShapeDefs[(i << 2) + 1], _wallOfForceShapeDefs[(i << 2) + 2], _wallOfForceShapeDefs[(i << 2) + 3]);
-		}
+			_wallOfForceShapes[i] = _screen->encodeShape(_wallOfForceShapeDefs[(i << 2)], _wallOfForceShapeDefs[(i << 2) + 1], _wallOfForceShapeDefs[(i << 2) + 2], _wallOfForceShapeDefs[(i << 2) + 3]);
+	}
 
-		for (int i = 0; i < 6; i++)
-			_teleporterShapes[i] = _screen->encodeShape(_teleporterShapeDefs[(i << 2)], _teleporterShapeDefs[(i << 2) + 1], _teleporterShapeDefs[(i << 2) + 2], _teleporterShapeDefs[(i << 2) + 3], false, _cgaMappingDefault);
+	for (int i = 0; i < 6; i++)
+		_teleporterShapes[i] = _screen->encodeShape(_teleporterShapeDefs[(i << 2)], _teleporterShapeDefs[(i << 2) + 1], _teleporterShapeDefs[(i << 2) + 2], _teleporterShapeDefs[(i << 2) + 3], false, _cgaMappingDefault);
 		
-		_sparkShapes[0] = _screen->encodeShape(29, 0, 2, 16, false, _cgaMappingDeco);
-		_sparkShapes[1] = _screen->encodeShape(31, 0, 2, 16, false, _cgaMappingDeco);
-		_sparkShapes[2] = _screen->encodeShape(33, 0, 2, 16, false, _cgaMappingDeco);
-		_deadCharShape = _screen->encodeShape(0, 88, 4, 32, false, _cgaMappingDeco);
-		_disabledCharGrid = _screen->encodeShape(4, 88, 4, 32, false, _cgaMappingDeco);
-		_blackBoxSmallGrid = _screen->encodeShape(9, 88, 2, 8, false, _cgaMappingDeco);
-		_weaponSlotGrid = _screen->encodeShape(8, 88, 4, 16, false, _cgaMappingDeco);
-		_blackBoxWideGrid = _screen->encodeShape(8, 104, 4, 8, false, _cgaMappingDeco);
-
-		static const uint8 dHeight[] = { 17, 10, 10 };
-		static const uint8 dY[] = { 120, 137, 147 };
+	_sparkShapes[0] = _screen->encodeShape(29, 0, 2, 16, false, _cgaMappingDeco);
+	_sparkShapes[1] = _screen->encodeShape(31, 0, 2, 16, false, _cgaMappingDeco);
+	_sparkShapes[2] = _screen->encodeShape(33, 0, 2, 16, false, _cgaMappingDeco);
+	_deadCharShape = _screen->encodeShape(0, 88, 4, 32, false, _cgaMappingDeco);
+	_disabledCharGrid = _screen->encodeShape(4, 88, 4, 32, false, _cgaMappingDeco);
+	_blackBoxSmallGrid = _screen->encodeShape(9, 88, 2, 8, false, _cgaMappingDeco);
+	_weaponSlotGrid = _screen->encodeShape(8, 88, 4, 16, false, _cgaMappingDeco);
+	_blackBoxWideGrid = _screen->encodeShape(8, 104, 4, 8, false, _cgaMappingDeco);
+
+	static const uint8 dHeight[] = { 17, 10, 10 };
+	static const uint8 dY[] = { 120, 137, 147 };
 		
-		for (int y = 0; y < 3; y++) {
-			for (int x = 0; x < 4; x++)
-				_compassShapes[(y << 2) + x] = _screen->encodeShape(x * 3, dY[y], 3, dHeight[y], false, _cgaMappingDeco);
-		}
+	for (int y = 0; y < 3; y++) {
+		for (int x = 0; x < 4; x++)
+			_compassShapes[(y << 2) + x] = _screen->encodeShape(x * 3, dY[y], 3, dHeight[y], false, _cgaMappingDeco);
 	}
 }
 
diff --git a/engines/kyra/engine/sprites_eob.cpp b/engines/kyra/engine/sprites_eob.cpp
index a8075c49bf..ec0ea8d688 100644
--- a/engines/kyra/engine/sprites_eob.cpp
+++ b/engines/kyra/engine/sprites_eob.cpp
@@ -339,16 +339,15 @@ void EoBCoreEngine::drawBlockItems(int index) {
 	uint8 w = _visibleBlocks[index]->walls[_sceneDrawVarDown];
 	uint8 flg = (index == 16) ? 0x80 : _wllWallFlags[w];
 
-	//if (_wllVmpMap[w] && !(flg & 0x80))
+	if (_wllVmpMap[w] && !(flg & 0x80))
 		return;
 
 	uint16 o2 = o = _items[o].next;
-	bool forceLoop = true;
 	static const int8 itemPosYNiche[] = { 0x25, 0x31, 0x38, 0x00 };
 	static const int8 itemPosFin[] = { 0, -2, 1, -1, 2, 0, 1, -1 };
 	int tile2 = 0;
 
-	while (o != o2 || forceLoop) {
+	for (bool loop = true; o != o2 || loop; ) {
 		EoBItem *itm = &_items[o];
 		if (itm->pos == 8 || itm->pos < 4) {
 			tile2 = -1;
@@ -360,6 +359,14 @@ void EoBCoreEngine::drawBlockItems(int index) {
 
 			if (itm->pos == 8) {
 				x = _dscItemShpX[index];
+
+				if (_flags.gameID == Common::kPlatformSegaCD && _currentLevel == 12 && (_currentBlock & 0x1F) < 17 && (_currentBlock >> 5) < 20) {
+					if (index == 8)
+						x += 20;
+					else if (index == 10)
+						x -= 20;
+				}
+
 				y = itemPosYNiche[_dscDimMap[index]];
 				ps = 0;
 			} else {
@@ -393,7 +400,7 @@ void EoBCoreEngine::drawBlockItems(int index) {
 		}
 
 		o = itm->next;
-		forceLoop = false;
+		loop = false;
 		if (tile2 != -1)
 			setLevelShapesDim(index, _shpDmX1, _shpDmX2, 5);
 	}
@@ -653,7 +660,7 @@ void EoBCoreEngine::drawFlyingObjects(int index) {
 		}
 
 		x -= (shp[2] << 2);
-		y -= (y == 44 ? (shp[1]  >> 1) : shp[1]);
+		y -= (y == 44 ? (shp[1] >> 1) : shp[1]);
 
 		drawBlockObject(flipped, 2, shp, x, y, 5);
 		_screen->setShapeFadingLevel(0);
diff --git a/engines/kyra/graphics/screen_eob.h b/engines/kyra/graphics/screen_eob.h
index dde516a7d6..46dfffe587 100644
--- a/engines/kyra/graphics/screen_eob.h
+++ b/engines/kyra/graphics/screen_eob.h
@@ -132,7 +132,7 @@ public:
 	void sega_loadTextBufferToVRAM(uint16 srcOffset, uint16 addr, int size);
 	void sega_gfxScale(uint8 *out, uint16 w, uint16 h, uint16 pitch, const uint8 *in, const uint16 *stampMap, const uint16 *traceVectors);
 	void sega_drawClippedLine(uint8 *dst, int pW, int pH, int x, int y, int w, int h, uint8 color);
-	uint8 *sega_convertShape(const uint8 *src, int w, int h, int pal);
+	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);
 
 	SegaRenderer *sega_getRenderer() const { return _segaRenderer; }
diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index 125502ccde..a7bdfaac91 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -289,13 +289,13 @@ void Screen_EoB::sega_drawClippedLine(uint8 *dst, int pW, int pH, int x, int y,
 	}
 }
 
-uint8 *Screen_EoB::sega_convertShape(const uint8 *src, int w, int h, int pal) {
+uint8 *Screen_EoB::sega_convertShape(const uint8 *src, int w, int h, int pal, int hOffs) {
 	uint8 *shp = new uint8[(w >> 1) * h + 20];
 	uint8 *dst = shp;
 	*dst++ = 2;
 	*dst++ = h;
 	*dst++ = w >> 3;
-	*dst++ = h;
+	*dst++ = h + hOffs;
 	*dst++ = 0;
 
 	for (int i = 1; i < 16; i++)


Commit: c803985605de0eacbd331081b2092d96e2204aa3
    https://github.com/scummvm/scummvm/commit/c803985605de0eacbd331081b2092d96e2204aa3
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:13+02:00

Commit Message:
KYRA: (EOB/SegaCD) - adjust timing

The SegaCD version runs faster

Changed paths:
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h
    engines/kyra/engine/sprites_eob.cpp


diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index 39bc998597..70ccb883db 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -137,7 +137,7 @@ Common::Error EoBEngine::init() {
 	_screen->sega_encodeShapesFromSprites(shapeBuffer, in, numShapes, width, height, 3); \
 	delete[] in
 
-#define loadAndEncodeShapes(resID, resOffset, shapeBuffer, numShapes, width, height, size) \
+#define loadAndConvertShapes(resID, resOffset, shapeBuffer, numShapes, width, height, size) \
 	shapeBuffer = new const uint8 *[numShapes]; \
 	memset(shapeBuffer, 0, numShapes * sizeof(uint8*)); \
 	in = _sres->resData(resID); \
@@ -159,20 +159,20 @@ void EoBEngine::loadItemsAndDecorationsShapes() {
 	loadSpritesAndEncodeToShapes(14, _blueItemIconShapes, _numItemIconShapes, 16, 16);
 	loadSpritesAndEncodeToShapes(13, _xtraItemIconShapes, 3, 16, 16);
 
-	loadAndEncodeShapes(1, 0, _smallItemShapes, _numSmallItemShapes, 32, 24, 768);
-	loadAndEncodeShapes(2, 0, _largeItemShapes, _numLargeItemShapes, 64, 24, 1472);
-	loadAndEncodeShapes(11, 0, _thrownItemShapes, _numThrownItemShapes, 32, 24, 768);
+	loadAndConvertShapes(1, 0, _smallItemShapes, _numSmallItemShapes, 32, 24, 768);
+	loadAndConvertShapes(2, 0, _largeItemShapes, _numLargeItemShapes, 64, 24, 1472);
+	loadAndConvertShapes(11, 0, _thrownItemShapes, _numThrownItemShapes, 32, 24, 768);
 	int offset1 = 0, offset2 = 0;
 	for (int i = 0; i < 3; ++i) {
 		offset1 += (0x180 / (i + 1));
 		offset2 += (0x300 / (i + 1));
-		loadAndEncodeShapes(1, offset1, _smallItemShapesScl[i], _numSmallItemShapes, (3 - i) << 3, 16 - ((i >> 1) << 3), 768);
-		loadAndEncodeShapes(2, offset2, _largeItemShapesScl[i], _numLargeItemShapes, (6 - 2 * i) << 3, 16 - ((i >> 1) << 3), 1472);
-		loadAndEncodeShapes(11, offset1, _thrownItemShapesScl[i], _numThrownItemShapes, (3 - i) << 3, 16 - ((i >> 1) << 3), 768);
+		loadAndConvertShapes(1, offset1, _smallItemShapesScl[i], _numSmallItemShapes, (3 - i) << 3, 16 - ((i >> 1) << 3), 768);
+		loadAndConvertShapes(2, offset2, _largeItemShapesScl[i], _numLargeItemShapes, (6 - 2 * i) << 3, 16 - ((i >> 1) << 3), 1472);
+		loadAndConvertShapes(11, offset1, _thrownItemShapesScl[i], _numThrownItemShapes, (3 - i) << 3, 16 - ((i >> 1) << 3), 768);
 	}
 }
 
-#undef loadAndEncodeShapes
+#undef loadAndConvertShapes
 #undef loadSpritesAndEncodeToShapes
 
 Common::SeekableReadStreamEndian *EoBEngine::getItemDefinitionFile(int index) {
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 6d929cb1c7..77c19af4f7 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -56,10 +56,12 @@ EoBCoreEngine::EoBCoreEngine(OSystem *system, const GameFlags &flags) : KyraRpgE
 
 	_enableHiResDithering = false;
 
-	_tickLength = 55;
+	_tickLength = (_flags.platform == Common::kPlatformSegaCD) ? 38 : 55;
 	_envAudioTimer = 0;
 	_flashShapeTimer = 0;
-	_flashShapeTimerIntv = (_flags.platform == Common::kPlatformSegaCD ? 16 : _tickLength) * 8;
+	_flashShapeTimerIntv0 = (_flags.platform == Common::kPlatformSegaCD ? 2 * _tickLength : 0);
+	_flashShapeTimerIntv1 = _tickLength * (_flags.platform == Common::kPlatformSegaCD ? 2 : 8);
+	_flashShapeTimerIntv2 = _tickLength * 8;
 	_drawSceneTimer = 0;
 	_vcnFilePattern = "%s.VCN";
 	_vmpFilePattern = "%s.VMP";
@@ -823,7 +825,6 @@ void EoBCoreEngine::loadItemsAndDecorationsShapes() {
 	_redSplatShape = _screen->encodeShape(16, _flags.gameID == GI_EOB1 ? 144 : 72, 5, 24, false, _cgaMappingThrown);
 	_greenSplatShape = _screen->encodeShape(16, _flags.gameID == GI_EOB1 ? 168 : 96, 5, 16, false, _cgaMappingThrown);
 
-
 	_itemIconShapes = new const uint8*[_numItemIconShapes];
 	_screen->loadShapeSetBitmap("ITEMICN", 5, 3);
 	for (int i = 0; i < _numItemIconShapes; i++)
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index 81833c0fc7..e033123242 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -648,7 +648,9 @@ protected:
 
 	uint32 _drawSceneTimer;
 	uint32 _flashShapeTimer;
-	uint32 _flashShapeTimerIntv;
+	uint32 _flashShapeTimerIntv0;
+	uint32 _flashShapeTimerIntv1;
+	uint32 _flashShapeTimerIntv2;
 	uint32 _envAudioTimer;
 	uint16 _teleporterPulse;
 
diff --git a/engines/kyra/engine/sprites_eob.cpp b/engines/kyra/engine/sprites_eob.cpp
index ec0ea8d688..54d2113a74 100644
--- a/engines/kyra/engine/sprites_eob.cpp
+++ b/engines/kyra/engine/sprites_eob.cpp
@@ -1040,14 +1040,14 @@ bool EoBCoreEngine::updateMonsterTryCloseAttack(EoBMonsterInPlay *m, int block)
 			if ((_flags.platform == Common::kPlatformSegaCD) == (m->type != 4))
 				snd_updateEnvironmentalSfx(_monsterProps[m->type].sound1);
 
-			_flashShapeTimer = 0;
+			_flashShapeTimer = _flashShapeTimerIntv0;
 			m->curAttackFrame = -2;
 
 			for (int i = 0; i < 16 && m->curAttackFrame < 0; ++i) {
 				if (m->type != 4 && m->curAttackFrame == -1)
 					snd_updateEnvironmentalSfx(_monsterProps[m->type].sound1);
 				drawScene(1);
-				_flashShapeTimer = _system->getMillis() + _flashShapeTimerIntv;
+				_flashShapeTimer = _system->getMillis() + _flashShapeTimerIntv1;
 			}
 
 		} else {
@@ -1061,7 +1061,7 @@ bool EoBCoreEngine::updateMonsterTryCloseAttack(EoBMonsterInPlay *m, int block)
 			m->animStep ^= 1;
 			_sceneUpdateRequired = 1;
 			enableSysTimer(2);
-			_flashShapeTimer = _system->getMillis() + _flashShapeTimerIntv;
+			_flashShapeTimer = _system->getMillis() + _flashShapeTimerIntv2;
 		}
 	} else {
 		int b = m->block;


Commit: bd97aba1713f8db23d9ea4c8ae1a527291f73ebb
    https://github.com/scummvm/scummvm/commit/bd97aba1713f8db23d9ea4c8ae1a527291f73ebb
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:13+02:00

Commit Message:
KYRA: (EOB/SegaCD) - fix some gui elements

(playfield background, weapon slots, number display, etc.)

Changed paths:
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eob.h
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h
    engines/kyra/graphics/screen_eob.h
    engines/kyra/graphics/screen_eob_segacd.cpp
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/sequence/sequences_eob.cpp


diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index 70ccb883db..50e0d46ff0 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -49,6 +49,7 @@ EoBEngine::EoBEngine(OSystem *system, const GameFlags &flags)
 	_doorSwitchShapeEncodeDefs = _doorSwitchCoords = 0;
 	_doorShapesSrc = _doorSwitchShapesSrc = 0;
 	_dscDoorCoordsExt = 0;
+	_invSmallDigits = _weaponSlotShapes = 0;
 	_useMainMenuGUISettings = false;
 	_ttlCfg = 0;
 	_xdth = false;
@@ -60,6 +61,8 @@ EoBEngine::EoBEngine(OSystem *system, const GameFlags &flags)
 }
 
 EoBEngine::~EoBEngine() {
+	releaseShpArr(_invSmallDigits, 32);
+	releaseShpArr(_weaponSlotShapes, 6);
 	delete[] _doorShapesSrc;
 	delete[] _doorSwitchShapesSrc;
 	delete[] _itemsOverlay;
@@ -130,11 +133,26 @@ Common::Error EoBEngine::init() {
 	return Common::kNoError;
 }
 
-#define loadSpritesAndEncodeToShapes(resID, shapeBuffer, numShapes, width, height) \
+#define loadSpritesAndEncodeToShapes(resID, resOffset, shapeBuffer, numShapes, width, height) \
 	shapeBuffer = new const uint8 *[numShapes]; \
 	memset(shapeBuffer, 0, numShapes * sizeof(uint8*)); \
 	in = _sres->resData(resID); \
-	_screen->sega_encodeShapesFromSprites(shapeBuffer, in, numShapes, width, height, 3); \
+	_screen->sega_encodeShapesFromSprites(shapeBuffer, in + (resOffset), numShapes, width, height, 3); \
+	delete[] in
+
+#define loadSpritesAndMergeToSingleShape(resID, resOffset, singleShape, numSprites, spriteWidth, spriteHeight) \
+	in = _sres->resData(resID); \
+	{ \
+	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(7, true); \
+	_screen->sega_getAnimator()->clearSprites(); \
+	int cp = _screen->setCurPage(7); \
+	singleShape = _screen->encodeShape(0, 0, numSprites, spriteHeight); \
+	_screen->setCurPage(cp); \
+	_screen->clearPage(7); \
+	} \
 	delete[] in
 
 #define loadAndConvertShapes(resID, resOffset, shapeBuffer, numShapes, width, height, size) \
@@ -142,7 +160,7 @@ Common::Error EoBEngine::init() {
 	memset(shapeBuffer, 0, numShapes * sizeof(uint8*)); \
 	in = _sres->resData(resID); \
 	for (int ii = 0; ii < numShapes; ++ii) \
-		shapeBuffer[ii] = _screen->sega_convertShape(in + resOffset + ii * size, width, height, 3); \
+		shapeBuffer[ii] = _screen->sega_convertShape(in + (resOffset) + ii * size, width, height, 3); \
 	delete[] in
 
 void EoBEngine::loadItemsAndDecorationsShapes() {
@@ -155,9 +173,13 @@ void EoBEngine::loadItemsAndDecorationsShapes() {
 	_sres->loadContainer("ITEM");
 	uint8 *in = 0;
 
-	loadSpritesAndEncodeToShapes(0, _itemIconShapes, _numItemIconShapes, 16, 16);
-	loadSpritesAndEncodeToShapes(14, _blueItemIconShapes, _numItemIconShapes, 16, 16);
-	loadSpritesAndEncodeToShapes(13, _xtraItemIconShapes, 3, 16, 16);
+	loadSpritesAndEncodeToShapes(0, 0, _itemIconShapes, _numItemIconShapes, 16, 16);
+	loadSpritesAndEncodeToShapes(14, 0, _blueItemIconShapes, _numItemIconShapes, 16, 16);
+	loadSpritesAndEncodeToShapes(13, 0, _xtraItemIconShapes, 3, 16, 16);
+
+	for (int i = 0; i < 7; ++i) {
+		loadAndConvertShapes(0, i << 11, _strikeAnimShapes[i], 5, 32, 32, 512);
+	}
 
 	loadAndConvertShapes(1, 0, _smallItemShapes, _numSmallItemShapes, 32, 24, 768);
 	loadAndConvertShapes(2, 0, _largeItemShapes, _numLargeItemShapes, 64, 24, 1472);
@@ -170,10 +192,20 @@ void EoBEngine::loadItemsAndDecorationsShapes() {
 		loadAndConvertShapes(2, offset2, _largeItemShapesScl[i], _numLargeItemShapes, (6 - 2 * i) << 3, 16 - ((i >> 1) << 3), 1472);
 		loadAndConvertShapes(11, offset1, _thrownItemShapesScl[i], _numThrownItemShapes, (3 - i) << 3, 16 - ((i >> 1) << 3), 768);
 	}
+
+	loadSpritesAndMergeToSingleShape(5, 0, _redSplatShape, 5, 8, 24);
+	loadSpritesAndMergeToSingleShape(5, 2016, _swapShape, 7, 8, 8);
+	loadSpritesAndEncodeToShapes(5, 480, _weaponSlotShapes, 6, 32, 16);
+	loadSpritesAndEncodeToShapes(6, 0, _invSmallDigits, 32, 16, 8);
+
+	in = _res->fileData("FACE", 0);
+	_screen->sega_encodeShapesFromSprites(&_deadCharShape, in + 53 * 512, 1, 32, 32, 3);
+	delete[] in;	
 }
 
 #undef loadAndConvertShapes
 #undef loadSpritesAndEncodeToShapes
+#undef loadSpritesAndMergeToSingleShape
 
 Common::SeekableReadStreamEndian *EoBEngine::getItemDefinitionFile(int index) {
 	assert(index == 0 || index == 1);
@@ -889,11 +921,49 @@ void EoBEngine::gui_drawPlayField(bool refresh) {
 		return;
 	}
 
+	if (!_loading)
+		_screen->sega_fadeToBlack(1);
+
+	_screen->sega_getAnimator()->clearSprites();
+	SegaRenderer *r = _screen->sega_getRenderer();
+	
+
 	uint8 *data = _res->fileData("PLAYFLD", 0);
 
 	delete[] data;
 
-	_screen->sega_fadeToNeutral(0);
+	if (refresh && !_sceneDrawPage2)
+		drawScene(0);
+
+	_screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK);
+
+	if (!_loading) {
+		_screen->sega_fadeToNeutral(1);
+		_screen->updateScreen()
+	}
+}
+
+void EoBEngine::gui_drawWeaponSlotStatus(int x, int y, int status) {
+	if (_flags.platform != Common::kPlatformSegaCD) {
+		EoBCoreEngine::gui_drawWeaponSlotStatus(x, y, status);
+		return;
+	}
+
+	if (status < 0) {
+		_screen->drawShape(_screen->_curPage, _weaponSlotShapes[status < -2 ? -status - 1 : 3 - status], x - 1, y, 0);
+	} else {
+		_screen->drawShape(_screen->_curPage, _weaponSlotShapes[0], x - 1, y, 0);
+		gui_printInventoryDigits(x + 8, y + 6, status);
+	}
+}
+
+void EoBEngine::gui_printInventoryDigits(int x, int y, int val) {
+	if (_flags.platform != Common::kPlatformSegaCD) {
+		EoBCoreEngine::gui_printInventoryDigits(x, y, val);
+		return;
+	}
+	_screen->drawShape(_screen->_curPage, _invSmallDigits[(val < 10) ? 22 + val : (val >= 100 ? 1 : 2 + val / 10)], x, y);
+	_screen->drawShape(_screen->_curPage, (val >= 10 && val < 100) ? _invSmallDigits[12 + (val % 10)] : 0, x, y);
 }
 
 const KyraRpgGUISettings *EoBEngine::guiSettings() const {
diff --git a/engines/kyra/engine/eob.h b/engines/kyra/engine/eob.h
index 1b5e48c046..71a042c185 100644
--- a/engines/kyra/engine/eob.h
+++ b/engines/kyra/engine/eob.h
@@ -181,9 +181,14 @@ private:
 
 	// GUI
 	void gui_drawPlayField(bool refresh) override;
+	void gui_drawWeaponSlotStatus(int x, int y, int status) override;
+	void gui_printInventoryDigits(int x, int y, int val) override;
 	const KyraRpgGUISettings *guiSettings() const override;
 	void useMainMenuGUISettings(bool toggle) override { _useMainMenuGUISettings = toggle; }
 
+	const uint8 **_invSmallDigits;
+	const uint8 **_weaponSlotShapes;
+
 	static const KyraRpgGUISettings _guiSettingsVGA;
 	static const KyraRpgGUISettings _guiSettingsEGA;
 	static const KyraRpgGUISettings _guiSettingsPC98;
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 77c19af4f7..e2d8b2949c 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -68,12 +68,13 @@ EoBCoreEngine::EoBCoreEngine(OSystem *system, const GameFlags &flags) : KyraRpgE
 
 	_largeItemShapes = _smallItemShapes = _thrownItemShapes = _spellShapes = _firebeamShapes = 0;
 	_itemIconShapes = _blueItemIconShapes = _xtraItemIconShapes = _wallOfForceShapes = _teleporterShapes = _sparkShapes = _compassShapes = 0;
-	_redSplatShape = _greenSplatShape = _deadCharShape = _disabledCharGrid = 0;
+	_redSplatShape = _greenSplatShape = _deadCharShape = _disabledCharGrid = _swapShape = 0;
 	_blackBoxSmallGrid = _weaponSlotGrid = _blackBoxWideGrid = _lightningColumnShape = 0;
 
 	memset(_largeItemShapesScl, 0, sizeof(_largeItemShapesScl));
 	memset(_smallItemShapesScl, 0, sizeof(_smallItemShapesScl));
 	memset(_thrownItemShapesScl, 0, sizeof(_thrownItemShapesScl));
+	memset(_strikeAnimShapes, 0, sizeof(_strikeAnimShapes));
 
 	_monsterAcHitChanceTable1 = _monsterAcHitChanceTable2 = 0;
 	
@@ -874,15 +875,6 @@ void EoBCoreEngine::loadItemsAndDecorationsShapes() {
 	}
 }
 
-#define releaseShpArr(shapes, num) \
-if (shapes) { \
-	for (int iii = 0; iii < num; iii++) { \
-		if (shapes[iii]) \
-			delete[] shapes[iii]; \
-	} \
-} \
-shapes = 0
-
 void EoBCoreEngine::releaseItemsAndDecorationsShapes() {
 	if (_flags.platform != Common::kPlatformFMTowns || _flags.gameID != GI_EOB2) {
 		releaseShpArr(_largeItemShapes, _numLargeItemShapes);
@@ -900,6 +892,7 @@ void EoBCoreEngine::releaseItemsAndDecorationsShapes() {
 
 		delete[] _redSplatShape;
 		delete[] _greenSplatShape;
+		delete[] _swapShape;
 		delete[] _deadCharShape;
 		delete[] _disabledCharGrid;
 		delete[] _blackBoxSmallGrid;
@@ -929,9 +922,12 @@ void EoBCoreEngine::releaseItemsAndDecorationsShapes() {
 		delete[] _smallItemShapesScl[i];
 		delete[] _thrownItemShapesScl[i];
 	}
-}
 
-#undef releaseShpArr
+	for (int i = 0; i < 7; ++i) {
+		releaseShpArr(_strikeAnimShapes[i], 5);
+		delete[] _strikeAnimShapes[i];
+	}
+}
 
 void EoBCoreEngine::setHandItem(Item itemIndex) {
 	if (itemIndex == -1) {
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index e033123242..252d9552cc 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -35,6 +35,15 @@ class Keymap;
 
 namespace Kyra {
 
+#define releaseShpArr(shapes, num) \
+if (shapes) { \
+	for (int iii = 0; iii < num; iii++) { \
+		if (shapes[iii]) \
+			delete[] shapes[iii]; \
+	} \
+} \
+shapes = 0
+
 struct DarkMoonShapeDef {
 	int16 index;
 	uint8 x, y, w, h;
@@ -293,6 +302,7 @@ protected:
 	const uint8 **_largeItemShapesScl[3];
 	const uint8 **_smallItemShapesScl[3];
 	const uint8 **_thrownItemShapesScl[3];
+	const uint8 **_strikeAnimShapes[7];
 	const uint8 **_blueItemIconShapes;
 	const uint8 **_xtraItemIconShapes;
 	const int _numLargeItemShapes;
@@ -704,7 +714,8 @@ protected:
 	void gui_drawCharPortraitWithStats(int index);
 	void gui_drawFaceShape(int index);
 	void gui_drawWeaponSlot(int charIndex, int slot);
-	void gui_drawWeaponSlotStatus(int x, int y, int status);
+	virtual void gui_drawWeaponSlotStatus(int x, int y, int status);
+	virtual void gui_printInventoryDigits(int x, int y, int val) {}
 	void gui_drawHitpoints(int index);
 	void gui_drawFoodStatusGraph(int index);
 	void gui_drawHorizontalBarGraph(int x, int y, int w, int h, int32 curVal, int32 maxVal, int col1, int col2) override;
@@ -797,6 +808,7 @@ protected:
 	Screen::FontId _conFont;
 	const uint8 **_compassShapes;
 	uint8 _charExchangeSwap;
+	uint8 *_swapShape;
 	bool _configHpBarGraphs;
 	bool _configMouseBtSwap;
 
diff --git a/engines/kyra/graphics/screen_eob.h b/engines/kyra/graphics/screen_eob.h
index 46dfffe587..cac3be9f46 100644
--- a/engines/kyra/graphics/screen_eob.h
+++ b/engines/kyra/graphics/screen_eob.h
@@ -133,7 +133,7 @@ public:
 	void sega_gfxScale(uint8 *out, uint16 w, uint16 h, uint16 pitch, const uint8 *in, const uint16 *stampMap, const uint16 *traceVectors);
 	void sega_drawClippedLine(uint8 *dst, int pW, int pH, int x, int y, int w, int h, uint8 color);
 	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);
+	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; }
 	SegaAnimator *sega_getAnimator() const { return _segaAnimator; }
diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index a7bdfaac91..168570a038 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -65,7 +65,7 @@ void Screen_EoB::sega_selectPalette(int srcPalID, int dstPalID, bool set) {
 	for (int i = 0; i < 16; ++i) {
 		uint16 in = *src++;
 		_segaCurPalette[dstPalID << 4 | i] = in;
-#if 1
+#if 0
 		static const uint8 col[8] = { 0, 52, 87, 116, 144, 172, 206, 255 };
 		*dst++ = col[CLIP<int>(((in & 0x00F) >> 1) + _palFaders[dstPalID]._brCur, 0, 7)];
 		*dst++ = col[CLIP<int>(((in & 0x0F0) >> 5) + _palFaders[dstPalID]._brCur, 0, 7)];
@@ -314,7 +314,7 @@ uint8 *Screen_EoB::sega_convertShape(const uint8 *src, int w, int h, int pal, in
 	return shp;
 }
 
-void Screen_EoB::sega_encodeShapesFromSprites(const uint8 **dst, const uint8 *src, int numShapes, int w, int h, int pal) {
+void Screen_EoB::sega_encodeShapesFromSprites(const uint8 **dst, const uint8 *src, int numShapes, int w, int h, int pal, bool removeSprites) {
 	int spriteSize = (w * h) >> 1;
 	_segaRenderer->loadToVRAM(src, numShapes * spriteSize, 0);
 	int hw = (((h >> 3) - 1) << 2) | ((w >> 3) - 1);
@@ -329,6 +329,12 @@ void Screen_EoB::sega_encodeShapesFromSprites(const uint8 **dst, const uint8 *sr
 		}
 		
 		_segaAnimator->update();
+
+		_segaRenderer->render(0, true);
+		sega_selectPalette(7, 3, true);
+		sega_fadeToNeutral(0);
+		updateScreen();
+
 		_segaRenderer->render(7, true);
 
 		for (int i = l; i < s; ++i)
@@ -337,9 +343,11 @@ void Screen_EoB::sega_encodeShapesFromSprites(const uint8 **dst, const uint8 *sr
 		clearPage(7);
 	}
 
-	_segaAnimator->clearSprites();
-	_segaAnimator->update();
-	_segaRenderer->memsetVRAM(0, 0, numShapes * spriteSize);
+	if (removeSprites) {
+		_segaAnimator->clearSprites();
+		_segaAnimator->update();
+		_segaRenderer->memsetVRAM(0, 0, numShapes * spriteSize);
+	}
 	setCurPage(cp);
 }
 
@@ -630,8 +638,8 @@ void SegaRenderer::render(int destPageNum, bool spritesOnly) {
 	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 bW = ((*pos >> 8) & 3) + 1;
+		uint8 bH = ((*pos >> 10) & 3) + 1;
 		uint8 next = *pos++ & 0x7F;
 		uint16 pal = ((*pos >> 13) & 3) << 4;
 		bool prio = (*pos & 0x8000);
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 73932bcf49..7ee59880f8 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -71,15 +71,6 @@ void EoBCoreEngine::gui_drawPlayField(bool refresh) {
 			_screen->getPalette(7).copy(_screen->getPalette(1), 0, 32);
 		}
 	}
-
-	// load playfield etc.
-	/*_screen->sega_getAnimator()->clearSprites();
-	_screen->sega_getRenderer()->setupWindowPlane(0, 0, SegaRenderer::kWinToLeft, SegaRenderer::kWinToTop);
-	_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 40, 28, 0x2000);
-	_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 28, 0x2000);
-	_screen->sega_selectPalette(6, 1, false);
-	_screen->sega_selectPalette(7, 3, true);*/
-	//_screen->sega_fadeToNeutral(0);
 }
 
 void EoBCoreEngine::gui_restorePlayField() {
@@ -98,6 +89,8 @@ void EoBCoreEngine::gui_drawCharPortraitWithStats(int index) {
 	if (!testCharacter(index, 1))
 		return;
 
+	// , , 23 * 8 = 184, 32 * 8 = 256
+	// 1 * 8 , 8 * 8, 15 * 8
 	static const uint16 charPortraitPosX[] = { 8, 80, 184, 256 };
 	static const uint16 charPortraitPosY[] = { 2, 54, 106 };
 
@@ -120,11 +113,14 @@ void EoBCoreEngine::gui_drawCharPortraitWithStats(int index) {
 
 		Screen::FontId cf = _screen->setFont(_invFont1);
 
-		if (index == _exchangeCharacterId)
-			_screen->printText(_characterGuiStringsSt[0], x2 + 2, y2 + 2, guiSettings()->colors.guiColorDarkRed, guiSettings()->colors.fill);
-		else
+		if (index == _exchangeCharacterId) {
+			if (_flags.platform == Common::kPlatformSegaCD)
+				_screen->drawShape(_screen->_curPage, _swapShape, x2 + 2, y2 + 2);
+			else
+				_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->setFont(_invFont2);
 
 		gui_drawFaceShape(index);
@@ -137,8 +133,12 @@ void EoBCoreEngine::gui_drawCharPortraitWithStats(int index) {
 
 		if (c->damageTaken > 0) {
 			_screen->drawShape(2, _redSplatShape, x2 + 13, y2 + 30, 0);
-			Common::String tmpStr = Common::String::format("%d", c->damageTaken);
-			_screen->printText(tmpStr.c_str(), x2 + 34 - tmpStr.size() * 3, y2 + 42, (_configRenderMode == Common::kRenderCGA) ? 12 : guiSettings()->colors.guiColorWhite, 0);
+			if (_flags.platform == Common::kPlatformSegaCD) {
+				gui_printInventoryDigits(x2 + 25, y2 + 40, c->damageTaken);
+			} else {
+				Common::String tmpStr = Common::String::format("%d", c->damageTaken);
+				_screen->printText(tmpStr.c_str(), x2 + 34 - tmpStr.size() * 3, y2 + 42, (_configRenderMode == Common::kRenderCGA) ? 12 : guiSettings()->colors.guiColorWhite, 0);
+			}
 		}
 
 		_screen->setCurPage(cp);
@@ -561,11 +561,15 @@ void EoBCoreEngine::gui_drawInventoryItem(int slot, int redraw, int pageNum) {
 		if (slot == 16) {
 			_screen->fillRect(227, 65, 238, 69, guiSettings()->colors.guiColorBlack);
 			int cnt = countQueuedItems(_characters[_updateCharNum].inventory[slot], -1, -1, 1, 1);
-			x = cnt >= 10 ? 227 : 233;
-			Screen::FontId cf = _screen->setFont(Screen::FID_6_FNT);
-			Common::String str = Common::String::format("%d", cnt);
-			_screen->printText(str.c_str(), x, 65, guiSettings()->colors.guiColorWhite, 0);
-			_screen->setFont(cf);
+			if (_flags.platform != Common::kPlatformSegaCD) {
+				x = cnt >= 10 ? 227 : 233;
+				Screen::FontId cf = _screen->setFont(Screen::FID_6_FNT);
+				Common::String str = Common::String::format("%d", cnt);
+				_screen->printText(str.c_str(), x, 65, guiSettings()->colors.guiColorWhite, 0);
+				_screen->setFont(cf);
+			} else {
+				gui_printInventoryDigits(227, 65, cnt);
+			}
 		}
 	}
 
diff --git a/engines/kyra/sequence/sequences_eob.cpp b/engines/kyra/sequence/sequences_eob.cpp
index d83cc5f63f..7a48d7de5e 100644
--- a/engines/kyra/sequence/sequences_eob.cpp
+++ b/engines/kyra/sequence/sequences_eob.cpp
@@ -2726,7 +2726,7 @@ void EoBEngine::seq_segaRestoreAfterSequence() {
 	r->setupWindowPlane(0, 0, SegaRenderer::kWinToLeft, SegaRenderer::kWinToTop);
 	r->fillRectWithTiles(0, 0, 0, 40, 28, 0x2000);
 	r->fillRectWithTiles(1, 0, 0, 40, 28, 0x2000);
-	r->render(0);
+	_screen->clearPage(0);
 }
 
 bool EoBEngine::seq_segaPlaySequence(int sequenceId, bool init) {


Commit: 59df137cf46057bc65b933aad8e3dca209430bdd
    https://github.com/scummvm/scummvm/commit/59df137cf46057bc65b933aad8e3dca209430bdd
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:13+02:00

Commit Message:
KYRA: (EOB) - fix Amiga, PC98 and SegaCD GUIO flags

- all versions were missing the GAMEOPTION_EOB_MOUSESWAP flag
- SegaCD should not have the GAMEOPTION_EOB_HPGRAPHS flag

Changed paths:
    engines/kyra/detection_tables.h


diff --git a/engines/kyra/detection_tables.h b/engines/kyra/detection_tables.h
index 26427c1d0a..a7962b46a1 100644
--- a/engines/kyra/detection_tables.h
+++ b/engines/kyra/detection_tables.h
@@ -1792,7 +1792,7 @@ const KYRAGameDescription adGameDescs[] = {
 			Common::EN_ANY,
 			Common::kPlatformAmiga,
 			ADGF_NO_FLAGS,
-			GUIO4(GUIO_NOSPEECH, GUIO_MIDIAMIGA, GUIO_RENDERAMIGA, GAMEOPTION_EOB_HPGRAPHS)
+			GUIO5(GUIO_NOSPEECH, GUIO_MIDIAMIGA, GUIO_RENDERAMIGA, GAMEOPTION_EOB_HPGRAPHS, GAMEOPTION_EOB_MOUSESWAP)
 		},
 		EOB_FLAGS
 	},
@@ -1808,7 +1808,7 @@ const KYRAGameDescription adGameDescs[] = {
 			Common::DE_DEU,
 			Common::kPlatformAmiga,
 			ADGF_NO_FLAGS,
-			GUIO4(GUIO_NOSPEECH, GUIO_MIDIAMIGA, GUIO_RENDERAMIGA, GAMEOPTION_EOB_HPGRAPHS)
+			GUIO5(GUIO_NOSPEECH, GUIO_MIDIAMIGA, GUIO_RENDERAMIGA, GAMEOPTION_EOB_HPGRAPHS, GAMEOPTION_EOB_MOUSESWAP)
 		},
 		EOB_FLAGS
 	},
@@ -1824,7 +1824,7 @@ const KYRAGameDescription adGameDescs[] = {
 			Common::JA_JPN,
 			Common::kPlatformPC98,
 			ADGF_NO_FLAGS,
-			GUIO4(GUIO_NOSPEECH, GUIO_MIDIPC98, GUIO_RENDERPC9801, GAMEOPTION_EOB_HPGRAPHS)
+			GUIO5(GUIO_NOSPEECH, GUIO_MIDIPC98, GUIO_RENDERPC9801, GAMEOPTION_EOB_HPGRAPHS, GAMEOPTION_EOB_MOUSESWAP)
 		},
 		EOB_PC98_FLAGS
 	},
@@ -1840,7 +1840,7 @@ const KYRAGameDescription adGameDescs[] = {
 			Common::EN_ANY,
 			Common::kPlatformSegaCD,
 			ADGF_NO_FLAGS,
-			GUIO3(GUIO_NOSPEECH, GUIO_NOMIDI, GAMEOPTION_EOB_HPGRAPHS)
+			GUIO3(GUIO_NOSPEECH, GUIO_NOMIDI, GAMEOPTION_EOB_MOUSESWAP)
 		},
 		EOB_FLAGS
 	},
@@ -1920,7 +1920,7 @@ const KYRAGameDescription adGameDescs[] = {
 			Common::EN_ANY,
 			Common::kPlatformAmiga,
 			ADGF_NO_FLAGS,
-			GUIO4(GUIO_NOSPEECH, GUIO_MIDIAMIGA, GUIO_RENDERAMIGA, GAMEOPTION_EOB_HPGRAPHS)
+			GUIO5(GUIO_NOSPEECH, GUIO_MIDIAMIGA, GUIO_RENDERAMIGA, GAMEOPTION_EOB_HPGRAPHS, GAMEOPTION_EOB_MOUSESWAP)
 		},
 		EOB2_FLAGS
 	},
@@ -1936,7 +1936,7 @@ const KYRAGameDescription adGameDescs[] = {
 			Common::DE_DEU,
 			Common::kPlatformAmiga,
 			ADGF_NO_FLAGS,
-			GUIO4(GUIO_NOSPEECH, GUIO_MIDIAMIGA, GUIO_RENDERAMIGA, GAMEOPTION_EOB_HPGRAPHS)
+			GUIO5(GUIO_NOSPEECH, GUIO_MIDIAMIGA, GUIO_RENDERAMIGA, GAMEOPTION_EOB_HPGRAPHS, GAMEOPTION_EOB_MOUSESWAP)
 		},
 		EOB2_FLAGS
 	},


Commit: 9d8c2490158ee9f19fa999f91ee5293dcdea77f3
    https://github.com/scummvm/scummvm/commit/9d8c2490158ee9f19fa999f91ee5293dcdea77f3
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:13+02:00

Commit Message:
KYRA: (EOB/SegaCD) - vertically shift screen output

The ingame graphics use up 208 pixels out of 224. The top and bottom 8x8 pixel tile rows are left blank.
This commit emulates that. But actually it looks ugly, especially in windowed mode. On a TV screen of that era it was probably unnoticeable.
TODO: GET RID OF THIS COMMIT AND FIND ANOTHER SOLUTION

Changed paths:
    engines/kyra/engine/kyra_v1.cpp
    engines/kyra/graphics/screen.cpp
    engines/kyra/graphics/screen.h
    engines/kyra/kyra_v1.h


diff --git a/engines/kyra/engine/kyra_v1.cpp b/engines/kyra/engine/kyra_v1.cpp
index 70e366fc81..4fe37bf3d6 100644
--- a/engines/kyra/engine/kyra_v1.cpp
+++ b/engines/kyra/engine/kyra_v1.cpp
@@ -66,6 +66,7 @@ KyraEngine_v1::KyraEngine_v1(OSystem *system, const GameFlags &flags)
 	_isSaveAllowed = false;
 
 	_mouseX = _mouseY = 0;
+	_transOffsY = 0;
 	_asciiCodeEvents = _kbEventSkip = false;
 
 	// sets up all engine specific debug levels
@@ -213,11 +214,12 @@ KyraEngine_v1::~KyraEngine_v1() {
 
 Common::Point KyraEngine_v1::getMousePos() {
 	Common::Point mouse = _eventMan->getMousePos();
+	mouse.y -= -_transOffsY;
 
 	if (_flags.useHiRes) {
 		mouse.x >>= 1;
 		mouse.y >>= 1;
-	}
+	}	
 
 	return mouse;
 }
@@ -297,7 +299,7 @@ int KyraEngine_v1::checkInput(Button *buttonList, bool mainLoop, int eventFlag)
 		case Common::EVENT_LBUTTONDOWN:
 		case Common::EVENT_LBUTTONUP: {
 			_mouseX = event.mouse.x;
-			_mouseY = event.mouse.y;
+			_mouseY = event.mouse.y - _transOffsY;
 			if (_flags.useHiRes) {
 				_mouseX >>= 1;
 				_mouseY >>= 1;
@@ -309,7 +311,7 @@ int KyraEngine_v1::checkInput(Button *buttonList, bool mainLoop, int eventFlag)
 		case Common::EVENT_RBUTTONDOWN:
 		case Common::EVENT_RBUTTONUP: {
 			_mouseX = event.mouse.x;
-			_mouseY = event.mouse.y;
+			_mouseY = event.mouse.y - _transOffsY;
 			if (_flags.useHiRes) {
 				_mouseX >>= 1;
 				_mouseY >>= 1;
@@ -493,6 +495,11 @@ void KyraEngine_v1::removeInputTop() {
 		_eventList.erase(_eventList.begin());
 }
 
+void KyraEngine_v1::transposeScreenOutputY(int yAdd) {
+	_transOffsY = yAdd;
+	screen()->transposeScreenOutputY(yAdd);
+}
+
 bool KyraEngine_v1::skipFlag() const {
 	for (Common::List<Event>::const_iterator i = _eventList.begin(); i != _eventList.end(); ++i) {
 		if (i->causedSkip)
diff --git a/engines/kyra/graphics/screen.cpp b/engines/kyra/graphics/screen.cpp
index 460e7bbe89..a45f5a2822 100644
--- a/engines/kyra/graphics/screen.cpp
+++ b/engines/kyra/graphics/screen.cpp
@@ -77,6 +77,8 @@ Screen::Screen(KyraEngine_v1 *vm, OSystem *system, const ScreenDim *dimTable, co
 	_textMarginRight = SCREEN_W;
 	_customDimTable = 0;
 	_curDim = 0;
+
+	_yTransOffs = 0;
 }
 
 Screen::~Screen() {
@@ -365,12 +367,12 @@ void Screen::updateScreen() {
 
 void Screen::updateDirtyRects() {
 	if (_forceFullUpdate) {
-		_system->copyRectToScreen(getCPagePtr(0), SCREEN_W, 0, 0, SCREEN_W, _screenHeight);
+		_system->copyRectToScreen(getCPagePtr(0), SCREEN_W, 0, _yTransOffs, SCREEN_W, _screenHeight - _yTransOffs);
 	} else {
 		const byte *page0 = getCPagePtr(0);
 		Common::List<Common::Rect>::iterator it;
 		for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
-			_system->copyRectToScreen(page0 + it->top * SCREEN_W + it->left, SCREEN_W, it->left, it->top, it->width(), it->height());
+			_system->copyRectToScreen(page0 + it->top * SCREEN_W + it->left, SCREEN_W, it->left, it->top + _yTransOffs, it->width(), it->height());
 		}
 	}
 	_forceFullUpdate = false;
@@ -921,6 +923,11 @@ void Screen::setScreenPalette(const Palette &pal) {
 	_system->getPaletteManager()->setPalette(screenPal, 0, pal.getNumColors());
 }
 
+void Screen::transposeScreenOutputY(int yAdd) {
+	updateScreen();
+	_yTransOffs = yAdd;
+}
+
 void Screen::enableDualPaletteMode(int splitY) {
 	_dualPaletteModeSplitY = splitY;
 
@@ -3387,7 +3394,7 @@ void Screen::addDirtyRect(int x, int y, int w, int h) {
 	Common::Rect r(x, y, x + w, y + h);
 
 	// Clip rectangle
-	r.clip(SCREEN_W, _screenHeight);
+	r.clip(SCREEN_W, _screenHeight - _yTransOffs);
 
 	// If it is empty after clipping, we are done
 	if (r.isEmpty())
diff --git a/engines/kyra/graphics/screen.h b/engines/kyra/graphics/screen.h
index 71dd24635a..41a8d90a88 100644
--- a/engines/kyra/graphics/screen.h
+++ b/engines/kyra/graphics/screen.h
@@ -479,6 +479,11 @@ public:
 	void setPaletteIndex(uint8 index, uint8 red, uint8 green, uint8 blue);
 	virtual void setScreenPalette(const Palette &pal);
 
+	// SegaCD version
+	// This is a somewhat hacky but probably least invasive way to
+	// move the whole ingame screen output down a couple of lines.
+	void transposeScreenOutputY(int yAdd);
+
 	// AMIGA version only
 	bool isInterfacePaletteEnabled() const { return _dualPaletteModeSplitY; }
 	void enableDualPaletteMode(int splitY);
@@ -635,6 +640,7 @@ protected:
 	int _bytesPerPixel;
 	int _screenPageSize;
 	const int _screenHeight;
+	int _yTransOffs;
 	
 	Common::SharedPtr<Graphics::FontSJIS> _sjisFontShared;
 	uint8 _sjisInvisibleColor;
diff --git a/engines/kyra/kyra_v1.h b/engines/kyra/kyra_v1.h
index bcda071767..f99303a6cf 100644
--- a/engines/kyra/kyra_v1.h
+++ b/engines/kyra/kyra_v1.h
@@ -275,6 +275,11 @@ protected:
 
 	int _mouseX, _mouseY;
 
+	// This is a somewhat hacky but probably least invasive way to move
+	// the whole ingame screen output down a couple of lines for EOB SegaCD.
+	void transposeScreenOutputY(int yAdd);
+	int _transOffsY;
+
 	struct Event {
 		Common::Event event;
 		bool causedSkip;


Commit: 8f41adc9697f633b00fb678b1a7931202ab273eb
    https://github.com/scummvm/scummvm/commit/8f41adc9697f633b00fb678b1a7931202ab273eb
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:13+02:00

Commit Message:
KYRA: - fix copyBlockToPage() glitch

(fix for negative out-of-bounds x offsets - apparently never used like that before)

Changed paths:
    engines/kyra/graphics/screen.cpp


diff --git a/engines/kyra/graphics/screen.cpp b/engines/kyra/graphics/screen.cpp
index a45f5a2822..a507dc0c65 100644
--- a/engines/kyra/graphics/screen.cpp
+++ b/engines/kyra/graphics/screen.cpp
@@ -1074,6 +1074,7 @@ void Screen::copyBlockToPage(int pageNum, int x, int y, int w, int h, const uint
 		h = _screenHeight - y;
 	}
 
+	int pitch = w;
 	if (x < 0) {
 		src += -x * _bytesPerPixel;
 		w += x;
@@ -1095,7 +1096,7 @@ void Screen::copyBlockToPage(int pageNum, int x, int y, int w, int h, const uint
 	while (h--) {
 		memcpy(dst, src, w * _bytesPerPixel);
 		dst += SCREEN_W * _bytesPerPixel;
-		src += w * _bytesPerPixel;
+		src += pitch * _bytesPerPixel;
 	}
 }
 


Commit: 0fc87659fb3d764a13cf8c4848b277184b9a4b3e
    https://github.com/scummvm/scummvm/commit/0fc87659fb3d764a13cf8c4848b277184b9a4b3e
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:14+02:00

Commit Message:
KYRA: (EOB/SegaCD) - more work on the gui

- HP bar graphs, hit animations, scene shaking, inventory, etc.
- Inventory and character and stats page are still unfinished

Changed paths:
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eob.h
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h
    engines/kyra/engine/kyra_rpg.h
    engines/kyra/engine/lol.h
    engines/kyra/engine/scene_eob.cpp
    engines/kyra/engine/sprites_eob.cpp
    engines/kyra/graphics/screen_eob_segacd.cpp
    engines/kyra/graphics/screen_eob_segacd.h
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/gui/saveload_eob.cpp
    engines/kyra/resource/staticres_eob.cpp
    engines/kyra/resource/staticres_lol.cpp
    engines/kyra/sequence/seqplayer_eob_segacd.cpp
    engines/kyra/sequence/sequences_eob.cpp


diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index 50e0d46ff0..d8256f3d92 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -30,6 +30,8 @@
 #include "kyra/sound/sound.h"
 #include "kyra/text/text_eob_segacd.h"
 
+#include "common/system.h"
+
 namespace Kyra {
 
 EoBEngine::EoBEngine(OSystem *system, const GameFlags &flags)
@@ -51,9 +53,17 @@ EoBEngine::EoBEngine(OSystem *system, const GameFlags &flags)
 	_dscDoorCoordsExt = 0;
 	_invSmallDigits = _weaponSlotShapes = 0;
 	_useMainMenuGUISettings = false;
+	_addrTbl1 = _textFieldPattern = 0;
+	_playFldPattern1 = _invPattern = _statsPattern;
+	_playFldPattern2 = 0;
 	_ttlCfg = 0;
 	_xdth = false;
 
+	memset(_strikeAnimShapes, 0, sizeof(_strikeAnimShapes));
+	_sceneShakeOffsetX = _sceneShakeOffsetY = 0;
+	_shakeBackBuffer1 = _shakeBackBuffer2 = 0;
+	_redGrid = 0;
+
 	_seqPlayer = 0;
 	_sres = 0;
 	_levelCurTrack = 0;
@@ -61,11 +71,21 @@ EoBEngine::EoBEngine(OSystem *system, const GameFlags &flags)
 }
 
 EoBEngine::~EoBEngine() {
-	releaseShpArr(_invSmallDigits, 32);
+	for (int i = 0; i < 7; ++i) {
+		releaseShpArr(_strikeAnimShapes[i], 4);
+	}
+
 	releaseShpArr(_weaponSlotShapes, 6);
+	releaseShpArr(_invSmallDigits, 32);
+	delete[] _redGrid;
+
 	delete[] _doorShapesSrc;
 	delete[] _doorSwitchShapesSrc;
 	delete[] _itemsOverlay;
+	delete[] _playFldPattern2;
+	delete[] _shakeBackBuffer1;
+	delete[] _shakeBackBuffer2;
+
 	delete _seqPlayer;
 	delete _sres;
 }
@@ -128,6 +148,9 @@ Common::Error EoBEngine::init() {
 		assert(_seqPlayer);
 		_txt = new TextDisplayer_SegaCD(this, _screen);
 		assert(_txt);
+		_playFldPattern2 = new uint16[1040];
+		_shakeBackBuffer1 = new uint8[120 * 6];
+		_shakeBackBuffer2 = new uint8[179 * 6];
 	}
 
 	return Common::kNoError;
@@ -149,7 +172,7 @@ Common::Error EoBEngine::init() {
 	_screen->sega_getRenderer()->render(7, true); \
 	_screen->sega_getAnimator()->clearSprites(); \
 	int cp = _screen->setCurPage(7); \
-	singleShape = _screen->encodeShape(0, 0, numSprites, spriteHeight); \
+	singleShape = _screen->encodeShape(0, 0, numSprites  * (spriteWidth >> 3), spriteHeight); \
 	_screen->setCurPage(cp); \
 	_screen->clearPage(7); \
 	} \
@@ -178,7 +201,7 @@ void EoBEngine::loadItemsAndDecorationsShapes() {
 	loadSpritesAndEncodeToShapes(13, 0, _xtraItemIconShapes, 3, 16, 16);
 
 	for (int i = 0; i < 7; ++i) {
-		loadAndConvertShapes(0, i << 11, _strikeAnimShapes[i], 5, 32, 32, 512);
+		loadAndConvertShapes(15, i << 11, _strikeAnimShapes[i], 4, 32, 32, 512);
 	}
 
 	loadAndConvertShapes(1, 0, _smallItemShapes, _numSmallItemShapes, 32, 24, 768);
@@ -195,12 +218,35 @@ void EoBEngine::loadItemsAndDecorationsShapes() {
 
 	loadSpritesAndMergeToSingleShape(5, 0, _redSplatShape, 5, 8, 24);
 	loadSpritesAndMergeToSingleShape(5, 2016, _swapShape, 7, 8, 8);
+	loadSpritesAndMergeToSingleShape(12, 0, _deadCharShape, 1, 32, 32);
 	loadSpritesAndEncodeToShapes(5, 480, _weaponSlotShapes, 6, 32, 16);
 	loadSpritesAndEncodeToShapes(6, 0, _invSmallDigits, 32, 16, 8);
 
-	in = _res->fileData("FACE", 0);
-	_screen->sega_encodeShapesFromSprites(&_deadCharShape, in + 53 * 512, 1, 32, 32, 3);
-	delete[] in;	
+	/*
+	// CAMP MENU
+	str = _sres->resStreamEndian(8);
+	_screen->sega_getRenderer()->loadStreamToVRAM(str, 0x20, true);
+	delete str;
+	_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 22, 15, 0);
+	_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 22, 21, 0x4001, true);
+	_screen->sega_getRenderer()->render(0);
+	_screen->sega_selectPalette(40, 2, true);
+	*/
+
+	int cp = _screen->setCurPage(7);
+	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(7);
+	_screen->drawShape(7, _weaponSlotShapes[1], 0, 0, 0);
+	_screen->drawShape(7, _weaponSlotShapes[1], 0, 16, 0);
+	_weaponSlotGrid = _screen->encodeShape(0, 0, 4, 16);
+	_disabledCharGrid = _screen->encodeShape(0, 0, 4, 32);
+	_blackBoxSmallGrid = _screen->encodeShape(0, 0, 2, 8);
+	_blackBoxWideGrid = _screen->encodeShape(0, 0, 4, 8);
+	_redGrid = _screen->encodeShape(0, 32, 4, 32);
+	_screen->clearPage(7);
+	_screen->setCurPage(cp);
 }
 
 #undef loadAndConvertShapes
@@ -225,7 +271,7 @@ void EoBEngine::startupNew() {
 		_screen->sega_selectPalette(6, 1);
 		_screen->sega_selectPalette(8, 2);
 		_screen->sega_selectPalette(7, 3);
-
+		makeNameShapes();
 		_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 40, 28, 0x2000);
 		_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 28, 0x2000);
 		_txt->clearDim(0);
@@ -246,6 +292,58 @@ void EoBEngine::startupLoad() {
 	_screen->selectPC98Palette(0, _screen->getPalette(0));
 }
 
+void EoBEngine::updateSpecialGfx() {
+	bool updScreen = false;
+
+	// Red grid effect
+	for (int i = 0; i < 6; ++i) {
+		if (!_characters[i].specialGfxCountdown)
+			continue;
+		_characters[i].specialGfxCountdown--;
+		int cp = _screen->setCurPage(0);
+
+		if (!_currentControlMode && (_characters[i].specialGfxCountdown & 1))
+			_screen->drawShape(0, _redGrid, 176 + guiSettings()->charBoxCoords.facePosX_1[i & 1], guiSettings()->charBoxCoords.facePosY_1[i >> 1], 0);
+		else if (_currentControlMode && _updateCharNum == i && (_characters[i].specialGfxCountdown & 1))
+			_screen->drawShape(0, _redGrid, guiSettings()->charBoxCoords.facePosX_2[0], guiSettings()->charBoxCoords.facePosY_2[0], 0);
+		else
+			gui_drawFaceShape(i);
+
+		_screen->setCurPage(cp);
+		updScreen = true;
+	}
+
+	// Scene shake
+	if (_specialGfxCountdown) {
+		--_specialGfxCountdown;
+		_sceneShakeOffsetX = _sceneShakeOffsets[_specialGfxCountdown << 1];
+		_sceneShakeOffsetY = _sceneShakeOffsets[(_specialGfxCountdown << 1) + 1];
+		_screen->fillRect(0, 0, 2, 119, 0, _sceneDrawPage1);
+		_screen->fillRect(0, 0, 175, 2, 0, _sceneDrawPage1);
+		_screen->copyBlockToPage(_sceneDrawPage1, 173, 0, 6, 120, _shakeBackBuffer1);
+		_screen->copyBlockToPage(_sceneDrawPage1, 0, 117, 179, 6, _shakeBackBuffer2);
+		_screen->copyBlockToPage(_sceneDrawPage1, _sceneXoffset + _sceneShakeOffsetX, _sceneShakeOffsetY, 176, 120, _sceneWindowBuffer);
+
+		// For whatever reason the original shakes all types of shapes (decorations, doors, etc.) except the monsters and
+		// the items lying on the floor. So we do the same. I've added drawing flags to drawSceneShapes() which allow
+		// separate drawing passes for the different shape types.
+		_shapeShakeOffsetX = _sceneShakeOffsetX;
+		_shapeShakeOffsetY = _sceneShakeOffsetY;
+		// All shapes except monsters and items
+		drawSceneShapes(0, 0xFF & ~0x2A);
+		_shapeShakeOffsetX = _shapeShakeOffsetY = 0;
+		// Monsters and items
+		drawSceneShapes(0, 0x2A);
+
+		_screen->copyRegion(0, 0, 0, 0, 179, 123, _sceneDrawPage1, 0, Screen::CR_NO_P_CHECK);
+		updScreen = true;
+	}
+
+	if (updScreen)
+		_screen->updateScreen();
+	_sceneDrawPage1 = 2;
+}
+
 void EoBEngine::drawNpcScene(int npcIndex) {
 	_screen->copyRegion(0, 0, 0, 0, 176, 120, 6, 0, Screen::CR_NO_P_CHECK);
 	switch (npcIndex) {
@@ -766,6 +864,45 @@ void EoBEngine::setLevelPalettes(int level) {
 	_screen->sega_selectPalette(palette2[level], 2);
 }
 
+void EoBEngine::playStrikeAnimation(uint8 pos, Item itm) {
+	static const uint8 aX[5] = { 28, 116, 28, 116, 72 };
+	static const uint8 aY[5] = { 60, 60, 40, 40, 50 };
+	static const uint8 aTypes[5][5] = {
+		{ 1,  3, 27,  48, 255 },
+		{ 2, 24, 26,  54,  77 },
+		{ 4,  6, 86, 255, 255 },
+		{ 5, 73, 90, 255, 255 },
+		{ 6, 35, 81, 255, 255 }
+	};
+
+	if (!_strikeAnimShapes[0])
+		return;
+
+	int aType = -1;
+	for (int i = 0; i < 5 && aType == -1; ++i) {
+		const uint8 *p = aTypes[i];
+		uint8 t = *p++;
+		for (int ii = 1; ii < 5 && aType == -1; ++ii) {
+			if (_items[itm].nameUnid == *p++)
+				aType = t;
+		}
+	}
+
+	if (aType < 0)
+		return;
+
+	int16 x = aX[pos];
+	int16 y = aY[pos];
+	for (int i = 0; i < 5; ++i) {
+		uint32 del = _system->getMillis() + _tickLength;
+		_screen->copyRegionToBuffer(0, x, y, 32, 32, _spellAnimBuffer);
+		_screen->drawShape(0, _strikeAnimShapes[aType][MIN(i, 3)], x, y);
+		_screen->updateScreen();
+		_screen->copyBlockToPage(0, x, y, 32, 32, _spellAnimBuffer);
+		delayUntil(del);
+	}
+}
+
 void EoBEngine::turnUndeadAuto() {
 	if (_currentLevel != 2 && _currentLevel != 7)
 		return;
@@ -915,6 +1052,39 @@ void EoBEngine::healParty() {
 	}
 }
 
+void EoBEngine::makeNameShapes() {
+	if (_flags.platform != Common::kPlatformSegaCD)
+		return;
+
+	int cd = _txt->clearDim(4);
+	int cp = _screen->setCurPage(2);
+	_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 28, 0x2000);
+	_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 30, 28, 0x600A, true);
+	_screen->sega_clearTextBuffer(0);
+
+	for (int i = 0; i < 6; ++i) {
+		if (!_characters[i].flags)
+			continue;
+		_txt->printShadowedText(_characters[i].name, 0, i << 4, 0xFF, 0xCC);
+	}
+
+	_screen->sega_getRenderer()->render(_screen->_curPage);
+
+	for (int i = 0; i < 6; ++i) {
+		if (!_characters[i].flags)
+			continue;
+		delete[] _characters[i].nameShape;
+		_characters[i].nameShape = _screen->encodeShape(0, i << 4, (_screen->getTextWidth(_characters[i].name) + 8) >> 3, 13);
+	}
+
+	_screen->clearPage(2);
+	_screen->setCurPage(cp);
+	_screen->sega_clearTextBuffer(0);
+
+	_txt->clearDim(4);
+	_txt->clearDim(cd);
+}
+
 void EoBEngine::gui_drawPlayField(bool refresh) {
 	if (_flags.platform != Common::kPlatformSegaCD) {
 		EoBCoreEngine::gui_drawPlayField(refresh);
@@ -924,23 +1094,73 @@ void EoBEngine::gui_drawPlayField(bool refresh) {
 	if (!_loading)
 		_screen->sega_fadeToBlack(1);
 
+	// transposeScreenOutputY(8);
+	_txt->clearDim(0);
 	_screen->sega_getAnimator()->clearSprites();
 	SegaRenderer *r = _screen->sega_getRenderer();
-	
 
 	uint8 *data = _res->fileData("PLAYFLD", 0);
-
+	for (int i = 0; i < 256; ++i)
+		r->loadToVRAM(&data[i << 5], 32, _addrTbl1[i] << 5);
 	delete[] data;
 
+	const uint16 *pattern = _playFldPattern1;
+	uint16 *dst = _playFldPattern2;
+
+	for (int i = 0; i < 1040; ++i) {
+		int ix = (*pattern++) - 11;
+		*dst++ = (ix < 0) ? 0 : _addrTbl1[ix];
+	}
+
+	const uint16 ps[] = { 0xCE, 0xE0, 0x2FE, 0x310, 0x52E, 0x540 };
+
+	for (int i = 0; i < 4; ++i) {
+		dst = &_playFldPattern2[ps[i] >> 1];
+		memset(dst, 0, 8);
+		memset(&dst[40], 0, 8);
+		memset(&dst[80], 0, 8);
+		memset(&dst[120], 0, 8);
+	}
+
+	r->fillRectWithTiles(1, 0, 0, 40, 26, 0x2000, true, false, _playFldPattern2);
+	r->fillRectWithTiles(0, 0, 21, 40, 5, 0x2000, true, false, _textFieldPattern);
+
+	// Name tables for scene window vcn block tiles. We don't need that. We draw the blocks with our "normal" graphics code.
+	// r->fillRectWithTiles(1, 0, 0, 22, 15, 0xC14B, true, true);
+	// Name tables for scene window shapes tiles. We don't need that, since we're not going to draw any shapes with the renderer.
+	// 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);
+
+	_sres->loadContainer("ITEM");
+	Common::SeekableReadStreamEndian *str = _sres->resStreamEndian(7);
+	r->loadStreamToVRAM(str, 0x8880, true);
+	delete str;
+	r->fillRectWithTiles(1, 22, 0, 18, 21, 0x6444, true, true, _invPattern);
+	r->render(2);
+	r->fillRectWithTiles(1, 22, 0, 18, 21, 0x6444, true, true, _statsPattern);
+	r->render(4);
+
 	if (refresh && !_sceneDrawPage2)
 		drawScene(0);
 
-	_screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK);
+	_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);
+	_screen->copyRegionToBuffer(0, 173, 0, 6, 120, _shakeBackBuffer1);
+	_screen->copyRegionToBuffer(0, 0, 117, 179, 6, _shakeBackBuffer2);
+
+	// Since we're no going to draw with the SegaRenderer but rather with our "normal" code, we have to backup some parts of
+	// the background between the character portraits. Unlike the other versions the red splat shape overlaps with that space.
+	for (int i = 0; i < 6; ++i) {
+		delete[] _redSplatBG[i];
+		_redSplatBG[i] = new uint8[_redSplatShape[2] << 5];
+		_screen->copyRegionToBuffer(0, guiSettings()->charBoxCoords.boxX[i & 1] + guiSettings()->charBoxCoords.redSplatOffsetX, guiSettings()->charBoxCoords.boxY[i >> 1] + guiSettings()->charBoxCoords.boxHeight - 1, _redSplatShape[2] << 3, 4, _redSplatBG[i]);
+	}
 
-	if (!_loading) {
+	if (!_loading)
 		_screen->sega_fadeToNeutral(1);
-		_screen->updateScreen()
-	}
 }
 
 void EoBEngine::gui_drawWeaponSlotStatus(int x, int y, int status) {
@@ -973,6 +1193,8 @@ const KyraRpgGUISettings *EoBEngine::guiSettings() const {
 		return &_guiSettingsEGA;
 	else if (_flags.platform == Common::kPlatformPC98)
 		return &_guiSettingsPC98;
+	else if (_flags.platform == Common::kPlatformSegaCD)
+		return &_guiSettingsSegaCD;
 	else
 		return &_guiSettingsVGA;
 }
diff --git a/engines/kyra/engine/eob.h b/engines/kyra/engine/eob.h
index 71a042c185..b02dff7910 100644
--- a/engines/kyra/engine/eob.h
+++ b/engines/kyra/engine/eob.h
@@ -81,6 +81,8 @@ private:
 	void startupNew() override;
 	void startupLoad() override;
 
+	void updateSpecialGfx() override;
+
 	// Intro/Outro/Sequence Playback
 	enum IntroPart {
 		kOnlyCredits = 0,
@@ -156,6 +158,10 @@ private:
 	int _dcrResCur;
 
 	// Fight
+	void playStrikeAnimation(uint8 pos, Item itm) override;
+
+	const uint8 *_redGrid;
+	const uint8 **_strikeAnimShapes[7];
 	static const uint8 _monsterAcHitChanceTbl1[];
 	static const uint8 _monsterAcHitChanceTbl2[];
 
@@ -175,6 +181,12 @@ private:
 	bool checkPartyStatusExtra() override;
 	int resurrectionSelectDialogue() override;
 	void healParty();
+	void makeNameShapes() override;
+
+	int _sceneShakeOffsetX;
+	int _sceneShakeOffsetY;
+	uint8 *_shakeBackBuffer1;
+	uint8 *_shakeBackBuffer2;
 
 	// Resource
 	SegaCDResource *_sres;
@@ -183,18 +195,29 @@ private:
 	void gui_drawPlayField(bool refresh) override;
 	void gui_drawWeaponSlotStatus(int x, int y, int status) override;
 	void gui_printInventoryDigits(int x, int y, int val) override;
+
 	const KyraRpgGUISettings *guiSettings() const override;
 	void useMainMenuGUISettings(bool toggle) override { _useMainMenuGUISettings = toggle; }
 
 	const uint8 **_invSmallDigits;
 	const uint8 **_weaponSlotShapes;
 
+	const uint16 *_addrTbl1;
+	const uint16 *_textFieldPattern;
+	const uint16 *_playFldPattern1;
+	const uint16 *_invPattern;
+	const uint16 *_statsPattern;
+	uint16 *_playFldPattern2;
+
 	static const KyraRpgGUISettings _guiSettingsVGA;
 	static const KyraRpgGUISettings _guiSettingsEGA;
 	static const KyraRpgGUISettings _guiSettingsPC98;
 	static const KyraRpgGUISettings _guiSettingsAmiga;
 	static const KyraRpgGUISettings _guiSettingsAmigaMainMenu;
+	static const KyraRpgGUISettings _guiSettingsSegaCD;
 	static const uint8 _egaDefaultPalette[];
+	static const uint8 _redGridTile[8];
+	static const int8 _sceneShakeOffsets[66];
 	bool _useMainMenuGUISettings;
 };
 
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index e2d8b2949c..6bd003941e 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -71,10 +71,10 @@ EoBCoreEngine::EoBCoreEngine(OSystem *system, const GameFlags &flags) : KyraRpgE
 	_redSplatShape = _greenSplatShape = _deadCharShape = _disabledCharGrid = _swapShape = 0;
 	_blackBoxSmallGrid = _weaponSlotGrid = _blackBoxWideGrid = _lightningColumnShape = 0;
 
+	memset(_redSplatBG, 0, sizeof(_redSplatBG));
 	memset(_largeItemShapesScl, 0, sizeof(_largeItemShapesScl));
 	memset(_smallItemShapesScl, 0, sizeof(_smallItemShapesScl));
 	memset(_thrownItemShapesScl, 0, sizeof(_thrownItemShapesScl));
-	memset(_strikeAnimShapes, 0, sizeof(_strikeAnimShapes));
 
 	_monsterAcHitChanceTable1 = _monsterAcHitChanceTable2 = 0;
 	
@@ -109,6 +109,7 @@ EoBCoreEngine::EoBCoreEngine(OSystem *system, const GameFlags &flags) : KyraRpgE
 	_monsters = 0;
 	_dstMonsterIndex = 0;
 	_preventMonsterFlash = false;
+	_specialGfxCountdown = 0;
 
 	_teleporterPulse = 0;
 
@@ -125,6 +126,7 @@ EoBCoreEngine::EoBCoreEngine(OSystem *system, const GameFlags &flags) : KyraRpgE
 	_dscDoorY1 = 0;
 	_dscDoorXE = 0;
 
+	_shapeShakeOffsetX = _shapeShakeOffsetY = 0;
 	_greenFadingTable = _blueFadingTable = _lightBlueFadingTable = _blackFadingTable = _greyFadingTable = 0;
 
 	_menuDefs = 0;
@@ -235,7 +237,9 @@ EoBCoreEngine::EoBCoreEngine(OSystem *system, const GameFlags &flags) : KyraRpgE
 	_dcrShpDataPos = 0;
 	_amigaSoundMap = 0;
 	_amigaCurSoundFile = -1;
-	_prefMenuPlatformOffset = 0;
+	_prefMenuPlatformOffset = 0;	
+	_lastVIntTick = _lastSecTick = _totalPlaySecs = _totalEnemiesKilled = _totalSteps = 0;
+
 	memset(_cgaMappingLevel, 0, sizeof(_cgaMappingLevel));
 	memset(_expRequirementTables, 0, sizeof(_expRequirementTables));
 	memset(_saveThrowTables, 0, sizeof(_saveThrowTables));
@@ -279,18 +283,17 @@ EoBCoreEngine::~EoBCoreEngine() {
 	}
 
 	if (_characters) {
-		for (int i = 0; i < 6; i++)
+		for (int i = 0; i < 6; i++) {
 			delete[] _characters[i].faceShape;
+			delete[] _characters[i].nameShape;
+		}
 	}
 
 	delete[] _characters;
 	delete[] _items;
 	delete[] _itemTypes;
-	if (_itemNames) {
-		for (int i = 0; i < 130; i++)
-			delete[] _itemNames[i];
-	}
-	delete[] _itemNames;
+
+	releaseShpArr(_itemNames, 130);
 	delete[] _flyingObjects;
 
 	delete[] _monsterFlashOverlay;
@@ -329,6 +332,9 @@ EoBCoreEngine::~EoBCoreEngine() {
 	delete[] _wallsOfForce;
 	delete[] _buttonDefs;
 
+	for (int i = 0; i < 6; i++)
+		delete[] _redSplatBG[i];
+
 	delete _gui;
 	_gui = 0;
 	delete _screen;
@@ -728,11 +734,23 @@ void EoBCoreEngine::runLoop() {
 		updateScriptTimers();
 		updateWallOfForceTimers();
 
-		if (_sceneUpdateRequired)
+		if (_sceneUpdateRequired && !_specialGfxCountdown)
 			drawScene(1);
 
-		if (_envAudioTimer < _system->getMillis() && !(_flags.gameID == GI_EOB1 && (_flags.platform == Common::kPlatformAmiga || _currentLevel == 0 || _currentLevel > 3))) {
-			_envAudioTimer = _system->getMillis() + (rollDice(1, 10, 3) * 18 * _tickLength);
+		uint32 curTime = _system->getMillis();
+		if (_lastVIntTick + 1000 <= curTime) {
+			_lastSecTick = curTime;
+			_totalPlaySecs++;
+		}
+
+		if (_lastVIntTick + 16 <= curTime) {
+			_lastVIntTick = curTime;
+			updateSpecialGfx();
+			curTime = _system->getMillis();
+		}
+		
+		if (_envAudioTimer < curTime && !(_flags.gameID == GI_EOB1 && (_flags.platform == Common::kPlatformSegaCD || _flags.platform == Common::kPlatformAmiga || _currentLevel == 0 || _currentLevel > 3))) {
+			_envAudioTimer = curTime + (rollDice(1, 10, 3) * 18 * _tickLength);
 			snd_processEnvironmentalSoundEffect(_flags.gameID == GI_EOB1 ? 30 : (rollDice(1, 2, -1) ? 27 : 28), _currentBlock + rollDice(1, 12, -1));
 		}
 
@@ -772,7 +790,6 @@ void EoBCoreEngine::loadItemsAndDecorationsShapes() {
 	releaseItemsAndDecorationsShapes();
 	int div = (_flags.gameID == GI_EOB1) ? 3 : 8;
 	int mul = (_flags.gameID == GI_EOB1) ? 64 : 24;
-	int size = 0;
 
 	_largeItemShapes = new const uint8*[_numLargeItemShapes];
 	_screen->loadShapeSetBitmap("ITEML1", 5, 3);
@@ -922,11 +939,6 @@ void EoBCoreEngine::releaseItemsAndDecorationsShapes() {
 		delete[] _smallItemShapesScl[i];
 		delete[] _thrownItemShapesScl[i];
 	}
-
-	for (int i = 0; i < 7; ++i) {
-		releaseShpArr(_strikeAnimShapes[i], 5);
-		delete[] _strikeAnimShapes[i];
-	}
 }
 
 void EoBCoreEngine::setHandItem(Item itemIndex) {
@@ -1998,6 +2010,7 @@ void EoBCoreEngine::useSlotWeapon(int charIndex, int slotIndex, Item item) {
 		if (!inflict)
 			inflict = -1;
 		snd_playSoundEffect(32);
+		playStrikeAnimation(inflict > 0 ? (_monsters[_dstMonsterIndex].pos == 4 ? 4 : _dscItemPosIndex[(_currentDirection << 2) | (_monsters[_dstMonsterIndex].pos & 3)]) : 4, item);
 	} else if (ep == 2) {
 		inflict = thrownAttack(charIndex, slotIndex, item);
 	} else if (ep == 3) {
@@ -2206,6 +2219,8 @@ void EoBCoreEngine::inflictCharacterDamage(int charIndex, int damage) {
 
 	if (c->hitPointsCur > -10) {
 		snd_playSoundEffect(21);
+		if (_flags.platform == Common::kPlatformSegaCD)
+			_specialGfxCountdown = c->specialGfxCountdown = 32;
 	} else {
 		c->hitPointsCur = -10;
 		c->flags &= 1;
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index 252d9552cc..50de75e415 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -41,6 +41,7 @@ if (shapes) { \
 		if (shapes[iii]) \
 			delete[] shapes[iii]; \
 	} \
+	delete[] shapes; \
 } \
 shapes = 0
 
@@ -117,6 +118,7 @@ struct EoBCharacter {
 	uint8 level[3];
 	uint32 experience[3];
 	const uint8 *faceShape;
+	const uint8 *nameShape;
 
 	int8 mageSpells[80];
 	int8 clericSpells[80];
@@ -129,6 +131,8 @@ struct EoBCharacter {
 	uint32 effectFlags;
 	uint8 damageTaken;
 	int8 slotStatus[5];
+
+	int8 specialGfxCountdown;
 };
 
 struct EoBItem {
@@ -302,7 +306,6 @@ protected:
 	const uint8 **_largeItemShapesScl[3];
 	const uint8 **_smallItemShapesScl[3];
 	const uint8 **_thrownItemShapesScl[3];
-	const uint8 **_strikeAnimShapes[7];
 	const uint8 **_blueItemIconShapes;
 	const uint8 **_xtraItemIconShapes;
 	const int _numLargeItemShapes;
@@ -324,6 +327,7 @@ protected:
 	const uint8 *_blackBoxWideGrid;
 	const uint8 *_lightningColumnShape;
 
+	uint8 *_redSplatBG[6];
 	uint8 *_itemsOverlay;
 	static const uint8 _itemsOverlayCGA[];
 
@@ -338,6 +342,7 @@ protected:
 	void runLoop();
 	void update() override { screen()->updateScreen(); }
 	bool checkPartyStatus(bool handleDeath);
+	virtual void updateSpecialGfx() {}
 
 	bool _runFlag;
 
@@ -381,6 +386,12 @@ protected:
 	uint32 _disableElapsedTime;
 	uint32 _restPartyElapsedTime;
 
+	uint32 _lastVIntTick;
+	uint32 _lastSecTick;
+	uint32 _totalPlaySecs;
+	uint32 _totalEnemiesKilled;
+	uint32 _totalSteps;
+
 	// Mouse
 	void setHandItem(Item itemIndex) override;
 
@@ -561,6 +572,8 @@ protected:
 
 	uint8 *_monsterFlashOverlay;
 	uint8 *_monsterStoneOverlay;
+	int16 _shapeShakeOffsetX;
+	int16 _shapeShakeOffsetY;
 
 	SpriteDecoration *_monsterDecorations;
 	EoBMonsterProperty *_monsterProps;
@@ -625,9 +638,9 @@ protected:
 	virtual const uint8 *loadDoorShapes(const char *filename, int doorIndex, const uint8 *shapeDefs) = 0;
 	virtual void setLevelPalettes(int level) {}
 
-	void drawScene(int refresh) override;
-	void drawSceneShapes(int start = 0) override;
-	void drawDecorations(int index) override;
+	void drawScene(int refresh);
+	void drawSceneShapes(int start = 0, int drawFlags = 0xFF);
+	void drawDecorations(int index);
 
 	int calcNewBlockPositionAndTestPassability(uint16 curBlock, uint16 direction);
 	void notifyBlockNotPassable();
@@ -716,6 +729,7 @@ protected:
 	void gui_drawWeaponSlot(int charIndex, int slot);
 	virtual void gui_drawWeaponSlotStatus(int x, int y, int status);
 	virtual void gui_printInventoryDigits(int x, int y, int val) {}
+	virtual void gui_playStrikeAnimation(uint8 pos, Item itm) {}
 	void gui_drawHitpoints(int index);
 	void gui_drawFoodStatusGraph(int index);
 	void gui_drawHorizontalBarGraph(int x, int y, int w, int h, int32 curVal, int32 maxVal, int col1, int col2) override;
@@ -888,6 +902,7 @@ protected:
 	virtual void drawLightningColumn() {}
 	virtual int charSelectDialogue() { return -1; }
 	virtual void characterLevelGain(int charIndex) {}
+	virtual void makeNameShapes() {}
 
 	Common::Error loadGameState(int slot) override;
 	Common::Error saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail) override;
@@ -942,6 +957,7 @@ protected:
 	int closeDistanceAttack(int charIndex, Item item);
 	int thrownAttack(int charIndex, int slotIndex, Item item);
 	int projectileWeaponAttack(int charIndex, Item item);
+	virtual void playStrikeAnimation(uint8 pos, Item itm) {}
 
 	void inflictMonsterDamage(EoBMonsterInPlay *m, int damage, bool giveExperience);
 	void calcAndInflictMonsterDamage(EoBMonsterInPlay *m, int times, int pips, int offs, int flags, int savingThrowType, int savingThrowEffect);
@@ -974,6 +990,7 @@ protected:
 
 	int _dstMonsterIndex;
 	bool _preventMonsterFlash;
+	int8 _specialGfxCountdown;
 	int16 _foundMonstersArray[5];
 	int8 _monsterBlockPosArray[6];
 	const uint8 *_monsterAcHitChanceTable1;
diff --git a/engines/kyra/engine/kyra_rpg.h b/engines/kyra/engine/kyra_rpg.h
index 95e78bf651..f3be83df8b 100644
--- a/engines/kyra/engine/kyra_rpg.h
+++ b/engines/kyra/engine/kyra_rpg.h
@@ -130,6 +130,29 @@ struct KyraRpgGUISettings {
 		uint8 guiColorDarkGreen;
 		uint8 guiColorBlack;
 	} colors;
+
+	struct CharacterBoxCoords {
+		int16 boxX[3];
+		int16 boxY[3];
+		uint8 boxWidth;
+		uint8 boxHeight;
+		int16 facePosX_1[3];
+		int16 facePosY_1[3];
+		int16 facePosX_2[3];
+		int16 facePosY_2[3];
+		int16 weaponSlotX[3];
+		int16 weaponSlotY[6];
+		int16 hpBarX_1[3];
+		int16 hpBarY_1[3];
+		uint8 hpBarWidth_1;
+		uint8 hpBarHeight_1;
+		int16 hpFoodBarX_2[3];
+		int16 hpFoodBarY_2[3];
+		uint8 hpFoodBarWidth_2;
+		uint8 hpFoodBarHeight_2;
+		int16 redSplatOffsetX;
+		int16 redSplatOffsetY;
+	} charBoxCoords;
 };
 
 class KyraRpgEngine : public KyraEngine_v1 {
@@ -198,10 +221,6 @@ protected:
 	virtual void addLevelItems() = 0;
 	virtual void loadBlockProperties(const char *file) = 0;
 
-	virtual void drawScene(int pageNum) = 0;
-	virtual void drawSceneShapes(int start) = 0;
-	virtual void drawDecorations(int index) = 0;
-
 	virtual const uint8 *getBlockFileData(int levelIndex) = 0;
 	void setLevelShapesDim(int index, int16 &x1, int16 &x2, int dim);
 	void setDoorShapeDim(int index, int16 &y1, int16 &y2, int dim);
diff --git a/engines/kyra/engine/lol.h b/engines/kyra/engine/lol.h
index cff94db138..974f1d9b4c 100644
--- a/engines/kyra/engine/lol.h
+++ b/engines/kyra/engine/lol.h
@@ -928,10 +928,10 @@ private:
 	bool testWallFlag(int block, int direction, int flag);
 	bool testWallInvisibility(int block, int direction);
 
-	void drawScene(int pageNum) override;
+	void drawScene(int pageNum);
+	void drawSceneShapes(int start = 0);
+	void drawDecorations(int index);
 
-	void drawSceneShapes(int start = 0) override;
-	void drawDecorations(int index) override;
 	void drawBlockEffects(int index, int type);
 	void drawSpecialGuiShape(int pageNum);
 	void setWallType(int block, int wall, int val);
diff --git a/engines/kyra/engine/scene_eob.cpp b/engines/kyra/engine/scene_eob.cpp
index 63855adf08..bbc34accba 100644
--- a/engines/kyra/engine/scene_eob.cpp
+++ b/engines/kyra/engine/scene_eob.cpp
@@ -589,7 +589,7 @@ void EoBCoreEngine::drawScene(int refresh) {
 	_sceneUpdateRequired = false;
 }
 
-void EoBCoreEngine::drawSceneShapes(int start) {
+void EoBCoreEngine::drawSceneShapes(int start, int drawFlags) {
 	for (int i = start; i < 18; i++) {
 		uint8 t = _dscTileIndex[i];
 		uint8 s = _visibleBlocks[t]->walls[_sceneDrawVarDown];
@@ -602,31 +602,33 @@ void EoBCoreEngine::drawSceneShapes(int start) {
 		if (_shpDmX2 <= _shpDmX1)
 			continue;
 
-		drawDecorations(t);
+		if (drawFlags & 0x01)
+			drawDecorations(t);
 
-		if (_visibleBlocks[t]->drawObjects)
+		if ((drawFlags & 0x02) && _visibleBlocks[t]->drawObjects)
 			drawBlockItems(t);
 
 		if (t < 15) {
 			uint16 w = _wllWallFlags[s];
 
-			if (w & 8)
+			if ((drawFlags & 0x04) && (w & 8))
 				drawDoor(t);
 
-			if (_visibleBlocks[t]->flags & 7) {
+			if ((drawFlags & 0x08) && (_visibleBlocks[t]->flags & 7)) {
 				const ScreenDim *dm = _screen->getScreenDim(5);
 				_screen->modifyScreenDim(5, dm->sx, _lvlShapeTop[t], dm->w, _lvlShapeBottom[t] - _lvlShapeTop[t]);
 				drawMonsters(t);
 				drawLevelModifyScreenDim(5, _lvlShapeLeftRight[(t << 1)], 0, _lvlShapeLeftRight[(t << 1) + 1], 15);
 			}
 
-			if (_flags.gameID == GI_EOB2 && s == 74)
+			if ((drawFlags & 0x10) && _flags.gameID == GI_EOB2 && s == 74)
 				drawWallOfForce(t);
 		}
 
-		drawFlyingObjects(t);
+		if (drawFlags & 0x20)
+			drawFlyingObjects(t);
 
-		if (s == _teleporterWallId)
+		if ((drawFlags & 0x40) && s == _teleporterWallId)
 			drawTeleporter(t);
 	}
 }
diff --git a/engines/kyra/engine/sprites_eob.cpp b/engines/kyra/engine/sprites_eob.cpp
index 54d2113a74..19f7482ce7 100644
--- a/engines/kyra/engine/sprites_eob.cpp
+++ b/engines/kyra/engine/sprites_eob.cpp
@@ -285,7 +285,7 @@ void EoBCoreEngine::drawBlockObject(int flipped, int page, const uint8 *shape, i
 	if (_flags.gameID == GI_EOB1)
 		x &= ~1;
 	
-	_screen->drawShape(page, shape, x - (d->sx << 3), y - d->sy, sd, flipped | (ovl ? 2 : 0), ovl);
+	_screen->drawShape(page, shape, x - (d->sx << 3) + _shapeShakeOffsetX, y - d->sy + _shapeShakeOffsetY, sd, flipped | (ovl ? 2 : 0), ovl);
 }
 
 void EoBCoreEngine::drawMonsterShape(const uint8 *shape, int x, int y, int flipped, int flags, int palIndex) {
diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index 168570a038..f3f26b4010 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -317,7 +317,7 @@ uint8 *Screen_EoB::sega_convertShape(const uint8 *src, int w, int h, int pal, in
 void Screen_EoB::sega_encodeShapesFromSprites(const uint8 **dst, const uint8 *src, int numShapes, int w, int h, int pal, bool removeSprites) {
 	int spriteSize = (w * h) >> 1;
 	_segaRenderer->loadToVRAM(src, numShapes * spriteSize, 0);
-	int hw = (((h >> 3) - 1) << 2) | ((w >> 3) - 1);
+	int hw = (((w >> 3) - 1) << 2) | ((h >> 3) - 1);
 
 	int cp = setCurPage(7);
 
@@ -330,11 +330,11 @@ void Screen_EoB::sega_encodeShapesFromSprites(const uint8 **dst, const uint8 *sr
 		
 		_segaAnimator->update();
 
-		_segaRenderer->render(0, true);
+/*		_segaRenderer->render(0, true);
 		sega_selectPalette(7, 3, true);
 		sega_fadeToNeutral(0);
 		updateScreen();
-
+*/
 		_segaRenderer->render(7, true);
 
 		for (int i = l; i < s; ++i)
@@ -348,6 +348,7 @@ void Screen_EoB::sega_encodeShapesFromSprites(const uint8 **dst, const uint8 *sr
 		_segaAnimator->update();
 		_segaRenderer->memsetVRAM(0, 0, numShapes * spriteSize);
 	}
+
 	setCurPage(cp);
 }
 
@@ -490,14 +491,14 @@ void SegaRenderer::setVScrollMode(int mode) {
 	_vScrollMode = mode;
 }
 
-void SegaRenderer::loadToVRAM(const void *data, int dataSize, int addr) {
+void SegaRenderer::loadToVRAM(const void *data, uint16 dataSize, uint16 addr) {
 	assert(data);
 	assert(addr + dataSize <= 0x10000);
 	memcpy(_vram + addr, data, dataSize);
 	checkUpdateDirtyRects(addr, dataSize);
 }
 
-void SegaRenderer::loadToVRAM(Common::SeekableReadStreamEndian *in, int addr, bool compressedData) {
+void SegaRenderer::loadStreamToVRAM(Common::SeekableReadStreamEndian *in, uint16 addr, bool compressedData) {
 	assert(in);
 	assert(addr <= 0xFFFF);
 
@@ -638,8 +639,8 @@ void SegaRenderer::render(int destPageNum, bool spritesOnly) {
 	const uint16 *pos = _spriteTable;
 	for (int i = 0; i < _numSpritesMax && pos; ++i) {
 		int y = *pos++ & 0x3FF;
-		uint8 bW = ((*pos >> 8) & 3) + 1;
-		uint8 bH = ((*pos >> 10) & 3) + 1;
+		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);
diff --git a/engines/kyra/graphics/screen_eob_segacd.h b/engines/kyra/graphics/screen_eob_segacd.h
index 83f43b71f2..d34bb5064a 100644
--- a/engines/kyra/graphics/screen_eob_segacd.h
+++ b/engines/kyra/graphics/screen_eob_segacd.h
@@ -77,8 +77,8 @@ public:
 	void setHScrollMode(int mode);
 	void setVScrollMode(int mode);
 
-	void loadToVRAM(const void *data, int dataSize, int addr);
-	void loadToVRAM(Common::SeekableReadStreamEndian *in, int addr, bool compressedData = false);
+	void loadToVRAM(const void *data, uint16 dataSize, uint16 addr);
+	void loadStreamToVRAM(Common::SeekableReadStreamEndian *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 writeVSRAMValue(int addr, uint16 value);
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 7ee59880f8..476055e42b 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -81,7 +81,7 @@ void EoBCoreEngine::gui_restorePlayField() {
 }
 
 void EoBCoreEngine::gui_drawAllCharPortraitsWithStats() {
-	for (int i = 0; i < 6; i++)
+	for (int i = 5; i >= 0; --i)
 		gui_drawCharPortraitWithStats(i);
 }
 
@@ -89,11 +89,6 @@ void EoBCoreEngine::gui_drawCharPortraitWithStats(int index) {
 	if (!testCharacter(index, 1))
 		return;
 
-	// , , 23 * 8 = 184, 32 * 8 = 256
-	// 1 * 8 , 8 * 8, 15 * 8
-	static const uint16 charPortraitPosX[] = { 8, 80, 184, 256 };
-	static const uint16 charPortraitPosY[] = { 2, 54, 106 };
-
 	EoBCharacter *c = &_characters[index];
 	int txtCol1 = guiSettings()->colors.guiColorBlack;
 	int txtCol2 = guiSettings()->colors.guiColorWhite;
@@ -104,22 +99,22 @@ void EoBCoreEngine::gui_drawCharPortraitWithStats(int index) {
 	}
 
 	if (_currentControlMode == 0) {
-		int x2 = charPortraitPosX[index & 1];
-		int y2 = charPortraitPosY[index >> 1];
-	
-		_screen->copyRegion(176, 168, x2 , y2, 64, 24, 2, 2, Screen::CR_NO_P_CHECK);
-		_screen->copyRegion(240, 168, x2, y2 + 24, 64, 26, 2, 2, Screen::CR_NO_P_CHECK);
-		int cp = _screen->setCurPage(2);
+		int x2 = guiSettings()->charBoxCoords.facePosX_1[index & 1];
+		int y2 = guiSettings()->charBoxCoords.boxY[index >> 1];
 
+		_screen->copyRegion(176, 168, x2, y2, guiSettings()->charBoxCoords.boxWidth, 24, 2, 2, Screen::CR_NO_P_CHECK);
+		_screen->copyRegion(240, 168, x2, y2 + 24, guiSettings()->charBoxCoords.boxWidth, guiSettings()->charBoxCoords.boxHeight - 24, 2, 2, Screen::CR_NO_P_CHECK);
+
+		int cp = _screen->setCurPage(2);
 		Screen::FontId cf = _screen->setFont(_invFont1);
 
-		if (index == _exchangeCharacterId) {
-			if (_flags.platform == Common::kPlatformSegaCD)
-				_screen->drawShape(_screen->_curPage, _swapShape, x2 + 2, y2 + 2);
-			else
-				_screen->printText(_characterGuiStringsSt[0], x2 + 2, y2 + 2, guiSettings()->colors.guiColorDarkRed, guiSettings()->colors.fill);
+		if (_flags.platform == Common::kPlatformSegaCD) {
+			_screen->drawShape(_screen->_curPage, (index == _exchangeCharacterId) ? _swapShape : c->nameShape, x2 + 4, y2 + 4);
 		} else {
-			_screen->printText(c->name, x2 + 2, y2 + (_flags.platform == Common::kPlatformFMTowns ? 1 : 2), txtCol1, _flags.use16ColorMode ? 0 : guiSettings()->colors.fill);
+			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->setFont(_invFont2);
 
@@ -132,9 +127,9 @@ void EoBCoreEngine::gui_drawCharPortraitWithStats(int index) {
 			gui_drawCharPortraitStatusFrame(index);
 
 		if (c->damageTaken > 0) {
-			_screen->drawShape(2, _redSplatShape, x2 + 13, y2 + 30, 0);
+			_screen->drawShape(2, _redSplatShape, x2 + guiSettings()->charBoxCoords.redSplatOffsetX, y2 + guiSettings()->charBoxCoords.redSplatOffsetY, 0);
 			if (_flags.platform == Common::kPlatformSegaCD) {
-				gui_printInventoryDigits(x2 + 25, y2 + 40, c->damageTaken);
+				gui_printInventoryDigits(x2 + guiSettings()->charBoxCoords.redSplatOffsetX + 12, y2 + guiSettings()->charBoxCoords.redSplatOffsetY + 10, c->damageTaken);
 			} else {
 				Common::String tmpStr = Common::String::format("%d", c->damageTaken);
 				_screen->printText(tmpStr.c_str(), x2 + 34 - tmpStr.size() * 3, y2 + 42, (_configRenderMode == Common::kRenderCGA) ? 12 : guiSettings()->colors.guiColorWhite, 0);
@@ -145,16 +140,33 @@ void EoBCoreEngine::gui_drawCharPortraitWithStats(int index) {
 		_screen->setFont(cf);
 
 		if (!cp) {
-			_screen->copyRegion(x2, y2, charPortraitPosX[2 + (index & 1)], y2, 64, 50, 2, 0, Screen::CR_NO_P_CHECK);
+			if (_redSplatBG[index])
+				_screen->copyBlockToPage(0, guiSettings()->charBoxCoords.boxX[index & 1] + guiSettings()->charBoxCoords.redSplatOffsetX, y2 + guiSettings()->charBoxCoords.boxHeight - 1, _redSplatShape[2] << 3, 4, _redSplatBG[index]);
+
+			_screen->copyRegion(x2, y2, guiSettings()->charBoxCoords.boxX[index & 1], y2, guiSettings()->charBoxCoords.boxWidth, guiSettings()->charBoxCoords.boxHeight, 2, 0, Screen::CR_NO_P_CHECK);
+
+			// Redraw this for the SegaCD version, since the red splat shapes do overlap with the next portrait to the bottom and would otherwise be cut off. 
+			if (_flags.platform == Common::kPlatformSegaCD && c->damageTaken > 0) {
+				_screen->drawShape(0, _redSplatShape, guiSettings()->charBoxCoords.boxX[index & 1] + guiSettings()->charBoxCoords.redSplatOffsetX, y2 + guiSettings()->charBoxCoords.redSplatOffsetY, 0);
+				gui_printInventoryDigits(guiSettings()->charBoxCoords.boxX[index & 1] + guiSettings()->charBoxCoords.redSplatOffsetX + 12, y2 + guiSettings()->charBoxCoords.redSplatOffsetY + 10, c->damageTaken);
+			}
 			_screen->updateScreen();
 		}
 	} else if ((_currentControlMode == 1 || _currentControlMode == 2) && index == _updateCharNum) {
 		_screen->copyRegion(176, 0, 0, 0, 144, 168, 2, 2, Screen::CR_NO_P_CHECK);
+		if (_flags.platform == Common::kPlatformSegaCD && _currentControlMode == 2)
+			_screen->copyRegion(176, 0, 176, 0, 144, 168, 4, 2, Screen::CR_NO_P_CHECK);
 		_screen->_curPage = 2;
+
 		gui_drawFaceShape(index);
+
 		Screen::FontId cf = _screen->setFont(_invFont1);
-		_screen->printShadedText(c->name, 219, 6, txtCol2, 0, guiSettings()->colors.guiColorBlack);
+		if (_flags.platform == Common::kPlatformSegaCD)
+			_screen->drawShape(_screen->_curPage, (index == _exchangeCharacterId) ? _swapShape : c->nameShape, 224, 8);
+		else
+			_screen->printShadedText(c->name, 219, 6, txtCol2, 0, guiSettings()->colors.guiColorBlack);
 		_screen->setFont(_invFont2);
+
 		gui_drawHitpoints(index);
 		gui_drawFoodStatusGraph(index);
 
@@ -165,18 +177,20 @@ void EoBCoreEngine::gui_drawCharPortraitWithStats(int index) {
 				_screen->setFont(_invFont3);
 			}
 
-			if (c->hitPointsCur == -10)
-				_screen->printShadedText(_characterGuiStringsSt[1], 247, statusTxtY, guiSettings()->colors.guiColorLightRed, 0, guiSettings()->colors.guiColorBlack);
-			else if (c->hitPointsCur < 1)
-				_screen->printShadedText(_characterGuiStringsSt[2], 226, statusTxtY, guiSettings()->colors.guiColorLightRed, 0, guiSettings()->colors.guiColorBlack);
-			else if (c->effectFlags & 0x2000)
-				_screen->printShadedText(_characterGuiStringsSt[3], 220, statusTxtY, guiSettings()->colors.guiColorLightRed, 0, guiSettings()->colors.guiColorBlack);
-			else if (c->flags & 2)
-				_screen->printShadedText(_characterGuiStringsSt[4], 235, statusTxtY, guiSettings()->colors.guiColorLightRed, 0, guiSettings()->colors.guiColorBlack);
-			else if (c->flags & 4)
-				_screen->printShadedText(_characterGuiStringsSt[5], 232, statusTxtY, guiSettings()->colors.guiColorLightRed, 0, guiSettings()->colors.guiColorBlack);
-			else if (c->flags & 8)
-				_screen->printShadedText(_characterGuiStringsSt[6], 232, statusTxtY, guiSettings()->colors.guiColorLightRed, 0, guiSettings()->colors.guiColorBlack);
+			if (_characterGuiStringsSt) {
+				if (c->hitPointsCur == -10)
+					_screen->printShadedText(_characterGuiStringsSt[1], 247, statusTxtY, guiSettings()->colors.guiColorLightRed, 0, guiSettings()->colors.guiColorBlack);
+				else if (c->hitPointsCur < 1)
+					_screen->printShadedText(_characterGuiStringsSt[2], 226, statusTxtY, guiSettings()->colors.guiColorLightRed, 0, guiSettings()->colors.guiColorBlack);
+				else if (c->effectFlags & 0x2000)
+					_screen->printShadedText(_characterGuiStringsSt[3], 220, statusTxtY, guiSettings()->colors.guiColorLightRed, 0, guiSettings()->colors.guiColorBlack);
+				else if (c->flags & 2)
+					_screen->printShadedText(_characterGuiStringsSt[4], 235, statusTxtY, guiSettings()->colors.guiColorLightRed, 0, guiSettings()->colors.guiColorBlack);
+				else if (c->flags & 4)
+					_screen->printShadedText(_characterGuiStringsSt[5], 232, statusTxtY, guiSettings()->colors.guiColorLightRed, 0, guiSettings()->colors.guiColorBlack);
+				else if (c->flags & 8)
+					_screen->printShadedText(_characterGuiStringsSt[6], 232, statusTxtY, guiSettings()->colors.guiColorLightRed, 0, guiSettings()->colors.guiColorBlack);
+			}
 
 			_screen->setFont(_invFont2);
 
@@ -192,8 +206,10 @@ void EoBCoreEngine::gui_drawCharPortraitWithStats(int index) {
 			static const uint16 cm2X2[] = { 271, 300, 318 };
 			static const uint16 cm2Y2[] = { 165, 165, 147 };
 
-			for (int i = 0; i < 3; i++)
-				_screen->fillRect(cm2X1[i], cm2Y1[i], cm2X2[i], cm2Y2[i], guiSettings()->colors.sfill);
+			if (_flags.platform != Common::kPlatformSegaCD) {
+				for (int i = 0; i < 3; i++)
+					_screen->fillRect(cm2X1[i], cm2Y1[i], cm2X2[i], cm2Y2[i], guiSettings()->colors.sfill);
+			}
 
 			_screen->setFont(cf);
 
@@ -265,11 +281,8 @@ void EoBCoreEngine::gui_drawFaceShape(int index) {
 	if (!testCharacter(index, 1))
 		return;
 
-	static const uint8 xCoords[] = { 8, 80 };
-	static const uint8 yCoords[] = { 11, 63, 115 };
-
-	int x = xCoords[index & 1];
-	int y = yCoords[index >> 1];
+	int x = guiSettings()->charBoxCoords.facePosX_1[index & 1];
+	int y = guiSettings()->charBoxCoords.facePosY_1[index >> 1];
 
 	if (!_screen->_curPage)
 		x += 176;
@@ -278,8 +291,8 @@ void EoBCoreEngine::gui_drawFaceShape(int index) {
 		if (_updateCharNum != index)
 			return;
 
-		x = 181;
-		y = 3;
+		x = guiSettings()->charBoxCoords.facePosX_2[0];
+		y = guiSettings()->charBoxCoords.facePosY_2[0];
 	}
 
 	EoBCharacter *c = &_characters[index];
@@ -323,17 +336,15 @@ void EoBCoreEngine::gui_drawFaceShape(int index) {
 }
 
 void EoBCoreEngine::gui_drawWeaponSlot(int charIndex, int slot) {
-	static const uint8 xCoords[] = { 40, 112 };
-	static const uint8 yCoords[] = { 11, 27, 63, 79, 115, 131 };
-
-	int x = xCoords[charIndex & 1];
-	int y = yCoords[(charIndex & 6) + slot];
+	int x = guiSettings()->charBoxCoords.weaponSlotX[charIndex & 1];
+	int y = guiSettings()->charBoxCoords.weaponSlotY[(charIndex & 6) + slot];
 
 	if (!_screen->_curPage)
 		x += 176;
 
 	int itm = _characters[charIndex].inventory[slot];
-	gui_drawBox(x, y, 31, 16, guiSettings()->colors.frame1, guiSettings()->colors.frame2, guiSettings()->colors.fill);
+	if (_flags.platform != Common::kPlatformSegaCD)
+		gui_drawBox(x, y, 31, 16, guiSettings()->colors.frame1, guiSettings()->colors.frame2, guiSettings()->colors.fill);
 
 	if (_characters[charIndex].slotStatus[slot]) {
 		gui_drawWeaponSlotStatus(x, y, _characters[charIndex].slotStatus[slot]);
@@ -400,23 +411,20 @@ void EoBCoreEngine::gui_drawHitpoints(int index) {
 	if (_currentControlMode && (index != _updateCharNum))
 		return;
 
-	static const uint8 xCoords[] = { 23, 95 };
-	static const uint8 yCoords[] = { 46, 98, 150 };
-
-	int x = xCoords[index & 1];
-	int y = yCoords[index >> 1];
-	int w = 38;
-	int h = 3;
+	int x = guiSettings()->charBoxCoords.hpBarX_1[index & 1];
+	int y = guiSettings()->charBoxCoords.hpBarY_1[index >> 1];
+	int w = guiSettings()->charBoxCoords.hpBarWidth_1;
+	int h = guiSettings()->charBoxCoords.hpBarHeight_1;
 	uint8 bgCol = guiSettings()->colors.fill;
 
 	if (!_screen->_curPage)
 		x += 176;
 
 	if (_currentControlMode) {
-		x = 250;
-		y = 16;
-		w = 51;
-		h = 5;
+		x = guiSettings()->charBoxCoords.hpFoodBarX_2[0];
+		y = guiSettings()->charBoxCoords.hpFoodBarY_2[0];
+		w = guiSettings()->charBoxCoords.hpFoodBarWidth_2;
+		h = guiSettings()->charBoxCoords.hpFoodBarHeight_2;
 		if (_flags.platform == Common::kPlatformAmiga && _flags.gameID == GI_EOB1)
 			bgCol = guiSettings()->colors.sfill;
 	}
@@ -430,7 +438,9 @@ void EoBCoreEngine::gui_drawHitpoints(int index) {
 		if (bgCur <= 10)
 			col = guiSettings()->colors.guiColorDarkRed;
 
-		if (!_currentControlMode && _flags.platform != Common::kPlatformSegaCD)
+		if (_flags.platform == Common::kPlatformSegaCD)
+			col = (bgCur < 12) ? guiSettings()->colors.guiColorDarkRed : (bgCur < 24 ? guiSettings()->colors.guiColorYellow : guiSettings()->colors.guiColorDarkGreen);
+		else if (!_currentControlMode)
 			_screen->printText(_characterGuiStringsHp[0], x - 13, y - 1, guiSettings()->colors.guiColorBlack, 0);
 
 		gui_drawHorizontalBarGraph(x, y, w, h, bgCur, bgMax, col, guiSettings()->colors.barGraph);
@@ -462,21 +472,19 @@ void EoBCoreEngine::gui_drawFoodStatusGraph(int index) {
 		return;
 
 	uint8 col = c->food < 20 ? guiSettings()->colors.guiColorDarkRed : (c->food < 33 ? guiSettings()->colors.guiColorYellow : guiSettings()->colors.guiColorDarkGreen);
-	gui_drawHorizontalBarGraph(250, 25, 51, 5, c->food, 100, col, guiSettings()->colors.barGraph);
+	gui_drawHorizontalBarGraph(guiSettings()->charBoxCoords.hpFoodBarX_2[1], guiSettings()->charBoxCoords.hpFoodBarY_2[1], guiSettings()->charBoxCoords.hpFoodBarWidth_2, guiSettings()->charBoxCoords.hpFoodBarHeight_2, c->food, 100, col, guiSettings()->colors.barGraph);
 }
 
 void EoBCoreEngine::gui_drawHorizontalBarGraph(int x, int y, int w, int h, int32 curVal, int32 maxVal, int col1, int col2) {
-	gui_drawBox(x - 1, y - 1, w + 3, h + 2, guiSettings()->colors.frame2, guiSettings()->colors.frame1, -1);
+	if (_flags.platform != Common::kPlatformSegaCD)
+		gui_drawBox(x - 1, y - 1, w + 3, h + 2, guiSettings()->colors.frame2, guiSettings()->colors.frame1, -1);
 	KyraRpgEngine::gui_drawHorizontalBarGraph(x, y, w + 2, h, curVal, maxVal, col1, col2);
 }
 
 void EoBCoreEngine::gui_drawCharPortraitStatusFrame(int index) {
 	uint8 redGreenColor = (_partyEffectFlags & 0x20000) ? guiSettings()->colors.guiColorLightGreen : ((_configRenderMode == Common::kRenderCGA) ? 3 : guiSettings()->colors.guiColorLightRed);
-
-	static const uint8 xCoords[] = { 8, 80 };
-	static const uint8 yCoords[] = { 2, 54, 106 };
-	int x = xCoords[index & 1];
-	int y = yCoords[index >> 1];
+	int x = guiSettings()->charBoxCoords.facePosX_1[index & 1];
+	int y = guiSettings()->charBoxCoords.boxY[index >> 1];
 	int xOffset = (_configRenderMode == Common::kRenderCGA) ? 0 : 1;
 
 	if (!_screen->_curPage)
@@ -489,12 +497,12 @@ void EoBCoreEngine::gui_drawCharPortraitStatusFrame(int index) {
 
 	if (redGreen || yellow) {
 		if (redGreen && !yellow) {
-			_screen->drawBox(x, y, x + 63, y + 49, redGreenColor);
+			_screen->drawBox(x, y, x + guiSettings()->charBoxCoords.boxWidth - 1, y + guiSettings()->charBoxCoords.boxHeight - 1, redGreenColor);
 			return;
 		}
 
 		if (yellow && !redGreen) {
-			_screen->drawBox(x, y, x + 63, y + 49, guiSettings()->colors.guiColorYellow);
+			_screen->drawBox(x, y, x + guiSettings()->charBoxCoords.boxWidth - 1, y + guiSettings()->charBoxCoords.boxHeight - 1, guiSettings()->colors.guiColorYellow);
 			return;
 		}
 
@@ -505,11 +513,11 @@ void EoBCoreEngine::gui_drawCharPortraitStatusFrame(int index) {
 			x = iX + i;
 			if (redGreen) {
 				_screen->drawClippedLine(x, y, x + 7, y, redGreenColor);
-				_screen->drawClippedLine(x + 8, y + 49, x + 15, y + 49, redGreenColor);
+				_screen->drawClippedLine(x + 8, y + guiSettings()->charBoxCoords.boxHeight - 1, x + 15, y + guiSettings()->charBoxCoords.boxHeight - 1, redGreenColor);
 			}
 			if (yellow) {
 				_screen->drawClippedLine(x + 8, y, x + 15, y, guiSettings()->colors.guiColorYellow);
-				_screen->drawClippedLine(x, y + 49, x + 7, y + 49, guiSettings()->colors.guiColorYellow);
+				_screen->drawClippedLine(x, y + guiSettings()->charBoxCoords.boxHeight - 1, x + 7, y + guiSettings()->charBoxCoords.boxHeight - 1, guiSettings()->colors.guiColorYellow);
 			}
 		}
 
@@ -520,19 +528,19 @@ void EoBCoreEngine::gui_drawCharPortraitStatusFrame(int index) {
 
 			if (yellow) {
 				_screen->drawClippedLine(x, y + 1, x, y + 6, guiSettings()->colors.guiColorYellow);
-				_screen->drawClippedLine(x + 63, y + 7, x + 63, y + 12, guiSettings()->colors.guiColorYellow);
+				_screen->drawClippedLine(x + guiSettings()->charBoxCoords.boxWidth - 1, y + 7, x + guiSettings()->charBoxCoords.boxWidth - 1, y + 12, guiSettings()->colors.guiColorYellow);
 			}
 			if (redGreen) {
 				_screen->drawClippedLine(x, y + 7, x, y + 12, redGreenColor);
-				_screen->drawClippedLine(x + 63, y + 1, x + 63, y + 6, redGreenColor);
+				_screen->drawClippedLine(x + guiSettings()->charBoxCoords.boxWidth - 1, y + 1, x + guiSettings()->charBoxCoords.boxWidth - 1, y + 6, redGreenColor);
 			}
 		}
 
 	} else {
-		_screen->drawClippedLine(x, y, x + 62, y, guiSettings()->colors.frame2);
-		_screen->drawClippedLine(x, y + 49, x + 62, y + 49, guiSettings()->colors.frame1);
-		_screen->drawClippedLine(x - xOffset, y, x - xOffset, y + 50, guiSettings()->colors.guiColorBlack);
-		_screen->drawClippedLine(x + 63, y, x + 63, y + 50, guiSettings()->colors.guiColorBlack);
+		_screen->drawClippedLine(x, y, x + guiSettings()->charBoxCoords.boxWidth - 2, y, guiSettings()->colors.frame2);
+		_screen->drawClippedLine(x, y + guiSettings()->charBoxCoords.boxHeight - 1, x + guiSettings()->charBoxCoords.boxWidth - 2, y + guiSettings()->charBoxCoords.boxHeight - 1, guiSettings()->colors.frame1);
+		_screen->drawClippedLine(x - xOffset, y, x - xOffset, y + guiSettings()->charBoxCoords.boxHeight, guiSettings()->colors.guiColorBlack);
+		_screen->drawClippedLine(x + guiSettings()->charBoxCoords.boxWidth - 1, y, x + guiSettings()->charBoxCoords.boxWidth - 1, y + guiSettings()->charBoxCoords.boxHeight, guiSettings()->colors.guiColorBlack);
 	}
 }
 
@@ -947,9 +955,7 @@ int EoBCoreEngine::clickedWeaponSlot(Button *button) {
 	// Fix this using the coordinates from gui_drawWeaponSlot().
 	// The coordinates used in the original are slightly wrong
 	// (most noticeable for characters 5 and 6).
-	static const uint8 sY[] = { 27, 27, 79, 79, 131, 131 };
-	int slot = sY[button->arg] > _mouseY ? 0 : 1;
-
+	int slot = guiSettings()->charBoxCoords.weaponSlotY[(button->arg & ~1) + 1] > _mouseY ? 0 : 1;
 	uint16 flags = _configMouseBtSwap ? _gui->_flagsMouseRight : _gui->_flagsMouseLeft;
 
 	if ((flags & 0x7F) == 1)
diff --git a/engines/kyra/gui/saveload_eob.cpp b/engines/kyra/gui/saveload_eob.cpp
index d7645005d5..1d5150531a 100644
--- a/engines/kyra/gui/saveload_eob.cpp
+++ b/engines/kyra/gui/saveload_eob.cpp
@@ -108,6 +108,7 @@ Common::Error EoBCoreEngine::loadGameState(int slot) {
 
 	setupCharacterTimers();
 
+	makeNameShapes();
 	_screen->loadShapeSetBitmap("CHARGENA", 3, 3);
 	for (int i = 0; i < 6; i++) {
 		EoBCharacter *c = &_characters[i];
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index b45b3a69e0..120841dc21 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -1149,6 +1149,7 @@ void EoBCoreEngine::initSpells() {
 }
 
 void EoBEngine::initStaticResource() {
+	bool bigEndian = (_flags.platform == Common::kPlatformAmiga || _flags.platform == Common::kPlatformSegaCD);
 	int temp;
 	_mainMenuStrings = _staticres->loadStrings(kEoB1MainMenuStrings, temp);
 	_finBonusStrings = _staticres->loadStrings(kEoB1BonusStrings, temp);
@@ -1217,9 +1218,9 @@ void EoBEngine::initStaticResource() {
 		p->dmgDc[2].base = (int8)*ps++;
 		ps++;
 		p->capsFlags = *ps++;
-		p->typeFlags = (_flags.platform == Common::kPlatformAmiga) ? READ_BE_UINT32(++ps) : READ_LE_UINT32(ps);
+		p->typeFlags = bigEndian ? READ_BE_UINT32(++ps) : READ_LE_UINT32(ps);
 		ps += 4;
-		p->experience = (_flags.platform == Common::kPlatformAmiga) ? READ_BE_UINT16(ps) : READ_LE_UINT16(ps);
+		p->experience = bigEndian ? READ_BE_UINT16(ps) : READ_LE_UINT16(ps);
 		ps += 2;
 		p->u30 = *ps++;
 		p->sound1 = (int8)*ps++;
@@ -1241,6 +1242,12 @@ void EoBEngine::initStaticResource() {
 		_sound->initAudioResourceInfo(kMusicFinale, &finale);
 	}
 
+	_addrTbl1 = _staticres->loadRawDataBe16(kEoB1PatternAddTable1, temp);
+	_textFieldPattern = _staticres->loadRawDataBe16(kEoB1PatternAddTable2, temp);
+	_playFldPattern1 = _staticres->loadRawDataBe16(kEoB1PatternTable0, temp);
+	_invPattern = _staticres->loadRawDataBe16(kEoB1PatternTable3, temp);
+	_statsPattern = _staticres->loadRawDataBe16(kEoB1PatternTable4, temp);
+
 	// Build offset tables for door shapes encoding
 	if (_flags.platform == Common::kPlatformSegaCD) {
 		const uint8 *shp = _screen->getCPagePtr(2);
@@ -1379,27 +1386,78 @@ void EoBEngine::initSpells() {
 
 const KyraRpgGUISettings EoBEngine::_guiSettingsVGA = {
 	{ 9, 15, 95, 9, 2, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
-	{ 135, 130, 132, 180, 133, 17, 23, 20, 184, 177, 180, 184, 177, 180, 15, 6, 8, 9, 2, 5, 4, 3, 12 }
+	{ 135, 130, 132, 180, 133, 17, 23, 20, 184, 177, 180, 184, 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 EoBEngine::_guiSettingsEGA = {
 	{ 9, 15, 95, 9, 2, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
-	{ 13, 9, 2, 14, 2, 6, 13, 8, 13, 15, 14, 13, 15, 14, 15, 6, 8, 9, 2, 5, 4, 3, 12 }
+	{ 13, 9, 2, 14, 2, 6, 13, 8, 13, 15, 14, 13, 15, 14, 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 EoBEngine::_guiSettingsPC98 = {
 	{ 9, 15, 95, 11, 1, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
-	{ 13, 9, 2, 14, 2, 6, 13, 8, 13, 15, 14, 13, 15, 14, 15, 6, 8, 9, 2, 5, 4, 3, 12 }
+	{ 13, 9, 2, 14, 2, 6, 13, 8, 13, 15, 14, 13, 15, 14, 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 EoBEngine::_guiSettingsAmiga = {
 	{ 28, 31, 95, 9, 2, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
-	{ 18, 17, 10, 17, 11, 24, 22, 25, 18, 9, 10, 18, 9, 10, 31, 24, 25, 28, 29, 7, 26, 27, 19 }
+	{ 18, 17, 10, 17, 11, 24, 22, 25, 18, 9, 10, 18, 9, 10, 31, 24, 25, 28, 29, 7, 26, 27, 19 },
+	{	{ 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 EoBEngine::_guiSettingsAmigaMainMenu = {
 	{ 28, 31, 95, 9, 2, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
-	{ 22, 28, 30, 17, 11, 24, 22, 25, 18, 9, 10, 18, 9, 10, 31, 24, 25, 28, 29, 7, 26, 27, 19 }
+	{ 22, 28, 30, 17, 11, 24, 22, 25, 18, 9, 10, 18, 9, 10, 31, 24, 25, 28, 29, 7, 26, 27, 19 },
+	{	{ 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 EoBEngine::_guiSettingsSegaCD = {
+	{ 9, 15, 95, 9, 2, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
+	{ 135, 130, 132, 180, 0x00, 17, 23, 20, 184, 177, 180, 184, 177, 180, 15, 6, 0x31, 9, 2, 0x35, 4, 0x33, 12 },
+	{	{ 184, 256, -1}, { 1, 57, 113 }, 64, 55,
+		{ 8, 80, -1 }, { 16, 72, 128 }, { 184, -1, -1 }, { 8, -1, -1 },
+		{ 40, 112, -1 }, { 16, 32, 72, 88, 128, 146 },
+		{ 24, 96, -1}, { 51, 107, 163 }, 40, 2, { 248, 248, -1}, { 19, 27, -1 }, 47, 2,
+		16, 39
+	}
+
+};
+
+const uint8 EoBEngine::_redGridTile[8] = {
+	0x1C, 0x1C, 0x1C, 0x1C, 0xC1, 0xC1, 0xC1, 0xC1
+};
+
+const int8 EoBEngine::_sceneShakeOffsets[66] = {
+	0, 0, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -2, -2, -2, 2, 2, 2, 2, -2, -2, -2, -2, 2, 2, 2, 2,
+	-2, -2, -2, -2, 2, 2, 2, 2, -2, -3, -3, -3, 3, 3, 3, 3, -3, -3, -3, -3, 3, 3, 3, 3, -3, -3, -3, -3, 3, 3, 3, 3, -3
 };
 
 const uint8 EoBEngine::_egaDefaultPalette[] = {
@@ -1597,17 +1655,35 @@ void DarkMoonEngine::initSpells() {
 
 const KyraRpgGUISettings DarkMoonEngine::_guiSettingsFMTowns = {
 	{ 9, 15, 95, 11, 1, 7, { 221, 76 }, { 187, 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 }
+	{ 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 = {
 	{ 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 }
+	{ 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::_guiSettingsAmiga = {
 	{ 28, 31, 95, 9, 2, 7, { 221, 76 }, { 189, 162 }, { 95, 95 } },
-	{ 18, 17, 10, 17, 11, 10, 12, 25, 18, 9, 10, 18, 9, 10, 31, 24, 25, 28, 29, 7, 26, 27, 19 }
+	{ 18, 17, 10, 17, 11, 10, 12, 25, 18, 9, 10, 18, 9, 10, 31, 24, 25, 28, 29, 7, 26, 27, 19 },
+	{	{ 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 uint8 DarkMoonEngine::_egaDefaultPalette[] = {
diff --git a/engines/kyra/resource/staticres_lol.cpp b/engines/kyra/resource/staticres_lol.cpp
index 7d0ada1597..3a71cb5000 100644
--- a/engines/kyra/resource/staticres_lol.cpp
+++ b/engines/kyra/resource/staticres_lol.cpp
@@ -777,9 +777,18 @@ const int8 LoLEngine::_mapCoords[12][4] = {
 	{  3,  1,  3,  1 }, { -1,  6, -1, -8 }, { -7, -1,  5, -1 }
 };
 
+// Most of these settings won't get used in LOL, since the engine was already finished when these were introduced.
+// And it is hardly worth the time to add any usage for this, since the only significant version difference would
+// be the PC-98 16 color version. That said, I have filled all the unused parts of the struct with zeroes.
 const KyraRpgGUISettings LoLEngine::_guiSettings = {
 	{ 144, 254, 74, 9, 2, 80, { 0, 0 }, { 0, 0 }, { 0, 0 } },
-	{ 136, 251, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+	{ 136, 251, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+	{	{ 0, 0, 0 }, { 0, 0, 0 }, 0, 0,
+		{ 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 },
+		{ 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 },
+		{ 0, 0, 0 }, { 0, 0, 0 }, 0, 0, { 0, 0, 0 }, { 0, 0, 0 }, 0, 0,
+		0, 0
+	}
 };
 
 const MistOfDoomAnimData LoLEngine::_mistAnimData[] = {
diff --git a/engines/kyra/sequence/seqplayer_eob_segacd.cpp b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
index ca87fbed3f..e565493ce9 100644
--- a/engines/kyra/sequence/seqplayer_eob_segacd.cpp
+++ b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
@@ -220,6 +220,7 @@ bool SegaSequencePlayer::play(int id) {
 
 	_scrollManager->setVScrollTimers(0, 1, 0, 0, 1, 0);
 	_scrollManager->setHScrollTimers(0, 1, 0, 0, 1, 0);
+	_scrollManager->updateScrollTimers();
 
 	_vm->_allowSkip = false;
 	_vm->resetSkipFlag();
diff --git a/engines/kyra/sequence/sequences_eob.cpp b/engines/kyra/sequence/sequences_eob.cpp
index 7a48d7de5e..67031c849d 100644
--- a/engines/kyra/sequence/sequences_eob.cpp
+++ b/engines/kyra/sequence/sequences_eob.cpp
@@ -2414,7 +2414,7 @@ void EoBEngine::seq_segaOpeningCredits() {
 
 	_sres->loadContainer("CREDIT");
 	Common::SeekableReadStreamEndian *in = _sres->resStreamEndian(1);
-	r->loadToVRAM(in, 32, true);
+	r->loadStreamToVRAM(in, 32, true);
 	delete in;
 
 	_screen->sega_selectPalette(50, 0, 0);
@@ -2431,7 +2431,7 @@ void EoBEngine::seq_segaOpeningCredits() {
 		_screen->sega_selectPalette(i == 3 ? 59 : 50, 0, true);
 
 		in = _sres->resStreamEndian(i);
-		r->loadToVRAM(in, 32, true);
+		r->loadStreamToVRAM(in, 32, true);
 		delete in;
 
 		r->render(0);
@@ -2478,7 +2478,7 @@ void EoBEngine::seq_segaOpeningCredits() {
 	_screen->sega_selectPalette(0, 0);
 
 	in = _sres->resStreamEndian(8);
-	r->loadToVRAM(in, 32, true);
+	r->loadStreamToVRAM(in, 32, true);
 	delete in;
 
 	r->memsetVRAM(0x8C20, 0xCC, 0x700);
@@ -2490,7 +2490,8 @@ void EoBEngine::seq_segaOpeningCredits() {
 	r->fillRectWithTiles(1, 0, 0, 40, 28, 1, true);
 	r->fillRectWithTiles(0, 0, 0, 40, 28, 0);
 	r->render(0);
-	_screen->sega_fadeToNeutral(3);
+	if (!(shouldQuit() || skipFlag()))
+		_screen->sega_fadeToNeutral(3);
 
 	while (!(shouldQuit() || skipFlag()))
 		delay(20);
@@ -2500,7 +2501,7 @@ void EoBEngine::seq_segaOpeningCredits() {
 
 	r->fillRectWithTiles(1, 0, 19, 40, 9, 1);
 	r->render(0);
-	_screen->updateScreen();
+	_screen->sega_fadeToNeutral(3);
 }
 
 void EoBEngine::seq_segaFinalCredits() {
@@ -2639,9 +2640,6 @@ void EoBEngine::seq_segaShowStats() {
 
 	////
 	////
-	uint32 totalPlayTicks = 0;
-	uint32 totalEnemiesKilled = 0;
-	uint32 totalSteps = 0;
 	uint32 partyArrows = 0;
 	uint32 numMaps = 0;
 	/////
@@ -2653,9 +2651,9 @@ void EoBEngine::seq_segaShowStats() {
 			++specialSearches;
 	}
 
-	_txt->printShadowedText(Common::String::format("%u:%02u:%02u", totalPlayTicks / 216000, totalPlayTicks / 3600, totalPlayTicks / 60).c_str(), 148, 28, 0xFF, 0x00, false);
-	_txt->printShadowedText(Common::String::format("%u", totalEnemiesKilled).c_str(), 148, 40, 0xFF, 0x00, false);
-	_txt->printShadowedText(Common::String::format("%u", totalSteps).c_str(), 148, 52, 0xFF, 0x00, false);
+	_txt->printShadowedText(Common::String::format("%u:%02u:%02u", _totalPlaySecs / 3600, _totalPlaySecs / 60, _totalPlaySecs).c_str(), 148, 28, 0xFF, 0x00, false);
+	_txt->printShadowedText(Common::String::format("%u", _totalEnemiesKilled).c_str(), 148, 40, 0xFF, 0x00, false);
+	_txt->printShadowedText(Common::String::format("%u", _totalSteps).c_str(), 148, 52, 0xFF, 0x00, false);
 	_txt->printShadowedText(Common::String::format("%u(%u%%)", partyArrows, partyArrows * 100 / 26).c_str(), 148, 64, 0xFF, 0x00, false);
 	_txt->printShadowedText(Common::String::format("%u(%u%%)", numMaps, numMaps * 100 / 12).c_str(), 148, 76, 0xFF, 0x00, false);
 	_txt->printShadowedText(Common::String::format("%u(%u%%)", specialSearches, specialSearches * 100 / 12).c_str(), 148, 88, 0xFF, 0x00, false);
@@ -2704,7 +2702,7 @@ void EoBEngine::seq_segaShowStats() {
 void EoBEngine::seq_segaSetupSequence(int sequenceId) {
 	if (_flags.platform != Common::kPlatformSegaCD || sequenceId == -1)
 		return;
-	
+
 	_screen->sega_fadeToBlack(1);
 	for (int i = 0; i < 6; i++) {
 		_characters[i].damageTaken = 0;
@@ -2712,6 +2710,7 @@ void EoBEngine::seq_segaSetupSequence(int sequenceId) {
 		gui_drawCharPortraitWithStats(i);
 	}
 
+	// transposeScreenOutputY(0);
 	_screen->sega_getRenderer()->setupWindowPlane(0, (sequenceId == 53 || sequenceId == 54) ? 23 : 18, SegaRenderer::kWinToRight, SegaRenderer::kWinToBottom);
 	_screen->sega_getRenderer()->memsetVRAM(0xD840, 0xEE, 512);
 	_screen->sega_getAnimator()->clearSprites();


Commit: a904ba4f48ea70acc05bac6024c54745419b7cfe
    https://github.com/scummvm/scummvm/commit/a904ba4f48ea70acc05bac6024c54745419b7cfe
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:14+02:00

Commit Message:
KYRA: (EOB/SegaCD) - fix text display, inventory and stats page

Changed paths:
  A engines/kyra/gui/gui_eob_segacd.cpp
    engines/kyra/engine/chargen.cpp
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eob.h
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h
    engines/kyra/engine/items_eob.cpp
    engines/kyra/engine/magic_eob.cpp
    engines/kyra/graphics/screen.cpp
    engines/kyra/graphics/screen.h
    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/saveload_eob.cpp
    engines/kyra/module.mk
    engines/kyra/resource/staticres_eob.cpp
    engines/kyra/script/script_eob.cpp
    engines/kyra/script/script_eob.h
    engines/kyra/sequence/seqplayer_eob_segacd.cpp
    engines/kyra/sequence/sequences_eob.cpp
    engines/kyra/text/text_eob_segacd.cpp
    engines/kyra/text/text_eob_segacd.h
    engines/kyra/text/text_rpg.h


diff --git a/engines/kyra/engine/chargen.cpp b/engines/kyra/engine/chargen.cpp
index 727eec167a..9ad2a0d98a 100644
--- a/engines/kyra/engine/chargen.cpp
+++ b/engines/kyra/engine/chargen.cpp
@@ -347,6 +347,7 @@ void CharacterGenerator::createDefaultParty() {
 		c.raceSex = *pos++;
 		c.cClass = *pos++;
 		c.alignment = *pos++;
+		generateStats(i);
 		c.portrait = *pos++;
 		c.faceShape = _faceShapes[c.portrait];
 		c.strengthCur = *pos++;
diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index d8256f3d92..b994512c16 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -55,7 +55,7 @@ EoBEngine::EoBEngine(OSystem *system, const GameFlags &flags)
 	_useMainMenuGUISettings = false;
 	_addrTbl1 = _textFieldPattern = 0;
 	_playFldPattern1 = _invPattern = _statsPattern;
-	_playFldPattern2 = 0;
+	_playFldPattern2 = _statsPattern2 = 0;
 	_ttlCfg = 0;
 	_xdth = false;
 
@@ -63,6 +63,7 @@ EoBEngine::EoBEngine(OSystem *system, const GameFlags &flags)
 	_sceneShakeOffsetX = _sceneShakeOffsetY = 0;
 	_shakeBackBuffer1 = _shakeBackBuffer2 = 0;
 	_redGrid = 0;
+	_charTilesTable = 0;
 
 	_seqPlayer = 0;
 	_sres = 0;
@@ -83,6 +84,7 @@ EoBEngine::~EoBEngine() {
 	delete[] _doorSwitchShapesSrc;
 	delete[] _itemsOverlay;
 	delete[] _playFldPattern2;
+	delete[] _statsPattern2;
 	delete[] _shakeBackBuffer1;
 	delete[] _shakeBackBuffer2;
 
@@ -149,6 +151,7 @@ Common::Error EoBEngine::init() {
 		_txt = new TextDisplayer_SegaCD(this, _screen);
 		assert(_txt);
 		_playFldPattern2 = new uint16[1040];
+		_statsPattern2 = new uint16[792];
 		_shakeBackBuffer1 = new uint8[120 * 6];
 		_shakeBackBuffer2 = new uint8[179 * 6];
 	}
@@ -169,12 +172,12 @@ 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(7, true); \
+	_screen->sega_getRenderer()->render(Screen_EoB::kSegaInitShapesPage, true); \
 	_screen->sega_getAnimator()->clearSprites(); \
-	int cp = _screen->setCurPage(7); \
+	int cp = _screen->setCurPage(Screen_EoB::kSegaInitShapesPage); \
 	singleShape = _screen->encodeShape(0, 0, numSprites  * (spriteWidth >> 3), spriteHeight); \
 	_screen->setCurPage(cp); \
-	_screen->clearPage(7); \
+	_screen->clearPage(Screen_EoB::kSegaInitShapesPage); \
 	} \
 	delete[] in
 
@@ -233,19 +236,19 @@ void EoBEngine::loadItemsAndDecorationsShapes() {
 	_screen->sega_selectPalette(40, 2, true);
 	*/
 
-	int cp = _screen->setCurPage(7);
+	int cp = _screen->setCurPage(Screen_EoB::kSegaInitShapesPage);
 	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(7);
-	_screen->drawShape(7, _weaponSlotShapes[1], 0, 0, 0);
-	_screen->drawShape(7, _weaponSlotShapes[1], 0, 16, 0);
+	_screen->sega_getRenderer()->render(Screen_EoB::kSegaInitShapesPage);
+	_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);
 	_disabledCharGrid = _screen->encodeShape(0, 0, 4, 32);
 	_blackBoxSmallGrid = _screen->encodeShape(0, 0, 2, 8);
 	_blackBoxWideGrid = _screen->encodeShape(0, 0, 4, 8);
 	_redGrid = _screen->encodeShape(0, 32, 4, 32);
-	_screen->clearPage(7);
+	_screen->clearPage(Screen_EoB::kSegaInitShapesPage);
 	_screen->setCurPage(cp);
 }
 
@@ -997,7 +1000,7 @@ void EoBEngine::snd_updateLevelScore() {
 }
 
 bool EoBEngine::checkPartyStatusExtra() {
-	_screen->copyPage(0, 10);
+	_screen->copyPage(0, Screen_EoB::kDefeatMsgBackupPage);
 	int cd = _screen->curDimIndex();
 	gui_drawBox(0, 121, 320, 80, guiSettings()->colors.frame1, guiSettings()->colors.frame2, guiSettings()->colors.fill);
 	_txt->setupField(9, false);
@@ -1007,7 +1010,7 @@ bool EoBEngine::checkPartyStatusExtra() {
 		if (checkInput(0, false, 0) & 0xFF)
 			break;
 	}
-	_screen->copyPage(10, 0);
+	_screen->copyPage(Screen_EoB::kDefeatMsgBackupPage, 0);
 	_eventList.clear();
 	_screen->setScreenDim(cd);
 	_txt->removePageBreakFlag();
@@ -1052,140 +1055,6 @@ void EoBEngine::healParty() {
 	}
 }
 
-void EoBEngine::makeNameShapes() {
-	if (_flags.platform != Common::kPlatformSegaCD)
-		return;
-
-	int cd = _txt->clearDim(4);
-	int cp = _screen->setCurPage(2);
-	_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 28, 0x2000);
-	_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 30, 28, 0x600A, true);
-	_screen->sega_clearTextBuffer(0);
-
-	for (int i = 0; i < 6; ++i) {
-		if (!_characters[i].flags)
-			continue;
-		_txt->printShadowedText(_characters[i].name, 0, i << 4, 0xFF, 0xCC);
-	}
-
-	_screen->sega_getRenderer()->render(_screen->_curPage);
-
-	for (int i = 0; i < 6; ++i) {
-		if (!_characters[i].flags)
-			continue;
-		delete[] _characters[i].nameShape;
-		_characters[i].nameShape = _screen->encodeShape(0, i << 4, (_screen->getTextWidth(_characters[i].name) + 8) >> 3, 13);
-	}
-
-	_screen->clearPage(2);
-	_screen->setCurPage(cp);
-	_screen->sega_clearTextBuffer(0);
-
-	_txt->clearDim(4);
-	_txt->clearDim(cd);
-}
-
-void EoBEngine::gui_drawPlayField(bool refresh) {
-	if (_flags.platform != Common::kPlatformSegaCD) {
-		EoBCoreEngine::gui_drawPlayField(refresh);
-		return;
-	}
-
-	if (!_loading)
-		_screen->sega_fadeToBlack(1);
-
-	// transposeScreenOutputY(8);
-	_txt->clearDim(0);
-	_screen->sega_getAnimator()->clearSprites();
-	SegaRenderer *r = _screen->sega_getRenderer();
-
-	uint8 *data = _res->fileData("PLAYFLD", 0);
-	for (int i = 0; i < 256; ++i)
-		r->loadToVRAM(&data[i << 5], 32, _addrTbl1[i] << 5);
-	delete[] data;
-
-	const uint16 *pattern = _playFldPattern1;
-	uint16 *dst = _playFldPattern2;
-
-	for (int i = 0; i < 1040; ++i) {
-		int ix = (*pattern++) - 11;
-		*dst++ = (ix < 0) ? 0 : _addrTbl1[ix];
-	}
-
-	const uint16 ps[] = { 0xCE, 0xE0, 0x2FE, 0x310, 0x52E, 0x540 };
-
-	for (int i = 0; i < 4; ++i) {
-		dst = &_playFldPattern2[ps[i] >> 1];
-		memset(dst, 0, 8);
-		memset(&dst[40], 0, 8);
-		memset(&dst[80], 0, 8);
-		memset(&dst[120], 0, 8);
-	}
-
-	r->fillRectWithTiles(1, 0, 0, 40, 26, 0x2000, true, false, _playFldPattern2);
-	r->fillRectWithTiles(0, 0, 21, 40, 5, 0x2000, true, false, _textFieldPattern);
-
-	// Name tables for scene window vcn block tiles. We don't need that. We draw the blocks with our "normal" graphics code.
-	// r->fillRectWithTiles(1, 0, 0, 22, 15, 0xC14B, true, true);
-	// Name tables for scene window shapes tiles. We don't need that, since we're not going to draw any shapes with the renderer.
-	// 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);
-
-	_sres->loadContainer("ITEM");
-	Common::SeekableReadStreamEndian *str = _sres->resStreamEndian(7);
-	r->loadStreamToVRAM(str, 0x8880, true);
-	delete str;
-	r->fillRectWithTiles(1, 22, 0, 18, 21, 0x6444, true, true, _invPattern);
-	r->render(2);
-	r->fillRectWithTiles(1, 22, 0, 18, 21, 0x6444, true, true, _statsPattern);
-	r->render(4);
-
-	if (refresh && !_sceneDrawPage2)
-		drawScene(0);
-
-	_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);
-	_screen->copyRegionToBuffer(0, 173, 0, 6, 120, _shakeBackBuffer1);
-	_screen->copyRegionToBuffer(0, 0, 117, 179, 6, _shakeBackBuffer2);
-
-	// Since we're no going to draw with the SegaRenderer but rather with our "normal" code, we have to backup some parts of
-	// the background between the character portraits. Unlike the other versions the red splat shape overlaps with that space.
-	for (int i = 0; i < 6; ++i) {
-		delete[] _redSplatBG[i];
-		_redSplatBG[i] = new uint8[_redSplatShape[2] << 5];
-		_screen->copyRegionToBuffer(0, guiSettings()->charBoxCoords.boxX[i & 1] + guiSettings()->charBoxCoords.redSplatOffsetX, guiSettings()->charBoxCoords.boxY[i >> 1] + guiSettings()->charBoxCoords.boxHeight - 1, _redSplatShape[2] << 3, 4, _redSplatBG[i]);
-	}
-
-	if (!_loading)
-		_screen->sega_fadeToNeutral(1);
-}
-
-void EoBEngine::gui_drawWeaponSlotStatus(int x, int y, int status) {
-	if (_flags.platform != Common::kPlatformSegaCD) {
-		EoBCoreEngine::gui_drawWeaponSlotStatus(x, y, status);
-		return;
-	}
-
-	if (status < 0) {
-		_screen->drawShape(_screen->_curPage, _weaponSlotShapes[status < -2 ? -status - 1 : 3 - status], x - 1, y, 0);
-	} else {
-		_screen->drawShape(_screen->_curPage, _weaponSlotShapes[0], x - 1, y, 0);
-		gui_printInventoryDigits(x + 8, y + 6, status);
-	}
-}
-
-void EoBEngine::gui_printInventoryDigits(int x, int y, int val) {
-	if (_flags.platform != Common::kPlatformSegaCD) {
-		EoBCoreEngine::gui_printInventoryDigits(x, y, val);
-		return;
-	}
-	_screen->drawShape(_screen->_curPage, _invSmallDigits[(val < 10) ? 22 + val : (val >= 100 ? 1 : 2 + val / 10)], x, y);
-	_screen->drawShape(_screen->_curPage, (val >= 10 && val < 100) ? _invSmallDigits[12 + (val % 10)] : 0, x, y);
-}
-
 const KyraRpgGUISettings *EoBEngine::guiSettings() const {
 	if (_flags.platform == Common::kPlatformAmiga)
 		return _useMainMenuGUISettings ? &_guiSettingsAmigaMainMenu : &_guiSettingsAmiga;
diff --git a/engines/kyra/engine/eob.h b/engines/kyra/engine/eob.h
index b02dff7910..ba54b6af57 100644
--- a/engines/kyra/engine/eob.h
+++ b/engines/kyra/engine/eob.h
@@ -181,7 +181,6 @@ private:
 	bool checkPartyStatusExtra() override;
 	int resurrectionSelectDialogue() override;
 	void healParty();
-	void makeNameShapes() override;
 
 	int _sceneShakeOffsetX;
 	int _sceneShakeOffsetY;
@@ -195,6 +194,10 @@ private:
 	void gui_drawPlayField(bool refresh) override;
 	void gui_drawWeaponSlotStatus(int x, int y, int status) override;
 	void gui_printInventoryDigits(int x, int y, int val) override;
+	void gui_drawCharacterStatsPage() override;
+
+	void makeNameShapes() override;
+	void printStatsString(const char *str, int x, int y);
 
 	const KyraRpgGUISettings *guiSettings() const override;
 	void useMainMenuGUISettings(bool toggle) override { _useMainMenuGUISettings = toggle; }
@@ -207,7 +210,9 @@ private:
 	const uint16 *_playFldPattern1;
 	const uint16 *_invPattern;
 	const uint16 *_statsPattern;
+	const uint8 *_charTilesTable;
 	uint16 *_playFldPattern2;
+	uint16 *_statsPattern2;
 
 	static const KyraRpgGUISettings _guiSettingsVGA;
 	static const KyraRpgGUISettings _guiSettingsEGA;
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 6bd003941e..bb021b32a9 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -239,6 +239,7 @@ EoBCoreEngine::EoBCoreEngine(OSystem *system, const GameFlags &flags) : KyraRpgE
 	_amigaCurSoundFile = -1;
 	_prefMenuPlatformOffset = 0;	
 	_lastVIntTick = _lastSecTick = _totalPlaySecs = _totalEnemiesKilled = _totalSteps = 0;
+	_levelMaps = 0;
 
 	memset(_cgaMappingLevel, 0, sizeof(_cgaMappingLevel));
 	memset(_expRequirementTables, 0, sizeof(_expRequirementTables));
@@ -1940,7 +1941,7 @@ void EoBCoreEngine::seq_portal() {
 bool EoBCoreEngine::checkPassword() {
 	char answ[20];
 	Screen::FontId of = _screen->setFont(Screen::FID_8_FNT);
-	_screen->copyPage(0, 10);
+	_screen->copyPage(0, Screen_EoB::kCheckPwBackupPage);
 
 	_screen->setScreenDim(13);
 	gui_drawBox(_screen->_curDim->sx << 3, _screen->_curDim->sy, _screen->_curDim->w << 3, _screen->_curDim->h, guiSettings()->colors.frame1, guiSettings()->colors.frame2, -1);
@@ -1967,7 +1968,7 @@ bool EoBCoreEngine::checkPassword() {
 
 	_screen->modifyScreenDim(13, _screen->_curDim->sx - 1, _screen->_curDim->sy - 2, _screen->_curDim->w + 2, _screen->_curDim->h + 16);
 	_screen->setFont(of);
-	_screen->copyPage(10, 0);
+	_screen->copyPage(Screen_EoB::kCheckPwBackupPage, 0);
 	return true;
 }
 
@@ -2611,6 +2612,27 @@ void EoBCoreEngine::explodeMonster(EoBMonsterInPlay *m) {
 	m->flags &= ~2;
 }
 
+void EoBCoreEngine::addCurrentLevelMap() {
+	assert(_currentLevel);
+	_levelMaps |= (1 << (_currentLevel - 1));
+}
+
+uint32 EoBCoreEngine::countMaps() const {
+	uint32 res = 0;
+	for (int i = 0; i < 12; ++i) {
+		if (_levelMaps & (1 << i))
+			res++;
+	}
+	return res;
+}
+
+uint32 EoBCoreEngine::countArrows() const {
+	uint32 res = 0;
+	for (int i = 0; i < 6; ++i)
+		res += countQueuedItems(_characters[i].inventory[16], -1, -1, 1, 1);
+	return res;
+}
+
 void EoBCoreEngine::snd_playSong(int track, bool loop) {
 	if (_flags.platform == Common::kPlatformSegaCD && !loop)
 		track |= 0x80;
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index 50de75e415..81a2fa263a 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -465,8 +465,8 @@ protected:
 	int stripPartyItems(int16 itemType, int16 itemValue, int handleValueMode, int numItems);
 	bool deletePartyItems(int16 itemType, int16 itemValue);
 	virtual void updateUsedCharacterHandItem(int charIndex, int slot) = 0;
-	int itemUsableByCharacter(int charIndex, Item item);
-	int countQueuedItems(Item itemQueue, int16 id, int16 type, int count, int includeFlyingItems);
+	int itemUsableByCharacter(int charIndex, Item item) const;
+	int countQueuedItems(Item itemQueue, int16 id, int16 type, int count, int includeFlyingItems) const;
 	int getQueuedItem(Item *items, int pos, int id);
 	void printFullItemName(Item item);
 	void identifyQueuedItems(Item itemQueue);
@@ -656,6 +656,10 @@ protected:
 	void openDoor(int block);
 	void closeDoor(int block);
 
+	void addCurrentLevelMap();
+	uint32 countMaps() const;
+	uint32 countArrows() const;
+
 	int16 _doorType[2];
 	int16 _noDoorSwitch[2];
 
@@ -703,6 +707,8 @@ protected:
 	const uint8 *_teleporterShapeCoords;
 	const int8 *_portalSeq;
 
+	uint32 _levelMaps;
+
 	// Script
 	void runLevelScript(int block, int flags) override;
 	void setScriptFlags(uint32 flags);
@@ -735,6 +741,7 @@ protected:
 	void gui_drawHorizontalBarGraph(int x, int y, int w, int h, int32 curVal, int32 maxVal, int col1, int col2) override;
 	void gui_drawCharPortraitStatusFrame(int index);
 	void gui_drawInventoryItem(int slot, int redraw, int pageNum);
+	virtual void gui_drawCharacterStatsPage();
 	void gui_drawCompass(bool force);
 	void gui_drawDialogueBox();
 	void gui_drawSpellbook();
diff --git a/engines/kyra/engine/items_eob.cpp b/engines/kyra/engine/items_eob.cpp
index 7cf95c893b..c0e68d7c45 100644
--- a/engines/kyra/engine/items_eob.cpp
+++ b/engines/kyra/engine/items_eob.cpp
@@ -309,14 +309,14 @@ bool EoBCoreEngine::deletePartyItems(int16 itemType, int16 itemValue) {
 	return res;
 }
 
-int EoBCoreEngine::itemUsableByCharacter(int charIndex, Item item) {
+int EoBCoreEngine::itemUsableByCharacter(int charIndex, Item item) const {
 	if (!item)
 		return 1;
 
 	return (_itemTypes[_items[item].type].allowedClasses & _classModifierFlags[_characters[charIndex].cClass]);
 }
 
-int EoBCoreEngine::countQueuedItems(Item itemQueue, int16 id, int16 type, int count, int includeFlyingItems) {
+int EoBCoreEngine::countQueuedItems(Item itemQueue, int16 id, int16 type, int count, int includeFlyingItems) const {
 	uint16 o1 = itemQueue;
 	uint16 o2 = o1;
 
diff --git a/engines/kyra/engine/magic_eob.cpp b/engines/kyra/engine/magic_eob.cpp
index d615077842..bdd2c9080e 100644
--- a/engines/kyra/engine/magic_eob.cpp
+++ b/engines/kyra/engine/magic_eob.cpp
@@ -60,7 +60,7 @@ void EoBCoreEngine::useMagicBookOrSymbol(int charIndex, int type) {
 	}
 
 	if (!_updateFlags)
-		_screen->copyRegion(64, 121, 0, 0, 112, 56, 0, 10, Screen::CR_NO_P_CHECK);
+		_screen->copyRegion(64, 121, 0, 0, 112, 56, 0, Screen_EoB::kSpellbookBackupPage, Screen::CR_NO_P_CHECK);
 	_updateFlags = 1;
 	gui_setPlayFieldButtons();
 	gui_drawSpellbook();
diff --git a/engines/kyra/graphics/screen.cpp b/engines/kyra/graphics/screen.cpp
index a507dc0c65..fdb913f98a 100644
--- a/engines/kyra/graphics/screen.cpp
+++ b/engines/kyra/graphics/screen.cpp
@@ -73,6 +73,7 @@ Screen::Screen(KyraEngine_v1 *vm, OSystem *system, const ScreenDim *dimTable, co
 	_useSJIS = _useOverlays = false;
 
 	_currentFont = FID_8_FNT;
+	_fontStyles = 0;
 	_paletteChanged = true;
 	_textMarginRight = SCREEN_W;
 	_customDimTable = 0;
@@ -1323,9 +1324,11 @@ void Screen::setTextColor16bit(const uint16 *cmap16) {
 	}
 }
 
-void Screen::setFontStyles(FontId fontId, int styles) {
+int Screen::setFontStyles(FontId fontId, int styles) {
 	assert(_fonts[fontId]);
-	_fonts[fontId]->setStyles(styles);
+	SWAP(_fontStyles, styles);
+	_fonts[fontId]->setStyles(_fontStyles);
+	return styles;
 }
 
 bool Screen::loadFont(FontId fontId, const char *filename) {
@@ -1393,7 +1396,7 @@ int Screen::getTextWidth(const char *str, bool nextWordOnly) {
 
 		uint c = fetchChar(str);
 
-		if (c == 0 || (nextWordOnly && (c == 32 || c == 0x4081))) {
+		if (c == 0 || (nextWordOnly && (c == 2 || c == 6 || c == 32 || c == 0x4081))) {
 			break;
 		} else if (c == '\r') {
 			if (curLineLen > maxLineLen)
diff --git a/engines/kyra/graphics/screen.h b/engines/kyra/graphics/screen.h
index 41a8d90a88..e9fbdbee6b 100644
--- a/engines/kyra/graphics/screen.h
+++ b/engines/kyra/graphics/screen.h
@@ -515,7 +515,7 @@ public:
 	virtual void setTextColorMap(const uint8 *cmap) = 0;
 	void setTextColor(const uint8 *cmap, int a, int b);
 	void setTextColor16bit(const uint16 *cmap16);
-	void setFontStyles(FontId fontId, int styles);
+	int setFontStyles(FontId fontId, int styles);
 
 	const ScreenDim *getScreenDim(int dim) const;
 	void modifyScreenDim(int dim, int x, int y, int w, int h);
@@ -628,6 +628,20 @@ protected:
 
 	bool _useOverlays;
 	bool _useSJIS;
+	int _fontStyles;
+
+	Font *_fonts[FID_NUM];
+	uint8 _textColorsMap[16];
+	uint16 _textColorsMap16bit[2];
+
+	uint8 *_textRenderBuffer;
+	int _textRenderBufferSize;
+
+	Common::SharedPtr<Graphics::FontSJIS> _sjisFontShared;
+	uint8 _sjisInvisibleColor;
+	bool _sjisMixedFontMode;
+
+	// colors/palette specific
 	bool _use16ColorMode;
 	bool _useShapeShading;
 	bool _4bitPixelPacking;
@@ -641,10 +655,6 @@ protected:
 	int _screenPageSize;
 	const int _screenHeight;
 	int _yTransOffs;
-	
-	Common::SharedPtr<Graphics::FontSJIS> _sjisFontShared;
-	uint8 _sjisInvisibleColor;
-	bool _sjisMixedFontMode;
 
 	Palette *_screenPalette;
 	Common::Array<Palette *> _palettes;
@@ -656,13 +666,6 @@ protected:
 	uint16 *_16bitConversionPalette;
 	uint8 _16bitShadingLevel;
 
-	Font *_fonts[FID_NUM];
-	uint8 _textColorsMap[16];
-	uint16 _textColorsMap16bit[2];
-
-	uint8 *_textRenderBuffer;
-	int _textRenderBufferSize;
-
 	uint8 *_animBlockPtr;
 	int _animBlockSize;
 
diff --git a/engines/kyra/graphics/screen_eob.h b/engines/kyra/graphics/screen_eob.h
index cac3be9f46..96ea6f92b7 100644
--- a/engines/kyra/graphics/screen_eob.h
+++ b/engines/kyra/graphics/screen_eob.h
@@ -34,6 +34,23 @@ class SegaRenderer;
 class SegaAnimator;
 class Screen_EoB : public Screen {
 friend class SegaRenderer;
+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
+	// than the original and also have to deal with the different ports which
+	// all do their own things. This is supposed to help avoid using pages that
+	// are already in use for something else. It also allows for quick fixes
+	// if necessary.
+	enum {
+		kSegaInitShapesPage		=	7,
+		kSegaRenderPage			=	8,
+		kDefeatMsgBackupPage	=	10,
+		kCheckPwBackupPage		=	10,
+		kSpellbookBackupPage	=	10,
+		kEoB2ScriptHelperPage	=	12,
+		kCampMenuBackupPage		=	12
+	};
+
 public:
 	Screen_EoB(EoBCoreEngine *vm, OSystem *system);
 	~Screen_EoB() override;
@@ -128,6 +145,8 @@ public:
 	void sega_fadeToNeutral(int delay) { sega_fadePalette(delay, 0); }
 	void sega_paletteOps(int16 opPal, int16 par1, int16 par2);
 	void sega_clearTextBuffer(uint8 col);
+	void sega_clearTextBufferLine(uint16 y, uint16 lineHeight, uint16 pitch, uint8 col);
+	void sega_copyTextBufferLine(uint16 srcY, uint16 dstY, uint16 lineHeight, uint16 pitch);
 	void sega_drawTextBox(int pW, int pH, int x, int y, int w, int h, uint8 color1, uint8 color2);
 	void sega_loadTextBufferToVRAM(uint16 srcOffset, uint16 addr, int size);
 	void sega_gfxScale(uint8 *out, uint16 w, uint16 h, uint16 pitch, const uint8 *in, const uint16 *stampMap, const uint16 *traceVectors);
diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index f3f26b4010..3c51ef86d2 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -208,6 +208,49 @@ void Screen_EoB::sega_clearTextBuffer(uint8 col) {
 	memset(_textRenderBuffer, col, _textRenderBufferSize);
 }
 
+void Screen_EoB::sega_clearTextBufferLine(uint16 y, uint16 lineHeight, uint16 pitch, uint8 col) {
+	uint32 *dst = (uint32*)(_textRenderBuffer + (((y >> 3) * pitch) << 5) + ((y & 7) << 2));
+	int ln = y;
+	uint32 c = col | (col << 8) | (col << 16) | (col << 24);
+	while (lineHeight--) {
+		uint32 *dst2 = dst;
+		for (uint16 w = pitch; w; --w) {
+			*dst = c;
+			dst += 8;
+		}
+		dst = dst2 + 1;
+		if (((++ln) & 7) == 0)
+			dst += ((pitch - 1) << 3);
+	}
+}
+
+
+void Screen_EoB::sega_copyTextBufferLine(uint16 srcY, uint16 dstY, uint16 lineHeight, uint16 pitch) {
+	uint32 *src = (uint32*)(_textRenderBuffer + (((srcY >> 3) * pitch) << 5) + ((srcY & 7) << 2));
+	uint32 *dst = (uint32*)(_textRenderBuffer + (((dstY >> 3) * pitch) << 5) + ((dstY & 7) << 2));
+	int src_ln = srcY;
+	int dst_ln = dstY;
+
+	while (lineHeight--) {
+		uint32 *src2 = src;
+		uint32 *dst2 = dst;
+
+		for (uint16 w = pitch; w; --w) {
+			*dst = *src;
+			src += 8;
+			dst += 8;
+		}
+
+		src = src2 + 1;
+		dst = dst2 + 1;
+
+		if (((++dst_ln) & 7) == 0)
+			dst += ((pitch - 1) << 3);
+		if (((++src_ln) & 7) == 0)
+			src += ((pitch - 1) << 3);
+	}
+}
+
 void Screen_EoB::sega_drawTextBox(int pW, int pH, int x, int y, int w, int h, uint8 color1, uint8 color2) {
 	sega_drawClippedLine(_textRenderBuffer, 26, 5, x, y, w, 1, color1);
 	sega_drawClippedLine(_textRenderBuffer, 26, 5, x, y + h - 1, w, 1, color1);
@@ -217,7 +260,6 @@ void Screen_EoB::sega_drawTextBox(int pW, int pH, int x, int y, int w, int h, ui
 	sega_drawClippedLine(_textRenderBuffer, 26, 5, x + 1, y + h - 2, w - 2, 1, color2);
 	sega_drawClippedLine(_textRenderBuffer, 26, 5, x + 1, y + 1, 1, h - 2, color2);
 	sega_drawClippedLine(_textRenderBuffer, 26, 5, x + w - 2, y + 1, 1, h - 2, color2);
-	
 }
 
 void Screen_EoB::sega_loadTextBufferToVRAM(uint16 srcOffset, uint16 addr, int size) {
@@ -319,7 +361,7 @@ void Screen_EoB::sega_encodeShapesFromSprites(const uint8 **dst, const uint8 *sr
 	_segaRenderer->loadToVRAM(src, numShapes * spriteSize, 0);
 	int hw = (((w >> 3) - 1) << 2) | ((h >> 3) - 1);
 
-	int cp = setCurPage(7);
+	int cp = setCurPage(Screen_EoB::kSegaInitShapesPage);
 
 	for (int l = 0, s = 0; s < numShapes; l = s) {
 		for (int i = s; i < numShapes; ++i) {
@@ -335,12 +377,12 @@ void Screen_EoB::sega_encodeShapesFromSprites(const uint8 **dst, const uint8 *sr
 		sega_fadeToNeutral(0);
 		updateScreen();
 */
-		_segaRenderer->render(7, true);
+		_segaRenderer->render(Screen_EoB::kSegaInitShapesPage, 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);
 
-		clearPage(7);
+		clearPage(Screen_EoB::kSegaInitShapesPage);
 	}
 
 	if (removeSprites) {
@@ -585,9 +627,23 @@ void SegaRenderer::fillRectWithTiles(int vramArea, int x, int y, int w, int h, u
 	}
 }
 
-void SegaRenderer::writeVSRAMValue(int addr, uint16 value) {
+void SegaRenderer::writeUint16VSRAM(int addr, uint16 value) {
 	assert(addr < 80);
+	assert(!(addr & 1));
 	_vsram[addr >> 1] = value;
+	checkUpdateDirtyRects(addr, 2);
+}
+
+void SegaRenderer::writeUint8VRAM(int addr, uint8 value) {
+	assert(addr < 0x10000);
+	_vram[addr] = value;
+	checkUpdateDirtyRects(addr, 1);
+}
+
+void SegaRenderer::writeUint16VRAM(int addr, uint16 value) {
+	assert(addr < 0x10000);
+	*((uint16*)(_vram + addr)) = value;
+	checkUpdateDirtyRects(addr, 2);
 }
 
 void SegaRenderer::clearPlanes() {
@@ -1132,7 +1188,7 @@ const uint8 *SegaCDFont::getGlyphData(uint16 c, uint8 &charWidth, uint8 &charHei
 	uint16 lo = 0;
 	uint16 hi = 0;
 
-	if (c == 0) {
+	if (c == 0 || c == 13) {
 		charWidth = charHeight = pitch = 0;
 		return 0;
 	}
@@ -1262,10 +1318,10 @@ void ScrollManager::updateScrollTimers() {
 		t._timer = t._delay;
 	}
 
-	_renderer->writeVSRAMValue(0, _vScrollTimers[0]._offsCur);
-	_renderer->writeVSRAMValue(2, _vScrollTimers[1]._offsCur);
-	uint16 hscr[2] = { (uint16)_hScrollTimers[0]._offsCur, (uint16)_hScrollTimers[1]._offsCur };
-	_renderer->loadToVRAM(hscr, 4, 0xD800);
+	_renderer->writeUint16VSRAM(0, _vScrollTimers[0]._offsCur);
+	_renderer->writeUint16VSRAM(2, _vScrollTimers[1]._offsCur);
+	_renderer->writeUint16VRAM(0xD800, _hScrollTimers[0]._offsCur);
+	_renderer->writeUint16VRAM(0xD802, _hScrollTimers[1]._offsCur);
 }
 
 } // End of namespace Kyra
diff --git a/engines/kyra/graphics/screen_eob_segacd.h b/engines/kyra/graphics/screen_eob_segacd.h
index d34bb5064a..05c5f41ba3 100644
--- a/engines/kyra/graphics/screen_eob_segacd.h
+++ b/engines/kyra/graphics/screen_eob_segacd.h
@@ -81,7 +81,9 @@ public:
 	void loadStreamToVRAM(Common::SeekableReadStreamEndian *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 writeVSRAMValue(int addr, uint16 value);
+	void writeUint16VSRAM(int addr, uint16 value);
+	void writeUint8VRAM(int addr, uint8 value);
+	void writeUint16VRAM(int addr, uint16 value);
 	void clearPlanes();
 	
 	void render(int destPageNum, bool spritesOnly = false);
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 476055e42b..6750d4ecac 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -155,7 +155,7 @@ void EoBCoreEngine::gui_drawCharPortraitWithStats(int index) {
 	} else if ((_currentControlMode == 1 || _currentControlMode == 2) && index == _updateCharNum) {
 		_screen->copyRegion(176, 0, 0, 0, 144, 168, 2, 2, Screen::CR_NO_P_CHECK);
 		if (_flags.platform == Common::kPlatformSegaCD && _currentControlMode == 2)
-			_screen->copyRegion(176, 0, 176, 0, 144, 168, 4, 2, Screen::CR_NO_P_CHECK);
+			_screen->copyRegion(176, 0, 176, 0, 144, 168, Screen_EoB::kSegaRenderPage, 2, Screen::CR_NO_P_CHECK);
 		_screen->_curPage = 2;
 
 		gui_drawFaceShape(index);
@@ -198,76 +198,14 @@ void EoBCoreEngine::gui_drawCharPortraitWithStats(int index) {
 				gui_drawInventoryItem(i, 0, 2);
 			gui_drawInventoryItem(16, 1, 2);
 
+			if (_flags.platform == Common::kPlatformSegaCD)
+				gui_drawInventoryItem(27, 1, 2);
+
 			_screen->setFont(cf);
 
 		} else {
-			static const uint16 cm2X1[] = { 179, 272, 301 };
-			static const uint16 cm2Y1[] = { 36, 51, 51 };
-			static const uint16 cm2X2[] = { 271, 300, 318 };
-			static const uint16 cm2Y2[] = { 165, 165, 147 };
-
-			if (_flags.platform != Common::kPlatformSegaCD) {
-				for (int i = 0; i < 3; i++)
-					_screen->fillRect(cm2X1[i], cm2Y1[i], cm2X2[i], cm2Y2[i], guiSettings()->colors.sfill);
-			}
-
 			_screen->setFont(cf);
-
-			int lineH = MIN(_screen->getFontHeight() + 1, 8);
-			_screen->printShadedText(_characterGuiStringsIn[0], 183, 42, guiSettings()->colors.guiColorWhite, guiSettings()->colors.sfill, guiSettings()->colors.guiColorBlack);
-			_screen->printText(_chargenClassStrings[c->cClass], 183, 55, guiSettings()->colors.guiColorBlack, guiSettings()->colors.sfill);
-			_screen->printText(_chargenAlignmentStrings[c->alignment], 183, 55 + lineH, guiSettings()->colors.guiColorBlack, guiSettings()->colors.sfill);
-			_screen->printText(_chargenRaceSexStrings[c->raceSex], 183, 55 + 2 * lineH, guiSettings()->colors.guiColorBlack, guiSettings()->colors.sfill);
-
-			lineH = _screen->getFontHeight() + 1;
-			int tX = 183;
-			int tY = _flags.use16ColorMode ? 87 : 82;
-			for (int i = 0; i < 3; i++)
-				_screen->printText(_chargenStatStrings[6 + i], tX, tY + i * lineH, guiSettings()->colors.guiColorBlack, guiSettings()->colors.sfill);
-			
-			if (_flags.use16ColorMode) {
-				tX += 72;
-				tY -= 27;
-			}			
-			for (int i = 3; i < 6; i++)
-				_screen->printText(_chargenStatStrings[6 + i], tX, tY + i * lineH, guiSettings()->colors.guiColorBlack, guiSettings()->colors.sfill);
-
-			_screen->printText(_characterGuiStringsIn[1], 183, tY + 6 * lineH, guiSettings()->colors.guiColorBlack, guiSettings()->colors.sfill);
-			
-			tY = _flags.use16ColorMode ? 127 : 138;
-			_screen->printText(_characterGuiStringsIn[2], 239, tY, guiSettings()->colors.guiColorBlack, guiSettings()->colors.sfill);
-			_screen->printText(_characterGuiStringsIn[3], 278, tY, guiSettings()->colors.guiColorBlack, guiSettings()->colors.sfill);
-
-			tX = _flags.use16ColorMode ? 210 : 275;
-			tY = _flags.use16ColorMode ? 87 : 82;
-			_screen->printText(getCharStrength(c->strengthCur, c->strengthExtCur).c_str(), tX, tY, guiSettings()->colors.guiColorWhite, guiSettings()->colors.sfill);
-			_screen->printText(Common::String::format("%d", c->intelligenceCur).c_str(), tX, tY + lineH, guiSettings()->colors.guiColorWhite, guiSettings()->colors.sfill);
-			_screen->printText(Common::String::format("%d", c->wisdomCur).c_str(), tX, tY + 2 * lineH, guiSettings()->colors.guiColorWhite, guiSettings()->colors.sfill);
-			
-			if (_flags.use16ColorMode) {
-				tX = 285;
-				tY -= 27;
-			}
-			_screen->printText(Common::String::format("%d", c->dexterityCur).c_str(), tX, tY + 3 * lineH, guiSettings()->colors.guiColorWhite, guiSettings()->colors.sfill);
-			_screen->printText(Common::String::format("%d", c->constitutionCur).c_str(), tX, tY + 4 * lineH, guiSettings()->colors.guiColorWhite, guiSettings()->colors.sfill);
-			_screen->printText(Common::String::format("%d", c->charismaCur).c_str(), tX, tY + 5 * lineH, guiSettings()->colors.guiColorWhite, guiSettings()->colors.sfill);
-			
-			if (_flags.use16ColorMode)
-				tX = 255;
-			_screen->printText(Common::String::format("%d", c->armorClass).c_str(), tX, tY + 6 * lineH, guiSettings()->colors.guiColorWhite, guiSettings()->colors.sfill);
-
-			tY = _flags.use16ColorMode ? 136 : 145;
-			for (int i = 0; i < 3; i++) {
-				int t = getCharacterClassType(c->cClass, i);
-				if (t == -1)
-					continue;
-				tX = (_flags.use16ColorMode) ? 183 : 180;
-				_screen->printText(_chargenClassStrings[t + 15], tX, tY + lineH * i, guiSettings()->colors.guiColorBlack, guiSettings()->colors.sfill);
-				Common::String tmpStr = Common::String::format("%d", c->experience[i]);
-				_screen->printText(tmpStr.c_str(), 251 - (_screen->getTextWidth(tmpStr.c_str()) >> 1), tY + lineH * i, guiSettings()->colors.guiColorWhite, guiSettings()->colors.sfill);
-				tmpStr = Common::String::format("%d", c->level[i]);
-				_screen->printText(tmpStr.c_str(), 286 - (_screen->getTextWidth(tmpStr.c_str()) >> 1), tY + lineH * i, guiSettings()->colors.guiColorWhite, guiSettings()->colors.sfill);
-			}
+			gui_drawCharacterStatsPage();
 		}
 
 		_screen->_curPage = 0;
@@ -548,7 +486,7 @@ void EoBCoreEngine::gui_drawInventoryItem(int slot, int redraw, int pageNum) {
 	int x = _inventorySlotsX[slot];
 	int y = _inventorySlotsY[slot];
 
-	int item = _characters[_updateCharNum].inventory[slot];
+	int item = (slot != 27) ? _characters[_updateCharNum].inventory[slot] : 0;
 	int cp = _screen->setCurPage(pageNum);
 
 	if (redraw) {
@@ -564,24 +502,29 @@ void EoBCoreEngine::gui_drawInventoryItem(int slot, int redraw, int pageNum) {
 			col2 = 3;
 		}
 
-		gui_drawBox(x - 1, y - 1, wh, wh, col1, col2, slot == 16 ? -1 : guiSettings()->colors.fill);
+		if (_flags.platform == Common::kPlatformSegaCD)
+			_screen->copyRegion(x - 1, y - 1, x - 1, y - 1, wh - 2, wh - 2, 2, 0, Screen::CR_NO_P_CHECK);
+		else
+			gui_drawBox(x - 1, y - 1, wh, wh, col1, col2, slot == 16 ? -1 : guiSettings()->colors.fill);
 
 		if (slot == 16) {
-			_screen->fillRect(227, 65, 238, 69, guiSettings()->colors.guiColorBlack);
+			_screen->fillRect(x + 3, y + 9, x + 14, y + 13, guiSettings()->colors.guiColorBlack);
 			int cnt = countQueuedItems(_characters[_updateCharNum].inventory[slot], -1, -1, 1, 1);
 			if (_flags.platform != Common::kPlatformSegaCD) {
-				x = cnt >= 10 ? 227 : 233;
 				Screen::FontId cf = _screen->setFont(Screen::FID_6_FNT);
 				Common::String str = Common::String::format("%d", cnt);
-				_screen->printText(str.c_str(), x, 65, guiSettings()->colors.guiColorWhite, 0);
+				_screen->printText(str.c_str(), x + (cnt < 10 ? 8 : 2), 65, guiSettings()->colors.guiColorWhite, 0);
 				_screen->setFont(cf);
 			} else {
-				gui_printInventoryDigits(227, 65, cnt);
+				gui_printInventoryDigits(x, y + 8, cnt);
 			}
+		} else if (slot == 27) {
+			_screen->fillRect(x + 3, y + 9, x + 14, y + 13, guiSettings()->colors.guiColorBlack);
+			gui_printInventoryDigits(x, y + 8, countMaps());
 		}
 	}
 
-	if (slot != 16 && item) {
+	if (slot != 16 && slot != 27 && item) {
 		if (slot == 25 || slot == 26) {
 			x -= 4;
 			y -= 4;
@@ -592,6 +535,76 @@ void EoBCoreEngine::gui_drawInventoryItem(int slot, int redraw, int pageNum) {
 	_screen->updateScreen();
 }
 
+void EoBCoreEngine::gui_drawCharacterStatsPage() {
+	static const uint16 cm2X1[] = { 179, 272, 301 };
+	static const uint16 cm2Y1[] = { 36, 51, 51 };
+	static const uint16 cm2X2[] = { 271, 300, 318 };
+	static const uint16 cm2Y2[] = { 165, 165, 147 };
+
+	EoBCharacter *c = &_characters[_updateCharNum];
+
+	if (_flags.platform != Common::kPlatformSegaCD) {
+		for (int i = 0; i < 3; i++)
+			_screen->fillRect(cm2X1[i], cm2Y1[i], cm2X2[i], cm2Y2[i], guiSettings()->colors.sfill);
+	}
+
+	int lineH = MIN(_screen->getFontHeight() + 1, 8);
+	_screen->printShadedText(_characterGuiStringsIn[0], 183, 42, guiSettings()->colors.guiColorWhite, guiSettings()->colors.sfill, guiSettings()->colors.guiColorBlack);
+	_screen->printText(_chargenClassStrings[c->cClass], 183, 55, guiSettings()->colors.guiColorBlack, guiSettings()->colors.sfill);
+	_screen->printText(_chargenAlignmentStrings[c->alignment], 183, 55 + lineH, guiSettings()->colors.guiColorBlack, guiSettings()->colors.sfill);
+	_screen->printText(_chargenRaceSexStrings[c->raceSex], 183, 55 + 2 * lineH, guiSettings()->colors.guiColorBlack, guiSettings()->colors.sfill);
+
+	lineH = _screen->getFontHeight() + 1;
+	int tX = 183;
+	int tY = _flags.use16ColorMode ? 87 : 82;
+	for (int i = 0; i < 3; i++)
+		_screen->printText(_chargenStatStrings[6 + i], tX, tY + i * lineH, guiSettings()->colors.guiColorBlack, guiSettings()->colors.sfill);
+
+	if (_flags.use16ColorMode) {
+		tX += 72;
+		tY -= 27;
+	}
+	for (int i = 3; i < 6; i++)
+		_screen->printText(_chargenStatStrings[6 + i], tX, tY + i * lineH, guiSettings()->colors.guiColorBlack, guiSettings()->colors.sfill);
+
+	_screen->printText(_characterGuiStringsIn[1], 183, tY + 6 * lineH, guiSettings()->colors.guiColorBlack, guiSettings()->colors.sfill);
+
+	tY = _flags.use16ColorMode ? 127 : 138;
+	_screen->printText(_characterGuiStringsIn[2], 239, tY, guiSettings()->colors.guiColorBlack, guiSettings()->colors.sfill);
+	_screen->printText(_characterGuiStringsIn[3], 278, tY, guiSettings()->colors.guiColorBlack, guiSettings()->colors.sfill);
+
+	tX = _flags.use16ColorMode ? 210 : 275;
+	tY = _flags.use16ColorMode ? 87 : 82;
+	_screen->printText(getCharStrength(c->strengthCur, c->strengthExtCur).c_str(), tX, tY, guiSettings()->colors.guiColorWhite, guiSettings()->colors.sfill);
+	_screen->printText(Common::String::format("%d", c->intelligenceCur).c_str(), tX, tY + lineH, guiSettings()->colors.guiColorWhite, guiSettings()->colors.sfill);
+	_screen->printText(Common::String::format("%d", c->wisdomCur).c_str(), tX, tY + 2 * lineH, guiSettings()->colors.guiColorWhite, guiSettings()->colors.sfill);
+
+	if (_flags.use16ColorMode) {
+		tX = 285;
+		tY -= 27;
+	}
+	_screen->printText(Common::String::format("%d", c->dexterityCur).c_str(), tX, tY + 3 * lineH, guiSettings()->colors.guiColorWhite, guiSettings()->colors.sfill);
+	_screen->printText(Common::String::format("%d", c->constitutionCur).c_str(), tX, tY + 4 * lineH, guiSettings()->colors.guiColorWhite, guiSettings()->colors.sfill);
+	_screen->printText(Common::String::format("%d", c->charismaCur).c_str(), tX, tY + 5 * lineH, guiSettings()->colors.guiColorWhite, guiSettings()->colors.sfill);
+
+	if (_flags.use16ColorMode)
+		tX = 255;
+	_screen->printText(Common::String::format("%d", c->armorClass).c_str(), tX, tY + 6 * lineH, guiSettings()->colors.guiColorWhite, guiSettings()->colors.sfill);
+
+	tY = _flags.use16ColorMode ? 136 : 145;
+	for (int i = 0; i < 3; i++) {
+		int t = getCharacterClassType(c->cClass, i);
+		if (t == -1)
+			continue;
+		tX = (_flags.use16ColorMode) ? 183 : 180;
+		_screen->printText(_chargenClassStrings[t + 15], tX, tY + lineH * i, guiSettings()->colors.guiColorBlack, guiSettings()->colors.sfill);
+		Common::String tmpStr = Common::String::format("%d", c->experience[i]);
+		_screen->printText(tmpStr.c_str(), 251 - (_screen->getTextWidth(tmpStr.c_str()) >> 1), tY + lineH * i, guiSettings()->colors.guiColorWhite, guiSettings()->colors.sfill);
+		tmpStr = Common::String::format("%d", c->level[i]);
+		_screen->printText(tmpStr.c_str(), 286 - (_screen->getTextWidth(tmpStr.c_str()) >> 1), tY + lineH * i, guiSettings()->colors.guiColorWhite, guiSettings()->colors.sfill);
+	}
+}
+
 void EoBCoreEngine::gui_drawCompass(bool force) {
 	//if (_currentDirection == _compassDirection && !force)
 		return;
@@ -822,26 +835,10 @@ void EoBCoreEngine::gui_initButton(int index, int, int, int) {
 	const EoBGuiButtonDef *d = &_buttonDefs[index];
 	b->buttonCallback = _buttonCallbacks[index];
 
-	if (_flags.gameID == GI_EOB1) {
-		// EOB1 spellbook modifications
-		if (index > 60 && index < 66) {
-			d = &_buttonDefs[index + 34];
-			b->buttonCallback = _buttonCallbacks[index + 34];
-		} else if (index == 88) {
-			d = &_buttonDefs[index + 12];
-			b->buttonCallback = _buttonCallbacks[index + 12];
-		}
-	}
-
 	b->x = d->x;
 	b->y = d->y;
 	b->width = d->w;
 	b->height = d->h;
-
-	// EOB1 spellbook modifications
-	if (_flags.gameID == GI_EOB1 && ((index > 66 && index < 73) || (index > 76 && index < 79)))
-		b->y++;
-
 	b->flags = d->flags;
 	b->keyCode = d->keyCode;
 	b->keyCode2 = d->keyCode2;
@@ -875,12 +872,12 @@ int EoBCoreEngine::clickedCamp(Button *button) {
 	// This ensures that all special rendering (EGA dithering, 16bit rendering, Japanese font rendering) will be visible on the thumbnail.
 	::createThumbnailFromScreen(&_thumbNail);
 
-	_screen->copyRegion(0, 120, 0, 0, 176, 24, 0, 12, Screen::CR_NO_P_CHECK);
+	_screen->copyRegion(0, 120, 0, 0, 176, 24, 0, Screen_EoB::kCampMenuBackupPage, Screen::CR_NO_P_CHECK);
 
 	_gui->runCampMenu();
 
 	_screen->fillRect(0, 0, 175, 143, 0, 2);
-	_screen->copyRegion(0, 0, 0, 120, 176, 24, 12, 2, Screen::CR_NO_P_CHECK);
+	_screen->copyRegion(0, 0, 0, 120, 176, 24, Screen_EoB::kCampMenuBackupPage, 2, Screen::CR_NO_P_CHECK);
 	_screen->setScreenDim(cd);
 
 	_thumbNail.free();
@@ -1275,7 +1272,7 @@ int EoBCoreEngine::clickedSpellbookAbort(Button *button) {
 	_updateFlags = 0;
 	_screen->fillRect(64, 121, 175, 176, 0, 0);
 	_screen->fillRect(64, 121, 175, 176, 0, 2);
-	_screen->copyRegion(0, 0, 64, 121, 112, 56, 10, 0, Screen::CR_NO_P_CHECK);
+	_screen->copyRegion(0, 0, 64, 121, 112, 56, Screen_EoB::kSpellbookBackupPage, 0, Screen::CR_NO_P_CHECK);
 	_screen->updateScreen();
 	gui_drawCompass(true);
 	gui_toggleButtons();
@@ -2307,7 +2304,7 @@ void GUI_EoB::runCampMenu() {
 				if (cnt > 4) {
 					_vm->dropCharacter(selectCharacterDialogue(53));
 					_vm->gui_drawPlayField(false);
-					_screen->copyRegion(0, 120, 0, 0, 176, 24, 0, 12, Screen::CR_NO_P_CHECK);
+					_screen->copyRegion(0, 120, 0, 0, 176, 24, 0, Screen_EoB::kCampMenuBackupPage, Screen::CR_NO_P_CHECK);
 					Screen::FontId cfn = _screen->setFont(_vm->_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT);
 					_vm->gui_drawAllCharPortraitsWithStats();
 					_screen->setFont(cfn);
diff --git a/engines/kyra/gui/gui_eob_segacd.cpp b/engines/kyra/gui/gui_eob_segacd.cpp
new file mode 100644
index 0000000000..68ebf5ebb4
--- /dev/null
+++ b/engines/kyra/gui/gui_eob_segacd.cpp
@@ -0,0 +1,224 @@
+/* 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.
+ *
+ */
+
+
+#ifdef ENABLE_EOB
+
+#include "kyra/engine/eob.h"
+#include "kyra/graphics/screen_eob.h"
+#include "kyra/graphics/screen_eob_segacd.h"
+#include "kyra/resource/resource.h"
+#include "kyra/resource/resource_segacd.h"
+
+namespace Kyra {
+
+void EoBEngine::gui_drawPlayField(bool refresh) {
+	if (_flags.platform != Common::kPlatformSegaCD) {
+		EoBCoreEngine::gui_drawPlayField(refresh);
+		return;
+	}
+
+	if (!_loading)
+		_screen->sega_fadeToBlack(1);
+
+	// transposeScreenOutputY(8);
+	_txt->clearDim(0);
+	_screen->sega_getAnimator()->clearSprites();
+	SegaRenderer *r = _screen->sega_getRenderer();
+
+	uint8 *data = _res->fileData("PLAYFLD", 0);
+	for (int i = 0; i < 256; ++i)
+		r->loadToVRAM(&data[i << 5], 32, _addrTbl1[i] << 5);
+	delete[] data;
+
+	const uint16 *pattern = _playFldPattern1;
+	uint16 *dst = _playFldPattern2;
+
+	for (int i = 0; i < 1040; ++i) {
+		int ix = (*pattern++) - 11;
+		*dst++ = (ix < 0) ? 0 : _addrTbl1[ix];
+	}
+
+	const uint16 ps[] = { 0xCE, 0xE0, 0x2FE, 0x310, 0x52E, 0x540 };
+
+	for (int i = 0; i < 4; ++i) {
+		dst = &_playFldPattern2[ps[i] >> 1];
+		memset(dst, 0, 8);
+		memset(&dst[40], 0, 8);
+		memset(&dst[80], 0, 8);
+		memset(&dst[120], 0, 8);
+	}
+
+	r->fillRectWithTiles(1, 0, 0, 40, 26, 0x2000, true, false, _playFldPattern2);
+	r->fillRectWithTiles(0, 0, 21, 40, 5, 0x2000, true, false, _textFieldPattern);
+
+	// Name tables for scene window vcn block tiles. We don't need that. We draw the blocks with our "normal" graphics code.
+	// r->fillRectWithTiles(1, 0, 0, 22, 15, 0xC14B, true, true);
+	// Name tables for scene window shapes tiles. We don't need that, since we're not going to draw any shapes with the renderer.
+	// 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);
+
+	_sres->loadContainer("ITEM");
+	Common::SeekableReadStreamEndian *str = _sres->resStreamEndian(7);
+	r->loadStreamToVRAM(str, 0x8880, true);
+	delete str;
+	str = _sres->resStreamEndian(9);
+	r->loadStreamToVRAM(str, 0xA4A0, false);
+	delete str;
+
+	r->fillRectWithTiles(1, 22, 0, 18, 21, 0x6444, true, true, _invPattern);
+	r->render(2);
+	r->fillRectWithTiles(1, 22, 0, 18, 21, 0x6444, true, true, _statsPattern);
+	r->render(Screen_EoB::kSegaRenderPage);
+
+	if (refresh && !_sceneDrawPage2)
+		drawScene(0);
+
+	_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);
+	_screen->copyRegionToBuffer(0, 173, 0, 6, 120, _shakeBackBuffer1);
+	_screen->copyRegionToBuffer(0, 0, 117, 179, 6, _shakeBackBuffer2);
+
+	// Since we're no going to draw with the SegaRenderer but rather with our "normal" code, we have to backup some parts of
+	// the background between the character portraits. Unlike the other versions the red splat shape overlaps with that space.
+	for (int i = 0; i < 6; ++i) {
+		delete[] _redSplatBG[i];
+		_redSplatBG[i] = new uint8[_redSplatShape[2] << 5];
+		_screen->copyRegionToBuffer(0, guiSettings()->charBoxCoords.boxX[i & 1] + guiSettings()->charBoxCoords.redSplatOffsetX, guiSettings()->charBoxCoords.boxY[i >> 1] + guiSettings()->charBoxCoords.boxHeight - 1, _redSplatShape[2] << 3, 4, _redSplatBG[i]);
+	}
+
+	if (!_loading)
+		_screen->sega_fadeToNeutral(1);
+}
+
+void EoBEngine::gui_drawWeaponSlotStatus(int x, int y, int status) {
+	if (_flags.platform != Common::kPlatformSegaCD) {
+		EoBCoreEngine::gui_drawWeaponSlotStatus(x, y, status);
+		return;
+	}
+
+	if (status < 0) {
+		_screen->drawShape(_screen->_curPage, _weaponSlotShapes[status < -2 ? -status - 1 : 3 - status], x - 1, y, 0);
+	} else {
+		_screen->drawShape(_screen->_curPage, _weaponSlotShapes[0], x - 1, y, 0);
+		gui_printInventoryDigits(x + 8, y + 6, status);
+	}
+}
+
+void EoBEngine::gui_printInventoryDigits(int x, int y, int val) {
+	if (_flags.platform != Common::kPlatformSegaCD) {
+		EoBCoreEngine::gui_printInventoryDigits(x, y, val);
+		return;
+	}
+	_screen->drawShape(_screen->_curPage, _invSmallDigits[(val < 10) ? 22 + val : (val >= 100 ? 1 : 2 + val / 10)], x, y);
+	_screen->drawShape(_screen->_curPage, (val >= 10 && val < 100) ? _invSmallDigits[12 + (val % 10)] : 0, x, y);
+}
+
+void EoBEngine::gui_drawCharacterStatsPage() {
+	SegaRenderer *r = _screen->sega_getRenderer();
+	EoBCharacter *c = &_characters[_updateCharNum];
+
+	memset(_statsPattern2, 0, 792);
+	for (int i = 0; i < 11; ++i) {
+		_statsPattern2[5 * 18 + i + 1] = 0x6555 + i;
+		_statsPattern2[6 * 18 + i + 1] = 0x6565 + i;
+	}
+
+	for (int i = 0; i < 4; i++)
+		printStatsString(_chargenStatStrings[6 + i], 1, 11 + i);
+	printStatsString(_chargenStatStrings[10], 7, 16);
+
+	printStatsString(_chargenClassStrings[c->cClass == 9 ? 27 : (c->cClass == 12 ? 28 : c->cClass)], 2, 7);
+	printStatsString(_chargenAlignmentStrings[c->alignment], 2, 8);
+	printStatsString(_chargenRaceSexStrings[c->raceSex], 2, 9);
+
+	// FIXME? For now I have kept the exact original layout. If the character has a strengthExt stat there will be
+	// no space left between the digits and the right stats row ("DEX" etc.). Maybe I should move that row one tile
+	// further to the right? There is space enough left over there.
+	printStatsString(getCharStrength(c->strengthCur, c->strengthExtCur).c_str(), c->strengthExtCur ? 4 : 5, 11);
+	printStatsString(Common::String::format("%d", c->intelligenceCur).c_str(), 5, 12);
+	printStatsString(Common::String::format("%d", c->wisdomCur).c_str(), 5, 13);
+	printStatsString(Common::String::format("%d", c->dexterityCur).c_str(), 13, 11);
+	printStatsString(Common::String::format("%d", c->constitutionCur).c_str(), 13, 12);
+	printStatsString(Common::String::format("%d", c->charismaCur).c_str(), 13, 13);
+	printStatsString(Common::String::format("%d", c->armorClass).c_str(), 5, 14);
+
+	for (int i = 0; i < 3; i++) {
+		int t = getCharacterClassType(c->cClass, i);
+		if (t == -1)
+			continue;
+		printStatsString(_chargenClassStrings[t + 21], 1, 17 + i);
+		printStatsString(Common::String::format("%2d", c->level[i]).c_str(), 14, 17 + i);
+		printStatsString(Common::String::format("%6d", c->experience[i]).c_str(), 7, 17 + i);
+	}
+
+	r->fillRectWithTiles(0, 22, 0, 18, 21, 0, true, true, _statsPattern2);
+	r->render(Screen_EoB::kSegaRenderPage);
+
+	_screen->copyRegion(176, 40, 176, 40, 144, 128, Screen_EoB::kSegaRenderPage, 2, Screen::CR_NO_P_CHECK);
+}
+
+void EoBEngine::makeNameShapes() {
+	if (_flags.platform != Common::kPlatformSegaCD)
+		return;
+
+	int cd = _txt->clearDim(4);
+	int cp = _screen->setCurPage(2);
+	_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 28, 0x2000);
+	_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 30, 28, 0x600A, true);
+	_screen->sega_clearTextBuffer(0);
+
+	for (int i = 0; i < 6; ++i) {
+		if (!_characters[i].flags)
+			continue;
+		_txt->printShadowedText(_characters[i].name, 0, i << 4, 0xFF, 0xCC);
+	}
+
+	_screen->sega_getRenderer()->render(_screen->_curPage);
+
+	for (int i = 0; i < 6; ++i) {
+		if (!_characters[i].flags)
+			continue;
+		delete[] _characters[i].nameShape;
+		_characters[i].nameShape = _screen->encodeShape(0, i << 4, (_screen->getTextWidth(_characters[i].name) + 8) >> 3, 13);
+	}
+
+	_screen->clearPage(2);
+	_screen->setCurPage(cp);
+	_screen->sega_clearTextBuffer(0);
+
+	_txt->clearDim(4);
+	_txt->clearDim(cd);
+}
+
+void EoBEngine::printStatsString(const char *str, int x, int y) {
+	uint16 *dst = &_statsPattern2[y * 18 + x];
+	for (const uint8 *pos = (const uint8*)str; *pos; ++pos)
+		*dst++ = 0x6525 + _charTilesTable[*pos];
+}
+
+} // End of namespace Kyra
+
+#endif // ENABLE_EOB
diff --git a/engines/kyra/gui/saveload_eob.cpp b/engines/kyra/gui/saveload_eob.cpp
index 1d5150531a..a0a8291dce 100644
--- a/engines/kyra/gui/saveload_eob.cpp
+++ b/engines/kyra/gui/saveload_eob.cpp
@@ -310,7 +310,7 @@ Common::Error EoBCoreEngine::loadGameState(int slot) {
 		useMagicBookOrSymbol(_openBookChar, _openBookType);
 	}
 
-	_screen->copyRegion(0, 120, 0, 0, 176, 24, 0, 12, Screen::CR_NO_P_CHECK);
+	_screen->copyRegion(0, 120, 0, 0, 176, 24, 0, Screen_EoB::kCampMenuBackupPage, Screen::CR_NO_P_CHECK);
 
 	gui_toggleButtons();
 	setHandItem(_itemInHand);
diff --git a/engines/kyra/module.mk b/engines/kyra/module.mk
index 75428e64e2..e07e7500dd 100644
--- a/engines/kyra/module.mk
+++ b/engines/kyra/module.mk
@@ -126,6 +126,7 @@ MODULE_OBJS += \
 	graphics/screen_eob_segacd.o \
 	graphics/screen_eob_towns.o \
 	gui/gui_eob.o \
+	gui/gui_eob_segacd.o \
 	gui/saveload_eob.o \
 	resource/resource_segacd.o \
 	resource/staticres_eob.o \
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index 120841dc21..6134d3ddd3 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -669,19 +669,31 @@ void EoBCoreEngine::initButtonData() {
 		{ 116, 117, 0x1100, 320, 200, 1, 1, 2 },
 		{ 7, 0, 0x1100, 158, 121, 15, 10, 5 },
 		{ 0, 0, 0x1100, 146, 168, 32, 10, 0 },
-
-		// EOB1 spellbook modifications
-		{ 2, 0, 0x1100, 71, 122, 20, 8, 0 },
-		{ 3, 0, 0x1100, 92, 122, 20, 8, 1 },
-		{ 4, 0, 0x1100, 113, 122, 20, 8, 2 },
-		{ 5, 0, 0x1100, 134, 122, 20, 8, 3 },
-		{ 6, 0, 0x1100, 155, 122, 20, 8, 4 },
-		{ 110, 0, 0x1100, 75, 168, 97, 6, 0 }
 	};
 
 	_buttonDefs = new EoBGuiButtonDef[ARRAYSIZE(buttonDefs)];
 	memcpy(_buttonDefs, buttonDefs, sizeof(buttonDefs));
 
+	// The spellbook buttons in the table above are from EOB II. We make the necessary coordinates modifications for EOB I.
+	if (_flags.gameID == GI_EOB1) {
+		static const EoBGuiButtonDef eob1SpellbookButtonDefs[6] = {
+			{ 2, 0, 0x1100, 71, 122, 20, 8, 0 },
+			{ 3, 0, 0x1100, 92, 122, 20, 8, 1 },
+			{ 4, 0, 0x1100, 113, 122, 20, 8, 2 },
+			{ 5, 0, 0x1100, 134, 122, 20, 8, 3 },
+			{ 6, 0, 0x1100, 155, 122, 20, 8, 4 },
+			{ 110, 0, 0x1100, 75, 168, 97, 6, 0 }
+		};
+
+		memcpy(&_buttonDefs[61], eob1SpellbookButtonDefs, 5 * sizeof(EoBGuiButtonDef));
+		memcpy(&_buttonDefs[88], &eob1SpellbookButtonDefs[5], sizeof(EoBGuiButtonDef));
+		for (int i = 67; i < 73; ++i)
+			_buttonDefs[i].y++;
+		for (int i = 77; i < 79; ++i)
+			_buttonDefs[i].y++;
+	}
+
+	// Replace keycodes for EOB II FM-Towns
 	if (_flags.platform == Common::kPlatformFMTowns) {
 		static const uint16 keyCodesFMTowns[] = {
 			93, 94, 95, 96, 67, 27, 24, 349, 350, 351, 352, 80, 27, 24, 30, 0, 31, 0, 29, 0, 28, 0, 127, 18, 27, 93, 94, 95, 96,
@@ -697,6 +709,20 @@ void EoBCoreEngine::initButtonData() {
 		}
 	}
 
+	// Match the inventory button coords to the values that are used for drawing the inventory slots, so that
+	// the buttons get fixed if the target uses a different inventory screen layout (currently only EOB I SegaCD).
+	int temp = 0;
+	const uint16 *invX = _staticres->loadRawDataBe16(kEoBBaseInvSlotX, temp);
+	const uint8 *invY = _staticres->loadRawData(kEoBBaseInvSlotY, temp);
+	for (int i = 0; i < 25; ++i) {
+		_buttonDefs[21 + i].x = invX[i];
+		_buttonDefs[21 + i].y = invY[i];
+	}
+	for (int i = 25; i < 27; ++i) {
+		_buttonDefs[59 + i].x = invX[i];
+		_buttonDefs[59 + i].y = invY[i];
+	}
+
 	_buttonCallbacks.clear();
 	_buttonCallbacks.reserve(ARRAYSIZE(buttonDefs));
 
@@ -738,8 +764,6 @@ void EoBCoreEngine::initButtonData() {
 	EOB_CBI(1, 60);
 	EOB_CBI(1, 61);
 	EOB_CBN(1, clickedSpellbookScroll);
-	EOB_CBI(5, 61);
-	EOB_CBI(1, 88);
 #undef EOB_CBI
 #undef EOB_CBN
 }
@@ -1247,6 +1271,7 @@ void EoBEngine::initStaticResource() {
 	_playFldPattern1 = _staticres->loadRawDataBe16(kEoB1PatternTable0, temp);
 	_invPattern = _staticres->loadRawDataBe16(kEoB1PatternTable3, temp);
 	_statsPattern = _staticres->loadRawDataBe16(kEoB1PatternTable4, temp);
+	_charTilesTable = _staticres->loadRawData(kEoB1CharTilesTable, temp);
 
 	// Build offset tables for door shapes encoding
 	if (_flags.platform == Common::kPlatformSegaCD) {
diff --git a/engines/kyra/script/script_eob.cpp b/engines/kyra/script/script_eob.cpp
index ab7cf81d61..76f580defb 100644
--- a/engines/kyra/script/script_eob.cpp
+++ b/engines/kyra/script/script_eob.cpp
@@ -527,9 +527,22 @@ int EoBInfProcessor::oeob_printMessage_v1(int8 *data) {
 	strcpy(col, colorConfig);
 	const char *str = (const char *)pos;
 	pos += (strlen(str) + 1);
+	bool lineBreak = true;
+
+	if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
+		assert((uint8)*pos < 16);
+		col[1] = _segaCDColorMap[*pos];
+		if (str[0] == '/') {
+			lineBreak = false;
+			str++;
+		}
+		_vm->txt()->clearDim(0);
+		_vm->snd_playSoundEffect(0x204F);
 
-	col[1] = *pos++;
-	col[3] = *pos++;
+	} else {
+		col[1] = *pos++;
+		col[3] = *pos++;
+	}
 
 	if (_vm->gameFlags().platform == Common::kPlatformAmiga) {
 		assert((uint8)col[1] < 16);
@@ -541,10 +554,12 @@ int EoBInfProcessor::oeob_printMessage_v1(int8 *data) {
 	_vm->txt()->printMessage(col);
 	_vm->txt()->printMessage(str);
 
-	col[1] = _vm->txt()->colorMap()[_screen->_curDim->unk8];
+	col[1] = _vm->gameFlags().platform == Common::kPlatformSegaCD ? 0xFF : _vm->txt()->colorMap()[_screen->_curDim->unk8];
 	col[3] = _vm->txt()->colorMap()[_screen->_curDim->unkA];
 	_vm->txt()->printMessage(col);
-	_vm->txt()->printMessage("\r");
+
+	if (lineBreak)
+		_vm->txt()->printMessage("\r");
 
 	return pos - data;
 }
@@ -1595,14 +1610,14 @@ int EoBInfProcessor::oeob_specialEvent(int8 *data) {
 	case 0:
 		_vm->drawScene(1);
 		_screen->_curPage = 2;
-		_screen->copyRegion(72, 0, 0, 0, 32, 120, 2, 12, Screen::CR_NO_P_CHECK);
+		_screen->copyRegion(72, 0, 0, 0, 32, 120, 2, Screen_EoB::kEoB2ScriptHelperPage, Screen::CR_NO_P_CHECK);
 
 		for (; i < 4; i++) {
 			endTime = _vm->_system->getMillis() + _vm->_tickLength;
 			_vm->drawLightningColumn();
 			_screen->copyRegion(72, 0, 72, 0, 32, 120, 2, 0, Screen::CR_NO_P_CHECK);
 			_screen->updateScreen();
-			_screen->copyRegion(0, 0, 72, 0, 32, 120, 12, 2, Screen::CR_NO_P_CHECK);
+			_screen->copyRegion(0, 0, 72, 0, 32, 120, Screen_EoB::kEoB2ScriptHelperPage, 2, Screen::CR_NO_P_CHECK);
 			_vm->delayUntil(endTime);
 		}
 
@@ -1647,6 +1662,10 @@ const uint8 EoBInfProcessor::_amigaColorMap[16] = {
 	0x00, 0x06, 0x1d, 0x1b, 0x1a, 0x17, 0x18, 0x0e, 0x19, 0x1c, 0x1c, 0x1e, 0x13, 0x0a, 0x11, 0x1f
 };
 
+const uint8 EoBInfProcessor::_segaCDColorMap[16] = {
+	0x00, 0xFF, 0x99, 0x55, 0xFF, 0x99, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
 } // End of namespace Kyra
 
 #endif // ENABLE_EOB
diff --git a/engines/kyra/script/script_eob.h b/engines/kyra/script/script_eob.h
index 67d2fffe38..b816616106 100644
--- a/engines/kyra/script/script_eob.h
+++ b/engines/kyra/script/script_eob.h
@@ -122,6 +122,7 @@ private:
 	int8 _activeCharacter;
 
 	static const uint8 _amigaColorMap[16];
+	static const uint8 _segaCDColorMap[16];
 
 	const int _commandMin;
 };
diff --git a/engines/kyra/sequence/seqplayer_eob_segacd.cpp b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
index e565493ce9..0075752f68 100644
--- a/engines/kyra/sequence/seqplayer_eob_segacd.cpp
+++ b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
@@ -602,9 +602,9 @@ void SegaSequencePlayer::s_displayTextEn(const uint8 *pos) {
 	_vm->_txt->clearDim(2);
 
 	if (_playingID >= 55) {
-		_screen->setFontStyles(_screen->_currentFont, Font::kStyleForceTwoByte | Font::kStyleFat);
+		int cs = _screen->setFontStyles(_screen->_currentFont, Font::kStyleForceTwoByte | Font::kStyleFat);
 		_vm->_txt->printShadowedText(str, 0, 0, -1, 0xEE);
-		_screen->setFontStyles(_screen->_currentFont, Font::kStyleFat);
+		_screen->setFontStyles(_screen->_currentFont, cs);
 	} else {
 		int x = 0;
 		int y = 0;
diff --git a/engines/kyra/sequence/sequences_eob.cpp b/engines/kyra/sequence/sequences_eob.cpp
index 67031c849d..c2318d950b 100644
--- a/engines/kyra/sequence/sequences_eob.cpp
+++ b/engines/kyra/sequence/sequences_eob.cpp
@@ -2621,7 +2621,7 @@ void EoBEngine::seq_segaShowStats() {
 	_txt->clearDim(5);
 
 	int styles = _flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat;
-	_screen->setFontStyles(_screen->_currentFont, styles);
+	int cs = _screen->setFontStyles(_screen->_currentFont, styles);
 
 	_txt->printShadowedText(_finBonusStrings[2], 90, 8, 0xFF, 0x00, false);
 
@@ -2638,13 +2638,8 @@ void EoBEngine::seq_segaShowStats() {
 	styles &= ~(Font::kStyleNarrow2);
 	_screen->setFontStyles(_screen->_currentFont, styles);
 
-	////
-	////
-	uint32 partyArrows = 0;
-	uint32 numMaps = 0;
-	/////
-	////
-
+	uint32 partyArrows = countArrows();
+	uint32 numMaps = countMaps();
 	uint32 specialSearches = 0;
 	for (int i = 1; i <= 12; ++i) {
 		if (checkScriptFlags(1 << i))
@@ -2695,7 +2690,7 @@ void EoBEngine::seq_segaShowStats() {
 	_allowSkip = false;
 	resetSkipFlag();
 
-	_screen->setFontStyles(_screen->_currentFont, _flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleFat);
+	_screen->setFontStyles(_screen->_currentFont, cs);
 	_screen->sega_fadeToBlack(3);
 }
 
diff --git a/engines/kyra/text/text_eob_segacd.cpp b/engines/kyra/text/text_eob_segacd.cpp
index 8895db1ea2..6cb139f1bc 100644
--- a/engines/kyra/text/text_eob_segacd.cpp
+++ b/engines/kyra/text/text_eob_segacd.cpp
@@ -29,12 +29,12 @@
 
 namespace Kyra {
 
-TextDisplayer_SegaCD::TextDisplayer_SegaCD(EoBEngine *engine, Screen_EoB *scr) : TextDisplayer_rpg(engine, scr), _screen(scr), _renderer(scr->sega_getRenderer()), _curDim(0) {
+TextDisplayer_SegaCD::TextDisplayer_SegaCD(EoBEngine *engine, Screen_EoB *scr) : TextDisplayer_rpg(engine, scr), _screen(scr), _renderer(scr->sega_getRenderer()),
+	_curDim(0), _textColor(0xFF), _curPosY(0), _curPosX(0) {
 	assert(_renderer);
 }
 
 TextDisplayer_SegaCD::~TextDisplayer_SegaCD() {
-
 }
 
 void TextDisplayer_SegaCD::printShadowedText(const char *str, int x, int y, int textColor, int shadowColor, bool noScreenUpdate) {
@@ -65,12 +65,84 @@ void TextDisplayer_SegaCD::printShadowedText(const char *str, int x, int y, int
 int TextDisplayer_SegaCD::clearDim(int dim) {
 	int res = _curDim;
 	_curDim = dim;
+	_curPosY = _curPosX = 0;
 	const ScreenDim *s = &_dimTable[dim];
 	_renderer->memsetVRAM((s->unkC & 0x7FF) << 5, s->unkA, (s->w * s->h) >> 1);
 	_screen->sega_clearTextBuffer(s->unkA);
 	return res;
 }
 
+void TextDisplayer_SegaCD::displayText(char *str, ...) {
+	int cs = _screen->setFontStyles(Screen::FID_8_FNT, _vm->gameFlags().lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleFat | Font::kStyleForceTwoByte);
+	char tmp[3] = "  ";
+	int posX = _curPosX;
+	bool updated = false;
+
+	for (const char *pos = str; *pos; updated = false) {
+		uint8 cmd = fetchCharacter(tmp, pos);
+
+		if (_dimTable[_curDim].h < _curPosY + _screen->getFontHeight()) {
+			_curPosY -= _screen->getFontHeight();
+			linefeed();
+		}
+
+		if (cmd == 6) {
+			_textColor = *pos++;
+		} else if (cmd == 2) {
+			pos++;
+		} else if (cmd == 13) {
+			_curPosX = 0;
+			_curPosY += _screen->getFontHeight();
+		} else if (cmd == 9) {
+			_curPosX = posX;
+			_curPosY += _screen->getFontHeight();
+		} else {
+			if (((tmp[0] == ' ' || (tmp[0] == '\x81' && tmp[1] == '\x40')) && (_curPosX + _screen->getTextWidth(tmp) + _screen->getTextWidth((const char*)(pos), true) >= _dimTable[_curDim].w)) || (_curPosX + _screen->getTextWidth(tmp) >= _dimTable[_curDim].w)) {
+				// Skip space at the beginning of the new line
+				if (tmp[0] == ' ' || (tmp[0] == '\x81' && tmp[1] == '\x40'))
+					fetchCharacter(tmp, pos);
+
+				_curPosX = 0;
+				_curPosY += _screen->getFontHeight();
+				if (_dimTable[_curDim].h < _curPosY + _screen->getFontHeight()) {
+					_curPosY -= _screen->getFontHeight();
+					linefeed();
+				}
+			}			
+
+			printShadowedText(tmp, _curPosX, _curPosY, _textColor);
+			_curPosX += _screen->getTextWidth(tmp);
+			updated = true;
+		}		
+	}
+
+	if (!updated)
+		printShadowedText("", _curPosX, _curPosY, _textColor);
+
+	_renderer->render(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);
+}
+
+uint8 TextDisplayer_SegaCD::fetchCharacter(char *dest, const char *&src) {
+	char c = *src++;
+
+	if (c <= '\r') {
+		dest[0] = '\0';
+		return (uint8)c;
+	}
+		
+	dest[0] = c;
+	dest[1] = (c <= 0x7F || (c >= 0xA1 && c <= 0xDF)) ? '\0' : *src++;
+
+	return 0;
+}
+
+void TextDisplayer_SegaCD::linefeed() {
+	_screen->sega_copyTextBufferLine(_screen->getFontHeight(), 0, (_dimTable[_curDim].h & ~7) - _screen->getFontHeight(), _dimTable[_curDim].w >> 3);
+	_screen->sega_clearTextBufferLine(_screen->getFontHeight(), _screen->getFontHeight(), _dimTable[_curDim].w >> 3, _dimTable[_curDim].unkA);
+}
+
 const ScreenDim TextDisplayer_SegaCD::_dimTable[6] = {
 	{ 0x0001, 0x0017, 0x0118, 0x0018, 0xff, 0x44, 0x2597, 0x0000 },
 	{ 0x0012, 0x0009, 0x00a0, 0x0080, 0xff, 0x00, 0x0153, 0x0028 },
diff --git a/engines/kyra/text/text_eob_segacd.h b/engines/kyra/text/text_eob_segacd.h
index ac75eba26b..a3b4d7cab5 100644
--- a/engines/kyra/text/text_eob_segacd.h
+++ b/engines/kyra/text/text_eob_segacd.h
@@ -37,29 +37,20 @@ public:
 	TextDisplayer_SegaCD(EoBEngine *engine, Screen_EoB *scr);
 	virtual ~TextDisplayer_SegaCD();
 
-	/*void setupField(int dim, bool mode);
-
-	void printDialogueText(int stringId, const char *pageBreakString);
-	void printDialogueText(const char *str, bool wait = false);
-	void printMessage(const char *str, int textColor = -1, ...);*/
 	void printShadowedText(const char *str, int x = -1, int y = -1, int textColor = -1, int shadowColor = -1, bool noScreenUpdate = false) override;
-
 	int clearDim(int dim) override;
-	//void clearCurDim() override;
-	
-	/*void resetDimTextPositions(int dim);
-	void resetPageBreakString();
-	void setPageBreakFlag();
-	void removePageBreakFlag();
-	*/
-	//void allowPageBreak(bool mode) { _allowPageBreak = mode; }
-	//void setWaitButtonMode(int mode) { _waitButtonMode = mode; }
-	//int lineCount() const { return _lineCount; }
 
 private:
+	void displayText(char *str, ...) override;
+	uint8 fetchCharacter(char *dest, const char *&src);
+	void linefeed();
+
 	Screen_EoB *_screen;
 	SegaRenderer *_renderer;
 	int _curDim;
+	int _curPosY;
+	int _curPosX;
+	int _textColor;
 
 	static const ScreenDim _dimTable[6];
 };
diff --git a/engines/kyra/text/text_rpg.h b/engines/kyra/text/text_rpg.h
index a43aa9bcf5..8b39c58269 100644
--- a/engines/kyra/text/text_rpg.h
+++ b/engines/kyra/text/text_rpg.h
@@ -61,7 +61,7 @@ protected:
 	virtual KyraRpgEngine *vm() { return _vm; }
 	virtual Screen *screen() { return _screen; }
 
-	void displayText(char *str, ...);
+	virtual void displayText(char *str, ...);
 	char parseCommand();
 	void readNextPara();
 	void printLine(char *str);
@@ -104,9 +104,9 @@ protected:
 	};
 
 	TextDimData *_textDimData;
+	KyraRpgEngine *_vm;
 
 private:
-	KyraRpgEngine *_vm;
 	Screen *_screen;
 
 	char *_table1;


Commit: c078242486d44dc5d353dc9eaad3448530c32d01
    https://github.com/scummvm/scummvm/commit/c078242486d44dc5d353dc9eaad3448530c32d01
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:14+02:00

Commit Message:
KYRA: (EOB/SegaCD) - adjust buttons and implement compass animations

Changed paths:
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eob.h
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h
    engines/kyra/engine/sprites_eob.cpp
    engines/kyra/graphics/screen_eob_segacd.cpp
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/gui/gui_eob_segacd.cpp
    engines/kyra/resource/staticres_eob.cpp


diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index b994512c16..86c560dedd 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -62,8 +62,10 @@ EoBEngine::EoBEngine(OSystem *system, const GameFlags &flags)
 	memset(_strikeAnimShapes, 0, sizeof(_strikeAnimShapes));
 	_sceneShakeOffsetX = _sceneShakeOffsetY = 0;
 	_shakeBackBuffer1 = _shakeBackBuffer2 = 0;
-	_redGrid = 0;
-	_charTilesTable = 0;
+	_compassDirection2 = _compassAnimDest = _compassAnimPhase = _compassAnimStep = _compassAnimDelayCounter = 0;
+	_compassAnimSwitch = _compassAnimDone = false;
+	_redGrid = _charTilesTable = 0;
+	_compassData = 0;
 
 	_seqPlayer = 0;
 	_sres = 0;
@@ -87,6 +89,7 @@ EoBEngine::~EoBEngine() {
 	delete[] _statsPattern2;
 	delete[] _shakeBackBuffer1;
 	delete[] _shakeBackBuffer2;
+	delete[] _compassData;
 
 	delete _seqPlayer;
 	delete _sres;
@@ -154,6 +157,7 @@ Common::Error EoBEngine::init() {
 		_statsPattern2 = new uint16[792];
 		_shakeBackBuffer1 = new uint8[120 * 6];
 		_shakeBackBuffer2 = new uint8[179 * 6];
+		_compassData = new uint8[0x5000];
 	}
 
 	return Common::kNoError;
@@ -295,58 +299,6 @@ void EoBEngine::startupLoad() {
 	_screen->selectPC98Palette(0, _screen->getPalette(0));
 }
 
-void EoBEngine::updateSpecialGfx() {
-	bool updScreen = false;
-
-	// Red grid effect
-	for (int i = 0; i < 6; ++i) {
-		if (!_characters[i].specialGfxCountdown)
-			continue;
-		_characters[i].specialGfxCountdown--;
-		int cp = _screen->setCurPage(0);
-
-		if (!_currentControlMode && (_characters[i].specialGfxCountdown & 1))
-			_screen->drawShape(0, _redGrid, 176 + guiSettings()->charBoxCoords.facePosX_1[i & 1], guiSettings()->charBoxCoords.facePosY_1[i >> 1], 0);
-		else if (_currentControlMode && _updateCharNum == i && (_characters[i].specialGfxCountdown & 1))
-			_screen->drawShape(0, _redGrid, guiSettings()->charBoxCoords.facePosX_2[0], guiSettings()->charBoxCoords.facePosY_2[0], 0);
-		else
-			gui_drawFaceShape(i);
-
-		_screen->setCurPage(cp);
-		updScreen = true;
-	}
-
-	// Scene shake
-	if (_specialGfxCountdown) {
-		--_specialGfxCountdown;
-		_sceneShakeOffsetX = _sceneShakeOffsets[_specialGfxCountdown << 1];
-		_sceneShakeOffsetY = _sceneShakeOffsets[(_specialGfxCountdown << 1) + 1];
-		_screen->fillRect(0, 0, 2, 119, 0, _sceneDrawPage1);
-		_screen->fillRect(0, 0, 175, 2, 0, _sceneDrawPage1);
-		_screen->copyBlockToPage(_sceneDrawPage1, 173, 0, 6, 120, _shakeBackBuffer1);
-		_screen->copyBlockToPage(_sceneDrawPage1, 0, 117, 179, 6, _shakeBackBuffer2);
-		_screen->copyBlockToPage(_sceneDrawPage1, _sceneXoffset + _sceneShakeOffsetX, _sceneShakeOffsetY, 176, 120, _sceneWindowBuffer);
-
-		// For whatever reason the original shakes all types of shapes (decorations, doors, etc.) except the monsters and
-		// the items lying on the floor. So we do the same. I've added drawing flags to drawSceneShapes() which allow
-		// separate drawing passes for the different shape types.
-		_shapeShakeOffsetX = _sceneShakeOffsetX;
-		_shapeShakeOffsetY = _sceneShakeOffsetY;
-		// All shapes except monsters and items
-		drawSceneShapes(0, 0xFF & ~0x2A);
-		_shapeShakeOffsetX = _shapeShakeOffsetY = 0;
-		// Monsters and items
-		drawSceneShapes(0, 0x2A);
-
-		_screen->copyRegion(0, 0, 0, 0, 179, 123, _sceneDrawPage1, 0, Screen::CR_NO_P_CHECK);
-		updScreen = true;
-	}
-
-	if (updScreen)
-		_screen->updateScreen();
-	_sceneDrawPage1 = 2;
-}
-
 void EoBEngine::drawNpcScene(int npcIndex) {
 	_screen->copyRegion(0, 0, 0, 0, 176, 120, 6, 0, Screen::CR_NO_P_CHECK);
 	switch (npcIndex) {
diff --git a/engines/kyra/engine/eob.h b/engines/kyra/engine/eob.h
index ba54b6af57..fcf058fcf9 100644
--- a/engines/kyra/engine/eob.h
+++ b/engines/kyra/engine/eob.h
@@ -81,8 +81,6 @@ private:
 	void startupNew() override;
 	void startupLoad() override;
 
-	void updateSpecialGfx() override;
-
 	// Intro/Outro/Sequence Playback
 	enum IntroPart {
 		kOnlyCredits = 0,
@@ -195,13 +193,25 @@ private:
 	void gui_drawWeaponSlotStatus(int x, int y, int status) override;
 	void gui_printInventoryDigits(int x, int y, int val) override;
 	void gui_drawCharacterStatsPage() override;
+	void gui_displayMap() override;
 
 	void makeNameShapes() override;
 	void printStatsString(const char *str, int x, int y);
 
+	void updateGuiAnimations() override;
+
 	const KyraRpgGUISettings *guiSettings() const override;
 	void useMainMenuGUISettings(bool toggle) override { _useMainMenuGUISettings = toggle; }
 
+	int _compassDirection2;
+	int _compassAnimDest;
+	int _compassAnimPhase;
+	int _compassAnimStep;
+	int _compassAnimDelayCounter;
+	bool _compassAnimSwitch;
+	bool _compassAnimDone;
+	uint8 *_compassData;
+
 	const uint8 **_invSmallDigits;
 	const uint8 **_weaponSlotShapes;
 
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index bb021b32a9..ad94557f81 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -109,7 +109,7 @@ EoBCoreEngine::EoBCoreEngine(OSystem *system, const GameFlags &flags) : KyraRpgE
 	_monsters = 0;
 	_dstMonsterIndex = 0;
 	_preventMonsterFlash = false;
-	_specialGfxCountdown = 0;
+	_sceneShakeCountdown = 0;
 
 	_teleporterPulse = 0;
 
@@ -735,7 +735,7 @@ void EoBCoreEngine::runLoop() {
 		updateScriptTimers();
 		updateWallOfForceTimers();
 
-		if (_sceneUpdateRequired && !_specialGfxCountdown)
+		if (_sceneUpdateRequired && !_sceneShakeCountdown)
 			drawScene(1);
 
 		uint32 curTime = _system->getMillis();
@@ -746,7 +746,7 @@ void EoBCoreEngine::runLoop() {
 
 		if (_lastVIntTick + 16 <= curTime) {
 			_lastVIntTick = curTime;
-			updateSpecialGfx();
+			updateGuiAnimations();
 			curTime = _system->getMillis();
 		}
 		
@@ -2221,7 +2221,7 @@ void EoBCoreEngine::inflictCharacterDamage(int charIndex, int damage) {
 	if (c->hitPointsCur > -10) {
 		snd_playSoundEffect(21);
 		if (_flags.platform == Common::kPlatformSegaCD)
-			_specialGfxCountdown = c->specialGfxCountdown = 32;
+			_sceneShakeCountdown = c->gfxUpdateCountdown = 32;
 	} else {
 		c->hitPointsCur = -10;
 		c->flags &= 1;
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index 81a2fa263a..1547351805 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -132,7 +132,7 @@ struct EoBCharacter {
 	uint8 damageTaken;
 	int8 slotStatus[5];
 
-	int8 specialGfxCountdown;
+	int8 gfxUpdateCountdown;
 };
 
 struct EoBItem {
@@ -342,7 +342,7 @@ protected:
 	void runLoop();
 	void update() override { screen()->updateScreen(); }
 	bool checkPartyStatus(bool handleDeath);
-	virtual void updateSpecialGfx() {}
+	virtual void updateGuiAnimations() {}
 
 	bool _runFlag;
 
@@ -742,6 +742,7 @@ protected:
 	void gui_drawCharPortraitStatusFrame(int index);
 	void gui_drawInventoryItem(int slot, int redraw, int pageNum);
 	virtual void gui_drawCharacterStatsPage();
+	virtual void gui_displayMap() {}
 	void gui_drawCompass(bool force);
 	void gui_drawDialogueBox();
 	void gui_drawSpellbook();
@@ -997,7 +998,7 @@ protected:
 
 	int _dstMonsterIndex;
 	bool _preventMonsterFlash;
-	int8 _specialGfxCountdown;
+	int8 _sceneShakeCountdown;
 	int16 _foundMonstersArray[5];
 	int8 _monsterBlockPosArray[6];
 	const uint8 *_monsterAcHitChanceTable1;
diff --git a/engines/kyra/engine/sprites_eob.cpp b/engines/kyra/engine/sprites_eob.cpp
index 19f7482ce7..9804740411 100644
--- a/engines/kyra/engine/sprites_eob.cpp
+++ b/engines/kyra/engine/sprites_eob.cpp
@@ -501,10 +501,12 @@ void EoBCoreEngine::drawMonsters(int index) {
 				numFrames += ((frm[0] >> 6) & 1);
 				xAdd2 = (int8)frm[1];
 				yAdd2 = (int8)frm[2];
-				if (frm[4] == 0xFE)
+				if (frm[4] == 0xFE) {
 					d->animProgress = 0;
-				else if (frm[4] == 0xFF)
+				} else if (frm[4] == 0xFF) {
 					d->curAttackFrame = 0;
+					d->animType = 0;
+				}
 			}
 
 			const uint8 *shp = _screen->scaleShape(_monsterShapes[shpOffs], blockDistance);
diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index 3c51ef86d2..062e9e05a4 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -371,12 +371,6 @@ void Screen_EoB::sega_encodeShapesFromSprites(const uint8 **dst, const uint8 *sr
 		}
 		
 		_segaAnimator->update();
-
-/*		_segaRenderer->render(0, true);
-		sega_selectPalette(7, 3, true);
-		sega_fadeToNeutral(0);
-		updateScreen();
-*/
 		_segaRenderer->render(Screen_EoB::kSegaInitShapesPage, true);
 
 		for (int i = l; i < s; ++i)
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 6750d4ecac..afbaf77739 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -109,15 +109,15 @@ void EoBCoreEngine::gui_drawCharPortraitWithStats(int index) {
 		Screen::FontId cf = _screen->setFont(_invFont1);
 
 		if (_flags.platform == Common::kPlatformSegaCD) {
-			_screen->drawShape(_screen->_curPage, (index == _exchangeCharacterId) ? _swapShape : c->nameShape, x2 + 4, y2 + 4);
+			_screen->drawShape(_screen->_curPage, (index == _exchangeCharacterId) ? _swapShape : c->nameShape, x2 + 4, y2 + 4, 0);
 		} else {
 			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->setFont(_invFont2);
 
+		_screen->setFont(_invFont2);
 		gui_drawFaceShape(index);
 		gui_drawWeaponSlot(index, 0);
 		gui_drawWeaponSlot(index, 1);
@@ -606,15 +606,17 @@ void EoBCoreEngine::gui_drawCharacterStatsPage() {
 }
 
 void EoBCoreEngine::gui_drawCompass(bool force) {
-	//if (_currentDirection == _compassDirection && !force)
+	if (_currentDirection == _compassDirection && !force)
 		return;
 
-	static const uint8 shpX[2][3] = { { 0x70, 0x4D, 0x95 }, { 0x72, 0x4F, 0x97 } };
-	static const uint8 shpY[2][3] = { { 0x7F, 0x9A, 0x9A }, { 0x83, 0x9E, 0x9E } };
-	int g = _flags.gameID == GI_EOB1 ? 0 : 1;
+	if (_flags.platform != Common::kPlatformSegaCD) {
+		const uint8 shpX[2][3] = { { 0x70, 0x4D, 0x95 }, { 0x72, 0x4F, 0x97 } };
+		const uint8 shpY[2][3] = { { 0x7F, 0x9A, 0x9A }, { 0x83, 0x9E, 0x9E } };
+		int shpId = _flags.gameID == GI_EOB1 ? 0 : 1;
 
-	for (int i = 0; i < 3; i++)
-		_screen->drawShape(_screen->_curPage, _compassShapes[(i << 2) + _currentDirection], shpX[g][i], shpY[g][i], 0);
+		for (int i = 0; i < 3; i++)
+			_screen->drawShape(_screen->_curPage, _compassShapes[(i << 2) + _currentDirection], shpX[shpId][i], shpY[shpId][i], 0);
+	}
 
 	_compassDirection = _currentDirection;
 }
@@ -787,6 +789,9 @@ void EoBCoreEngine::gui_setPlayFieldButtons() {
 void EoBCoreEngine::gui_setInventoryButtons() {
 	gui_resetButtonList();
 	gui_initButtonsFromList(_updateFlags ? _buttonList5 : _buttonList3);
+
+	if (_flags.platform == Common::kPlatformSegaCD)
+		gui_initButton(95);
 }
 
 void EoBCoreEngine::gui_setStatsListButtons() {
@@ -808,7 +813,7 @@ void EoBCoreEngine::gui_initButton(int index, int, int, int) {
 	Button *b = 0;
 	int cnt = 1;
 
-	if (_flags.gameID == GI_EOB1 && index > 92)
+	if (_flags.gameID == GI_EOB1 && !(_flags.platform == Common::kPlatformSegaCD && index == 95) && index > 92)
 		return;
 
 	if (_activeButtons) {
@@ -1452,6 +1457,9 @@ void EoBCoreEngine::gui_processInventorySlotClick(int slot) {
 			setHandItem(itm);
 		}
 
+	} else if (slot == 27) {
+		gui_displayMap();
+
 	} else {
 		setHandItem(itm);
 		_characters[_updateCharNum].inventory[slot] = ih;
diff --git a/engines/kyra/gui/gui_eob_segacd.cpp b/engines/kyra/gui/gui_eob_segacd.cpp
index 68ebf5ebb4..b9b00eda1b 100644
--- a/engines/kyra/gui/gui_eob_segacd.cpp
+++ b/engines/kyra/gui/gui_eob_segacd.cpp
@@ -48,6 +48,7 @@ void EoBEngine::gui_drawPlayField(bool refresh) {
 	uint8 *data = _res->fileData("PLAYFLD", 0);
 	for (int i = 0; i < 256; ++i)
 		r->loadToVRAM(&data[i << 5], 32, _addrTbl1[i] << 5);
+	memcpy(_compassData, data + 0x2000, 0x5000);
 	delete[] data;
 
 	const uint16 *pattern = _playFldPattern1;
@@ -101,13 +102,20 @@ 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 no going to draw with the SegaRenderer but rather with our "normal" code, we have to backup some parts of
-	// the background between the character portraits. Unlike the other versions the red splat shape overlaps with that space.
+	// Since we're no going to draw the character portrait boxes with the SegaRenderer 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];
 		_redSplatBG[i] = new uint8[_redSplatShape[2] << 5];
 		_screen->copyRegionToBuffer(0, guiSettings()->charBoxCoords.boxX[i & 1] + guiSettings()->charBoxCoords.redSplatOffsetX, guiSettings()->charBoxCoords.boxY[i >> 1] + guiSettings()->charBoxCoords.boxHeight - 1, _redSplatShape[2] << 3, 4, _redSplatBG[i]);
 	}
+	for (int i = 2; i < 4; ++i) {
+		if (_characters[i + 2].flags & 1)
+			memcpy(_redSplatBG[i] + _redSplatShape[2] * 24, _redSplatBG[0] + _redSplatShape[2] * 24, _redSplatShape[2] << 3);
+	}
+
+	_compassDirection2 = -1;
+	gui_drawCompass(false);;
 
 	if (!_loading)
 		_screen->sega_fadeToNeutral(1);
@@ -158,12 +166,12 @@ void EoBEngine::gui_drawCharacterStatsPage() {
 	// no space left between the digits and the right stats row ("DEX" etc.). Maybe I should move that row one tile
 	// further to the right? There is space enough left over there.
 	printStatsString(getCharStrength(c->strengthCur, c->strengthExtCur).c_str(), c->strengthExtCur ? 4 : 5, 11);
-	printStatsString(Common::String::format("%d", c->intelligenceCur).c_str(), 5, 12);
-	printStatsString(Common::String::format("%d", c->wisdomCur).c_str(), 5, 13);
-	printStatsString(Common::String::format("%d", c->dexterityCur).c_str(), 13, 11);
-	printStatsString(Common::String::format("%d", c->constitutionCur).c_str(), 13, 12);
-	printStatsString(Common::String::format("%d", c->charismaCur).c_str(), 13, 13);
-	printStatsString(Common::String::format("%d", c->armorClass).c_str(), 5, 14);
+	printStatsString(Common::String::format("%2d", c->intelligenceCur).c_str(), 5, 12);
+	printStatsString(Common::String::format("%2d", c->wisdomCur).c_str(), 5, 13);
+	printStatsString(Common::String::format("%2d", c->dexterityCur).c_str(), 13, 11);
+	printStatsString(Common::String::format("%2d", c->constitutionCur).c_str(), 13, 12);
+	printStatsString(Common::String::format("%2d", c->charismaCur).c_str(), 13, 13);
+	printStatsString(Common::String::format("%2d", c->armorClass).c_str(), 5, 14);
 
 	for (int i = 0; i < 3; i++) {
 		int t = getCharacterClassType(c->cClass, i);
@@ -180,6 +188,10 @@ void EoBEngine::gui_drawCharacterStatsPage() {
 	_screen->copyRegion(176, 40, 176, 40, 144, 128, Screen_EoB::kSegaRenderPage, 2, Screen::CR_NO_P_CHECK);
 }
 
+void EoBEngine::gui_displayMap() {
+
+}
+
 void EoBEngine::makeNameShapes() {
 	if (_flags.platform != Common::kPlatformSegaCD)
 		return;
@@ -219,6 +231,100 @@ void EoBEngine::printStatsString(const char *str, int x, int y) {
 		*dst++ = 0x6525 + _charTilesTable[*pos];
 }
 
+void EoBEngine::updateGuiAnimations() {
+	if (_flags.platform != Common::kPlatformSegaCD)
+		return;
+
+	bool updScreen = false;
+	bool redrawCompass = false;
+
+	// Compass
+	if (_compassDirection != _compassDirection2) {
+		_compassAnimDest = _compassDirection << 2;
+		int diff = _compassAnimDest - _compassAnimPhase;
+		if (diff < 0)
+			diff += 16;
+		if (diff) {
+			_compassAnimStep = (diff < 8) ? 1 : -1;
+			_compassAnimDone = false;
+		}
+		_compassDirection2 = _compassDirection;
+		redrawCompass = true;
+	}
+	if (_compassAnimDelayCounter) {
+		--_compassAnimDelayCounter;
+	} else if (!redrawCompass) {
+		if (_compassAnimDest != _compassAnimPhase) {
+			_compassAnimPhase = (_compassAnimPhase + _compassAnimStep) & 0x0F;
+			_compassAnimDelayCounter = 6;
+			redrawCompass = true;
+		} else if (!_compassAnimDone) {
+			if (_compassAnimSwitch) {
+				_compassAnimPhase = (_compassAnimPhase + _compassAnimStep) & 0x0F;
+				_compassAnimDelayCounter = 6;
+				redrawCompass = true;
+				_compassAnimStep = -_compassAnimStep;
+				_compassAnimSwitch = false;
+			} else {
+				_compassAnimDone = _compassAnimSwitch = true;
+			}
+		}
+	}
+	if (redrawCompass) {
+		_screen->sega_getRenderer()->loadToVRAM(_compassData + (_compassAnimPhase & 0x0F) * 0x500, 0x500, 0xEE00);
+		_screen->sega_getRenderer()->render(Screen_EoB::kSegaRenderPage);
+		_screen->copyRegion(88, 120, 88, 120, 80, 48, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+		updScreen = true;
+	}
+
+	// Red grid effect
+	for (int i = 0; i < 6; ++i) {
+		if (!_characters[i].gfxUpdateCountdown)
+			continue;
+		_characters[i].gfxUpdateCountdown--;
+		int cp = _screen->setCurPage(0);
+
+		if (!_currentControlMode && (_characters[i].gfxUpdateCountdown & 1))
+			_screen->drawShape(0, _redGrid, 176 + guiSettings()->charBoxCoords.facePosX_1[i & 1], guiSettings()->charBoxCoords.facePosY_1[i >> 1], 0);
+		else if (_currentControlMode && _updateCharNum == i && (_characters[i].gfxUpdateCountdown & 1))
+			_screen->drawShape(0, _redGrid, guiSettings()->charBoxCoords.facePosX_2[0], guiSettings()->charBoxCoords.facePosY_2[0], 0);
+		else
+			gui_drawFaceShape(i);
+
+		_screen->setCurPage(cp);
+		updScreen = true;
+	}
+
+	// Scene shake
+	if (_sceneShakeCountdown) {
+		--_sceneShakeCountdown;
+		_sceneShakeOffsetX = _sceneShakeOffsets[_sceneShakeCountdown << 1];
+		_sceneShakeOffsetY = _sceneShakeOffsets[(_sceneShakeCountdown << 1) + 1];
+		_screen->fillRect(0, 0, 2, 119, 0, _sceneDrawPage1);
+		_screen->fillRect(0, 0, 175, 2, 0, _sceneDrawPage1);
+		_screen->copyBlockToPage(_sceneDrawPage1, 173, 0, 6, 120, _shakeBackBuffer1);
+		_screen->copyBlockToPage(_sceneDrawPage1, 0, 117, 179, 6, _shakeBackBuffer2);
+		_screen->copyBlockToPage(_sceneDrawPage1, _sceneXoffset + _sceneShakeOffsetX, _sceneShakeOffsetY, 176, 120, _sceneWindowBuffer);
+
+		// For whatever reason the original shakes all types of shapes (decorations, doors, etc.) except the monsters and
+		// the items lying on the floor. So we do the same. I've added drawing flags to drawSceneShapes() which allow
+		// separate drawing passes for the different shape types.
+		_shapeShakeOffsetX = _sceneShakeOffsetX;
+		_shapeShakeOffsetY = _sceneShakeOffsetY;
+		// All shapes except monsters and items
+		drawSceneShapes(0, 0xFF & ~0x2A);
+		_shapeShakeOffsetX = _shapeShakeOffsetY = 0;
+		// Monsters and items
+		drawSceneShapes(0, 0x2A);
+
+		_screen->copyRegion(0, 0, 0, 0, 179, 123, _sceneDrawPage1, 0, Screen::CR_NO_P_CHECK);
+		updScreen = true;
+	}
+
+	if (updScreen)
+		_screen->updateScreen();
+}
+
 } // End of namespace Kyra
 
 #endif // ENABLE_EOB
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index 6134d3ddd3..6e0bb9a73d 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -669,6 +669,7 @@ void EoBCoreEngine::initButtonData() {
 		{ 116, 117, 0x1100, 320, 200, 1, 1, 2 },
 		{ 7, 0, 0x1100, 158, 121, 15, 10, 5 },
 		{ 0, 0, 0x1100, 146, 168, 32, 10, 0 },
+		{ 0, 0, 0x1100, 296, 56, 16, 16, 27 }
 	};
 
 	_buttonDefs = new EoBGuiButtonDef[ARRAYSIZE(buttonDefs)];
@@ -709,6 +710,39 @@ void EoBCoreEngine::initButtonData() {
 		}
 	}
 
+	// Adjust EOB I SegaCD button coordinates
+	if (_flags.platform == Common::kPlatformSegaCD) {
+		// Arrow field
+		static const EoBGuiButtonDef eob1SegaArrowDefs[6] = {
+			{ 96, 352, 0x1100, 29, 128, 21, 19, 25 },
+			{ 98, 97, 0x1100, 29, 148, 21, 19, 25 },
+			{ 92, 348, 0x1100, 8, 148, 21, 19, 25 },
+			{ 102, 358, 0x1100, 50, 148, 21, 19, 25 },
+			{ 91, 0, 0x1100, 8, 128, 21, 19, 25 },
+			{ 101, 0, 0x1100, 50, 128, 21, 19, 25 }
+		};
+		memcpy(&_buttonDefs[49], eob1SegaArrowDefs, 6 * sizeof(EoBGuiButtonDef));
+		// Character portrait boxes
+		for (int i = 0; i < 4; ++i) {
+			_buttonDefs[i].y = _buttonDefs[i + 17].y = _buttonDefs[i + 72].y = guiSettings()->charBoxCoords.boxY[i >> 1];
+			_buttonDefs[i].h = _buttonDefs[i + 72].h = guiSettings()->charBoxCoords.boxHeight;
+			_buttonDefs[i + 17].h = 16;
+			_buttonDefs[9 + i].x = guiSettings()->charBoxCoords.facePosX_1[i & 1] + 176;
+			_buttonDefs[9 + i].y = guiSettings()->charBoxCoords.facePosY_1[i >> 1];
+			_buttonDefs[13 + i].y = guiSettings()->charBoxCoords.boxY[i >> 1] + 15;
+		}
+		for (int i = 4; i < 6; ++i) {
+			_buttonDefs[i + 78].y = _buttonDefs[i + 82].y = _buttonDefs[i + 86].y = guiSettings()->charBoxCoords.boxY[i >> 1];
+			_buttonDefs[i + 82].h = _buttonDefs[i + 86].h = guiSettings()->charBoxCoords.boxHeight;
+			_buttonDefs[i + 78].h = 16;
+			_buttonDefs[i + 74].x = guiSettings()->charBoxCoords.facePosX_1[i & 1] + 176;
+			_buttonDefs[i + 74].y = guiSettings()->charBoxCoords.facePosY_1[i >> 1];
+			_buttonDefs[i + 76].y = guiSettings()->charBoxCoords.boxY[i >> 1] + 15;
+		}
+		_buttonDefs[48].x = guiSettings()->charBoxCoords.facePosX_2[0];
+		_buttonDefs[48].y = guiSettings()->charBoxCoords.facePosY_2[0];
+	}
+
 	// Match the inventory button coords to the values that are used for drawing the inventory slots, so that
 	// the buttons get fixed if the target uses a different inventory screen layout (currently only EOB I SegaCD).
 	int temp = 0;
@@ -764,6 +798,7 @@ void EoBCoreEngine::initButtonData() {
 	EOB_CBI(1, 60);
 	EOB_CBI(1, 61);
 	EOB_CBN(1, clickedSpellbookScroll);
+	EOB_CBI(1, 21);
 #undef EOB_CBI
 #undef EOB_CBN
 }
@@ -1469,7 +1504,7 @@ const KyraRpgGUISettings EoBEngine::_guiSettingsSegaCD = {
 	{ 135, 130, 132, 180, 0x00, 17, 23, 20, 184, 177, 180, 184, 177, 180, 15, 6, 0x31, 9, 2, 0x35, 4, 0x33, 12 },
 	{	{ 184, 256, -1}, { 1, 57, 113 }, 64, 55,
 		{ 8, 80, -1 }, { 16, 72, 128 }, { 184, -1, -1 }, { 8, -1, -1 },
-		{ 40, 112, -1 }, { 16, 32, 72, 88, 128, 146 },
+		{ 40, 112, -1 }, { 16, 32, 72, 88, 128, 144 },
 		{ 24, 96, -1}, { 51, 107, 163 }, 40, 2, { 248, 248, -1}, { 19, 27, -1 }, 47, 2,
 		16, 39
 	}


Commit: 6c67807e17e2e01e2caaaaea00b8166afecb5317
    https://github.com/scummvm/scummvm/commit/6c67807e17e2e01e2caaaaea00b8166afecb5317
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:14+02:00

Commit Message:
KYRA: (EOB/SegaCD) - adjust savegame code and some cleanup

(some extra vars that need to be maintained and also put in the save files)

Changed paths:
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eob.h
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h
    engines/kyra/engine/scene_eob.cpp
    engines/kyra/engine/sprites_eob.cpp
    engines/kyra/gui/gui_eob_segacd.cpp
    engines/kyra/gui/saveload_eob.cpp


diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index 86c560dedd..21a7bdccc8 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -297,6 +297,16 @@ void EoBEngine::startupLoad() {
 	_sound->selectAudioResourceSet(kMusicIngame);
 	_sound->loadSoundFile(0);
 	_screen->selectPC98Palette(0, _screen->getPalette(0));
+
+	if (_flags.platform == Common::kPlatformSegaCD) {
+		_screen->sega_selectPalette(4, 0);
+		_screen->sega_selectPalette(6, 1);
+		_screen->sega_selectPalette(8, 2);
+		_screen->sega_selectPalette(7, 3);
+		_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 40, 28, 0x2000);
+		_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 28, 0x2000);
+		_txt->clearDim(0);
+	}
 }
 
 void EoBEngine::drawNpcScene(int npcIndex) {
diff --git a/engines/kyra/engine/eob.h b/engines/kyra/engine/eob.h
index fcf058fcf9..1ca891231b 100644
--- a/engines/kyra/engine/eob.h
+++ b/engines/kyra/engine/eob.h
@@ -196,9 +196,10 @@ private:
 	void gui_displayMap() override;
 
 	void makeNameShapes() override;
+	void makeFaceShapes() override;
 	void printStatsString(const char *str, int x, int y);
 
-	void updateGuiAnimations() override;
+	void gui_updateAnimations() override;
 
 	const KyraRpgGUISettings *guiSettings() const override;
 	void useMainMenuGUISettings(bool toggle) override { _useMainMenuGUISettings = toggle; }
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index ad94557f81..e8d02e1416 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -738,18 +738,9 @@ void EoBCoreEngine::runLoop() {
 		if (_sceneUpdateRequired && !_sceneShakeCountdown)
 			drawScene(1);
 
-		uint32 curTime = _system->getMillis();
-		if (_lastVIntTick + 1000 <= curTime) {
-			_lastSecTick = curTime;
-			_totalPlaySecs++;
-		}
+		updateAnimTimers();
 
-		if (_lastVIntTick + 16 <= curTime) {
-			_lastVIntTick = curTime;
-			updateGuiAnimations();
-			curTime = _system->getMillis();
-		}
-		
+		uint32 curTime = _system->getMillis();
 		if (_envAudioTimer < curTime && !(_flags.gameID == GI_EOB1 && (_flags.platform == Common::kPlatformSegaCD || _flags.platform == Common::kPlatformAmiga || _currentLevel == 0 || _currentLevel > 3))) {
 			_envAudioTimer = curTime + (rollDice(1, 10, 3) * 18 * _tickLength);
 			snd_processEnvironmentalSoundEffect(_flags.gameID == GI_EOB1 ? 30 : (rollDice(1, 2, -1) ? 27 : 28), _currentBlock + rollDice(1, 12, -1));
@@ -787,6 +778,19 @@ bool EoBCoreEngine::checkPartyStatus(bool handleDeath) {
 	return false;
 }
 
+void EoBCoreEngine::updateAnimTimers() {
+	uint32 curTime = _system->getMillis();
+	if (_lastVIntTick + 1000 <= curTime) {
+		_lastSecTick = curTime;
+		_totalPlaySecs++;
+	}
+
+	if (_lastVIntTick + 16 <= curTime) {
+		_lastVIntTick = curTime;
+		gui_updateAnimations();
+	}
+}
+
 void EoBCoreEngine::loadItemsAndDecorationsShapes() {
 	releaseItemsAndDecorationsShapes();
 	int div = (_flags.gameID == GI_EOB1) ? 3 : 8;
@@ -2612,9 +2616,9 @@ void EoBCoreEngine::explodeMonster(EoBMonsterInPlay *m) {
 	m->flags &= ~2;
 }
 
-void EoBCoreEngine::addCurrentLevelMap() {
-	assert(_currentLevel);
-	_levelMaps |= (1 << (_currentLevel - 1));
+void EoBCoreEngine::addLevelMap(int level) {
+	assert(level);
+	_levelMaps |= (1 << (level - 1));
 }
 
 uint32 EoBCoreEngine::countMaps() const {
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index 1547351805..0add4ed938 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -342,7 +342,7 @@ protected:
 	void runLoop();
 	void update() override { screen()->updateScreen(); }
 	bool checkPartyStatus(bool handleDeath);
-	virtual void updateGuiAnimations() {}
+	void updateAnimTimers();
 
 	bool _runFlag;
 
@@ -644,6 +644,7 @@ protected:
 
 	int calcNewBlockPositionAndTestPassability(uint16 curBlock, uint16 direction);
 	void notifyBlockNotPassable();
+	void increaseStepsCounter();
 	void moveParty(uint16 block);
 
 	int clickedDoorSwitch(uint16 block, uint16 direction) override;
@@ -656,7 +657,7 @@ protected:
 	void openDoor(int block);
 	void closeDoor(int block);
 
-	void addCurrentLevelMap();
+	void addLevelMap(int level);
 	uint32 countMaps() const;
 	uint32 countArrows() const;
 
@@ -791,6 +792,8 @@ protected:
 	void gui_processWeaponSlotClickRight(int charIndex, int slotIndex);
 	void gui_processInventorySlotClick(int slot);
 
+	virtual void gui_updateAnimations() {}
+
 	static const uint8 _buttonList1[];
 	int _buttonList1Size;
 	static const uint8 _buttonList2[];
@@ -910,10 +913,6 @@ protected:
 	virtual void drawLightningColumn() {}
 	virtual int charSelectDialogue() { return -1; }
 	virtual void characterLevelGain(int charIndex) {}
-	virtual void makeNameShapes() {}
-
-	Common::Error loadGameState(int slot) override;
-	Common::Error saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail) override;
 
 	const uint8 *_cgaMappingDefault;
 	const uint8 *_cgaMappingAlt;
@@ -931,6 +930,10 @@ protected:
 
 	bool _enableHiResDithering;
 
+	Common::Error loadGameState(int slot) override;
+	Common::Error saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail) override;
+	virtual void makeNameShapes() {}
+	virtual void makeFaceShapes();
 	// Default parameters will import all present original save files and push them to the top of the save dialog.
 	bool importOriginalSaveFile(int destSlot, const char *sourceFile = 0);
 	Common::String readOriginalSaveFile(Common::String &file);
diff --git a/engines/kyra/engine/scene_eob.cpp b/engines/kyra/engine/scene_eob.cpp
index bbc34accba..a6496460aa 100644
--- a/engines/kyra/engine/scene_eob.cpp
+++ b/engines/kyra/engine/scene_eob.cpp
@@ -561,6 +561,7 @@ void EoBCoreEngine::drawScene(int refresh) {
 		int diff = _flashShapeTimer - ct;
 		while ((diff > 0) && !shouldQuit()) {
 			updateInput();
+			updateAnimTimers();
 			uint32 step = MIN<uint32>(diff, _tickLength / 5);
 			_system->delayMillis(step);
 			diff -= step;
@@ -712,6 +713,11 @@ void EoBCoreEngine::notifyBlockNotPassable() {
 	removeInputTop();
 }
 
+void EoBCoreEngine::increaseStepsCounter() {
+	if (_totalSteps < 0xFFFFFFFF)
+		_totalSteps++;
+}
+
 void EoBCoreEngine::moveParty(uint16 block) {
 	updateAllMonsterDests();
 	uint16 old = _currentBlock;
diff --git a/engines/kyra/engine/sprites_eob.cpp b/engines/kyra/engine/sprites_eob.cpp
index 9804740411..e90c63dcb9 100644
--- a/engines/kyra/engine/sprites_eob.cpp
+++ b/engines/kyra/engine/sprites_eob.cpp
@@ -163,6 +163,9 @@ void EoBCoreEngine::killMonster(EoBMonsterInPlay *m, bool giveExperience) {
 	if (giveExperience)
 		increasePartyExperience(_monsterProps[m->type].experience);
 
+	if (_totalEnemiesKilled < 0xFFFF)
+		_totalEnemiesKilled++;
+
 	if (killMonsterExtra(m)) {
 		placeMonster(m, 0, -1);
 
diff --git a/engines/kyra/gui/gui_eob_segacd.cpp b/engines/kyra/gui/gui_eob_segacd.cpp
index b9b00eda1b..ae4807b16a 100644
--- a/engines/kyra/gui/gui_eob_segacd.cpp
+++ b/engines/kyra/gui/gui_eob_segacd.cpp
@@ -37,7 +37,7 @@ void EoBEngine::gui_drawPlayField(bool refresh) {
 		return;
 	}
 
-	if (!_loading)
+	//if (!_loading)
 		_screen->sega_fadeToBlack(1);
 
 	// transposeScreenOutputY(8);
@@ -117,7 +117,7 @@ void EoBEngine::gui_drawPlayField(bool refresh) {
 	_compassDirection2 = -1;
 	gui_drawCompass(false);;
 
-	if (!_loading)
+	//if (!_loading)
 		_screen->sega_fadeToNeutral(1);
 }
 
@@ -225,13 +225,24 @@ void EoBEngine::makeNameShapes() {
 	_txt->clearDim(cd);
 }
 
+void EoBEngine::makeFaceShapes() {
+	uint8 *in = _res->fileData("FACE", 0);
+	for (int i = 0; i < 6; i++) {
+		EoBCharacter *c = &_characters[i];
+		if (!c->flags)
+			continue;
+		_screen->sega_encodeShapesFromSprites(&c->faceShape, &in[(c->portrait < 0 ? -c->portrait + 43 : c->portrait) << 9], 1, 32, 32, 3);
+	}	
+	delete[] in;
+}
+
 void EoBEngine::printStatsString(const char *str, int x, int y) {
 	uint16 *dst = &_statsPattern2[y * 18 + x];
 	for (const uint8 *pos = (const uint8*)str; *pos; ++pos)
 		*dst++ = 0x6525 + _charTilesTable[*pos];
 }
 
-void EoBEngine::updateGuiAnimations() {
+void EoBEngine::gui_updateAnimations() {
 	if (_flags.platform != Common::kPlatformSegaCD)
 		return;
 
diff --git a/engines/kyra/gui/saveload_eob.cpp b/engines/kyra/gui/saveload_eob.cpp
index a0a8291dce..93115bb6f4 100644
--- a/engines/kyra/gui/saveload_eob.cpp
+++ b/engines/kyra/gui/saveload_eob.cpp
@@ -48,7 +48,7 @@ Common::Error EoBCoreEngine::loadGameState(int slot) {
 	Common::SeekableSubReadStreamEndian in(saveFile, saveFile->pos(), saveFile->size(), !header.originalSave, DisposeAfterUse::YES);
 	_loading = true;
 
-	if (slot != -1)
+	if (slot != -1 && _flags.gameID != Common::kPlatformSegaCD)
 		_screen->fadeToBlack(10);
 
 	enableSysTimer(2);
@@ -109,22 +109,7 @@ Common::Error EoBCoreEngine::loadGameState(int slot) {
 	setupCharacterTimers();
 
 	makeNameShapes();
-	_screen->loadShapeSetBitmap("CHARGENA", 3, 3);
-	for (int i = 0; i < 6; i++) {
-		EoBCharacter *c = &_characters[i];
-		if (!c->flags || c->portrait < 0)
-			continue;
-		c->faceShape = _screen->encodeShape((c->portrait % 10) << 2, (c->portrait / 10) << 5, 4, 32, true, _cgaMappingDefault);
-	}
-
-	_screen->loadShapeSetBitmap(_flags.gameID == GI_EOB2 ? "OUTPORTS" : "OUTTAKE", 3, 3);
-	for (int i = 0; i < 6; i++) {
-		EoBCharacter *c = &_characters[i];
-		if (!c->flags || c->portrait >= 0)
-			continue;
-		c->faceShape = _screen->encodeShape((-(c->portrait + 1)) << 2, _flags.gameID == GI_EOB2 ? 0 : 160, 4, 32, true, _cgaMappingDefault);
-	}
-	_screen->_curPage = 0;
+	makeFaceShapes();
 
 	if (slot == -1) {
 		// Skip all settings which aren't necessary for party transfer.
@@ -155,6 +140,13 @@ Common::Error EoBCoreEngine::loadGameState(int slot) {
 		_activeSpell = in.readByte();
 		_returnAfterSpellCallback = in.readByte() ? true : false;
 
+		if (_flags.platform == Common::kPlatformSegaCD) {
+			_totalPlaySecs = in.readUint32BE();
+			_totalEnemiesKilled = in.readUint32BE();
+			_totalSteps = in.readUint32BE();
+			_levelMaps = in.readUint32BE();
+		}
+
 		_inf->loadState(in);
 	}
 
@@ -283,7 +275,7 @@ Common::Error EoBCoreEngine::loadGameState(int slot) {
 		_screen->setScreenPalette(_screen->getPalette(0));
 
 	_sceneUpdateRequired = true;
-	_screen->setFont(_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT);
+	_screen->setFont(_conFont);
 
 	for (int i = 0; i < 6; i++) {
 		for (int ii = 0; ii < 10; ii++) {
@@ -318,8 +310,10 @@ Common::Error EoBCoreEngine::loadGameState(int slot) {
 	while (!_screen->isMouseVisible())
 		_screen->showMouse();
 
+	if (_flags.platform != Common::kPlatformSegaCD)
+		_screen->fadeFromBlack(20);
+
 	_loading = false;
-	_screen->fadeFromBlack(20);
 	removeInputTop();
 
 	return Common::kNoError;
@@ -425,6 +419,13 @@ Common::Error EoBCoreEngine::saveGameStateIntern(int slot, const char *saveName,
 	out->writeByte(_activeSpell);
 	out->writeByte(_returnAfterSpellCallback ? 1 : 0);
 
+	if (_flags.platform == Common::kPlatformSegaCD) {
+		out->writeUint32BE(_totalPlaySecs);
+		out->writeUint32BE(_totalEnemiesKilled);
+		out->writeUint32BE(_totalSteps);
+		out->writeUint32BE(_levelMaps);
+	}
+
 	_inf->saveState(out);
 
 	for (int i = 0; i < 600; i++) {
@@ -543,6 +544,25 @@ Common::Error EoBCoreEngine::saveGameStateIntern(int slot, const char *saveName,
 	return Common::kNoError;
 }
 
+void EoBCoreEngine::makeFaceShapes() {
+	_screen->loadShapeSetBitmap("CHARGENA", 3, 3);
+	for (int i = 0; i < 6; i++) {
+		EoBCharacter *c = &_characters[i];
+		if (!c->flags || c->portrait < 0)
+			continue;
+		c->faceShape = _screen->encodeShape((c->portrait % 10) << 2, (c->portrait / 10) << 5, 4, 32, true, _cgaMappingDefault);
+	}
+
+	_screen->loadShapeSetBitmap(_flags.gameID == GI_EOB2 ? "OUTPORTS" : "OUTTAKE", 3, 3);
+	for (int i = 0; i < 6; i++) {
+		EoBCharacter *c = &_characters[i];
+		if (!c->flags || c->portrait >= 0)
+			continue;
+		c->faceShape = _screen->encodeShape((-(c->portrait + 1)) << 2, _flags.gameID == GI_EOB2 ? 0 : 160, 4, 32, true, _cgaMappingDefault);
+	}
+	_screen->_curPage = 0;
+}
+
 bool EoBCoreEngine::importOriginalSaveFile(int destSlot, const char *sourceFile) {
 	Common::Array<Common::String> origFiles;
 	Common::Array<int> newSlots;


Commit: e2d01ae8e0e8f55c42a16c31c90280c51af2025f
    https://github.com/scummvm/scummvm/commit/e2d01ae8e0e8f55c42a16c31c90280c51af2025f
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:14+02:00

Commit Message:
KYRA: (EOB/SegaCD) - fix door shapes

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


diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index 21a7bdccc8..e40b3dacc6 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -732,7 +732,7 @@ void EoBEngine::loadDoorShapes(int doorType1, int shapeId1, int doorType2, int s
 				int offs = lvlIndex[_currentLevel] * 6 + shapeId[a] + i;
 				const uint8 *enc = &_doorShapeEncodeDefs[offs << 2];
 				_doorShapes[shapeId[a] + i] = _screen->sega_convertShape(_doorShapesSrc[offs], enc[0] << 3, enc[1] << 3, 0, enc[2] - enc[3]);
-				enc = &_doorSwitchShapeEncodeDefs[(offs << 2) - shapeId[a]];
+				enc = &_doorSwitchShapeEncodeDefs[(offs - shapeId[a]) << 2];
 				_doorSwitches[shapeId[a] + i].shp = _screen->sega_convertShape(_doorSwitchShapesSrc[offs], enc[0] << 3, enc[1] << 3, 0, enc[2] - enc[3]);
 			} else {
 				const uint8 *enc = &_doorShapeEncodeDefs[(doorType[a] * 3 + i) << 2];
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index 6e0bb9a73d..e541d4b316 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -1310,13 +1310,13 @@ void EoBEngine::initStaticResource() {
 
 	// Build offset tables for door shapes encoding
 	if (_flags.platform == Common::kPlatformSegaCD) {
-		const uint8 *shp = _screen->getCPagePtr(2);
 		const uint8 *e1 = _doorShapeEncodeDefs;
 		const uint8 *e2 = _doorSwitchShapeEncodeDefs;
 		const uint8 **doorShapesSrc = new const uint8*[30];
 		const uint8 **doorSwitchShapesSrc = new const uint8*[30];
 
 		for (int i = 0; i < 5; ++i) {
+			const uint8 *shp = _screen->getCPagePtr(2);
 			for (int ii = 0; ii < 6; ++ii) {
 				doorShapesSrc[i * 6 + ii] = shp;
 				shp += ((e1[0] * e1[1]) << 5);


Commit: fcad95af09e61dbd401e2a3c0729d7def99d3e24
    https://github.com/scummvm/scummvm/commit/fcad95af09e61dbd401e2a3c0729d7def99d3e24
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:14+02:00

Commit Message:
KYRA: (EOB/SegaCD) - add level map function

Changed paths:
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eob.h
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h
    engines/kyra/engine/items_eob.cpp
    engines/kyra/engine/scene_eob.cpp
    engines/kyra/graphics/screen_eob.h
    engines/kyra/graphics/screen_eob_segacd.cpp
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/gui/gui_eob_segacd.cpp
    engines/kyra/gui/saveload_eob.cpp
    engines/kyra/resource/staticres_eob.cpp
    engines/kyra/sequence/sequences_eob.cpp
    engines/kyra/text/text_eob_segacd.cpp
    engines/kyra/text/text_eob_segacd.h
    engines/kyra/text/text_rpg.cpp
    engines/kyra/text/text_rpg.h


diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index e40b3dacc6..a83817b1d2 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -66,6 +66,7 @@ EoBEngine::EoBEngine(OSystem *system, const GameFlags &flags)
 	_compassAnimSwitch = _compassAnimDone = false;
 	_redGrid = _charTilesTable = 0;
 	_compassData = 0;
+	_mapStrings1 = _mapStrings2 = _mapStrings3 = 0;
 
 	_seqPlayer = 0;
 	_sres = 0;
@@ -275,8 +276,6 @@ void EoBEngine::startupNew() {
 	_screen->selectPC98Palette(0, _screen->getPalette(0));
 	if (_flags.platform == Common::kPlatformSegaCD) {
 		_screen->sega_selectPalette(4, 0);
-		_screen->sega_selectPalette(6, 1);
-		_screen->sega_selectPalette(8, 2);
 		_screen->sega_selectPalette(7, 3);
 		makeNameShapes();
 		_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 40, 28, 0x2000);
@@ -300,8 +299,6 @@ void EoBEngine::startupLoad() {
 
 	if (_flags.platform == Common::kPlatformSegaCD) {
 		_screen->sega_selectPalette(4, 0);
-		_screen->sega_selectPalette(6, 1);
-		_screen->sega_selectPalette(8, 2);
 		_screen->sega_selectPalette(7, 3);
 		_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 40, 28, 0x2000);
 		_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 28, 0x2000);
diff --git a/engines/kyra/engine/eob.h b/engines/kyra/engine/eob.h
index 1ca891231b..adddddd09c 100644
--- a/engines/kyra/engine/eob.h
+++ b/engines/kyra/engine/eob.h
@@ -185,21 +185,29 @@ private:
 	uint8 *_shakeBackBuffer1;
 	uint8 *_shakeBackBuffer2;
 
+	// Map
+	const char *const *_mapStrings1;
+	const char *const *_mapStrings2;
+	const char *const *_mapStrings3;
+
 	// Resource
 	SegaCDResource *_sres;
 
 	// GUI
 	void gui_drawPlayField(bool refresh) override;
+	void gui_setupPlayFieldHelperPages() override;
 	void gui_drawWeaponSlotStatus(int x, int y, int status) override;
 	void gui_printInventoryDigits(int x, int y, int val) override;
 	void gui_drawCharacterStatsPage() override;
 	void gui_displayMap() override;
+	void gui_updateAnimations() override;
 
 	void makeNameShapes() override;
 	void makeFaceShapes() override;
 	void printStatsString(const char *str, int x, int y);
-
-	void gui_updateAnimations() override;
+	void drawMapButton(const char *str, int x, int y);
+	void drawMapPage(int level);
+	void drawMapSpots(int level, int animState);
 
 	const KyraRpgGUISettings *guiSettings() const override;
 	void useMainMenuGUISettings(bool toggle) override { _useMainMenuGUISettings = toggle; }
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index e8d02e1416..1add30f0cb 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -2621,10 +2621,14 @@ void EoBCoreEngine::addLevelMap(int level) {
 	_levelMaps |= (1 << (level - 1));
 }
 
+bool EoBCoreEngine::hasLevelMap(int level) const {
+	return _levelMaps & (1 << (level - 1));
+}
+
 uint32 EoBCoreEngine::countMaps() const {
 	uint32 res = 0;
-	for (int i = 0; i < 12; ++i) {
-		if (_levelMaps & (1 << i))
+	for (int i = 1; i < 13; ++i) {
+		if (hasLevelMap(i))
 			res++;
 	}
 	return res;
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index 0add4ed938..635323ea36 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -658,6 +658,7 @@ protected:
 	void closeDoor(int block);
 
 	void addLevelMap(int level);
+	bool hasLevelMap(int level) const;
 	uint32 countMaps() const;
 	uint32 countArrows() const;
 
@@ -729,6 +730,7 @@ protected:
 
 	// Gui
 	virtual void gui_drawPlayField(bool refresh);
+	virtual void gui_setupPlayFieldHelperPages();
 	void gui_restorePlayField();
 	void gui_drawAllCharPortraitsWithStats();
 	void gui_drawCharPortraitWithStats(int index);
@@ -785,7 +787,7 @@ protected:
 	int clickedSceneSpecial(Button *button);
 	int clickedSpellbookAbort(Button *button);
 	int clickedSpellbookScroll(Button *button);
-	int clickedUnk(Button *button);
+	int clickedButtonReturnIndex(Button *button);
 
 	void gui_processCharPortraitClick(int index);
 	void gui_processWeaponSlotClickLeft(int charIndex, int slotIndex);
diff --git a/engines/kyra/engine/items_eob.cpp b/engines/kyra/engine/items_eob.cpp
index c0e68d7c45..231d21dd0e 100644
--- a/engines/kyra/engine/items_eob.cpp
+++ b/engines/kyra/engine/items_eob.cpp
@@ -41,9 +41,6 @@ void EoBCoreEngine::loadItemDefs() {
 	for (int i = 0; i < 600; i++)
 		_items[i].block = -1;
 
-	if (_flags.platform == Common::kPlatformSegaCD)
-		_items[498].block = _items[499].block = -2;
-
 	for (int i = 0; i < _numItems; i++) {
 		_items[i].nameUnid = s->readByte();
 		_items[i].nameId = s->readByte();
@@ -54,10 +51,36 @@ void EoBCoreEngine::loadItemDefs() {
 		_items[i].block = s->readSint16();
 		_items[i].next = s->readSint16();
 		_items[i].prev = s->readSint16();
-		_items[i].level = s->readSByte();
+		_items[i].level = s->readByte();
 		_items[i].value = s->readSByte();
 	}
 
+	if (_flags.platform == Common::kPlatformSegaCD) {
+		_items[498].block = _items[499].block = -2;
+
+		int temp = 0;
+		const uint8 *pos = _staticres->loadRawData(kEoB1MapLevelData, temp);
+
+		for (int i = _numItems; i < _numItems + temp / 14; i++) {
+			_items[i].nameUnid = *pos++;
+			_items[i].nameId = *pos++;
+			_items[i].flags = *pos++;
+			_items[i].icon = (int8)*pos++;
+			_items[i].type = (int8)*pos++;
+			_items[i].pos = (int8)*pos++;
+			_items[i].block = (int16)READ_BE_UINT16(pos);
+			pos += 2;
+			_items[i].next = (int16)READ_BE_UINT16(pos);
+			pos += 2;
+			_items[i].prev = (int16)READ_BE_UINT16(pos);
+			pos += 2;
+			_items[i].level = *pos++;
+			_items[i].value = (int8)*pos++;
+		}
+		_numItems += (temp / 14);
+		_items[22].nameUnid = _items[27].nameUnid = _items[28].nameUnid = _items[29].nameUnid = _items[29].nameUnid = 96;
+	}
+
 	if (_itemNamesStatic) {
 		_numItemNames = _numItemNamesStatic;
 		for (int i = 0; i < _numItemNames; i++) {
diff --git a/engines/kyra/engine/scene_eob.cpp b/engines/kyra/engine/scene_eob.cpp
index a6496460aa..4061a1a588 100644
--- a/engines/kyra/engine/scene_eob.cpp
+++ b/engines/kyra/engine/scene_eob.cpp
@@ -95,8 +95,7 @@ void EoBCoreEngine::loadLevel(int level, int sub) {
 		_levelBlockProperties[0x035C].assignedObjects = 0x0E8D;
 
 	loadVcnData(gfxFile.c_str(), _cgaLevelMappingIndex ? _cgaMappingLevel[_cgaLevelMappingIndex[level - 1]] : 0);
-	if (_flags.platform != Common::kPlatformSegaCD)
-		_screen->loadEoBBitmap("INVENT", _cgaMappingInv, 5, 3, 2);
+	gui_setupPlayFieldHelperPages();		
 	if (_flags.platform == Common::kPlatformAmiga && _flags.gameID == GI_EOB1)
 		_screen->getPalette(0).copy(_screen->getPalette(1), 1, 5, 1);
 
diff --git a/engines/kyra/graphics/screen_eob.h b/engines/kyra/graphics/screen_eob.h
index 96ea6f92b7..5e988d6070 100644
--- a/engines/kyra/graphics/screen_eob.h
+++ b/engines/kyra/graphics/screen_eob.h
@@ -150,7 +150,7 @@ public:
 	void sega_drawTextBox(int pW, int pH, int x, int y, int w, int h, uint8 color1, uint8 color2);
 	void sega_loadTextBufferToVRAM(uint16 srcOffset, uint16 addr, int size);
 	void sega_gfxScale(uint8 *out, uint16 w, uint16 h, uint16 pitch, const uint8 *in, const uint16 *stampMap, const uint16 *traceVectors);
-	void sega_drawClippedLine(uint8 *dst, int pW, int pH, int x, int y, int w, int h, uint8 color);
+	void sega_drawClippedLine(int pW, int pH, int x, int y, int w, int h, uint8 color);
 	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);
 
diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index 062e9e05a4..e6993f7fd6 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -252,14 +252,14 @@ void Screen_EoB::sega_copyTextBufferLine(uint16 srcY, uint16 dstY, uint16 lineHe
 }
 
 void Screen_EoB::sega_drawTextBox(int pW, int pH, int x, int y, int w, int h, uint8 color1, uint8 color2) {
-	sega_drawClippedLine(_textRenderBuffer, 26, 5, x, y, w, 1, color1);
-	sega_drawClippedLine(_textRenderBuffer, 26, 5, x, y + h - 1, w, 1, color1);
-	sega_drawClippedLine(_textRenderBuffer, 26, 5, x, y, 1, h, color1);
-	sega_drawClippedLine(_textRenderBuffer, 26, 5, x + w - 1, y, 1, h, color1);
-	sega_drawClippedLine(_textRenderBuffer, 26, 5, x + 1, y + 1, w - 2, 1, color2);
-	sega_drawClippedLine(_textRenderBuffer, 26, 5, x + 1, y + h - 2, w - 2, 1, color2);
-	sega_drawClippedLine(_textRenderBuffer, 26, 5, x + 1, y + 1, 1, h - 2, color2);
-	sega_drawClippedLine(_textRenderBuffer, 26, 5, x + w - 2, y + 1, 1, h - 2, color2);
+	sega_drawClippedLine(26, 5, x, y, w, 1, color1);
+	sega_drawClippedLine(26, 5, x, y + h - 1, w, 1, color1);
+	sega_drawClippedLine(26, 5, x, y, 1, h, color1);
+	sega_drawClippedLine(26, 5, x + w - 1, y, 1, h, color1);
+	sega_drawClippedLine(26, 5, x + 1, y + 1, w - 2, 1, color2);
+	sega_drawClippedLine(26, 5, x + 1, y + h - 2, w - 2, 1, color2);
+	sega_drawClippedLine(26, 5, x + 1, y + 1, 1, h - 2, color2);
+	sega_drawClippedLine(26, 5, x + w - 2, y + 1, 1, h - 2, color2);
 }
 
 void Screen_EoB::sega_loadTextBufferToVRAM(uint16 srcOffset, uint16 addr, int size) {
@@ -303,7 +303,8 @@ void Screen_EoB::sega_gfxScale(uint8 *out, uint16 w, uint16 h, uint16 pitch, con
 	}
 }
 
-void Screen_EoB::sega_drawClippedLine(uint8 *dst, int pW, int pH, int x, int y, int w, int h, uint8 color) {
+void Screen_EoB::sega_drawClippedLine(int pW, int pH, int x, int y, int w, int h, uint8 color) {
+	uint8 *dst = _textRenderBuffer;
 	uint8 p = (x & 1) ? 0x0F : 0xF0;
 	color &= p;
 	p = ~p;
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index afbaf77739..419387cd3e 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -61,7 +61,7 @@ void EoBCoreEngine::gui_drawPlayField(bool refresh) {
 	if (!_loading)
 		_screen->updateScreen();
 
-	_screen->loadEoBBitmap("INVENT", _cgaMappingInv, 5, 3, 2);
+	gui_setupPlayFieldHelperPages();
 
 	if (_flags.platform == Common::kPlatformAmiga) {
 		if (_flags.gameID == GI_EOB1) {
@@ -73,6 +73,10 @@ void EoBCoreEngine::gui_drawPlayField(bool refresh) {
 	}
 }
 
+void EoBCoreEngine::gui_setupPlayFieldHelperPages() {
+	_screen->loadEoBBitmap("INVENT", _cgaMappingInv, 5, 3, 2);
+}
+
 void EoBCoreEngine::gui_restorePlayField() {
 	loadVcnData(0, _cgaLevelMappingIndex ? _cgaMappingLevel[_cgaLevelMappingIndex[_currentLevel - 1]] : 0);
 	_screen->_curPage = 0;
@@ -377,7 +381,7 @@ void EoBCoreEngine::gui_drawHitpoints(int index) {
 			col = guiSettings()->colors.guiColorDarkRed;
 
 		if (_flags.platform == Common::kPlatformSegaCD)
-			col = (bgCur < 12) ? guiSettings()->colors.guiColorDarkRed : (bgCur < 24 ? guiSettings()->colors.guiColorYellow : guiSettings()->colors.guiColorDarkGreen);
+			col = (bgCur * 40 / bgMax) < 12 ? guiSettings()->colors.guiColorDarkRed : ((bgCur * 40 / bgMax) < 24 ? guiSettings()->colors.guiColorYellow : guiSettings()->colors.guiColorDarkGreen);
 		else if (!_currentControlMode)
 			_screen->printText(_characterGuiStringsHp[0], x - 13, y - 1, guiSettings()->colors.guiColorBlack, 0);
 
@@ -813,7 +817,7 @@ void EoBCoreEngine::gui_initButton(int index, int, int, int) {
 	Button *b = 0;
 	int cnt = 1;
 
-	if (_flags.gameID == GI_EOB1 && !(_flags.platform == Common::kPlatformSegaCD && index == 95) && index > 92)
+	if (_flags.gameID == GI_EOB1 && !(_flags.platform == Common::kPlatformSegaCD && index >= 95) && index > 92)
 		return;
 
 	if (_activeButtons) {
@@ -925,6 +929,17 @@ int EoBCoreEngine::clickedSceneDropPickupItem(Button *button) {
 		d = getQueuedItem((Item *)&_levelBlockProperties[block].drawObjects, d, -1);
 		if (!d)
 			return 1;
+
+		if (_items[d].nameUnid == 97) {
+			_items[d].block = -1;
+			addLevelMap(_items[d].value);
+			snd_playSoundEffect(0x101C);
+			_txt->printMessage(_warningStrings[3], 0x55);
+			if (_currentControlMode == 1)
+				gui_drawCharPortraitWithStats(_updateCharNum);
+			d = 0;
+		}
+
 		setHandItem(d);
 		runLevelScript(block, 8);
 	}
@@ -1186,6 +1201,7 @@ int EoBCoreEngine::clickedUpArrow(Button *button) {
 		notifyBlockNotPassable();
 	} else {
 		moveParty(b);
+		increaseStepsCounter();
 		_sceneDefaultUpdate = 1;
 	}
 
@@ -1199,6 +1215,7 @@ int EoBCoreEngine::clickedDownArrow(Button *button) {
 		notifyBlockNotPassable();
 	} else {
 		moveParty(b);
+		increaseStepsCounter();
 		_sceneDefaultUpdate = 1;
 	}
 
@@ -1212,6 +1229,7 @@ int EoBCoreEngine::clickedLeftArrow(Button *button) {
 		notifyBlockNotPassable();
 	} else {
 		moveParty(b);
+		increaseStepsCounter();
 		_sceneDefaultUpdate = 1;
 	}
 
@@ -1225,6 +1243,7 @@ int EoBCoreEngine::clickedRightArrow(Button *button) {
 		notifyBlockNotPassable();
 	} else {
 		moveParty(b);
+		increaseStepsCounter();
 		_sceneDefaultUpdate = 1;
 	}
 
@@ -1300,7 +1319,7 @@ int EoBCoreEngine::clickedSpellbookScroll(Button *button) {
 	return button->index;
 }
 
-int EoBCoreEngine::clickedUnk(Button *button) {
+int EoBCoreEngine::clickedButtonReturnIndex(Button *button) {
 	return button->index;
 }
 
diff --git a/engines/kyra/gui/gui_eob_segacd.cpp b/engines/kyra/gui/gui_eob_segacd.cpp
index ae4807b16a..d9213351b7 100644
--- a/engines/kyra/gui/gui_eob_segacd.cpp
+++ b/engines/kyra/gui/gui_eob_segacd.cpp
@@ -29,6 +29,8 @@
 #include "kyra/resource/resource.h"
 #include "kyra/resource/resource_segacd.h"
 
+#include "common/system.h"
+
 namespace Kyra {
 
 void EoBEngine::gui_drawPlayField(bool refresh) {
@@ -37,13 +39,17 @@ void EoBEngine::gui_drawPlayField(bool refresh) {
 		return;
 	}
 
-	//if (!_loading)
-		_screen->sega_fadeToBlack(1);
+	_screen->sega_fadeToBlack(_loading ? 0 : 1);
+	_screen->sega_selectPalette(6, 1);
+	_screen->sega_selectPalette(8, 2);
 
 	// transposeScreenOutputY(8);
 	_txt->clearDim(0);
 	_screen->sega_getAnimator()->clearSprites();
+	_screen->sega_getAnimator()->update();
 	SegaRenderer *r = _screen->sega_getRenderer();
+	r->fillRectWithTiles(0, 0, 0, 40, 28, 0);
+	r->fillRectWithTiles(1, 0, 0, 40, 28, 0);
 
 	uint8 *data = _res->fileData("PLAYFLD", 0);
 	for (int i = 0; i < 256; ++i)
@@ -69,18 +75,6 @@ void EoBEngine::gui_drawPlayField(bool refresh) {
 		memset(&dst[120], 0, 8);
 	}
 
-	r->fillRectWithTiles(1, 0, 0, 40, 26, 0x2000, true, false, _playFldPattern2);
-	r->fillRectWithTiles(0, 0, 21, 40, 5, 0x2000, true, false, _textFieldPattern);
-
-	// Name tables for scene window vcn block tiles. We don't need that. We draw the blocks with our "normal" graphics code.
-	// r->fillRectWithTiles(1, 0, 0, 22, 15, 0xC14B, true, true);
-	// Name tables for scene window shapes tiles. We don't need that, since we're not going to draw any shapes with the renderer.
-	// 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);
-
 	_sres->loadContainer("ITEM");
 	Common::SeekableReadStreamEndian *str = _sres->resStreamEndian(7);
 	r->loadStreamToVRAM(str, 0x8880, true);
@@ -89,20 +83,15 @@ void EoBEngine::gui_drawPlayField(bool refresh) {
 	r->loadStreamToVRAM(str, 0xA4A0, false);
 	delete str;
 
-	r->fillRectWithTiles(1, 22, 0, 18, 21, 0x6444, true, true, _invPattern);
-	r->render(2);
-	r->fillRectWithTiles(1, 22, 0, 18, 21, 0x6444, true, true, _statsPattern);
-	r->render(Screen_EoB::kSegaRenderPage);
+	gui_setupPlayFieldHelperPages();
 
 	if (refresh && !_sceneDrawPage2)
-		drawScene(0);
+		drawScene(1);
 
-	_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);
 	_screen->copyRegionToBuffer(0, 173, 0, 6, 120, _shakeBackBuffer1);
 	_screen->copyRegionToBuffer(0, 0, 117, 179, 6, _shakeBackBuffer2);
 
-	// Since we're no 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 SegaRenderer 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];
@@ -115,10 +104,30 @@ void EoBEngine::gui_drawPlayField(bool refresh) {
 	}
 
 	_compassDirection2 = -1;
-	gui_drawCompass(false);;
+	gui_drawCompass(false);
+
+	_screen->sega_fadeToNeutral(1);
+}
 
-	//if (!_loading)
-		_screen->sega_fadeToNeutral(1);
+void EoBEngine::gui_setupPlayFieldHelperPages() {
+	_txt->clearDim(0);
+	SegaRenderer *r = _screen->sega_getRenderer();
+	r->fillRectWithTiles(0, 22, 0, 18, 21, 0);
+	r->fillRectWithTiles(1, 0, 0, 40, 26, 0x2000, true, false, _playFldPattern2);
+	r->fillRectWithTiles(0, 0, 21, 40, 5, 0x2000, true, false, _textFieldPattern);
+	// Nametables for scene window vcn block tiles. We don't need that. We draw the blocks with our "normal" graphics code.
+	// r->fillRectWithTiles(1, 0, 0, 22, 15, 0xC14B, true, true);
+	// Nametables for scene window shapes tiles. We don't need that, since we're not going to draw any scene shapes with the renderer.
+	// 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->fillRectWithTiles(1, 22, 0, 18, 21, 0x6444, true, true, _invPattern);
+	r->render(2);
+	r->fillRectWithTiles(1, 22, 0, 18, 21, 0x6444, true, true, _statsPattern);
+	r->render(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);
 }
 
 void EoBEngine::gui_drawWeaponSlotStatus(int x, int y, int status) {
@@ -189,57 +198,96 @@ void EoBEngine::gui_drawCharacterStatsPage() {
 }
 
 void EoBEngine::gui_displayMap() {
+	uint32 startTime = _system->getMillis();
+	disableSysTimer(2);
 
-}
+	_screen->sega_fadeToBlack(2);
 
-void EoBEngine::makeNameShapes() {
-	if (_flags.platform != Common::kPlatformSegaCD)
-		return;
-
-	int cd = _txt->clearDim(4);
-	int cp = _screen->setCurPage(2);
-	_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 28, 0x2000);
-	_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 30, 28, 0x600A, true);
-	_screen->sega_clearTextBuffer(0);
-
-	for (int i = 0; i < 6; ++i) {
-		if (!_characters[i].flags)
+	_sceneShakeCountdown = 0;
+	for (int i = 0; i < 6; i++) {
+		if (!testCharacter(i, 1))
 			continue;
-		_txt->printShadowedText(_characters[i].name, 0, i << 4, 0xFF, 0xCC);
+		_characters[i].damageTaken = 0;
+		_characters[i].slotStatus[0] = _characters[i].slotStatus[1] = _characters[i].gfxUpdateCountdown = 0;
+		gui_drawCharPortraitWithStats(i);
 	}
 
-	_screen->sega_getRenderer()->render(_screen->_curPage);
+	SegaRenderer *r = _screen->sega_getRenderer();
+	r->fillRectWithTiles(0, 0, 0, 40, 28, 0);
+	r->fillRectWithTiles(1, 0, 0, 40, 28, 0);
+	_screen->sega_getAnimator()->clearSprites();
+	_screen->sega_getAnimator()->update();
+	_screen->sega_selectPalette(55, 1);
+	_screen->sega_selectPalette(56, 2);
 
-	for (int i = 0; i < 6; ++i) {
-		if (!_characters[i].flags)
-			continue;
-		delete[] _characters[i].nameShape;
-		_characters[i].nameShape = _screen->encodeShape(0, i << 4, (_screen->getTextWidth(_characters[i].name) + 8) >> 3, 13);
-	}
+	snd_stopSound();
+	_sres->loadContainer("MAP");
+	Common::SeekableReadStreamEndian *in = _sres->resStreamEndian(0);
+	r->loadStreamToVRAM(in, 0x20);
+	delete in;
+	in = _sres->resStreamEndian(1);
+	r->loadStreamToVRAM(in, 0x80);
+	delete in;
 
-	_screen->clearPage(2);
-	_screen->setCurPage(cp);
+	int cs = _screen->setFontStyles(_screen->_currentFont, _flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat);
+
+	_screen->sega_clearTextBuffer(0);
+	for (int i = 0; i < 3; ++i)
+		drawMapButton(_mapStrings1[i], 0, i << 4);
+	_screen->sega_loadTextBufferToVRAM(0, 0x7E20, 1536);
+	r->fillRectWithTiles(0, 31, 19, 8, 6, 0x63F1, true);
 	_screen->sega_clearTextBuffer(0);
 
-	_txt->clearDim(4);
-	_txt->clearDim(cd);
-}
+	_screen->sega_clearTextBuffer(0);
+	_txt->printShadowedText(_mapStrings2[_currentLevel - 1], 0, 0, 0xFF, 0, 64, 16, false);
+	_screen->sega_loadTextBufferToVRAM(0, 0x7C20, 512);
+	r->fillRectWithTiles(0, 31, 16, 8, 2, 0x63E1, true);
 
-void EoBEngine::makeFaceShapes() {
-	uint8 *in = _res->fileData("FACE", 0);
-	for (int i = 0; i < 6; i++) {
-		EoBCharacter *c = &_characters[i];
-		if (!c->flags)
-			continue;
-		_screen->sega_encodeShapesFromSprites(&c->faceShape, &in[(c->portrait < 0 ? -c->portrait + 43 : c->portrait) << 9], 1, 32, 32, 3);
-	}	
-	delete[] in;
-}
+	drawMapPage(_currentLevel);
+	r->render(0);
+	_screen->sega_fadeToNeutral(3);
+
+	gui_resetButtonList();
+	for (int i = 96; i < 99; ++i)
+		gui_initButton(i);
+
+	int animState = 0;
+	for (int level = _currentLevel; level && !shouldQuit(); ) {
+		uint32 del = _system->getMillis() + 16;
+		int inputFlag = checkInput(_activeButtons, false, 0);
+		removeInputTop();
+
+		drawMapSpots(level, animState < 20 ? 0 : 1);
+		bool update = (animState == 0 || animState == 20);
+		if (++animState == 40)
+			animState = 0;
+
+		int op = (inputFlag & 0x8000) ? (int16)gui_getButton(_activeButtons, inputFlag & 0xFF)->arg : 0;
+		if (op) {
+			snd_playSoundEffect(0x81);
+			level = (op == 2) ? 0 : CLIP(level + op, 1, 12);
+			if (level)
+				drawMapPage(level);
+		}
 
-void EoBEngine::printStatsString(const char *str, int x, int y) {
-	uint16 *dst = &_statsPattern2[y * 18 + x];
-	for (const uint8 *pos = (const uint8*)str; *pos; ++pos)
-		*dst++ = 0x6525 + _charTilesTable[*pos];
+		if (update) {
+			r->render(0);
+			_screen->updateScreen();
+		}
+		delayUntil(del);
+	}
+
+	_screen->setFontStyles(_screen->_currentFont, cs);
+	_screen->sega_fadeToBlack(3);
+
+	setLevelPalettes(_currentLevel);
+	gui_drawPlayField(true);
+	gui_drawAllCharPortraitsWithStats();
+	gui_setInventoryButtons();
+	snd_playLevelScore();
+
+	enableSysTimer(2);
+	_totalPlaySecs += ((_system->getMillis() - startTime) / 1000);
 }
 
 void EoBEngine::gui_updateAnimations() {
@@ -325,17 +373,114 @@ void EoBEngine::gui_updateAnimations() {
 		// All shapes except monsters and items
 		drawSceneShapes(0, 0xFF & ~0x2A);
 		_shapeShakeOffsetX = _shapeShakeOffsetY = 0;
-		// Monsters and items
-		drawSceneShapes(0, 0x2A);
+// Monsters and items
+drawSceneShapes(0, 0x2A);
 
-		_screen->copyRegion(0, 0, 0, 0, 179, 123, _sceneDrawPage1, 0, Screen::CR_NO_P_CHECK);
-		updScreen = true;
+_screen->copyRegion(0, 0, 0, 0, 179, 123, _sceneDrawPage1, 0, Screen::CR_NO_P_CHECK);
+updScreen = true;
 	}
 
 	if (updScreen)
 		_screen->updateScreen();
 }
 
+void EoBEngine::makeNameShapes() {
+	if (_flags.platform != Common::kPlatformSegaCD)
+		return;
+
+	int cd = _txt->clearDim(4);
+	int cp = _screen->setCurPage(2);
+	_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 28, 0x2000);
+	_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 30, 28, 0x600A, true);
+	_screen->sega_clearTextBuffer(0);
+
+	for (int i = 0; i < 6; ++i) {
+		if (!_characters[i].flags)
+			continue;
+		_txt->printShadowedText(_characters[i].name, 0, i << 4, 0xFF, 0xCC);
+	}
+
+	_screen->sega_getRenderer()->render(_screen->_curPage);
+
+	for (int i = 0; i < 6; ++i) {
+		if (!_characters[i].flags)
+			continue;
+		delete[] _characters[i].nameShape;
+		_characters[i].nameShape = _screen->encodeShape(0, i << 4, (_screen->getTextWidth(_characters[i].name) + 8) >> 3, 13);
+	}
+
+	_screen->clearPage(2);
+	_screen->setCurPage(cp);
+	_screen->sega_clearTextBuffer(0);
+
+	_txt->clearDim(4);
+	_txt->clearDim(cd);
+}
+
+void EoBEngine::makeFaceShapes() {
+	uint8 *in = _res->fileData("FACE", 0);
+	for (int i = 0; i < 6; i++) {
+		EoBCharacter *c = &_characters[i];
+		if (!c->flags)
+			continue;
+		_screen->sega_encodeShapesFromSprites(&c->faceShape, &in[(c->portrait < 0 ? -c->portrait + 43 : c->portrait) << 9], 1, 32, 32, 3);
+	}
+	delete[] in;
+}
+
+void EoBEngine::printStatsString(const char *str, int x, int y) {
+	uint16 *dst = &_statsPattern2[y * 18 + x];
+	for (const uint8 *pos = (const uint8*)str; *pos; ++pos)
+		*dst++ = 0x6525 + _charTilesTable[*pos];
+}
+
+void EoBEngine::drawMapButton(const char *str, int x, int y) {
+	_screen->sega_drawClippedLine(8, 9, x, y, 64, 14, 0x99);
+	_screen->sega_drawClippedLine(8, 9, x, y + 1, 63, 13, 0xBB);
+	_screen->sega_drawClippedLine(8, 9, x + 1, y + 1, 62, 12, 0xAA);
+	_txt->printShadowedText(str, x + 14, y + 1, 0xFF, 0xCC, 64, 72, false);
+}
+
+void EoBEngine::drawMapPage(int level) {
+	int temp = 0;
+	_screen->sega_clearTextBuffer(0);
+	int cs = _screen->setFontStyles(_screen->_currentFont, _flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat | Font::kStyleNarrow1);
+	_txt->printShadowedText(_mapStrings3[level - 1], 0, 0, 0xCC, 0, 48, 16, false);
+	_screen->setFontStyles(_screen->_currentFont, cs);
+	_screen->sega_loadTextBufferToVRAM(0, 0x7920, 384);
+	SegaRenderer *r = _screen->sega_getRenderer();
+	r->fillRectWithTiles(0, 23, 8, 6, 2, 0x63C9, true);
+
+	Common::SeekableReadStreamEndian *in = _sres->resStreamEndian(hasLevelMap(level) ? 2 + level : 2);
+	r->loadStreamToVRAM(in, 0x5500, true);
+	delete in;
+	r->fillRectWithTiles(1, 3, 0, 26, 26, 0x2004, true);
+	r->fillRectWithTiles(0, 5, 6, 17, 17, 0x42A8, true);
+}
+
+void EoBEngine::drawMapSpots(int level, int animState) {
+	SegaAnimator *a = _screen->sega_getAnimator();
+	const EoBItem &m = _items[447 + level];
+	int curX = _currentBlock & 0x1F;
+	int curY = _currentBlock >> 5;
+	int mX = m.block & 0x1F;
+	int mY = m.block >> 5;
+
+	if (hasLevelMap(level)) {
+		if (!animState && level == _currentLevel)
+			a->initSprite(0, (curX << 2) + 48, (curY << 2) + 56, 0x6001, 0);
+		else
+			a->initSprite(0, 0x4000, 0, 0, 0);
+		a->initSprite(1, 0x4000, 0, 0, 0);
+	} else {
+		a->initSprite(0, 0x4000, 0, 0, 0);
+		if (level == _currentLevel)
+			a->initSprite(0, (curX << 2) + 48, (curY << 2) + 56, animState ? 0x2002 : 0x2001, 0);
+		a->initSprite(1, (mX << 2) + 48, (mY << 2) + 56, animState ? 0x2002 : 0x2003, 0);
+	}
+	a->update();
+}
+
 } // End of namespace Kyra
 
 #endif // ENABLE_EOB
diff --git a/engines/kyra/gui/saveload_eob.cpp b/engines/kyra/gui/saveload_eob.cpp
index 93115bb6f4..a6f85c6c66 100644
--- a/engines/kyra/gui/saveload_eob.cpp
+++ b/engines/kyra/gui/saveload_eob.cpp
@@ -48,7 +48,7 @@ Common::Error EoBCoreEngine::loadGameState(int slot) {
 	Common::SeekableSubReadStreamEndian in(saveFile, saveFile->pos(), saveFile->size(), !header.originalSave, DisposeAfterUse::YES);
 	_loading = true;
 
-	if (slot != -1 && _flags.gameID != Common::kPlatformSegaCD)
+	if (slot != -1)
 		_screen->fadeToBlack(10);
 
 	enableSysTimer(2);
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index e541d4b316..83e5ff4a21 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -669,7 +669,11 @@ void EoBCoreEngine::initButtonData() {
 		{ 116, 117, 0x1100, 320, 200, 1, 1, 2 },
 		{ 7, 0, 0x1100, 158, 121, 15, 10, 5 },
 		{ 0, 0, 0x1100, 146, 168, 32, 10, 0 },
-		{ 0, 0, 0x1100, 296, 56, 16, 16, 27 }
+		{ 0, 0, 0x1100, 296, 56, 16, 16, 27 },
+
+		{ 0, 0, 0x1100, 248, 152, 64, 14, -1 },
+		{ 0, 0, 0x1100, 248, 168, 64, 14, 1 },
+		{ 110, 0, 0x1100, 248, 184, 64, 14, 2 }
 	};
 
 	_buttonDefs = new EoBGuiButtonDef[ARRAYSIZE(buttonDefs)];
@@ -799,6 +803,7 @@ void EoBCoreEngine::initButtonData() {
 	EOB_CBI(1, 61);
 	EOB_CBN(1, clickedSpellbookScroll);
 	EOB_CBI(1, 21);
+	EOB_CBN(3, clickedButtonReturnIndex);
 #undef EOB_CBI
 #undef EOB_CBN
 }
@@ -1307,6 +1312,9 @@ void EoBEngine::initStaticResource() {
 	_invPattern = _staticres->loadRawDataBe16(kEoB1PatternTable3, temp);
 	_statsPattern = _staticres->loadRawDataBe16(kEoB1PatternTable4, temp);
 	_charTilesTable = _staticres->loadRawData(kEoB1CharTilesTable, temp);
+	_mapStrings1 = _staticres->loadStrings(kEoB1MapStrings1, temp);
+	_mapStrings2 = _staticres->loadStrings(kEoB1MapStrings2, temp);
+	_mapStrings3 = _staticres->loadStrings(kEoB1MapStrings3, temp);
 
 	// Build offset tables for door shapes encoding
 	if (_flags.platform == Common::kPlatformSegaCD) {
diff --git a/engines/kyra/sequence/sequences_eob.cpp b/engines/kyra/sequence/sequences_eob.cpp
index c2318d950b..ffa5a67611 100644
--- a/engines/kyra/sequence/sequences_eob.cpp
+++ b/engines/kyra/sequence/sequences_eob.cpp
@@ -2575,7 +2575,7 @@ void EoBEngine::seq_segaFinalCredits() {
 					pos++;
 
 				_screen->setFontStyles(_screen->_currentFont, styles);
-				_txt->printShadowedText(pos, 120 - (_screen->getTextWidth(pos) >> 1), 0, 0xFF, 0xCC, true);
+				_txt->printShadowedText(pos, 120 - (_screen->getTextWidth(pos) >> 1), 0, 0xFF, 0xCC, -1, -1, false);
 				curStr++;
 			}
 		} else {
@@ -2623,17 +2623,17 @@ void EoBEngine::seq_segaShowStats() {
 	int styles = _flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat;
 	int cs = _screen->setFontStyles(_screen->_currentFont, styles);
 
-	_txt->printShadowedText(_finBonusStrings[2], 90, 8, 0xFF, 0x00, false);
+	_txt->printShadowedText(_finBonusStrings[2], 90, 8, 0xFF, 0x00, -1, -1, false);
 
 	styles |= Font::kStyleNarrow2;
 	_screen->setFontStyles(_screen->_currentFont, styles);
 
-	_txt->printShadowedText(_finBonusStrings[3], 48, 28, 0xFF, 0x00, false);
-	_txt->printShadowedText(_finBonusStrings[4], 48, 40, 0xFF, 0x00, false);
-	_txt->printShadowedText(_finBonusStrings[5], 48, 52, 0xFF, 0x00, false);
-	_txt->printShadowedText(_finBonusStrings[6], 48, 64, 0xFF, 0x00, false);
-	_txt->printShadowedText(_finBonusStrings[7], 48, 76, 0xFF, 0x00, false);
-	_txt->printShadowedText(_finBonusStrings[8], 48, 88, 0xFF, 0x00, false);
+	_txt->printShadowedText(_finBonusStrings[3], 48, 28, 0xFF, 0x00, -1, -1, false);
+	_txt->printShadowedText(_finBonusStrings[4], 48, 40, 0xFF, 0x00, -1, -1, false);
+	_txt->printShadowedText(_finBonusStrings[5], 48, 52, 0xFF, 0x00, -1, -1, false);
+	_txt->printShadowedText(_finBonusStrings[6], 48, 64, 0xFF, 0x00, -1, -1, false);
+	_txt->printShadowedText(_finBonusStrings[7], 48, 76, 0xFF, 0x00, -1, -1, false);
+	_txt->printShadowedText(_finBonusStrings[8], 48, 88, 0xFF, 0x00, -1, -1, false);
 
 	styles &= ~(Font::kStyleNarrow2);
 	_screen->setFontStyles(_screen->_currentFont, styles);
@@ -2646,12 +2646,12 @@ void EoBEngine::seq_segaShowStats() {
 			++specialSearches;
 	}
 
-	_txt->printShadowedText(Common::String::format("%u:%02u:%02u", _totalPlaySecs / 3600, _totalPlaySecs / 60, _totalPlaySecs).c_str(), 148, 28, 0xFF, 0x00, false);
-	_txt->printShadowedText(Common::String::format("%u", _totalEnemiesKilled).c_str(), 148, 40, 0xFF, 0x00, false);
-	_txt->printShadowedText(Common::String::format("%u", _totalSteps).c_str(), 148, 52, 0xFF, 0x00, false);
-	_txt->printShadowedText(Common::String::format("%u(%u%%)", partyArrows, partyArrows * 100 / 26).c_str(), 148, 64, 0xFF, 0x00, false);
-	_txt->printShadowedText(Common::String::format("%u(%u%%)", numMaps, numMaps * 100 / 12).c_str(), 148, 76, 0xFF, 0x00, false);
-	_txt->printShadowedText(Common::String::format("%u(%u%%)", specialSearches, specialSearches * 100 / 12).c_str(), 148, 88, 0xFF, 0x00, false);
+	_txt->printShadowedText(Common::String::format("%u:%02u:%02u", _totalPlaySecs / 3600, _totalPlaySecs / 60, _totalPlaySecs).c_str(), 148, 28, 0xFF, 0x00, -1, -1, false);
+	_txt->printShadowedText(Common::String::format("%u", _totalEnemiesKilled).c_str(), 148, 40, 0xFF, 0x00, -1, -1, false);
+	_txt->printShadowedText(Common::String::format("%u", _totalSteps).c_str(), 148, 52, 0xFF, 0x00, -1, -1, false);
+	_txt->printShadowedText(Common::String::format("%u(%u%%)", partyArrows, partyArrows * 100 / 26).c_str(), 148, 64, 0xFF, 0x00, -1, -1, false);
+	_txt->printShadowedText(Common::String::format("%u(%u%%)", numMaps, numMaps * 100 / 12).c_str(), 148, 76, 0xFF, 0x00, -1, -1, false);
+	_txt->printShadowedText(Common::String::format("%u(%u%%)", specialSearches, specialSearches * 100 / 12).c_str(), 148, 88, 0xFF, 0x00, -1, -1, false);
 
 	if (checkScriptFlags(0x1FFE)) {
 		const char pwgen[] = "A15BZFQ3CDXYEKNM279GHIUSJLR84P6T";
@@ -2665,9 +2665,9 @@ void EoBEngine::seq_segaShowStats() {
 		}
 		password[5] = pwgen[v];
 
-		_txt->printShadowedText(_finBonusStrings[0], 30, 108, 0x22, 0x00, false);
-		_txt->printShadowedText(_finBonusStrings[1], 30, 132, 0x22, 0x00, false);
-		_txt->printShadowedText(password, 140, 156, 0xFF, 0x00, false);
+		_txt->printShadowedText(_finBonusStrings[0], 30, 108, 0x22, 0x00, -1, -1, false);
+		_txt->printShadowedText(_finBonusStrings[1], 30, 132, 0x22, 0x00, -1, -1, false);
+		_txt->printShadowedText(password, 140, 156, 0xFF, 0x00, -1, -1, false);
 	}
 
 	_screen->sega_loadTextBufferToVRAM(0, 32, 28160);
diff --git a/engines/kyra/text/text_eob_segacd.cpp b/engines/kyra/text/text_eob_segacd.cpp
index 6cb139f1bc..f1e61cd685 100644
--- a/engines/kyra/text/text_eob_segacd.cpp
+++ b/engines/kyra/text/text_eob_segacd.cpp
@@ -37,7 +37,7 @@ TextDisplayer_SegaCD::TextDisplayer_SegaCD(EoBEngine *engine, Screen_EoB *scr) :
 TextDisplayer_SegaCD::~TextDisplayer_SegaCD() {
 }
 
-void TextDisplayer_SegaCD::printShadowedText(const char *str, int x, int y, int textColor, int shadowColor, bool noScreenUpdate) {
+void TextDisplayer_SegaCD::printShadowedText(const char *str, int x, int y, int textColor, int shadowColor, int pitchW, int pitchH, bool screenUpdate) {
 	const ScreenDim *s = &_dimTable[_curDim];
 	if (x == -1)
 		x = s->sx;
@@ -47,18 +47,22 @@ void TextDisplayer_SegaCD::printShadowedText(const char *str, int x, int y, int
 		textColor = s->unk8;
 	if (shadowColor == -1)
 		shadowColor = 0;
+	if (pitchW == -1)
+		pitchW = s->w;
+	if (pitchH == -1)
+		pitchH = s->h;
 
-	_screen->setTextMarginRight(s->w);
-	_screen->printShadedText(str, x, y, textColor, 0, shadowColor, s->w >> 3);
+	_screen->setTextMarginRight(pitchW);
+	_screen->printShadedText(str, x, y, textColor, 0, shadowColor, pitchW >> 3);
 
-	if (noScreenUpdate)
+	if (!screenUpdate)
 		return;
 
 	if (s->unkE) {
-		for (int i = 0; i < s->h >> 3; ++i)
-			_screen->sega_loadTextBufferToVRAM(i * (s->w >> 3), (s->unkC & 0x7FF) << 5, (s->w * s->h) >> 1);
+		for (int i = 0; i < pitchH >> 3; ++i)
+			_screen->sega_loadTextBufferToVRAM(i * (pitchW >> 3), (s->unkC & 0x7FF) << 5, (pitchW * pitchH) >> 1);
 	} else {
-		_screen->sega_loadTextBufferToVRAM(0, (s->unkC & 0x7FF) << 5, (s->w * s->h) >> 1);
+		_screen->sega_loadTextBufferToVRAM(0, (s->unkC & 0x7FF) << 5, (pitchW * pitchH) >> 1);
 	}	
 }
 
@@ -78,6 +82,14 @@ void TextDisplayer_SegaCD::displayText(char *str, ...) {
 	int posX = _curPosX;
 	bool updated = false;
 
+	va_list args;
+	va_start(args, str);
+	int tc = va_arg(args, int);
+	va_end(args);
+
+	if (tc != -1)
+		SWAP(_textColor, tc);
+
 	for (const char *pos = str; *pos; updated = false) {
 		uint8 cmd = fetchCharacter(tmp, pos);
 
@@ -119,6 +131,9 @@ void TextDisplayer_SegaCD::displayText(char *str, ...) {
 	if (!updated)
 		printShadowedText("", _curPosX, _curPosY, _textColor);
 
+	if (tc != -1)
+		SWAP(_textColor, tc);
+
 	_renderer->render(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);
diff --git a/engines/kyra/text/text_eob_segacd.h b/engines/kyra/text/text_eob_segacd.h
index a3b4d7cab5..a7a721a02b 100644
--- a/engines/kyra/text/text_eob_segacd.h
+++ b/engines/kyra/text/text_eob_segacd.h
@@ -37,7 +37,7 @@ public:
 	TextDisplayer_SegaCD(EoBEngine *engine, Screen_EoB *scr);
 	virtual ~TextDisplayer_SegaCD();
 
-	void printShadowedText(const char *str, int x = -1, int y = -1, int textColor = -1, int shadowColor = -1, bool noScreenUpdate = false) override;
+	void printShadowedText(const char *str, int x = -1, int y = -1, int textColor = -1, int shadowColor = -1, int pitchW = -1, int pitchH = -1, bool screenUpdate = true) override;
 	int clearDim(int dim) override;
 
 private:
diff --git a/engines/kyra/text/text_rpg.cpp b/engines/kyra/text/text_rpg.cpp
index d76326050b..1f81c8b4f1 100644
--- a/engines/kyra/text/text_rpg.cpp
+++ b/engines/kyra/text/text_rpg.cpp
@@ -163,7 +163,7 @@ void TextDisplayer_rpg::displayText(char *str, ...) {
 				strcpy(_scriptParaString, Common::String::format("%d", va_arg(args, int)).c_str());
 				_tempString2 = _scriptParaString;
 			} else if (a == 's') {
-				_tempString2 = va_arg(args, char *);
+				_tempString2 = va_arg(args, char*);
 			} else {
 				break;
 			}
@@ -578,7 +578,7 @@ void TextDisplayer_rpg::printMessage(const char *str, int textColor, ...) {
 	vsnprintf(_dialogueBuffer, kEoBTextBufferSize - 1, str, args);
 	va_end(args);
 
-	displayText(_dialogueBuffer);
+	displayText(_dialogueBuffer, textColor);
 
 	if (_vm->game() != GI_EOB1)
 		_textDimData[_screen->curDimIndex()].color1 = tc;
diff --git a/engines/kyra/text/text_rpg.h b/engines/kyra/text/text_rpg.h
index 8b39c58269..6e18a28801 100644
--- a/engines/kyra/text/text_rpg.h
+++ b/engines/kyra/text/text_rpg.h
@@ -42,7 +42,7 @@ public:
 	void printDialogueText(int stringId, const char *pageBreakString);
 	void printDialogueText(const char *str, bool wait = false);
 	void printMessage(const char *str, int textColor = -1, ...);
-	virtual void printShadowedText(const char *str, int x = -1, int y = -1, int textColor = -1, int shadowColor = -1, bool noScreenUpdate = false) {}
+	virtual void printShadowedText(const char *str, int x = -1, int y = -1, int textColor = -1, int shadowColor = -1, int pitchW = -1, int pitchH = -1, bool screenUpdate = true) {}
 
 	virtual int clearDim(int dim);
 	void clearCurDim();


Commit: 5bffb5b9def0bc86ecdec8f03a2fce9ef138180f
    https://github.com/scummvm/scummvm/commit/5bffb5b9def0bc86ecdec8f03a2fce9ef138180f
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:14+02:00

Commit Message:
KYRA: (EOB/SegaCD) - fix parchment display

Changed paths:
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eob.h
    engines/kyra/engine/eobcommon.h
    engines/kyra/graphics/screen_eob.cpp
    engines/kyra/graphics/screen_eob.h
    engines/kyra/graphics/screen_eob_segacd.cpp
    engines/kyra/gui/gui_eob_segacd.cpp
    engines/kyra/sequence/sequences_eob.cpp
    engines/kyra/text/text_eob_segacd.cpp
    engines/kyra/text/text_eob_segacd.h
    engines/kyra/text/text_rpg.h


diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index a83817b1d2..82cc62cde0 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -276,7 +276,7 @@ void EoBEngine::startupNew() {
 	_screen->selectPC98Palette(0, _screen->getPalette(0));
 	if (_flags.platform == Common::kPlatformSegaCD) {
 		_screen->sega_selectPalette(4, 0);
-		_screen->sega_selectPalette(7, 3);
+		_screen->sega_selectPalette(8, 2);
 		makeNameShapes();
 		_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 40, 28, 0x2000);
 		_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 28, 0x2000);
@@ -299,7 +299,7 @@ void EoBEngine::startupLoad() {
 
 	if (_flags.platform == Common::kPlatformSegaCD) {
 		_screen->sega_selectPalette(4, 0);
-		_screen->sega_selectPalette(7, 3);
+		_screen->sega_selectPalette(8, 2);
 		_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 40, 28, 0x2000);
 		_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 28, 0x2000);
 		_txt->clearDim(0);
@@ -958,6 +958,79 @@ void EoBEngine::snd_updateLevelScore() {
 	snd_playSong(track);
 }
 
+void EoBEngine::displayParchment(int id) {
+	if (_flags.platform != Common::kPlatformSegaCD) {
+		EoBCoreEngine::displayParchment(id);
+		return;
+	}
+
+	if (id < 46 || id > 50)
+		return;
+
+	uint32 startTime = _system->getMillis();
+	disableSysTimer(2);
+
+	_screen->sega_fadeToBlack(2);
+
+	int temp = 0;
+	const char *const *strings = _staticres->loadStrings(kEoB1ParchmentStrings, temp);
+
+	_sceneShakeCountdown = 0;
+	for (int i = 0; i < 6; i++) {
+		if (!testCharacter(i, 1))
+			continue;
+		_characters[i].damageTaken = 0;
+		_characters[i].slotStatus[0] = _characters[i].slotStatus[1] = _characters[i].gfxUpdateCountdown = 0;
+		gui_drawCharPortraitWithStats(i);
+	}
+
+	SegaRenderer *r = _screen->sega_getRenderer();
+	r->fillRectWithTiles(0, 0, 0, 40, 28, 0);
+	r->fillRectWithTiles(1, 0, 0, 40, 28, 0);
+	_screen->sega_getAnimator()->clearSprites();
+	_screen->sega_getAnimator()->update();
+	_screen->sega_selectPalette(54, 2);
+	int cs = _screen->setFontStyles(_screen->_currentFont, _flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth | Font::kStyleNarrow2 : Font::kStyleForceTwoByte | Font::kStyleFat | Font::kStyleNarrow2);
+
+	snd_stopSound();
+	uint8 *data = _res->fileData("LT", 0);
+
+	int numPages = (id == 46) ? 3 : 1;
+	int curPage = (id == 46) ? 4 : id - 47;
+	for (int i = 0; i < numPages && !shouldQuit(); ++i) {
+		_screen->sega_copyToTextBuffer(data, 22464);
+		_txt->printShadowedText(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);
+
+		_screen->sega_fadeToNeutral(1);
+
+		resetSkipFlag();
+		_allowSkip = true;
+		while (!(shouldQuit() || skipFlag()))
+			delay(20);
+		_allowSkip = false;
+		resetSkipFlag();
+
+		_screen->sega_fadeToBlack(1);
+	}
+
+	delete[] data;
+	_screen->setFontStyles(_screen->_currentFont, cs);
+	setLevelPalettes(_currentLevel);
+	gui_drawPlayField(true);
+	gui_drawAllCharPortraitsWithStats();
+	snd_playLevelScore();
+
+	enableSysTimer(2);
+	_totalPlaySecs += ((_system->getMillis() - startTime) / 1000);
+}
+
+void EoBEngine::drawParchmentPage(int page) {
+	
+}
+
 bool EoBEngine::checkPartyStatusExtra() {
 	_screen->copyPage(0, Screen_EoB::kDefeatMsgBackupPage);
 	int cd = _screen->curDimIndex();
diff --git a/engines/kyra/engine/eob.h b/engines/kyra/engine/eob.h
index adddddd09c..64d92a7ab1 100644
--- a/engines/kyra/engine/eob.h
+++ b/engines/kyra/engine/eob.h
@@ -176,6 +176,8 @@ private:
 	int _levelCurTrack;
 
 	// Misc
+	void displayParchment(int id) override;
+	void drawParchmentPage(int page);
 	bool checkPartyStatusExtra() override;
 	int resurrectionSelectDialogue() override;
 	void healParty();
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index 635323ea36..dd52462e72 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -900,7 +900,7 @@ protected:
 	// misc
 	void delay(uint32 millis, bool doUpdate = false, bool isMainLoop = false) override;
 
-	void displayParchment(int id);
+	virtual void displayParchment(int id);
 	int countResurrectionCandidates();
 
 	void seq_portal();
diff --git a/engines/kyra/graphics/screen_eob.cpp b/engines/kyra/graphics/screen_eob.cpp
index 29d82e2333..25a507c476 100644
--- a/engines/kyra/graphics/screen_eob.cpp
+++ b/engines/kyra/graphics/screen_eob.cpp
@@ -258,13 +258,13 @@ 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) {
+	if (_isSegaCD && shadowCol) {
 		printText(string, x + 1, y + 1, shadowCol, 0, pitch);
-	} else if (_vm->gameFlags().lang != Common::JA_JPN) {
+	} else if (!_isSegaCD && _vm->gameFlags().lang != Common::JA_JPN) {
 		printText(string, x - 1, y, shadowCol, col2);
 		printText(string, x, y + 1, shadowCol, 0);
 		printText(string, x - 1, y + 1, shadowCol, 0);
-	} else if (col2) {
+	} else if (!_isSegaCD && col2) {
 		fillRect(x, y, x + getTextWidth(string) - 1, y + getFontHeight() - 1, col2);
 	}
 
diff --git a/engines/kyra/graphics/screen_eob.h b/engines/kyra/graphics/screen_eob.h
index 5e988d6070..9cfef9b863 100644
--- a/engines/kyra/graphics/screen_eob.h
+++ b/engines/kyra/graphics/screen_eob.h
@@ -147,6 +147,7 @@ public:
 	void sega_clearTextBuffer(uint8 col);
 	void sega_clearTextBufferLine(uint16 y, uint16 lineHeight, uint16 pitch, uint8 col);
 	void sega_copyTextBufferLine(uint16 srcY, uint16 dstY, uint16 lineHeight, uint16 pitch);
+	void sega_copyToTextBuffer(const uint8 *src, uint16 size);
 	void sega_drawTextBox(int pW, int pH, int x, int y, int w, int h, uint8 color1, uint8 color2);
 	void sega_loadTextBufferToVRAM(uint16 srcOffset, uint16 addr, int size);
 	void sega_gfxScale(uint8 *out, uint16 w, uint16 h, uint16 pitch, const uint8 *in, const uint16 *stampMap, const uint16 *traceVectors);
diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index e6993f7fd6..e4e6c9918d 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -251,6 +251,11 @@ void Screen_EoB::sega_copyTextBufferLine(uint16 srcY, uint16 dstY, uint16 lineHe
 	}
 }
 
+void Screen_EoB::sega_copyToTextBuffer(const uint8 *src, uint16 size) {
+	assert(size <= _textRenderBufferSize);
+	memcpy(_textRenderBuffer, src, size);
+}
+
 void Screen_EoB::sega_drawTextBox(int pW, int pH, int x, int y, int w, int h, uint8 color1, uint8 color2) {
 	sega_drawClippedLine(26, 5, x, y, w, 1, color1);
 	sega_drawClippedLine(26, 5, x, y + h - 1, w, 1, color1);
diff --git a/engines/kyra/gui/gui_eob_segacd.cpp b/engines/kyra/gui/gui_eob_segacd.cpp
index d9213351b7..8091b0828e 100644
--- a/engines/kyra/gui/gui_eob_segacd.cpp
+++ b/engines/kyra/gui/gui_eob_segacd.cpp
@@ -41,7 +41,7 @@ void EoBEngine::gui_drawPlayField(bool refresh) {
 
 	_screen->sega_fadeToBlack(_loading ? 0 : 1);
 	_screen->sega_selectPalette(6, 1);
-	_screen->sega_selectPalette(8, 2);
+	_screen->sega_selectPalette(7, 3);
 
 	// transposeScreenOutputY(8);
 	_txt->clearDim(0);
@@ -239,7 +239,7 @@ void EoBEngine::gui_displayMap() {
 	_screen->sega_clearTextBuffer(0);
 
 	_screen->sega_clearTextBuffer(0);
-	_txt->printShadowedText(_mapStrings2[_currentLevel - 1], 0, 0, 0xFF, 0, 64, 16, false);
+	_txt->printShadowedText(_mapStrings2[_currentLevel - 1], 0, 0, 0xFF, 0, 64, 16, 0, false);
 	_screen->sega_loadTextBufferToVRAM(0, 0x7C20, 512);
 	r->fillRectWithTiles(0, 31, 16, 8, 2, 0x63E1, true);
 
@@ -438,14 +438,14 @@ void EoBEngine::drawMapButton(const char *str, int x, int y) {
 	_screen->sega_drawClippedLine(8, 9, x, y, 64, 14, 0x99);
 	_screen->sega_drawClippedLine(8, 9, x, y + 1, 63, 13, 0xBB);
 	_screen->sega_drawClippedLine(8, 9, x + 1, y + 1, 62, 12, 0xAA);
-	_txt->printShadowedText(str, x + 14, y + 1, 0xFF, 0xCC, 64, 72, false);
+	_txt->printShadowedText(str, x + 14, y + 1, 0xFF, 0xCC, 64, 72, 0, false);
 }
 
 void EoBEngine::drawMapPage(int level) {
 	int temp = 0;
 	_screen->sega_clearTextBuffer(0);
 	int cs = _screen->setFontStyles(_screen->_currentFont, _flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat | Font::kStyleNarrow1);
-	_txt->printShadowedText(_mapStrings3[level - 1], 0, 0, 0xCC, 0, 48, 16, false);
+	_txt->printShadowedText(_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();
diff --git a/engines/kyra/sequence/sequences_eob.cpp b/engines/kyra/sequence/sequences_eob.cpp
index ffa5a67611..04c323d9f3 100644
--- a/engines/kyra/sequence/sequences_eob.cpp
+++ b/engines/kyra/sequence/sequences_eob.cpp
@@ -2575,7 +2575,7 @@ void EoBEngine::seq_segaFinalCredits() {
 					pos++;
 
 				_screen->setFontStyles(_screen->_currentFont, styles);
-				_txt->printShadowedText(pos, 120 - (_screen->getTextWidth(pos) >> 1), 0, 0xFF, 0xCC, -1, -1, false);
+				_txt->printShadowedText(pos, 120 - (_screen->getTextWidth(pos) >> 1), 0, 0xFF, 0xCC, -1, -1, 0, false);
 				curStr++;
 			}
 		} else {
@@ -2623,17 +2623,17 @@ void EoBEngine::seq_segaShowStats() {
 	int styles = _flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat;
 	int cs = _screen->setFontStyles(_screen->_currentFont, styles);
 
-	_txt->printShadowedText(_finBonusStrings[2], 90, 8, 0xFF, 0x00, -1, -1, false);
+	_txt->printShadowedText(_finBonusStrings[2], 90, 8, 0xFF, 0x00, -1, -1, 0, false);
 
 	styles |= Font::kStyleNarrow2;
 	_screen->setFontStyles(_screen->_currentFont, styles);
 
-	_txt->printShadowedText(_finBonusStrings[3], 48, 28, 0xFF, 0x00, -1, -1, false);
-	_txt->printShadowedText(_finBonusStrings[4], 48, 40, 0xFF, 0x00, -1, -1, false);
-	_txt->printShadowedText(_finBonusStrings[5], 48, 52, 0xFF, 0x00, -1, -1, false);
-	_txt->printShadowedText(_finBonusStrings[6], 48, 64, 0xFF, 0x00, -1, -1, false);
-	_txt->printShadowedText(_finBonusStrings[7], 48, 76, 0xFF, 0x00, -1, -1, false);
-	_txt->printShadowedText(_finBonusStrings[8], 48, 88, 0xFF, 0x00, -1, -1, false);
+	_txt->printShadowedText(_finBonusStrings[3], 48, 28, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadowedText(_finBonusStrings[4], 48, 40, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadowedText(_finBonusStrings[5], 48, 52, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadowedText(_finBonusStrings[6], 48, 64, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadowedText(_finBonusStrings[7], 48, 76, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadowedText(_finBonusStrings[8], 48, 88, 0xFF, 0x00, -1, -1, 0, false);
 
 	styles &= ~(Font::kStyleNarrow2);
 	_screen->setFontStyles(_screen->_currentFont, styles);
@@ -2646,12 +2646,12 @@ void EoBEngine::seq_segaShowStats() {
 			++specialSearches;
 	}
 
-	_txt->printShadowedText(Common::String::format("%u:%02u:%02u", _totalPlaySecs / 3600, _totalPlaySecs / 60, _totalPlaySecs).c_str(), 148, 28, 0xFF, 0x00, -1, -1, false);
-	_txt->printShadowedText(Common::String::format("%u", _totalEnemiesKilled).c_str(), 148, 40, 0xFF, 0x00, -1, -1, false);
-	_txt->printShadowedText(Common::String::format("%u", _totalSteps).c_str(), 148, 52, 0xFF, 0x00, -1, -1, false);
-	_txt->printShadowedText(Common::String::format("%u(%u%%)", partyArrows, partyArrows * 100 / 26).c_str(), 148, 64, 0xFF, 0x00, -1, -1, false);
-	_txt->printShadowedText(Common::String::format("%u(%u%%)", numMaps, numMaps * 100 / 12).c_str(), 148, 76, 0xFF, 0x00, -1, -1, false);
-	_txt->printShadowedText(Common::String::format("%u(%u%%)", specialSearches, specialSearches * 100 / 12).c_str(), 148, 88, 0xFF, 0x00, -1, -1, false);
+	_txt->printShadowedText(Common::String::format("%u:%02u:%02u", _totalPlaySecs / 3600, _totalPlaySecs / 60, _totalPlaySecs).c_str(), 148, 28, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadowedText(Common::String::format("%u", _totalEnemiesKilled).c_str(), 148, 40, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadowedText(Common::String::format("%u", _totalSteps).c_str(), 148, 52, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadowedText(Common::String::format("%u(%u%%)", partyArrows, partyArrows * 100 / 26).c_str(), 148, 64, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadowedText(Common::String::format("%u(%u%%)", numMaps, numMaps * 100 / 12).c_str(), 148, 76, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadowedText(Common::String::format("%u(%u%%)", specialSearches, specialSearches * 100 / 12).c_str(), 148, 88, 0xFF, 0x00, -1, -1, 0, false);
 
 	if (checkScriptFlags(0x1FFE)) {
 		const char pwgen[] = "A15BZFQ3CDXYEKNM279GHIUSJLR84P6T";
@@ -2665,9 +2665,9 @@ void EoBEngine::seq_segaShowStats() {
 		}
 		password[5] = pwgen[v];
 
-		_txt->printShadowedText(_finBonusStrings[0], 30, 108, 0x22, 0x00, -1, -1, false);
-		_txt->printShadowedText(_finBonusStrings[1], 30, 132, 0x22, 0x00, -1, -1, false);
-		_txt->printShadowedText(password, 140, 156, 0xFF, 0x00, -1, -1, false);
+		_txt->printShadowedText(_finBonusStrings[0], 30, 108, 0x22, 0x00, -1, -1, 0, false);
+		_txt->printShadowedText(_finBonusStrings[1], 30, 132, 0x22, 0x00, -1, -1, 0, false);
+		_txt->printShadowedText(password, 140, 156, 0xFF, 0x00, -1, -1, 0, false);
 	}
 
 	_screen->sega_loadTextBufferToVRAM(0, 32, 28160);
@@ -2682,7 +2682,7 @@ void EoBEngine::seq_segaShowStats() {
 	_screen->sega_fadeToNeutral(3);
 
 	resetSkipFlag();
-	_allowSkip = false;
+	_allowSkip = true;
 
 	while (!(shouldQuit() || skipFlag()))
 		delay(20);
diff --git a/engines/kyra/text/text_eob_segacd.cpp b/engines/kyra/text/text_eob_segacd.cpp
index f1e61cd685..d7ecc22a25 100644
--- a/engines/kyra/text/text_eob_segacd.cpp
+++ b/engines/kyra/text/text_eob_segacd.cpp
@@ -37,7 +37,7 @@ TextDisplayer_SegaCD::TextDisplayer_SegaCD(EoBEngine *engine, Screen_EoB *scr) :
 TextDisplayer_SegaCD::~TextDisplayer_SegaCD() {
 }
 
-void TextDisplayer_SegaCD::printShadowedText(const char *str, int x, int y, int textColor, int shadowColor, int pitchW, int pitchH, bool screenUpdate) {
+void TextDisplayer_SegaCD::printShadowedText(const char *str, int x, int y, int textColor, int shadowColor, int pitchW, int pitchH, int marginRight, bool screenUpdate) {
 	const ScreenDim *s = &_dimTable[_curDim];
 	if (x == -1)
 		x = s->sx;
@@ -52,7 +52,7 @@ void TextDisplayer_SegaCD::printShadowedText(const char *str, int x, int y, int
 	if (pitchH == -1)
 		pitchH = s->h;
 
-	_screen->setTextMarginRight(pitchW);
+	_screen->setTextMarginRight(pitchW - marginRight);
 	_screen->printShadedText(str, x, y, textColor, 0, shadowColor, pitchW >> 3);
 
 	if (!screenUpdate)
diff --git a/engines/kyra/text/text_eob_segacd.h b/engines/kyra/text/text_eob_segacd.h
index a7a721a02b..01b58dbfc0 100644
--- a/engines/kyra/text/text_eob_segacd.h
+++ b/engines/kyra/text/text_eob_segacd.h
@@ -37,7 +37,7 @@ public:
 	TextDisplayer_SegaCD(EoBEngine *engine, Screen_EoB *scr);
 	virtual ~TextDisplayer_SegaCD();
 
-	void printShadowedText(const char *str, int x = -1, int y = -1, int textColor = -1, int shadowColor = -1, int pitchW = -1, int pitchH = -1, bool screenUpdate = true) override;
+	void printShadowedText(const char *str, int x = -1, int y = -1, int textColor = -1, int shadowColor = -1, int pitchW = -1, int pitchH = -1, int marginRight = 0, bool screenUpdate = true) override;
 	int clearDim(int dim) override;
 
 private:
diff --git a/engines/kyra/text/text_rpg.h b/engines/kyra/text/text_rpg.h
index 6e18a28801..9217420136 100644
--- a/engines/kyra/text/text_rpg.h
+++ b/engines/kyra/text/text_rpg.h
@@ -42,7 +42,7 @@ public:
 	void printDialogueText(int stringId, const char *pageBreakString);
 	void printDialogueText(const char *str, bool wait = false);
 	void printMessage(const char *str, int textColor = -1, ...);
-	virtual void printShadowedText(const char *str, int x = -1, int y = -1, int textColor = -1, int shadowColor = -1, int pitchW = -1, int pitchH = -1, bool screenUpdate = true) {}
+	virtual void printShadowedText(const char *str, int x = -1, int y = -1, int textColor = -1, int shadowColor = -1, int pitchW = -1, int pitchH = -1, int marginRight = 0, bool screenUpdate = true) {}
 
 	virtual int clearDim(int dim);
 	void clearCurDim();


Commit: b48a37dc2f33d2599e4a65dee7eddef511eeb5bc
    https://github.com/scummvm/scummvm/commit/b48a37dc2f33d2599e4a65dee7eddef511eeb5bc
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:15+02:00

Commit Message:
KYRA: (EOB/SegaCD) - fix various sequences and gameover screen

(xdeath sequence, portal sequence, npc sequences)

Changed paths:
    engines/kyra/engine/darkmoon.cpp
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eob.h
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h
    engines/kyra/engine/items_eob.cpp
    engines/kyra/engine/kyra_rpg.cpp
    engines/kyra/engine/kyra_rpg.h
    engines/kyra/graphics/screen_eob_segacd.cpp
    engines/kyra/gui/gui_eob_segacd.cpp
    engines/kyra/gui/saveload_eob.cpp
    engines/kyra/resource/staticres_eob.cpp
    engines/kyra/resource/staticres_lol.cpp
    engines/kyra/resource/staticres_rpg.cpp
    engines/kyra/script/script_eob.cpp
    engines/kyra/sequence/seqplayer_eob_segacd.cpp
    engines/kyra/sequence/sequences_eob.cpp
    engines/kyra/text/text_eob_segacd.cpp
    engines/kyra/text/text_eob_segacd.h
    engines/kyra/text/text_rpg.cpp
    engines/kyra/text/text_rpg.h


diff --git a/engines/kyra/engine/darkmoon.cpp b/engines/kyra/engine/darkmoon.cpp
index 46b74f2a46..1c608339aa 100644
--- a/engines/kyra/engine/darkmoon.cpp
+++ b/engines/kyra/engine/darkmoon.cpp
@@ -169,7 +169,7 @@ void DarkMoonEngine::runNpcDialogue(int npcIndex) {
 		gui_drawDialogueBox();
 
 		_txt->printDialogueText(4, 0);
-		int r = runDialogue(-1, 2, _npcStrings[0][0], _npcStrings[0][1]) - 1;
+		int r = runDialogue(-1, 2, -1, _npcStrings[0][0], _npcStrings[0][1]) - 1;
 
 		if (r == 0) {
 			snd_stopSound();
@@ -185,7 +185,7 @@ void DarkMoonEngine::runNpcDialogue(int npcIndex) {
 		gui_drawDialogueBox();
 
 		_txt->printDialogueText(8, 0);
-		int r = runDialogue(-1, 2, _npcStrings[1][0], _npcStrings[1][1]) - 1;
+		int r = runDialogue(-1, 2, -1, _npcStrings[1][0], _npcStrings[1][1]) - 1;
 
 		if (r == 0) {
 			if (rollDice(1, 2, -1))
@@ -717,7 +717,7 @@ int DarkMoonEngine::resurrectionSelectDialogue() {
 	_rrNames[_rrCount] = _abortStrings[0];
 	_rrId[_rrCount++] = 99;
 
-	int r = _rrId[runDialogue(-1, 9, _rrNames[0], _rrNames[1], _rrNames[2], _rrNames[3], _rrNames[4], _rrNames[5], _rrNames[6], _rrNames[7], _rrNames[8]) - 1];
+	int r = _rrId[runDialogue(-1, 9, -1, _rrNames[0], _rrNames[1], _rrNames[2], _rrNames[3], _rrNames[4], _rrNames[5], _rrNames[6], _rrNames[7], _rrNames[8]) - 1];
 	if (r == 99)
 		return 0;
 
@@ -745,7 +745,7 @@ int DarkMoonEngine::charSelectDialogue() {
 
 	namesList[cnt++] = _abortStrings[0];
 
-	int r = runDialogue(-1, 7, namesList[0], namesList[1], namesList[2], namesList[3], namesList[4], namesList[5], namesList[6]) - 1;
+	int r = runDialogue(-1, 7, -1, namesList[0], namesList[1], namesList[2], namesList[3], namesList[4], namesList[5], namesList[6]) - 1;
 	if (r == cnt - 1)
 		return 99;
 
diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index 82cc62cde0..4bd7c56937 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -214,6 +214,7 @@ void EoBEngine::loadItemsAndDecorationsShapes() {
 
 	loadAndConvertShapes(1, 0, _smallItemShapes, _numSmallItemShapes, 32, 24, 768);
 	loadAndConvertShapes(2, 0, _largeItemShapes, _numLargeItemShapes, 64, 24, 1472);
+	loadAndConvertShapes(3, 0, _sparkShapes, 3, 16, 16, 128);
 	loadAndConvertShapes(11, 0, _thrownItemShapes, _numThrownItemShapes, 32, 24, 768);
 	int offset1 = 0, offset2 = 0;
 	for (int i = 0; i < 3; ++i) {
@@ -230,16 +231,7 @@ void EoBEngine::loadItemsAndDecorationsShapes() {
 	loadSpritesAndEncodeToShapes(5, 480, _weaponSlotShapes, 6, 32, 16);
 	loadSpritesAndEncodeToShapes(6, 0, _invSmallDigits, 32, 16, 8);
 
-	/*
-	// CAMP MENU
-	str = _sres->resStreamEndian(8);
-	_screen->sega_getRenderer()->loadStreamToVRAM(str, 0x20, true);
-	delete str;
-	_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 22, 15, 0);
-	_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 22, 21, 0x4001, true);
-	_screen->sega_getRenderer()->render(0);
-	_screen->sega_selectPalette(40, 2, true);
-	*/
+
 
 	int cp = _screen->setCurPage(Screen_EoB::kSegaInitShapesPage);
 	for (int i = 0; i < 4; ++i)
@@ -366,23 +358,27 @@ void EoBEngine::encodeDrawNpcSeqShape(int npcIndex, int drawX, int drawY) {
 	delete[] shp;
 }
 
-#define DLG2(txt, buttonstr) (runDialogue(txt, 2, _npcStrings[buttonstr][0], _npcStrings[buttonstr][1]) - 1)
-#define DLG3(txt, buttonstr) (runDialogue(txt, 3, _npcStrings[buttonstr][0], _npcStrings[buttonstr][1], _npcStrings[buttonstr][2]) - 1)
-#define DLG2A3(cond, txt, buttonstr1, buttonstr2) ((cond) ? (DLG2(txt, buttonstr1) ? 2 : 0) : DLG3(txt, buttonstr2))
-#define TXT(txt) _txt->printDialogueText(txt, _moreStrings[0])
+#define DLG2(txt, buttonstr, repeat) ((_flags.platform == Common::kPlatformSegaCD && repeat ? runDialogue(txt, 3, 3, _npcStrings[buttonstr][0], _npcStrings[buttonstr][1], _npcStrings[buttonstr][2]) : runDialogue(txt, 2, -1, _npcStrings[buttonstr][0], _npcStrings[buttonstr][1])) - 1)
+#define DLG3(txt, buttonstr, repeat) ((_flags.platform == Common::kPlatformSegaCD && repeat ? runDialogue(txt, 4, 4, _npcStrings[buttonstr][0], _npcStrings[buttonstr][1], _npcStrings[buttonstr][2], _npcStrings[buttonstr][3]) : runDialogue(txt, 3, -1, _npcStrings[buttonstr][0], _npcStrings[buttonstr][1], _npcStrings[buttonstr][2])) - 1)
+#define DLG2A3(cond, txt, buttonstr1, buttonstr2) ((cond) ? (DLG2(txt, buttonstr1, 0) ? 2 : 0) : DLG3(txt, buttonstr2, 0))
+#define TXT(txt) _txt->printDialogueText(txt, _moreStrings[0], _flags.platform == Common::kPlatformSegaCD ? _moreStrings[1] : 0)
+#define TXTNB(txt) _txt->printDialogueText(txt, _flags.platform == Common::kPlatformSegaCD ? 0 : _moreStrings[0])
+#define JOIN(npc, txt_query, txt_conf, txt_deny) npcJoinDialogue(npc, txt_query, _flags.platform == Common::kPlatformSegaCD ? -1 : txt_conf, _flags.platform == Common::kPlatformSegaCD ? -1 : txt_deny)
 
 void EoBEngine::runNpcDialogue(int npcIndex) {
 	int r = 0;
 	int a = 0;
 	Item itm = 0;
+	seq_segaSetupSequence(0);
 
 	switch (npcIndex) {
 	case 0:
 		for (r = 1; r == 1;) {
-			gui_drawDialogueBox();
+			if (_flags.platform != Common::kPlatformSegaCD)
+				gui_drawDialogueBox();
 			r = DLG2A3(checkScriptFlags(0x2000), 8, 2, 1);
 			if (r == 1) {
-				TXT(1);
+				TXTNB(1);
 				setScriptFlags(0x2000);
 			} else if (r == 0) {
 				npcJoinDialogue(6, 12, 23, 2);
@@ -397,11 +393,11 @@ void EoBEngine::runNpcDialogue(int npcIndex) {
 				a = 13;
 			} else {
 				setScriptFlags(0x8000);
-				r = DLG2(3, 3);
+				r = DLG2(3, 3, 1);
 				a = 4;
 			}
 			if (!r)
-				r = DLG2(a, 4);
+				r = DLG2(a, 4, 1);
 
 			if (!r) {
 				for (a = 0; a < 6; a++)
@@ -409,9 +405,9 @@ void EoBEngine::runNpcDialogue(int npcIndex) {
 				createItemOnCurrentBlock(62);
 				setScriptFlags(0x10000);
 				TXT(6);
-				npcJoinDialogue(7, 7, 29, 30);
+				JOIN(7, 7, 29, 30);
 			} else {
-				TXT(5);
+				TXTNB(5);
 			}
 			r = 1;
 		}
@@ -423,7 +419,8 @@ void EoBEngine::runNpcDialogue(int npcIndex) {
 			}
 			if (a != 6) {
 				TXT(25);
-				TXT(26);
+				if (_flags.platform != Common::kPlatformSegaCD)
+					TXT(26);
 				setScriptFlags(0x80000);
 				r = 1;
 			}
@@ -440,15 +437,19 @@ void EoBEngine::runNpcDialogue(int npcIndex) {
 			}
 		}
 
-		if (!r)
-			_txt->printDialogueText(_npcStrings[0][0], true);
+		if (!r) {
+			if (_flags.platform == Common::kPlatformSegaCD)
+				TXTNB(44);
+			else
+				_txt->printDialogueText(_npcStrings[0][0], true);
+		}
 
 		break;
 
 	case 2:
 		if (checkScriptFlags(0x10000)) {
 			if (checkScriptFlags(0x20000)) {
-				TXT(11);
+				TXTNB(11);
 			} else {
 				r = DLG2A3(!countResurrectionCandidates(), 9, 5, 6);
 				if (r < 2) {
@@ -460,12 +461,12 @@ void EoBEngine::runNpcDialogue(int npcIndex) {
 				}
 			}
 		} else {
-			TXT(24);
+			TXTNB(24);
 		}
 		break;
 
 	case 3:
-		if (!DLG2(18, 7)) {
+		if (!DLG2(18, 7, 0)) {
 			setScriptFlags(0x8400000);
 			for (a = 0; a < 30; a++) {
 				if (_monsters[a].mode == 8)
@@ -480,7 +481,7 @@ void EoBEngine::runNpcDialogue(int npcIndex) {
 		break;
 
 	case 4:
-		r = DLG3(14, 8);
+		r = DLG3(14, 8, 1);
 		if (r == 0)
 			setScriptFlags(0x200000);
 		else if (r == 1)
@@ -489,8 +490,9 @@ void EoBEngine::runNpcDialogue(int npcIndex) {
 		break;
 
 	case 5:
-		if (!DLG2(16, 9)) {
-			TXT(17);
+		if (!DLG2(16, 9, 1)) {
+			if (_flags.platform != Common::kPlatformSegaCD)
+				TXT(17);
 			for (a = 0; a < 6; a++) {
 				for (r = 0; r < 2; r++) {
 					itm = _characters[a].inventory[r];
@@ -510,10 +512,10 @@ void EoBEngine::runNpcDialogue(int npcIndex) {
 		break;
 
 	case 7:
-		r = DLG3(22, 10);
-		if (r  < 2) {
+		r = DLG3(22, 10, 1);
+		if (r < 2) {
 			if (r == 0)
-				npcJoinDialogue(8, 27, 44, 45);
+				JOIN(8, 27, 44, 45);
 			else
 				TXT(31);
 			setScriptFlags(0x4000000);
@@ -523,8 +525,16 @@ void EoBEngine::runNpcDialogue(int npcIndex) {
 	default:
 		break;
 	}
+
+	seq_segaRestoreAfterSequence();
+	setLevelPalettes(_currentLevel);
+	_levelCurTrack = -1;
+	if (_flags.platform == Common::kPlatformSegaCD)
+		snd_playLevelScore();
 }
 
+#undef JOIN
+#undef TXTNB
 #undef TXT
 #undef DLG2
 #undef DLG3
@@ -975,12 +985,12 @@ void EoBEngine::displayParchment(int id) {
 	int temp = 0;
 	const char *const *strings = _staticres->loadStrings(kEoB1ParchmentStrings, temp);
 
-	_sceneShakeCountdown = 0;
+	gui_resetAnimations();
 	for (int i = 0; i < 6; i++) {
 		if (!testCharacter(i, 1))
 			continue;
 		_characters[i].damageTaken = 0;
-		_characters[i].slotStatus[0] = _characters[i].slotStatus[1] = _characters[i].gfxUpdateCountdown = 0;
+		_characters[i].slotStatus[0] = _characters[i].slotStatus[1] = 0;
 		gui_drawCharPortraitWithStats(i);
 	}
 
@@ -1027,25 +1037,137 @@ void EoBEngine::displayParchment(int id) {
 	_totalPlaySecs += ((_system->getMillis() - startTime) / 1000);
 }
 
-void EoBEngine::drawParchmentPage(int page) {
-	
+const uint8 **EoBEngine::makePortalShapes() {
+	if (_flags.platform != Common::kPlatformSegaCD) {
+		 return EoBCoreEngine::makePortalShapes();
+	}
+
+	gui_resetAnimations();
+	gui_updateAnimations();
+
+	snd_stopSound();
+	uint8 *data = _res->fileData("PORT", 0);
+	const uint8 *in = data;
+	const uint8 **shapes = new const uint8*[16];
+
+	for (int i = 0; i < 10; ++i) {
+		shapes[1 + i] = _screen->sega_convertShape(in, 24, 80, 2);
+		in += 960;
+	}
+
+	for (int i = 0; i < 5; ++i) {
+		shapes[11 + i] = _screen->sega_convertShape(in, 120, 24, 2);
+		in += 1440;
+	}
+
+	shapes[0] = _screen->sega_convertShape(in, 64, 80, 2);
+	in += 2560;
+
+	_screen->clearPage(2);
+	for (int i = 0; i < 10; ++i) {
+		uint8 *shp = _screen->sega_convertShape(in, 64, 80, 2);
+		_screen->drawShape(2, shp, (i % 5) << 6, (i / 5) * 77, 0);
+		in += 2560;
+	}
+
+	delete[] data;
+	return shapes;
 }
 
 bool EoBEngine::checkPartyStatusExtra() {
 	_screen->copyPage(0, Screen_EoB::kDefeatMsgBackupPage);
 	int cd = _screen->curDimIndex();
-	gui_drawBox(0, 121, 320, 80, guiSettings()->colors.frame1, guiSettings()->colors.frame2, guiSettings()->colors.fill);
-	_txt->setupField(9, false);
-	_txt->printMessage(_menuStringsDefeat[0]);
-	while (!shouldQuit()) {
-		removeInputTop();
-		if (checkInput(0, false, 0) & 0xFF)
-			break;
+
+	if (_flags.platform == Common::kPlatformSegaCD) {
+		_screen->sega_fadeToBlack(4);
+
+		gui_resetAnimations();
+		gui_updateAnimations();
+		snd_stopSound();
+
+		Common::SeekableReadStreamEndian *in = _res->createEndianAwareReadStream("GO");
+		SegaRenderer *r = _screen->sega_getRenderer();
+		r->loadStreamToVRAM(in, 0x20);
+		delete in;
+
+		_screen->hideMouse();
+		_screen->sega_selectPalette(51, 0);
+		_screen->sega_selectPalette(52, 1);
+		_screen->sega_selectPalette(53, 2);
+		_screen->sega_selectPalette(7, 3);
+		r->fillRectWithTiles(0, 0, 0, 40, 28, 0);
+		r->fillRectWithTiles(1, 0, 0, 40, 28, 0);
+		r->fillRectWithTiles(0, 0, 3, 32, 16, 1, true);
+		r->fillRectWithTiles(0, 32, 3, 8, 16, 0x201, true);
+		r->fillRectWithTiles(1, 0, 3, 32, 16, 0x2281, true);
+		r->fillRectWithTiles(1, 32, 3, 8, 16, 0x2481, true);
+
+		int cs = _screen->setFontStyles(_screen->_currentFont, _flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat);
+		_screen->sega_clearTextBuffer(0);
+		_txt->printShadowedText(_menuStringsDefeat[0], 12, 0, 0xff, 0xcc, 304, 48, 0, false);
+		_txt->printShadowedText(_menuStringsDefeat[1], 20, 16, 0xff, 0xcc, 304, 48, 0, false);
+		_txt->printShadowedText(_menuStringsDefeat[2], 20, 32, 0xff, 0xcc, 304, 48, 0, false);
+		_screen->setFontStyles(_screen->_currentFont, cs);
+		_screen->sega_loadTextBufferToVRAM(0, 0xA3A0, 7296);
+		r->fillRectWithTiles(0, 1, 20, 38, 6, 0x651D, true);
+		r->render(0);
+
+		snd_playSoundEffect(0x5086);
+
+		_screen->sega_paletteOps(0, 0, 5);
+		_screen->sega_paletteOps(1, 0, 5);
+		uint32 del = _system->getMillis() + 1333;
+		for (uint32 cur = _system->getMillis(); cur < del; cur = _system->getMillis()) {
+			_screen->sega_updatePaletteFaders(0);
+			_screen->sega_updatePaletteFaders(1);
+			delay(MIN<uint32>(8, del - cur));
+		}
+		_screen->sega_paletteOps(3, 0, 6);
+		del = _system->getMillis() + 1600;
+		for (uint32 cur = _system->getMillis(); cur < del; cur = _system->getMillis()) {
+			_screen->sega_updatePaletteFaders(3);
+			delay(MIN<uint32>(8, del - cur));
+		}
+
+		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);
+
+		_screen->sega_paletteOps(2, 0, 5);
+
+		resetSkipFlag();
+		_allowSkip = true;
+		while (!(shouldQuit() || skipFlag())) {
+			_screen->sega_updatePaletteFaders(2);
+			delay(8);
+		}
+		_allowSkip = false;
+		resetSkipFlag();
+
+		_screen->sega_fadeToBlack(4);
+		_screen->sega_getAnimator()->clearSprites();
+		_screen->sega_getAnimator()->update();
+
+		snd_playSoundEffect(0x5087);
+		_screen->showMouse();
+
+	} else {
+		gui_drawBox(0, 121, 320, 80, guiSettings()->colors.frame1, guiSettings()->colors.frame2, guiSettings()->colors.fill);
+		_txt->setupField(9, false);
+		_txt->printMessage(_menuStringsDefeat[0]);
+		while (!shouldQuit()) {
+			removeInputTop();
+			if (checkInput(0, false, 0) & 0xFF)
+				break;
+		}
 	}
+
 	_screen->copyPage(Screen_EoB::kDefeatMsgBackupPage, 0);
 	_eventList.clear();
 	_screen->setScreenDim(cd);
 	_txt->removePageBreakFlag();
+
 	return true;
 }
 
@@ -1053,13 +1175,29 @@ int EoBEngine::resurrectionSelectDialogue() {
 	gui_drawDialogueBox();
 	_txt->printDialogueText(_npcStrings[0][1]);
 
-	int r = _rrId[runDialogue(-1, 9, _rrNames[0], _rrNames[1], _rrNames[2], _rrNames[3], _rrNames[4], _rrNames[5], _rrNames[6], _rrNames[7], _rrNames[8]) - 1];
+	if (_flags.platform == Common::kPlatformSegaCD) {
+			resetSkipFlag();
+		_allowSkip = true;
+		while (!(shouldQuit() || skipFlag()))
+			delay(20);
+		_allowSkip = false;
+		resetSkipFlag();
+
+		_rrNames[_rrCount] = _abortStrings[0];
+		_rrId[_rrCount++] = 99;
+	}
+
+	int r = _rrId[runDialogue(-1, 9, -1, _rrNames[0], _rrNames[1], _rrNames[2], _rrNames[3], _rrNames[4], _rrNames[5], _rrNames[6], _rrNames[7], _rrNames[8]) - 1];
+
+	if (r == 99)
+		return 0;
 
 	if (r < 0) {
 		r = -r;
 		deletePartyItems(33, r);
 		_npcSequenceSub = r - 1;
-		drawNpcScene(2);
+		if (_flags.platform != Common::kPlatformSegaCD)
+			drawNpcScene(2);
 		npcJoinDialogue(_npcSequenceSub, 32 + (_npcSequenceSub << 1), -1, 33 + (_npcSequenceSub << 1));
 	} else {
 		_characters[r].hitPointsCur = _characters[r].hitPointsMax;
diff --git a/engines/kyra/engine/eob.h b/engines/kyra/engine/eob.h
index 64d92a7ab1..7638d91ec0 100644
--- a/engines/kyra/engine/eob.h
+++ b/engines/kyra/engine/eob.h
@@ -37,6 +37,7 @@ friend class EoBSeqPlayerCommon;
 friend class EoBIntroPlayer;
 friend class EoBPC98FinalePlayer;
 friend class EoBAmigaFinalePlayer;
+friend class TextDisplayer_SegaCD;
 friend class SegaSequencePlayer;
 public:
 	EoBEngine(OSystem *system, const GameFlags &flags);
@@ -98,7 +99,7 @@ private:
 
 	void seq_segaSetupSequence(int sequenceId);
 	void seq_segaRestoreAfterSequence();
-	bool seq_segaPlaySequence(int sequenceId, bool init = false);
+	bool seq_segaPlaySequence(int sequenceId, bool setupScreen = false);
 
 	const char *const *_finBonusStrings;
 	SegaSequencePlayer *_seqPlayer;
@@ -177,7 +178,7 @@ private:
 
 	// Misc
 	void displayParchment(int id) override;
-	void drawParchmentPage(int page);
+	const uint8 **makePortalShapes() override;
 	bool checkPartyStatusExtra() override;
 	int resurrectionSelectDialogue() override;
 	void healParty();
@@ -187,11 +188,6 @@ private:
 	uint8 *_shakeBackBuffer1;
 	uint8 *_shakeBackBuffer2;
 
-	// Map
-	const char *const *_mapStrings1;
-	const char *const *_mapStrings2;
-	const char *const *_mapStrings3;
-
 	// Resource
 	SegaCDResource *_sres;
 
@@ -203,13 +199,15 @@ private:
 	void gui_drawCharacterStatsPage() override;
 	void gui_displayMap() override;
 	void gui_updateAnimations() override;
+	void gui_resetAnimations();
 
-	void makeNameShapes() override;
-	void makeFaceShapes() override;
+	void makeNameShapes(int charId = -1) override;
+	void makeFaceShapes(int charId = -1) override;
 	void printStatsString(const char *str, int x, int y);
 	void drawMapButton(const char *str, int x, int y);
 	void drawMapPage(int level);
 	void drawMapSpots(int level, int animState);
+	void drawDialogueButtons() override;
 
 	const KyraRpgGUISettings *guiSettings() const override;
 	void useMainMenuGUISettings(bool toggle) override { _useMainMenuGUISettings = toggle; }
@@ -223,9 +221,11 @@ private:
 	bool _compassAnimDone;
 	uint8 *_compassData;
 
+	const char *const *_mapStrings1;
+	const char *const *_mapStrings2;
+	const char *const *_mapStrings3;
 	const uint8 **_invSmallDigits;
 	const uint8 **_weaponSlotShapes;
-
 	const uint16 *_addrTbl1;
 	const uint16 *_textFieldPattern;
 	const uint16 *_playFldPattern1;
@@ -244,6 +244,8 @@ private:
 	static const uint8 _egaDefaultPalette[];
 	static const uint8 _redGridTile[8];
 	static const int8 _sceneShakeOffsets[66];
+	static const uint16 _dlgButtonPosX_Sega[18];
+	static const uint8 _dlgButtonPosY_Sega[18];
 	bool _useMainMenuGUISettings;
 };
 
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 1add30f0cb..8ef80ea5fc 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -402,9 +402,8 @@ Common::Error EoBCoreEngine::init() {
 	if (!_staticres->init())
 		error("_staticres->init() failed");
 
-	// We start the respective sound driver even if "No Music" has
-	// been selected, because we don't have a null driver class (and
-	// don't really need one). We just disable the sound here.
+	// We start the respective sound driver even if "No Music" has been selected, because we
+	// don't have a null driver class (and don't really need one). We just disable the sound here.
 	MidiDriver::DeviceHandle dev = 0;
 	switch (_flags.platform) {
 	case Common::kPlatformDOS: {
@@ -1301,26 +1300,28 @@ void EoBCoreEngine::neutralizePoison(int character) {
 }
 
 void EoBCoreEngine::npcSequence(int npcIndex) {
-	_screen->loadShapeSetBitmap("OUTTAKE", 5, 3);
-	_screen->copyRegion(0, 0, 0, 0, 176, 120, 0, 6, Screen::CR_NO_P_CHECK);
+	if (_flags.platform != Common::kPlatformSegaCD) {
+		_screen->loadShapeSetBitmap("OUTTAKE", 5, 3);
+		_screen->copyRegion(0, 0, 0, 0, 176, 120, 0, 6, Screen::CR_NO_P_CHECK);
 
-	drawNpcScene(npcIndex);
+		drawNpcScene(npcIndex);
 
-	Common::SeekableReadStream *s = _res->createReadStream("TEXT.DAT");
-	if (s) {
-		_screen->loadFileDataToPage(s, 5, 32000);
-	} else {
-		s = _res->createReadStream("TEXT.CPS");
-		if (s->readSint32BE() + 12 == s->size())
-			_screen->loadSpecialAmigaCPS("TEXT.CPS", 5, false);
-		else
-			_screen->loadBitmap("TEXT.CPS", 5, 5, 0, true);
-	}		
-	delete s;
+		Common::SeekableReadStream *s = _res->createReadStream("TEXT.DAT");
+		if (s) {
+			_screen->loadFileDataToPage(s, 5, 32000);
+		} else {
+			s = _res->createReadStream("TEXT.CPS");
+			if (s->readSint32BE() + 12 == s->size())
+				_screen->loadSpecialAmigaCPS("TEXT.CPS", 5, false);
+			else
+				_screen->loadBitmap("TEXT.CPS", 5, 5, 0, true);
+		}
+		delete s;
 
-	gui_drawBox(0, 121, 320, 79, guiSettings()->colors.frame1, guiSettings()->colors.frame2, guiSettings()->colors.fill);
-	_txt->setupField(9, true);
-	_txt->resetPageBreakString();
+		gui_drawBox(0, 121, 320, 79, guiSettings()->colors.frame1, guiSettings()->colors.frame2, guiSettings()->colors.fill);
+		_txt->setupField(9, true);
+		_txt->resetPageBreakString();
+	}
 
 	runNpcDialogue(npcIndex);
 
@@ -1341,24 +1342,19 @@ void EoBCoreEngine::initNpc(int npcIndex) {
 	delete[] c->faceShape;
 	memcpy(c, &_npcPreset[npcIndex], sizeof(EoBCharacter));
 	recalcArmorClass(i);
+	makeFaceShapes(i);
+	makeNameShapes(i);
 
 	for (i = 0; i < 25; i++) {
 		if (!c->inventory[i])
 			continue;
 		c->inventory[i] = duplicateItem(c->inventory[i]);
 	}
-
-	_screen->loadShapeSetBitmap(_flags.gameID == GI_EOB2 ? "OUTPORTS" : "OUTTAKE", 3, 3);
-	_screen->_curPage = 2;
-	c->faceShape = _screen->encodeShape(npcIndex << 2, _flags.gameID == GI_EOB2 ? 0 : 160, 4, 32, true, _cgaMappingDefault);
-	_screen->_curPage = 0;
 }
 
 int EoBCoreEngine::npcJoinDialogue(int npcIndex, int queryJoinTextId, int confirmJoinTextId, int noJoinTextId) {
 	gui_drawDialogueBox();
-	_txt->printDialogueText(queryJoinTextId, 0);
-
-	int r = runDialogue(-1, 2, _yesNoStrings[0], _yesNoStrings[1]) - 1;
+	int r = runDialogue(queryJoinTextId, _flags.platform == Common::kPlatformSegaCD ? 3 : 2, _flags.platform == Common::kPlatformSegaCD ? 3 : -1, _yesNoStrings[0], _yesNoStrings[1], _flags.platform == Common::kPlatformSegaCD ? _yesNoStrings[2] : 0) - 1;
 	if (r == 0) {
 		if (confirmJoinTextId == -1) {
 			Common::String tmp = Common::String::format(_npcJoinStrings[0], _npcPreset[npcIndex].name);
@@ -1370,7 +1366,7 @@ int EoBCoreEngine::npcJoinDialogue(int npcIndex, int queryJoinTextId, int confir
 		if (prepareForNewPartyMember(33, npcIndex + 1))
 			initNpc(npcIndex);
 
-	} else if (r == 1) {
+	} else if (r == 1 && noJoinTextId != -1) {
 		_txt->printDialogueText(noJoinTextId, _okStrings[0]);
 	}
 
@@ -1389,8 +1385,17 @@ int EoBCoreEngine::prepareForNewPartyMember(int16 itemType, int16 itemValue) {
 		_screen->set16bitShadingLevel(4);
 		_txt->printDialogueText(_npcMaxStrings[0]);
 		_screen->set16bitShadingLevel(0);
-		int r = runDialogue(-1, 7, _characters[0].name, _characters[1].name, _characters[2].name, _characters[3].name,
-		                    _characters[4].name, _characters[5].name, _abortStrings[0]) - 1;
+
+		if (_flags.platform == Common::kPlatformSegaCD) {
+			resetSkipFlag();
+			_allowSkip = true;
+			while (!(shouldQuit() || skipFlag()))
+				delay(20);
+			_allowSkip = false;
+			resetSkipFlag();
+		}
+
+		int r = runDialogue(-1, 7, -1, _characters[0].name, _characters[1].name, _characters[2].name, _characters[3].name, _characters[4].name, _characters[5].name, _abortStrings[0]) - 1;
 
 		if (r == 6)
 			return 0;
@@ -1527,15 +1532,13 @@ void EoBCoreEngine::setupDialogueButtons(int presetfirst, int numStr, va_list &a
 			_dialogueNumButtons = numStr = i;
 	}
 
-	static const uint16 prsX[] = { 59, 166, 4, 112, 220, 4, 112, 220, 4, 112, 220, 4, 112, 220 };
-	static const uint8 prsY[] = { 0, 0, 0, 0, 0, 12, 12, 12, 24, 24, 24, 36, 36, 36 };
-
 	const ScreenDim *dm = screen()->_curDim;
 	int yOffs = (_txt->lineCount() + 1) * _screen->getFontHeight() + dm->sy + 4;
 
-	_dialogueButtonPosX = &prsX[presetfirst];
-	_dialogueButtonPosY = &prsY[presetfirst];
-	_dialogueButtonYoffs = yOffs;
+	_dialogueButtonPosX = &guiSettings()->buttons.posX[presetfirst];
+	_dialogueButtonPosY = &guiSettings()->buttons.posY[presetfirst];
+	_dialogueButtonXoffs = (_flags.platform == Common::kPlatformSegaCD) ? 8 : 0;
+	_dialogueButtonYoffs = (_flags.platform == Common::kPlatformSegaCD) ? 160 : yOffs;
 
 	drawDialogueButtons();
 
@@ -1644,23 +1647,29 @@ void EoBCoreEngine::drawSequenceBitmap(const char *file, int destRect, int x1, i
 	_screen->updateScreen();
 }
 
-int EoBCoreEngine::runDialogue(int dialogueTextId, int numStr, ...) {
-	if (dialogueTextId != -1)
-		txt()->printDialogueText(dialogueTextId, 0);
-
-	va_list args;
-	va_start(args, numStr);
-	if (numStr > 2)
-		setupDialogueButtons(2, numStr, args);
-	else
-		setupDialogueButtons(0, numStr, args);
-	va_end(args);
+int EoBCoreEngine::runDialogue(int dialogueTextId, int numStr, int loopButtonId, ...) {
+	int res;
+	do {
+		res = 0;
+		if (dialogueTextId != -1)
+			txt()->printDialogueText(dialogueTextId, 0);
+
+		va_list args;
+		va_start(args, loopButtonId);
+		if (_flags.platform == Common::kPlatformSegaCD && numStr > 3)
+			setupDialogueButtons(numStr == 4 ? 14 : 5, numStr, args);
+		else if (numStr > 2)
+			setupDialogueButtons(2, numStr, args);
+		else
+			setupDialogueButtons(0, numStr, args);
+		va_end(args);
 
-	int res = 0;
-	while (res == 0 && !shouldQuit())
-		res = processDialogue();
+		while (res == 0 && !shouldQuit())
+			res = processDialogue();
+	} while (res == loopButtonId && !shouldQuit());
 
-	gui_drawDialogueBox();
+	if (_flags.platform != Common::kPlatformSegaCD)
+		gui_drawDialogueBox();
 
 	return res;
 }
@@ -1874,72 +1883,84 @@ int EoBCoreEngine::countResurrectionCandidates() {
 }
 
 void EoBCoreEngine::seq_portal() {
-	uint8 *shapes1[5];
-	uint8 *shapes2[5];
-	uint8 *shapes3[5];
-	uint8 *shape0;
-
-	_screen->loadShapeSetBitmap("PORTALA", 5, 3);
-
-	for (int i = 0; i < 5; i++) {
-		shapes1[i] = _screen->encodeShape(i * 3, 0, 3, 75, false, _cgaMappingDefault);
-		shapes2[i] = _screen->encodeShape(i * 3, 80, 3, 75, false, _cgaMappingDefault);
-		shapes3[i] = _screen->encodeShape(15, i * 18, 15, 18, false, _cgaMappingDefault);
-	}
-
-	shape0 = _screen->encodeShape(30, 0, 8, 77, false, _cgaMappingDefault);
-	_screen->loadEoBBitmap("PORTALB", _cgaMappingDefault, 5, 3, 2);
+	const uint8 **shapes = makePortalShapes();
+	assert(shapes);
 
-	snd_playSoundEffect(33);
-	snd_playSoundEffect(19);
 	_screen->copyRegion(24, 0, 24, 0, 144, 104, 2, 5, Screen::CR_NO_P_CHECK);
 	_screen->copyRegion(24, 0, 24, 0, 144, 104, 0, 2, Screen::CR_NO_P_CHECK);
-	_screen->drawShape(2, shapes3[0], 28, 9, 0);
-	_screen->drawShape(2, shapes1[0], 34, 28, 0);
-	_screen->drawShape(2, shapes2[0], 120, 28, 0);
-	_screen->drawShape(2, shape0, 56, 27, 0);
-	_screen->crossFadeRegion(24, 0, 24, 0, 144, 104, 2, 0);
+	_screen->drawShape(2, shapes[11], 28, 9, 0);
+	_screen->drawShape(2, shapes[1], 34, 28, 0);
+	_screen->drawShape(2, shapes[6], 120, 28, 0);
+	_screen->drawShape(2, shapes[0], 56, 27, 0);
+
+	if (_flags.platform == Common::kPlatformSegaCD) {
+		snd_playSoundEffect(19);
+		_screen->copyRegion(24, 0, 24, 0, 144, 104, 2, 0, Screen::CR_NO_P_CHECK);
+		_screen->updateScreen();
+	} else {
+		snd_playSoundEffect(33);
+		snd_playSoundEffect(19);
+		_screen->crossFadeRegion(24, 0, 24, 0, 144, 104, 2, 0);
+		delay(30 * _tickLength);
+	}
+
 	_screen->copyRegion(24, 0, 24, 0, 144, 104, 5, 2, Screen::CR_NO_P_CHECK);
-	delay(30 * _tickLength);
 
 	for (const int8 *pos = _portalSeq; *pos > -1 && !shouldQuit();) {
 		int s = *pos++;
-		_screen->drawShape(0, shapes3[s], 28, 9, 0);
-		_screen->drawShape(0, shapes1[s], 34, 28, 0);
-		_screen->drawShape(0, shapes2[s], 120, 28, 0);
-
-		if ((s == 1) && (pos >= _portalSeq + 3)) {
-			if (*(pos - 3) == 0) {
-				snd_playSoundEffect(24);
-				snd_playSoundEffect(86);
+		_screen->drawShape(0, shapes[11 + s], 28, 9, 0);
+		_screen->drawShape(0, shapes[1 + s], 34, 28, 0);
+		_screen->drawShape(0, shapes[6 + s], 120, 28, 0);
+
+		if (_flags.platform != Common::kPlatformSegaCD) {
+			if ((s == 1) && (pos >= _portalSeq + 3)) {
+				if (*(pos - 3) == 0) {
+					snd_playSoundEffect(24);
+					snd_playSoundEffect(86);
+				}
 			}
 		}
 
 		s = *pos++;
 		if (s == 0) {
-			_screen->drawShape(0, shape0, 56, 27, 0);
+			_screen->drawShape(0, shapes[0], 56, 27, 0);
 		} else {
 			s--;
 			_screen->copyRegion((s % 5) << 6, s / 5 * 77, 56, 27, 64, 77, 2, 0, Screen::CR_NO_P_CHECK);
 		}
 
-		if (s == 1)
-			snd_playSoundEffect(31);
-		else if (s == 3) {
-			if (*(pos - 2) == 3)
-				snd_playSoundEffect(90);
+		if (_flags.platform != Common::kPlatformSegaCD) {
+			if (s == 1)
+				snd_playSoundEffect(31);
+			else if (s == 3) {
+				if (*(pos - 2) == 3)
+					snd_playSoundEffect(90);
+			}
 		}
-
+	
 		_screen->updateScreen();
 		delay(2 * _tickLength);
 	}
+	
+	for (int i = 0; i < 16; i++)
+		delete[] shapes[i];
+	delete[] shapes;
+}
+
+const uint8 **EoBCoreEngine::makePortalShapes() {
+	const uint8 **shapes = new const uint8*[16];
+	_screen->loadShapeSetBitmap("PORTALA", 5, 3);
 
-	delete[] shape0;
 	for (int i = 0; i < 5; i++) {
-		delete[] shapes1[i];
-		delete[] shapes2[i];
-		delete[] shapes3[i];
+		shapes[1 + i] = _screen->encodeShape(i * 3, 0, 3, 75, false, _cgaMappingDefault);
+		shapes[6 + i] = _screen->encodeShape(i * 3, 80, 3, 75, false, _cgaMappingDefault);
+		shapes[11 + i] = _screen->encodeShape(15, i * 18, 15, 18, false, _cgaMappingDefault);
 	}
+
+	shapes[0] = _screen->encodeShape(30, 0, 8, 77, false, _cgaMappingDefault);
+	_screen->loadEoBBitmap("PORTALB", _cgaMappingDefault, 5, 3, 2);
+
+	return shapes;
 }
 
 bool EoBCoreEngine::checkPassword() {
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index dd52462e72..98eaf7e284 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -846,7 +846,7 @@ protected:
 	void initDialogueSequence();
 	void restoreAfterDialogueSequence();
 	void drawSequenceBitmap(const char *file, int destRect, int x1, int y1, int flags);
-	int runDialogue(int dialogueTextId, int numStr, ...);
+	int runDialogue(int dialogueTextId, int numStr, int loopButtonId, ...);
 
 	char _dialogueLastBitmap[13];
 	int _moveCounter;
@@ -904,6 +904,7 @@ protected:
 	int countResurrectionCandidates();
 
 	void seq_portal();
+	virtual const uint8 **makePortalShapes();
 	bool seq_playSegaSequence(int id) { return true; }
 	bool checkPassword();
 
@@ -934,8 +935,8 @@ protected:
 
 	Common::Error loadGameState(int slot) override;
 	Common::Error saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail) override;
-	virtual void makeNameShapes() {}
-	virtual void makeFaceShapes();
+	virtual void makeNameShapes(int charId = -1) {}
+	virtual void makeFaceShapes(int charId = -1);
 	// Default parameters will import all present original save files and push them to the top of the save dialog.
 	bool importOriginalSaveFile(int destSlot, const char *sourceFile = 0);
 	Common::String readOriginalSaveFile(Common::String &file);
diff --git a/engines/kyra/engine/items_eob.cpp b/engines/kyra/engine/items_eob.cpp
index 231d21dd0e..63abe0d2e1 100644
--- a/engines/kyra/engine/items_eob.cpp
+++ b/engines/kyra/engine/items_eob.cpp
@@ -241,6 +241,9 @@ int EoBCoreEngine::validateInventorySlotForItem(Item item, int charIndex, int sl
 	if (item < 0)
 		return 0;
 
+	if (slot == 27)
+		return 1;
+
 	if (slot == 17 && item && !itemUsableByCharacter(charIndex, item)) {
 		_txt->printMessage(_validateArmorString[0], -1, _characters[charIndex].name);
 		return 0;
diff --git a/engines/kyra/engine/kyra_rpg.cpp b/engines/kyra/engine/kyra_rpg.cpp
index b23fa5a866..4341c9c7a5 100644
--- a/engines/kyra/engine/kyra_rpg.cpp
+++ b/engines/kyra/engine/kyra_rpg.cpp
@@ -114,7 +114,7 @@ KyraRpgEngine::KyraRpgEngine(OSystem *system, const GameFlags &flags) : KyraEngi
 	memset(_dialogueButtonString, 0, 3 * sizeof(const char *));
 	_dialogueButtonPosX = 0;
 	_dialogueButtonPosY = 0;
-	_dialogueNumButtons = _dialogueButtonYoffs = _dialogueHighlightedButton = 0;
+	_dialogueNumButtons = _dialogueButtonXoffs = _dialogueButtonYoffs = _dialogueHighlightedButton = 0;
 	_currentControlMode = 0;
 	_specialSceneFlag = 0;
 	_updateCharNum = -1;
@@ -277,7 +277,7 @@ uint16 KyraRpgEngine::processDialogue() {
 	int res = 0;
 
 	for (int i = 0; i < _dialogueNumButtons; i++) {
-		int x = _dialogueButtonPosX[i];
+		int x = _dialogueButtonPosX[i] + _dialogueButtonXoffs;
 		int y = ((_flags.gameID == GI_LOL && _flags.use16ColorMode) ? ((_dialogueButtonYoffs + _dialogueButtonPosY[i]) & ~7) - 1 : (_dialogueButtonYoffs + _dialogueButtonPosY[i]));
 		Common::Point p = getMousePos();
 		if (posWithinRect(p.x, p.y, x, y, x + _dialogueButtonWidth, y + guiSettings()->buttons.height)) {
diff --git a/engines/kyra/engine/kyra_rpg.h b/engines/kyra/engine/kyra_rpg.h
index f3be83df8b..73bcb72824 100644
--- a/engines/kyra/engine/kyra_rpg.h
+++ b/engines/kyra/engine/kyra_rpg.h
@@ -89,6 +89,8 @@ struct EoBFlyingObject {
 
 struct KyraRpgGUISettings {
 	struct DialogueButtons {
+		const uint16 *posX;
+		const uint8 *posY;
 		uint8 labelColor1;
 		uint8 labelColor2;
 		uint16 width;
@@ -393,7 +395,7 @@ protected:
 	static const uint8 _dropItemDirIndex[];
 
 	// text
-	void drawDialogueButtons();
+	virtual void drawDialogueButtons();
 	uint16 processDialogue();
 
 	TextDisplayer_rpg *_txt;
@@ -406,6 +408,7 @@ protected:
 	const char *_dialogueButtonString[9];
 	const uint16 *_dialogueButtonPosX;
 	const uint8 *_dialogueButtonPosY;
+	int16 _dialogueButtonXoffs;
 	int16 _dialogueButtonYoffs;
 	uint16 _dialogueButtonWidth;
 	int _dialogueNumButtons;
@@ -417,6 +420,9 @@ protected:
 
 	const char *const *_moreStrings;
 
+	static const uint16 _dlgButtonPosX_Def[14];
+	static const uint8 _dlgButtonPosY_Def[14];
+
 	// misc
 	void delay(uint32 millis, bool doUpdate = false, bool isMainLoop = false) override = 0;
 	void delayUntil(uint32 time, bool unused = false, bool doUpdate = false, bool isMainLoop = false) override;
diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index e4e6c9918d..6c498a3826 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -1257,13 +1257,11 @@ const uint8 *SegaCDFont::getGlyphData(uint16 c, uint8 &charWidth, uint8 &charHei
 		charHeight = pitch = 12;
 		res = &_data[0x19A0 + 18 * c];
 	} else if (_style == 2) {
-		assert(c < 188);
-		charWidth = _widthTable3[c];
+		charWidth = (!_fixedWidth && (c < 188)) ? _widthTable3[c] : 12;
 		charHeight = pitch = 12;
 		res = &_data[0x3410 + 18 * c];
 	} else {
-		assert(c < 188);
-		charWidth = _widthTable2[c];
+		charWidth = (!_fixedWidth && (c < 188)) ? _widthTable2[c] : 12;
 		charHeight = 12;
 		pitch = 8;
 		res = &_data[0x800 + 12 * c];
diff --git a/engines/kyra/gui/gui_eob_segacd.cpp b/engines/kyra/gui/gui_eob_segacd.cpp
index 8091b0828e..fceccc9381 100644
--- a/engines/kyra/gui/gui_eob_segacd.cpp
+++ b/engines/kyra/gui/gui_eob_segacd.cpp
@@ -82,6 +82,16 @@ void EoBEngine::gui_drawPlayField(bool refresh) {
 	str = _sres->resStreamEndian(9);
 	r->loadStreamToVRAM(str, 0xA4A0, false);
 	delete str;
+	/*
+// CAMP MENU
+str = _sres->resStreamEndian(8);
+_screen->sega_getRenderer()->loadStreamToVRAM(str, 0x20, true);
+delete str;
+_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 22, 15, 0);
+_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 22, 21, 0x4001, true);
+_screen->sega_getRenderer()->render(0);
+_screen->sega_selectPalette(40, 2, true);
+*/
 
 	gui_setupPlayFieldHelperPages();
 
@@ -110,6 +120,11 @@ void EoBEngine::gui_drawPlayField(bool refresh) {
 }
 
 void EoBEngine::gui_setupPlayFieldHelperPages() {
+	if (_flags.platform != Common::kPlatformSegaCD) {
+		EoBCoreEngine::gui_setupPlayFieldHelperPages();
+		return;
+	}
+
 	_txt->clearDim(0);
 	SegaRenderer *r = _screen->sega_getRenderer();
 	r->fillRectWithTiles(0, 22, 0, 18, 21, 0);
@@ -203,12 +218,12 @@ void EoBEngine::gui_displayMap() {
 
 	_screen->sega_fadeToBlack(2);
 
-	_sceneShakeCountdown = 0;
+	gui_resetAnimations();
 	for (int i = 0; i < 6; i++) {
 		if (!testCharacter(i, 1))
 			continue;
 		_characters[i].damageTaken = 0;
-		_characters[i].slotStatus[0] = _characters[i].slotStatus[1] = _characters[i].gfxUpdateCountdown = 0;
+		_characters[i].slotStatus[0] = _characters[i].slotStatus[1] = 0;
 		gui_drawCharPortraitWithStats(i);
 	}
 
@@ -321,12 +336,12 @@ void EoBEngine::gui_updateAnimations() {
 			if (_compassAnimSwitch) {
 				_compassAnimPhase = (_compassAnimPhase + _compassAnimStep) & 0x0F;
 				_compassAnimDelayCounter = 6;
-				redrawCompass = true;
 				_compassAnimStep = -_compassAnimStep;
 				_compassAnimSwitch = false;
 			} else {
 				_compassAnimDone = _compassAnimSwitch = true;
 			}
+			redrawCompass = true;
 		}
 	}
 	if (redrawCompass) {
@@ -373,28 +388,44 @@ void EoBEngine::gui_updateAnimations() {
 		// All shapes except monsters and items
 		drawSceneShapes(0, 0xFF & ~0x2A);
 		_shapeShakeOffsetX = _shapeShakeOffsetY = 0;
-// Monsters and items
-drawSceneShapes(0, 0x2A);
+		// Monsters and items
+		drawSceneShapes(0, 0x2A);
 
-_screen->copyRegion(0, 0, 0, 0, 179, 123, _sceneDrawPage1, 0, Screen::CR_NO_P_CHECK);
-updScreen = true;
+		_screen->copyRegion(0, 0, 0, 0, 179, 123, _sceneDrawPage1, 0, Screen::CR_NO_P_CHECK);
+		updScreen = true;
 	}
 
 	if (updScreen)
 		_screen->updateScreen();
 }
 
-void EoBEngine::makeNameShapes() {
+void EoBEngine::gui_resetAnimations() {
 	if (_flags.platform != Common::kPlatformSegaCD)
 		return;
 
+	for (int i = 0; i < 6; ++i)
+		_characters[i].gfxUpdateCountdown = 1;
+	_sceneShakeCountdown = 1;
+	_compassAnimDelayCounter = _compassAnimSwitch = 0;
+	_compassAnimPhase = _compassAnimDest;
+}
+
+void EoBEngine::makeNameShapes(int charId) {
+	if (_flags.platform != Common::kPlatformSegaCD)
+		return;
+
+	int first = 0;
+	int last = 5;
+	if (charId != -1)
+		first = last = charId;
+
 	int cd = _txt->clearDim(4);
 	int cp = _screen->setCurPage(2);
 	_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 28, 0x2000);
 	_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 30, 28, 0x600A, true);
 	_screen->sega_clearTextBuffer(0);
 
-	for (int i = 0; i < 6; ++i) {
+	for (int i = first; i <= last; ++i) {
 		if (!_characters[i].flags)
 			continue;
 		_txt->printShadowedText(_characters[i].name, 0, i << 4, 0xFF, 0xCC);
@@ -402,7 +433,7 @@ void EoBEngine::makeNameShapes() {
 
 	_screen->sega_getRenderer()->render(_screen->_curPage);
 
-	for (int i = 0; i < 6; ++i) {
+	for (int i = first; i <= last; ++i) {
 		if (!_characters[i].flags)
 			continue;
 		delete[] _characters[i].nameShape;
@@ -417,9 +448,19 @@ void EoBEngine::makeNameShapes() {
 	_txt->clearDim(cd);
 }
 
-void EoBEngine::makeFaceShapes() {
+void EoBEngine::makeFaceShapes(int charId) {
+	if (_flags.platform != Common::kPlatformSegaCD) {
+		EoBCoreEngine::makeFaceShapes();
+		return;
+	}
+
+	int first = 0;
+	int last = 5;
+	if (charId != -1)
+		first = last = charId;
+
 	uint8 *in = _res->fileData("FACE", 0);
-	for (int i = 0; i < 6; i++) {
+	for (int i = first; i <= last; i++) {
 		EoBCharacter *c = &_characters[i];
 		if (!c->flags)
 			continue;
@@ -444,7 +485,7 @@ void EoBEngine::drawMapButton(const char *str, int x, int y) {
 void EoBEngine::drawMapPage(int level) {
 	int temp = 0;
 	_screen->sega_clearTextBuffer(0);
-	int cs = _screen->setFontStyles(_screen->_currentFont, _flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat | Font::kStyleNarrow1);
+	int cs = _screen->setFontStyles(_screen->_currentFont, (_flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat) | Font::kStyleNarrow1);
 	_txt->printShadowedText(_mapStrings3[level - 1], 0, 0, 0xCC, 0, 48, 16, 0, false);
 	_screen->setFontStyles(_screen->_currentFont, cs);
 	_screen->sega_loadTextBufferToVRAM(0, 0x7920, 384);
@@ -481,6 +522,30 @@ void EoBEngine::drawMapSpots(int level, int animState) {
 	a->update();
 }
 
+void EoBEngine::drawDialogueButtons() {
+	if (_flags.platform != Common::kPlatformSegaCD) {
+		KyraRpgEngine::drawDialogueButtons();
+		return;
+	}
+
+	_screen->sega_clearTextBuffer(0);
+
+	for (int i = 0; i < _dialogueNumButtons; i++) {
+		int cs = _screen->setFontStyles(_screen->_currentFont, (_flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat) | Font::kStyleNarrow2);
+		if (_screen->getTextWidth(_dialogueButtonString[i]) > 90)
+			_screen->setFontStyles(_screen->_currentFont, (_flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat) | Font::kStyleNarrow1);
+		_screen->sega_drawClippedLine(38, 6, _dialogueButtonPosX[i], _dialogueButtonPosY[i], 90, 14, 0x99);
+		_screen->sega_drawClippedLine(38, 6, _dialogueButtonPosX[i], _dialogueButtonPosY[i] + 1, 89, 13, 0xBB);
+		_screen->sega_drawClippedLine(38, 6, _dialogueButtonPosX[i] + 1, _dialogueButtonPosY[i] + 1, 88, 12, 0xAA);
+		_txt->printShadowedText(_dialogueButtonString[i], _dialogueButtonPosX[i] + (_dialogueButtonWidth >> 1) - MIN<int>(_dialogueButtonWidth, _screen->getTextWidth(_dialogueButtonString[i])) / 2,
+			_dialogueButtonPosY[i] + 1, _dialogueHighlightedButton == i ? _dialogueButtonLabelColor1 : _dialogueButtonLabelColor2, 0xEE, 304, 48, 0, false);
+		_screen->setFontStyles(_screen->_currentFont, cs);
+	}
+
+	_screen->sega_loadTextBufferToVRAM(0, 0xA380, 7296);
+	_screen->sega_getRenderer()->render(0);
+}
+
 } // End of namespace Kyra
 
 #endif // ENABLE_EOB
diff --git a/engines/kyra/gui/saveload_eob.cpp b/engines/kyra/gui/saveload_eob.cpp
index a6f85c6c66..ad0d96bdcb 100644
--- a/engines/kyra/gui/saveload_eob.cpp
+++ b/engines/kyra/gui/saveload_eob.cpp
@@ -544,9 +544,14 @@ Common::Error EoBCoreEngine::saveGameStateIntern(int slot, const char *saveName,
 	return Common::kNoError;
 }
 
-void EoBCoreEngine::makeFaceShapes() {
+void EoBCoreEngine::makeFaceShapes(int charId) {
+	int first = 0;
+	int last = 5;
+	if (charId != -1)
+		first = last = charId;
+
 	_screen->loadShapeSetBitmap("CHARGENA", 3, 3);
-	for (int i = 0; i < 6; i++) {
+	for (int i = first; i <= last; i++) {
 		EoBCharacter *c = &_characters[i];
 		if (!c->flags || c->portrait < 0)
 			continue;
@@ -554,7 +559,7 @@ void EoBCoreEngine::makeFaceShapes() {
 	}
 
 	_screen->loadShapeSetBitmap(_flags.gameID == GI_EOB2 ? "OUTPORTS" : "OUTTAKE", 3, 3);
-	for (int i = 0; i < 6; i++) {
+	for (int i = first; i <= last; i++) {
 		EoBCharacter *c = &_characters[i];
 		if (!c->flags || c->portrait >= 0)
 			continue;
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index 83e5ff4a21..6a6cf6a49c 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -1453,7 +1453,7 @@ void EoBEngine::initSpells() {
 }
 
 const KyraRpgGUISettings EoBEngine::_guiSettingsVGA = {
-	{ 9, 15, 95, 9, 2, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
+	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 9, 15, 95, 9, 2, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
 	{ 135, 130, 132, 180, 133, 17, 23, 20, 184, 177, 180, 184, 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 },
@@ -1464,7 +1464,7 @@ const KyraRpgGUISettings EoBEngine::_guiSettingsVGA = {
 };
 
 const KyraRpgGUISettings EoBEngine::_guiSettingsEGA = {
-	{ 9, 15, 95, 9, 2, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
+	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 9, 15, 95, 9, 2, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
 	{ 13, 9, 2, 14, 2, 6, 13, 8, 13, 15, 14, 13, 15, 14, 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 },
@@ -1475,7 +1475,7 @@ const KyraRpgGUISettings EoBEngine::_guiSettingsEGA = {
 };
 
 const KyraRpgGUISettings EoBEngine::_guiSettingsPC98 = {
-	{ 9, 15, 95, 11, 1, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
+	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 9, 15, 95, 11, 1, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
 	{ 13, 9, 2, 14, 2, 6, 13, 8, 13, 15, 14, 13, 15, 14, 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 },
@@ -1486,7 +1486,7 @@ const KyraRpgGUISettings EoBEngine::_guiSettingsPC98 = {
 };
 
 const KyraRpgGUISettings EoBEngine::_guiSettingsAmiga = {
-	{ 28, 31, 95, 9, 2, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
+	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 28, 31, 95, 9, 2, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
 	{ 18, 17, 10, 17, 11, 24, 22, 25, 18, 9, 10, 18, 9, 10, 31, 24, 25, 28, 29, 7, 26, 27, 19 },
 	{	{ 184, 256, -1}, { 2, 54, 106 }, 64, 50,
 		{ 8, 80, -1 }, { 11, 63, 115 }, { 181, -1, -1 }, { 3, -1, -1 },
@@ -1497,7 +1497,7 @@ const KyraRpgGUISettings EoBEngine::_guiSettingsAmiga = {
 };
 
 const KyraRpgGUISettings EoBEngine::_guiSettingsAmigaMainMenu = {
-	{ 28, 31, 95, 9, 2, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
+	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 28, 31, 95, 9, 2, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
 	{ 22, 28, 30, 17, 11, 24, 22, 25, 18, 9, 10, 18, 9, 10, 31, 24, 25, 28, 29, 7, 26, 27, 19 },
 	{	{ 184, 256, -1}, { 2, 54, 106 }, 64, 50,
 		{ 8, 80, -1 }, { 11, 63, 115 }, { 181, -1, -1 }, { 3, -1, -1 },
@@ -1508,7 +1508,7 @@ const KyraRpgGUISettings EoBEngine::_guiSettingsAmigaMainMenu = {
 };
 
 const KyraRpgGUISettings EoBEngine::_guiSettingsSegaCD = {
-	{ 9, 15, 95, 9, 2, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
+	{ _dlgButtonPosX_Sega, _dlgButtonPosY_Sega, 0x66, 0xFF, 90, 14, 2, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
 	{ 135, 130, 132, 180, 0x00, 17, 23, 20, 184, 177, 180, 184, 177, 180, 15, 6, 0x31, 9, 2, 0x35, 4, 0x33, 12 },
 	{	{ 184, 256, -1}, { 1, 57, 113 }, 64, 55,
 		{ 8, 80, -1 }, { 16, 72, 128 }, { 184, -1, -1 }, { 8, -1, -1 },
@@ -1540,6 +1540,10 @@ const uint8 EoBEngine::_monsterAcHitChanceTbl2[] = {
 	2, 1, 1, 1
 };
 
+const uint16 EoBEngine::_dlgButtonPosX_Sega[18] = { 59, 166, 4, 104, 204, 4, 104, 204, 4, 104, 204, 4, 104, 204, 4, 104, 204, 4 };
+
+const uint8 EoBEngine::_dlgButtonPosY_Sega[18] = { 16, 16, 16, 16, 16, 0, 0, 0, 16, 16, 16, 32, 32, 32, 16, 16, 16, 32 };
+
 const EoBEngine::RenderModePalFile EoBEngine::_renderModePalFiles[3] = {
 	{	Common::kRenderDefault, "EOBPAL.COL" },
 	{	Common::kRenderVGA, "EOBPAL.COL" },
@@ -1722,7 +1726,7 @@ void DarkMoonEngine::initSpells() {
 }
 
 const KyraRpgGUISettings DarkMoonEngine::_guiSettingsFMTowns = {
-	{ 9, 15, 95, 11, 1, 7, { 221, 76 }, { 187, 162 }, { 95, 95 } },
+	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 9, 15, 95, 11, 1, 7, { 221, 76 }, { 187, 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 },
@@ -1733,7 +1737,7 @@ const KyraRpgGUISettings DarkMoonEngine::_guiSettingsFMTowns = {
 };
 
 const KyraRpgGUISettings DarkMoonEngine::_guiSettingsDOS = {
-	{ 9, 15, 95, 9, 2, 7, { 221, 76 }, { 189, 162 }, { 95, 95 } },
+	{ _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 },
 	{	{ 184, 256, -1}, { 2, 54, 106 }, 64, 50,
 		{ 8, 80, -1 }, { 11, 63, 115 }, { 181, -1, -1 }, { 3, -1, -1 },
@@ -1744,7 +1748,7 @@ const KyraRpgGUISettings DarkMoonEngine::_guiSettingsDOS = {
 };
 
 const KyraRpgGUISettings DarkMoonEngine::_guiSettingsAmiga = {
-	{ 28, 31, 95, 9, 2, 7, { 221, 76 }, { 189, 162 }, { 95, 95 } },
+	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 28, 31, 95, 9, 2, 7, { 221, 76 }, { 189, 162 }, { 95, 95 } },
 	{ 18, 17, 10, 17, 11, 10, 12, 25, 18, 9, 10, 18, 9, 10, 31, 24, 25, 28, 29, 7, 26, 27, 19 },
 	{	{ 184, 256, -1}, { 2, 54, 106 }, 64, 50,
 		{ 8, 80, -1 }, { 11, 63, 115 }, { 181, -1, -1 }, { 3, -1, -1 },
diff --git a/engines/kyra/resource/staticres_lol.cpp b/engines/kyra/resource/staticres_lol.cpp
index 3a71cb5000..77d4f05616 100644
--- a/engines/kyra/resource/staticres_lol.cpp
+++ b/engines/kyra/resource/staticres_lol.cpp
@@ -781,7 +781,7 @@ const int8 LoLEngine::_mapCoords[12][4] = {
 // And it is hardly worth the time to add any usage for this, since the only significant version difference would
 // be the PC-98 16 color version. That said, I have filled all the unused parts of the struct with zeroes.
 const KyraRpgGUISettings LoLEngine::_guiSettings = {
-	{ 144, 254, 74, 9, 2, 80, { 0, 0 }, { 0, 0 }, { 0, 0 } },
+	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 144, 254, 74, 9, 2, 80, { 0, 0 }, { 0, 0 }, { 0, 0 } },
 	{ 136, 251, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
 	{	{ 0, 0, 0 }, { 0, 0, 0 }, 0, 0,
 		{ 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 },
diff --git a/engines/kyra/resource/staticres_rpg.cpp b/engines/kyra/resource/staticres_rpg.cpp
index 0c7b536fa4..91e9908737 100644
--- a/engines/kyra/resource/staticres_rpg.cpp
+++ b/engines/kyra/resource/staticres_rpg.cpp
@@ -78,6 +78,10 @@ const uint16 KyraRpgEngine::_vmpOffsetsDefault[9] = { 102, 97, 129, 117, 81, 159
 
 const uint16 KyraRpgEngine::_vmpOffsetsSegaCD[9] = { 0, 15, 20, 50, 62, 78, 158, 194, 386 };
 
+const uint16 KyraRpgEngine::_dlgButtonPosX_Def[14] = { 59, 166, 4, 112, 220, 4, 112, 220, 4, 112, 220, 4, 112, 220 };
+
+const uint8 KyraRpgEngine::_dlgButtonPosY_Def[14] = { 0, 0, 0, 0, 0, 12, 12, 12, 24, 24, 24, 36, 36, 36 };
+
 void KyraRpgEngine::initStaticResource() {
 	int temp;
 	_dscShapeX = (const int16 *)_staticres->loadRawDataBe16(kRpgCommonDscX, temp);
diff --git a/engines/kyra/script/script_eob.cpp b/engines/kyra/script/script_eob.cpp
index 76f580defb..99791de14d 100644
--- a/engines/kyra/script/script_eob.cpp
+++ b/engines/kyra/script/script_eob.cpp
@@ -1582,7 +1582,7 @@ int EoBInfProcessor::oeob_dialogue(int8 *data) {
 		break;
 
 	case -40:
-		_dlgResult = _vm->runDialogue(READ_LE_UINT16(pos), READ_LE_UINT16(pos + 6) == 0xFFFF ? 2 : 3, getString(READ_LE_UINT16(pos + 2)), getString(READ_LE_UINT16(pos + 4)), getString(READ_LE_UINT16(pos + 6)));
+		_dlgResult = _vm->runDialogue(READ_LE_UINT16(pos), READ_LE_UINT16(pos + 6) == 0xFFFF ? 2 : 3, -1, getString(READ_LE_UINT16(pos + 2)), getString(READ_LE_UINT16(pos + 4)), getString(READ_LE_UINT16(pos + 6)));
 		pos += 8;
 		break;
 
diff --git a/engines/kyra/sequence/seqplayer_eob_segacd.cpp b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
index 0075752f68..a8d0283c2b 100644
--- a/engines/kyra/sequence/seqplayer_eob_segacd.cpp
+++ b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
@@ -127,7 +127,10 @@ SegaSequencePlayer::~SegaSequencePlayer() {
 }
 
 bool SegaSequencePlayer::play(int id) {
+	_renderer->render(0);
 	_screen->sega_fadeToBlack(2);
+	_screen->clearPage(0);
+
 	_animator->clearSprites();
 	_scrollManager->setVScrollTimers(0, 1, 0, 0, 1, 0);
 	_scrollManager->setHScrollTimers(0, 1, 0, 0, 1, 0);
@@ -215,7 +218,7 @@ bool SegaSequencePlayer::play(int id) {
 	if (_vm->shouldQuit() || _vm->skipFlag()) {
 		if (!(_playingID == 55 || _playingID == 56))
 			_vm->snd_stopSound();
-		_screen->sega_fadeToBlack(5);
+		_screen->clearPage(0);
 	}
 
 	_scrollManager->setVScrollTimers(0, 1, 0, 0, 1, 0);
@@ -255,6 +258,7 @@ void SegaSequencePlayer::run(const uint8 *data) {
 			for (uint16 timeStamp2 = timeStamp; timeStamp2 == timeStamp; ) {
 				uint16 op = READ_BE_UINT16(data + 4);
 				_opcodes[op]->run(data + 6);
+				_screen->clearPage(0);
 
 				frameSize = READ_BE_UINT16(data);
 				data += (frameSize & ~1);
diff --git a/engines/kyra/sequence/sequences_eob.cpp b/engines/kyra/sequence/sequences_eob.cpp
index 04c323d9f3..e86c1ecaf7 100644
--- a/engines/kyra/sequence/sequences_eob.cpp
+++ b/engines/kyra/sequence/sequences_eob.cpp
@@ -2283,6 +2283,7 @@ void EoBEngine::seq_playFinale() {
 		EoBPC98FinalePlayer(this, _screen).start(_xdth);
 		return;
 	} else if (_flags.platform == Common::kPlatformSegaCD) {
+		_screen->hideMouse();
 		seq_segaPlaySequence(_xdth ? 55 : 56, true);
 		seq_segaFinalCredits();
 		seq_segaShowStats();
@@ -2332,44 +2333,76 @@ void EoBEngine::seq_playFinale() {
 }
 
 void EoBEngine::seq_xdeath() {
-	uint8 *shapes1[5];
+	uint8 *shapes1[4];
 	uint8 *shapes2;
+	memset(shapes1, 0, sizeof(shapes1));
 	_xdth = true;
+	_totalEnemiesKilled++;
 
-	_screen->loadShapeSetBitmap("XDEATH2", 5, 3);
-	for (int i = 0; i < 4; i++)
-		shapes1[i] = _screen->encodeShape(i / 2 * 14, i / 2 * 88, 14, 88, true, _cgaMappingDefault);
-	_screen->loadShapeSetBitmap("XDEATH3", 5, 3);
-	shapes2 = _screen->encodeShape(22, 0, 16, 95, true, _cgaMappingDefault);
-	_screen->loadEoBBitmap("XDEATH1", _cgaMappingDefault, 5, 3, -1);
-	_screen->convertPage(3, 2, _cgaMappingDefault);
-	_screen->setCurPage(0);
+	if (_flags.platform == Common::kPlatformSegaCD) {
+		_screen->sega_selectPalette(57, 2, true);
+		snd_stopSound();
+		uint8 *in = _res->fileData("XD", 0);
+		_sceneShakeCountdown = 1;
+
+		snd_playSoundEffect(0x502d);
+		for (int i = 0; i < 10 && !shouldQuit(); i++) {
+			uint32 del = _system->getMillis() + 4 * _tickLength;
+			shapes2 = _screen->sega_convertShape(in + 6144 + i * 4928, 112, 88, 2);
+			_screen->copyBlockToPage(2, 0, 0, 176, 120, _sceneWindowBuffer);
+			drawDecorations(13);
+			_screen->copyRegion(0, 0, 0, 0, 176, 120, 2, 0, Screen::CR_NO_P_CHECK);
+			_screen->drawShape(0, shapes2, 32, 10, 0);
+			_screen->updateScreen();
+			updateAnimTimers();
+			delete[] shapes2;
+			for (uint32 cur = _system->getMillis(); cur < del; cur = _system->getMillis()) {
+				updateAnimTimers();
+				delay(MIN<uint32>(8, del - cur));
+			}
+		}
 
-	for (int i = 0; i < 10; i++) {
-		if (i == 2)
-			snd_playSoundEffect(72);
-		else if (i == 4 || i == 6)
-			snd_playSoundEffect(54);
-		else
-			snd_playSoundEffect(34);
+		snd_playSoundEffect(0x500e);
+		shapes2 = _screen->sega_convertShape(in, 128, 96, 2);
+		delete[] in;
 
-		if (i < 6) {
-			_screen->copyRegion((i % 3) * 104, i / 3 * 88, 32, 10, 104, 88, 2, 0, Screen::CR_NO_P_CHECK);
-		} else {
-			snd_playSoundEffect(42);
-			_screen->drawShape(0, shapes1[i - 6], 32, 10, 0);
-		}
+	} else {
+		_screen->loadShapeSetBitmap("XDEATH2", 5, 3);
+		for (int i = 0; i < 4; i++)
+			shapes1[i] = _screen->encodeShape(i / 2 * 14, i / 2 * 88, 14, 88, true, _cgaMappingDefault);
+		_screen->loadShapeSetBitmap("XDEATH3", 5, 3);
+		shapes2 = _screen->encodeShape(22, 0, 16, 95, true, _cgaMappingDefault);
+		_screen->loadEoBBitmap("XDEATH1", _cgaMappingDefault, 5, 3, -1);
+		_screen->convertPage(3, 2, _cgaMappingDefault);
+		_screen->setCurPage(0);
+
+		for (int i = 0; i < 10 && !shouldQuit(); i++) {
+			if (i == 2)
+				snd_playSoundEffect(72);
+			else if (i == 4 || i == 6)
+				snd_playSoundEffect(54);
+			else
+				snd_playSoundEffect(34);
 
-		_screen->updateScreen();
-		delay(4 * _tickLength);
+			if (i < 6) {
+				_screen->copyRegion((i % 3) * 104, i / 3 * 88, 32, 10, 104, 88, 2, 0, Screen::CR_NO_P_CHECK);
+			} else {
+				snd_playSoundEffect(42);
+				_screen->drawShape(0, shapes1[i - 6], 32, 10, 0);
+			}
+
+			_screen->updateScreen();
+			delay(4 * _tickLength);
+		}
 	}
 
 	const ScreenDim *dm = _screen->getScreenDim(5);
 	_screen->modifyScreenDim(5, dm->sx, 8, dm->w, dm->h);
 	_screen->copyRegion(0, 0, 0, 0, 176, 120, 0, 5, Screen::CR_NO_P_CHECK);
 
-	for (int i = 0; i < 19; i++) {
-		snd_playSoundEffect(119);
+	for (int i = 0; i < 19 && !shouldQuit(); i++) {
+		if (_flags.platform != Common::kPlatformSegaCD)
+			snd_playSoundEffect(119);
 		_screen->copyRegion(0, 0, 0, 0, 176, 120, 5, 2, Screen::CR_NO_P_CHECK);
 		_screen->drawShape(2, shapes2, 24, i * 5 - 90, 5);
 		_screen->copyRegion(0, 0, 0, 0, 176, 120, 2, 0, Screen::CR_NO_P_CHECK);
@@ -2379,14 +2412,17 @@ void EoBEngine::seq_xdeath() {
 
 	_screen->modifyScreenDim(5, dm->sx, 0, dm->w, dm->h);
 
-	snd_playSoundEffect(5);
+	snd_playSoundEffect(_flags.platform == Common::kPlatformSegaCD ? 0x5002 : 5);
 	delay(60 * _tickLength);
 
 	for (int i = 0; i < 4; i++)
 		delete[] shapes1[i];
 	delete[] shapes2;
 
-	gui_drawPlayField(false);
+	if (_flags.platform == Common::kPlatformSegaCD)
+		_screen->sega_fadeToBlack(7);
+	else
+		gui_drawPlayField(false);
 	gui_drawAllCharPortraitsWithStats();
 }
 
@@ -2698,13 +2734,18 @@ void EoBEngine::seq_segaSetupSequence(int sequenceId) {
 	if (_flags.platform != Common::kPlatformSegaCD || sequenceId == -1)
 		return;
 
-	_screen->sega_fadeToBlack(1);
-	for (int i = 0; i < 6; i++) {
-		_characters[i].damageTaken = 0;
-		_characters[i].slotStatus[0] = _characters[i].slotStatus[1] = 0;
-		gui_drawCharPortraitWithStats(i);
+	if (sequenceId != 53 && sequenceId != 54) {
+		gui_resetAnimations();
+		for (int i = 0; i < 6; i++) {
+			_characters[i].damageTaken = 0;
+			_characters[i].slotStatus[0] = _characters[i].slotStatus[1] = 0;
+			gui_drawCharPortraitWithStats(i);
+		}
 	}
 
+	_screen->sega_fadeToBlack(1);
+	_screen->clearPage(0);
+
 	// transposeScreenOutputY(0);
 	_screen->sega_getRenderer()->setupWindowPlane(0, (sequenceId == 53 || sequenceId == 54) ? 23 : 18, SegaRenderer::kWinToRight, SegaRenderer::kWinToBottom);
 	_screen->sega_getRenderer()->memsetVRAM(0xD840, 0xEE, 512);
@@ -2713,6 +2754,9 @@ void EoBEngine::seq_segaSetupSequence(int sequenceId) {
 }
 
 void EoBEngine::seq_segaRestoreAfterSequence() {
+	if (_flags.platform != Common::kPlatformSegaCD)
+		return;
+
 	SegaRenderer *r = _screen->sega_getRenderer();
 	_screen->sega_fadeToBlack(1);
 	_screen->sega_getAnimator()->clearSprites();
@@ -2723,14 +2767,14 @@ void EoBEngine::seq_segaRestoreAfterSequence() {
 	_screen->clearPage(0);
 }
 
-bool EoBEngine::seq_segaPlaySequence(int sequenceId, bool init) {
+bool EoBEngine::seq_segaPlaySequence(int sequenceId, bool setupScreen) {
 	if (_flags.platform != Common::kPlatformSegaCD)
 		return true;
 
 	_allowSkip = true;
 	resetSkipFlag();
 
-	if (init)
+	if (setupScreen)
 		seq_segaSetupSequence(sequenceId);
 
 	_allowSkip = false;
@@ -2739,7 +2783,9 @@ bool EoBEngine::seq_segaPlaySequence(int sequenceId, bool init) {
 	if (!_seqPlayer->play(sequenceId))
 		return false;
 
-	seq_segaRestoreAfterSequence();
+	if (setupScreen)
+		seq_segaRestoreAfterSequence();
+
 	return true;
 }
 
diff --git a/engines/kyra/text/text_eob_segacd.cpp b/engines/kyra/text/text_eob_segacd.cpp
index d7ecc22a25..a89b193520 100644
--- a/engines/kyra/text/text_eob_segacd.cpp
+++ b/engines/kyra/text/text_eob_segacd.cpp
@@ -29,7 +29,7 @@
 
 namespace Kyra {
 
-TextDisplayer_SegaCD::TextDisplayer_SegaCD(EoBEngine *engine, Screen_EoB *scr) : TextDisplayer_rpg(engine, scr), _screen(scr), _renderer(scr->sega_getRenderer()),
+TextDisplayer_SegaCD::TextDisplayer_SegaCD(EoBEngine *engine, Screen_EoB *scr) : TextDisplayer_rpg(engine, scr), _engine(engine), _screen(scr), _renderer(scr->sega_getRenderer()),
 	_curDim(0), _textColor(0xFF), _curPosY(0), _curPosX(0) {
 	assert(_renderer);
 }
@@ -37,6 +37,35 @@ TextDisplayer_SegaCD::TextDisplayer_SegaCD(EoBEngine *engine, Screen_EoB *scr) :
 TextDisplayer_SegaCD::~TextDisplayer_SegaCD() {
 }
 
+void TextDisplayer_SegaCD::printDialogueText(int id, const char *string1, const char *string2) {
+	if (string1 && string2) {
+		_engine->runDialogue(id, 2, 2, string1, string2);
+	} else {
+		_screen->hideMouse();
+		_engine->seq_segaPlaySequence(id);
+		_screen->showMouse();
+	}
+}
+
+void TextDisplayer_SegaCD::printDialogueText(const char *str, bool wait) {
+	int cs = _screen->setFontStyles(Screen::FID_8_FNT, _vm->gameFlags().lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleFat | Font::kStyleForceTwoByte);
+	clearDim(_curDim);
+
+	if (wait) {
+		printShadowedText(str, 32, 12);
+		_engine->resetSkipFlag();
+		_renderer->render(0);
+		_screen->updateScreen();
+		_engine->delay(500);
+	} else {
+		printShadowedText(str, 0, 0);
+		_renderer->render(0);
+		_screen->updateScreen();
+	}
+
+	_screen->setFontStyles(Screen::FID_8_FNT, cs);
+}
+
 void TextDisplayer_SegaCD::printShadowedText(const char *str, int x, int y, int textColor, int shadowColor, int pitchW, int pitchH, int marginRight, bool screenUpdate) {
 	const ScreenDim *s = &_dimTable[_curDim];
 	if (x == -1)
diff --git a/engines/kyra/text/text_eob_segacd.h b/engines/kyra/text/text_eob_segacd.h
index 01b58dbfc0..309ce62290 100644
--- a/engines/kyra/text/text_eob_segacd.h
+++ b/engines/kyra/text/text_eob_segacd.h
@@ -37,6 +37,8 @@ public:
 	TextDisplayer_SegaCD(EoBEngine *engine, Screen_EoB *scr);
 	virtual ~TextDisplayer_SegaCD();
 
+	void printDialogueText(int id, const char *string1, const char *string2) override;
+	void printDialogueText(const char *str, bool wait = false) override;
 	void printShadowedText(const char *str, int x = -1, int y = -1, int textColor = -1, int shadowColor = -1, int pitchW = -1, int pitchH = -1, int marginRight = 0, bool screenUpdate = true) override;
 	int clearDim(int dim) override;
 
@@ -47,6 +49,8 @@ private:
 
 	Screen_EoB *_screen;
 	SegaRenderer *_renderer;
+	EoBEngine *_engine;
+
 	int _curDim;
 	int _curPosY;
 	int _curPosX;
diff --git a/engines/kyra/text/text_rpg.cpp b/engines/kyra/text/text_rpg.cpp
index 1f81c8b4f1..87513bb0dc 100644
--- a/engines/kyra/text/text_rpg.cpp
+++ b/engines/kyra/text/text_rpg.cpp
@@ -537,7 +537,7 @@ void TextDisplayer_rpg::printLine(char *str) {
 	printLine(str);
 }
 
-void TextDisplayer_rpg::printDialogueText(int stringId, const char *pageBreakString) {
+void TextDisplayer_rpg::printDialogueText(int stringId, const char *pageBreakString, const char*) {
 	const char *str = (const char *)(_screen->getCPagePtr(5) + READ_LE_UINT16(&_screen->getCPagePtr(5)[(stringId - 1) << 1]));
 	assert(strlen(str) < kEoBTextBufferSize);
 	Common::strlcpy(_dialogueBuffer, str, kEoBTextBufferSize);
diff --git a/engines/kyra/text/text_rpg.h b/engines/kyra/text/text_rpg.h
index 9217420136..d4740f8a6a 100644
--- a/engines/kyra/text/text_rpg.h
+++ b/engines/kyra/text/text_rpg.h
@@ -39,8 +39,8 @@ public:
 
 	void setupField(int dim, bool mode);
 
-	void printDialogueText(int stringId, const char *pageBreakString);
-	void printDialogueText(const char *str, bool wait = false);
+	virtual void printDialogueText(int stringId, const char *pageBreakString, const char *pageBreakString2 = 0);
+	virtual void printDialogueText(const char *str, bool wait = false);
 	void printMessage(const char *str, int textColor = -1, ...);
 	virtual void printShadowedText(const char *str, int x = -1, int y = -1, int textColor = -1, int shadowColor = -1, int pitchW = -1, int pitchH = -1, int marginRight = 0, bool screenUpdate = true) {}
 


Commit: 5d1801750925f53c055bb403f986787585977121
    https://github.com/scummvm/scummvm/commit/5d1801750925f53c055bb403f986787585977121
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:15+02:00

Commit Message:
KYRA: (EOB/SegaCD) - fix NPC portrait font glitch

Changed paths:
    engines/kyra/graphics/screen_eob_segacd.cpp
    engines/kyra/gui/gui_eob_segacd.cpp
    engines/kyra/resource/staticres_eob.cpp


diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index 6c498a3826..a827becfb6 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -956,6 +956,8 @@ template<bool hflip> void SegaRenderer::renderLineFragment(uint8 *dst, uint8 *ma
 }
 #endif
 
+#undef mRenderLineFragment
+
 void SegaRenderer::checkUpdateDirtyRects(int addr, int len) {
 	//void *tbl[] = { _vram, _hScrollTable, , _planes[kPlaneA].nameTable, _planes[kPlaneB].nameTable, _planes[kWindowPlane].nameTable };
 	addDirtyRect(0, 0, _screenW, _screenH);
@@ -1270,8 +1272,6 @@ const uint8 *SegaCDFont::getGlyphData(uint16 c, uint8 &charWidth, uint8 &charHei
 	return res;
 }
 
-#undef mRenderLineFragment
-
 ScrollManager::ScrollManager(SegaRenderer *renderer) :_renderer(renderer) {
 	_vScrollTimers = new ScrollTimer[2];
 	assert(_vScrollTimers);
diff --git a/engines/kyra/gui/gui_eob_segacd.cpp b/engines/kyra/gui/gui_eob_segacd.cpp
index fceccc9381..9b321ead4e 100644
--- a/engines/kyra/gui/gui_eob_segacd.cpp
+++ b/engines/kyra/gui/gui_eob_segacd.cpp
@@ -425,19 +425,29 @@ void EoBEngine::makeNameShapes(int charId) {
 	_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 30, 28, 0x600A, true);
 	_screen->sega_clearTextBuffer(0);
 
+	uint8 *in = _res->fileData("FACE", 0);
 	for (int i = first; i <= last; ++i) {
 		if (!_characters[i].flags)
 			continue;
-		_txt->printShadowedText(_characters[i].name, 0, i << 4, 0xFF, 0xCC);
+		if (_characters[i].portrait < 0) {
+			_screen->sega_getRenderer()->loadToVRAM(in + 27648 + (-_characters[i].portrait - 1) * 224, 224, 0x3F00 + i * 0xE0);
+			_screen->sega_getRenderer()->fillRectWithTiles(0, 0, i << 1, 7, 1, 0x61F8 + i * 7, true);
+		} else {
+			_txt->printShadowedText(_characters[i].name, 0, i << 4, 0xFF, 0xCC);
+		}
 	}
+	delete[] in;
 
 	_screen->sega_getRenderer()->render(_screen->_curPage);
+	_screen->sega_getRenderer()->render(0);
+	_screen->updateScreen();
+	_screen->sega_fadeToNeutral(0);
 
 	for (int i = first; i <= last; ++i) {
 		if (!_characters[i].flags)
 			continue;
 		delete[] _characters[i].nameShape;
-		_characters[i].nameShape = _screen->encodeShape(0, i << 4, (_screen->getTextWidth(_characters[i].name) + 8) >> 3, 13);
+		_characters[i].nameShape = _screen->encodeShape(0, i << 4, 8, 13);
 	}
 
 	_screen->clearPage(2);
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index 6a6cf6a49c..549efc7010 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -671,8 +671,8 @@ void EoBCoreEngine::initButtonData() {
 		{ 0, 0, 0x1100, 146, 168, 32, 10, 0 },
 		{ 0, 0, 0x1100, 296, 56, 16, 16, 27 },
 
-		{ 0, 0, 0x1100, 248, 152, 64, 14, -1 },
-		{ 0, 0, 0x1100, 248, 168, 64, 14, 1 },
+		{ 101, 96, 0x1100, 248, 152, 64, 14, -1 },
+		{ 103, 98, 0x1100, 248, 168, 64, 14, 1 },
 		{ 110, 0, 0x1100, 248, 184, 64, 14, 2 }
 	};
 


Commit: 1db312cd3b682ec1a62c6eb6ac19a57baebc48cf
    https://github.com/scummvm/scummvm/commit/1db312cd3b682ec1a62c6eb6ac19a57baebc48cf
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:15+02:00

Commit Message:
KYRA: (EOB/SegaCD) - fix spellbook and magic effects

- implement/fix spellbook drawing
- fix various magic effect gfx
- also fix level 12 door switches

Changed paths:
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eob.h
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h
    engines/kyra/engine/magic_eob.cpp
    engines/kyra/engine/scene_eob.cpp
    engines/kyra/engine/sprites_eob.cpp
    engines/kyra/gui/debugger.cpp
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/gui/gui_eob_segacd.cpp
    engines/kyra/resource/staticres_eob.cpp
    engines/kyra/sequence/seqplayer_eob_segacd.cpp


diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index 4bd7c56937..d1d7a26695 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -55,7 +55,7 @@ EoBEngine::EoBEngine(OSystem *system, const GameFlags &flags)
 	_useMainMenuGUISettings = false;
 	_addrTbl1 = _textFieldPattern = 0;
 	_playFldPattern1 = _invPattern = _statsPattern;
-	_playFldPattern2 = _statsPattern2 = 0;
+	_playFldPattern2 = _tempPattern = 0;
 	_ttlCfg = 0;
 	_xdth = false;
 
@@ -63,7 +63,7 @@ EoBEngine::EoBEngine(OSystem *system, const GameFlags &flags)
 	_sceneShakeOffsetX = _sceneShakeOffsetY = 0;
 	_shakeBackBuffer1 = _shakeBackBuffer2 = 0;
 	_compassDirection2 = _compassAnimDest = _compassAnimPhase = _compassAnimStep = _compassAnimDelayCounter = 0;
-	_compassAnimSwitch = _compassAnimDone = false;
+	_compassAnimSwitch = _compassAnimDone = _compassTilesRestore = false;
 	_redGrid = _charTilesTable = 0;
 	_compassData = 0;
 	_mapStrings1 = _mapStrings2 = _mapStrings3 = 0;
@@ -81,13 +81,13 @@ EoBEngine::~EoBEngine() {
 
 	releaseShpArr(_weaponSlotShapes, 6);
 	releaseShpArr(_invSmallDigits, 32);
-	delete[] _redGrid;
 
+	delete[] _redGrid;
 	delete[] _doorShapesSrc;
 	delete[] _doorSwitchShapesSrc;
 	delete[] _itemsOverlay;
 	delete[] _playFldPattern2;
-	delete[] _statsPattern2;
+	delete[] _tempPattern;
 	delete[] _shakeBackBuffer1;
 	delete[] _shakeBackBuffer2;
 	delete[] _compassData;
@@ -155,10 +155,11 @@ Common::Error EoBEngine::init() {
 		_txt = new TextDisplayer_SegaCD(this, _screen);
 		assert(_txt);
 		_playFldPattern2 = new uint16[1040];
-		_statsPattern2 = new uint16[792];
+		_tempPattern = new uint16[792];
 		_shakeBackBuffer1 = new uint8[120 * 6];
 		_shakeBackBuffer2 = new uint8[179 * 6];
 		_compassData = new uint8[0x5000];
+		_closeSpellbookAfterUse = false;
 	}
 
 	return Common::kNoError;
@@ -214,7 +215,7 @@ void EoBEngine::loadItemsAndDecorationsShapes() {
 
 	loadAndConvertShapes(1, 0, _smallItemShapes, _numSmallItemShapes, 32, 24, 768);
 	loadAndConvertShapes(2, 0, _largeItemShapes, _numLargeItemShapes, 64, 24, 1472);
-	loadAndConvertShapes(3, 0, _sparkShapes, 3, 16, 16, 128);
+	loadAndConvertShapes(3, 0, _sparkShapes, 4, 16, 16, 128);
 	loadAndConvertShapes(11, 0, _thrownItemShapes, _numThrownItemShapes, 32, 24, 768);
 	int offset1 = 0, offset2 = 0;
 	for (int i = 0; i < 3; ++i) {
@@ -231,7 +232,9 @@ void EoBEngine::loadItemsAndDecorationsShapes() {
 	loadSpritesAndEncodeToShapes(5, 480, _weaponSlotShapes, 6, 32, 16);
 	loadSpritesAndEncodeToShapes(6, 0, _invSmallDigits, 32, 16, 8);
 
-
+	_teleporterShapes = new const uint8*[6];
+	for (int i = 0; i < 6; ++i)
+		_teleporterShapes[i] = _sparkShapes[(i + 1) >> 1];
 
 	int cp = _screen->setCurPage(Screen_EoB::kSegaInitShapesPage);
 	for (int i = 0; i < 4; ++i)
@@ -739,7 +742,8 @@ void EoBEngine::loadDoorShapes(int doorType1, int shapeId1, int doorType2, int s
 				int offs = lvlIndex[_currentLevel] * 6 + shapeId[a] + i;
 				const uint8 *enc = &_doorShapeEncodeDefs[offs << 2];
 				_doorShapes[shapeId[a] + i] = _screen->sega_convertShape(_doorShapesSrc[offs], enc[0] << 3, enc[1] << 3, 0, enc[2] - enc[3]);
-				enc = &_doorSwitchShapeEncodeDefs[(offs - shapeId[a]) << 2];
+				offs = lvlIndex[_currentLevel] * 3 + i;
+				enc = &_doorSwitchShapeEncodeDefs[offs << 2];
 				_doorSwitches[shapeId[a] + i].shp = _screen->sega_convertShape(_doorSwitchShapesSrc[offs], enc[0] << 3, enc[1] << 3, 0, enc[2] - enc[3]);
 			} else {
 				const uint8 *enc = &_doorShapeEncodeDefs[(doorType[a] * 3 + i) << 2];
diff --git a/engines/kyra/engine/eob.h b/engines/kyra/engine/eob.h
index 7638d91ec0..5b8c0d7f56 100644
--- a/engines/kyra/engine/eob.h
+++ b/engines/kyra/engine/eob.h
@@ -198,12 +198,14 @@ private:
 	void gui_printInventoryDigits(int x, int y, int val) override;
 	void gui_drawCharacterStatsPage() override;
 	void gui_displayMap() override;
+	void gui_drawSpellbook() override;
 	void gui_updateAnimations() override;
 	void gui_resetAnimations();
 
 	void makeNameShapes(int charId = -1) override;
 	void makeFaceShapes(int charId = -1) override;
 	void printStatsString(const char *str, int x, int y);
+	void printSpellbookString(uint16 *dst, const char *str, uint16 ntbl);
 	void drawMapButton(const char *str, int x, int y);
 	void drawMapPage(int level);
 	void drawMapSpots(int level, int animState);
@@ -219,6 +221,7 @@ private:
 	int _compassAnimDelayCounter;
 	bool _compassAnimSwitch;
 	bool _compassAnimDone;
+	bool _compassTilesRestore;
 	uint8 *_compassData;
 
 	const char *const *_mapStrings1;
@@ -233,7 +236,7 @@ private:
 	const uint16 *_statsPattern;
 	const uint8 *_charTilesTable;
 	uint16 *_playFldPattern2;
-	uint16 *_statsPattern2;
+	uint16 *_tempPattern;
 
 	static const KyraRpgGUISettings _guiSettingsVGA;
 	static const KyraRpgGUISettings _guiSettingsEGA;
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 8ef80ea5fc..616af2a1e9 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -240,6 +240,7 @@ EoBCoreEngine::EoBCoreEngine(OSystem *system, const GameFlags &flags) : KyraRpgE
 	_prefMenuPlatformOffset = 0;	
 	_lastVIntTick = _lastSecTick = _totalPlaySecs = _totalEnemiesKilled = _totalSteps = 0;
 	_levelMaps = 0;
+	_closeSpellbookAfterUse = false;
 
 	memset(_cgaMappingLevel, 0, sizeof(_cgaMappingLevel));
 	memset(_expRequirementTables, 0, sizeof(_expRequirementTables));
@@ -591,7 +592,6 @@ void EoBCoreEngine::loadFonts() {
 		_conFont = _invFont3 = Screen::FID_SJIS_FNT;
 	} else if (_flags.platform == Common::kPlatformSegaCD) {
 		_screen->loadFont(Screen::FID_8_FNT, "FONTK12");
-		//_screen->loadFont(Screen::FID_6_FNT, "FONT8SH");
 		_screen->setFontStyles(Screen::FID_8_FNT, _flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleFat);
 		_invFont1 = _invFont2 = _conFont = Screen::FID_8_FNT;
 	}
@@ -851,7 +851,7 @@ void EoBCoreEngine::loadItemsAndDecorationsShapes() {
 	_screen->loadShapeSetBitmap("ITEMICN", 5, 3);
 	for (int i = 0; i < _numItemIconShapes; i++)
 		_itemIconShapes[i] = _screen->encodeShape((i % 0x14) << 1, (i / 0x14) << 4, 2, 0x10, false, _cgaMappingIcons);
-		
+
 	if (_flags.platform == Common::kPlatformAmiga) {
 		const uint8 offsY = (_flags.gameID == GI_EOB1) ? 80 : 96;
 		_blueItemIconShapes = new const uint8*[_numItemIconShapes];
@@ -863,7 +863,7 @@ void EoBCoreEngine::loadItemsAndDecorationsShapes() {
 	}
 
 	_teleporterShapes = new const uint8*[6];
-	_sparkShapes = new const uint8*[3];
+	_sparkShapes = new const uint8*[4];
 	_compassShapes = new const uint8*[12];
 	if (_flags.gameID == GI_EOB2)
 		_wallOfForceShapes = new const uint8*[6];
@@ -881,6 +881,7 @@ void EoBCoreEngine::loadItemsAndDecorationsShapes() {
 	_sparkShapes[0] = _screen->encodeShape(29, 0, 2, 16, false, _cgaMappingDeco);
 	_sparkShapes[1] = _screen->encodeShape(31, 0, 2, 16, false, _cgaMappingDeco);
 	_sparkShapes[2] = _screen->encodeShape(33, 0, 2, 16, false, _cgaMappingDeco);
+	_sparkShapes[3] = 0;
 	_deadCharShape = _screen->encodeShape(0, 88, 4, 32, false, _cgaMappingDeco);
 	_disabledCharGrid = _screen->encodeShape(4, 88, 4, 32, false, _cgaMappingDeco);
 	_blackBoxSmallGrid = _screen->encodeShape(9, 88, 2, 8, false, _cgaMappingDeco);
@@ -905,12 +906,16 @@ void EoBCoreEngine::releaseItemsAndDecorationsShapes() {
 		releaseShpArr(_itemIconShapes, _numItemIconShapes);
 		releaseShpArr(_blueItemIconShapes, _numItemIconShapes);
 		releaseShpArr(_xtraItemIconShapes, 3);
-		releaseShpArr(_sparkShapes, 3);
+		releaseShpArr(_sparkShapes, 4);
 		releaseShpArr(_wallOfForceShapes, 6);
-		releaseShpArr(_teleporterShapes, 6);
 		releaseShpArr(_compassShapes, 12);
 		releaseShpArr(_firebeamShapes, 3);
 
+		// SegaCD uses the spark shapes for drawing the teleporters. We copy only the pointers, not the whole shapes.
+		if (_flags.platform != Common::kPlatformSegaCD) {
+			releaseShpArr(_teleporterShapes, 6);
+		}
+
 		delete[] _redSplatShape;
 		delete[] _greenSplatShape;
 		delete[] _swapShape;
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index 98eaf7e284..ec24cca0c8 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -748,7 +748,7 @@ protected:
 	virtual void gui_displayMap() {}
 	void gui_drawCompass(bool force);
 	void gui_drawDialogueBox();
-	void gui_drawSpellbook();
+	virtual void gui_drawSpellbook();
 	void gui_drawSpellbookScrollArrow(int x, int y, int direction);
 	void gui_updateSlotAfterScrollUse();
 	void gui_updateControls();
@@ -963,6 +963,8 @@ protected:
 	bool _allowSkip;
 	bool _allowImport;
 
+	bool _closeSpellbookAfterUse;
+
 	Screen_EoB *_screen;
 	GUI_EoB *_gui;
 
@@ -1167,6 +1169,8 @@ protected:
 	int _clericSpellOffset;
 	const char *const *_clericSpellList;
 	const char *const *_spellNames;
+	const char *const *_mageSpellList2;
+	const char *const *_clericSpellList2;	
 	const char *const *_magicStrings1;
 	const char *const *_magicStrings2;
 	const char *const *_magicStrings3;
diff --git a/engines/kyra/engine/magic_eob.cpp b/engines/kyra/engine/magic_eob.cpp
index bdd2c9080e..134371972a 100644
--- a/engines/kyra/engine/magic_eob.cpp
+++ b/engines/kyra/engine/magic_eob.cpp
@@ -35,7 +35,7 @@ void EoBCoreEngine::useMagicBookOrSymbol(int charIndex, int type) {
 	_openBookSpellListOffset = c->slotStatus[4];
 	_openBookChar = charIndex;
 	_openBookType = type;
-	_openBookSpellList = (type == 1) ? _clericSpellList : _mageSpellList;
+	_openBookSpellList = (type == 1) ? _clericSpellList2 : _mageSpellList2;
 	_openBookAvailableSpells = (type == 1) ? c->clericSpells : c->mageSpells;
 	int8 *tmp = _openBookAvailableSpells + _openBookSpellLevel * 10 + _openBookSpellListOffset + _openBookSpellSelectedItem;
 
@@ -60,7 +60,7 @@ void EoBCoreEngine::useMagicBookOrSymbol(int charIndex, int type) {
 	}
 
 	if (!_updateFlags)
-		_screen->copyRegion(64, 121, 0, 0, 112, 56, 0, Screen_EoB::kSpellbookBackupPage, Screen::CR_NO_P_CHECK);
+		_screen->copyRegion(64, _flags.platform == Common::kPlatformSegaCD ? 120 : 121, 0, 0, 112, 56, 0, Screen_EoB::kSpellbookBackupPage, Screen::CR_NO_P_CHECK);
 	_updateFlags = 1;
 	gui_setPlayFieldButtons();
 	gui_drawSpellbook();
@@ -238,7 +238,7 @@ void EoBCoreEngine::removeCharacterEffect(int spell, int charIndex, int showWarn
 
 	if (showWarning) {
 		int od = _screen->curDimIndex();
-		Screen::FontId of = _screen->setFont(_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT);
+		Screen::FontId of = _screen->setFont(_conFont);
 		_screen->setScreenDim(7);
 		printWarning(Common::String::format(_magicStrings3[_flags.gameID == GI_EOB1 ? 3 : 2], c->name, s->name).c_str());
 		_screen->setScreenDim(od);
@@ -315,6 +315,8 @@ void EoBCoreEngine::startSpell(int spell) {
 		sparkEffectOffensive();
 
 	if (s->flags & 0x20) {
+		if (_flags.platform == Common::kPlatformSegaCD)
+			_txt->printMessage(_magicStrings3[1]);
 		_txt->printMessage(c->name);
 		_txt->printMessage(_flags.gameID == GI_EOB1 ? _magicStrings3[1] : _magicStrings1[5]);
 	}
@@ -367,10 +369,16 @@ void EoBCoreEngine::startSpell(int spell) {
 	if (_castScrollSlot) {
 		gui_updateSlotAfterScrollUse();
 	} else {
-		_characters[_openBookChar].disabledSlots |= 4;
-		setCharEventTimer(_openBookChar, 72, 11, 1);
-		gui_toggleButtons();
-		gui_drawSpellbook();
+		// The SegaCD version closes the spell book after each spell instead of disabling it for a short period.
+		if (_closeSpellbookAfterUse) {
+			Button b;
+			clickedSpellbookAbort(&b);
+		} else {
+			_characters[_openBookChar].disabledSlots |= 4;
+			setCharEventTimer(_openBookChar, 72, 11, 1);
+			gui_toggleButtons();
+			gui_drawSpellbook();
+		}
 	}
 
 	if (_flags.gameID == GI_EOB2) {
@@ -405,8 +413,8 @@ void EoBCoreEngine::sparkEffectDefensive(int charIndex) {
 				int x = _sparkEffectDefAdd[iii * 2] - 8;
 				int y = _sparkEffectDefAdd[iii * 2 + 1];
 				if (_currentControlMode) {
-					x += 181;
-					y += 3;
+					x += guiSettings()->charBoxCoords.facePosX_2[0];
+					y += guiSettings()->charBoxCoords.facePosY_2[0];
 				} else {
 					x += (_sparkEffectDefX[ii] << 3);
 					y += _sparkEffectDefY[ii];
diff --git a/engines/kyra/engine/scene_eob.cpp b/engines/kyra/engine/scene_eob.cpp
index 4061a1a588..5e7e4ee98b 100644
--- a/engines/kyra/engine/scene_eob.cpp
+++ b/engines/kyra/engine/scene_eob.cpp
@@ -547,7 +547,7 @@ void EoBCoreEngine::drawScene(int refresh) {
 
 	if (_sceneDrawPage2) {
 		if (refresh)
-			_screen->fillRect(0, 0, 176, 120, guiSettings()->colors.guiColorBlack);
+			_screen->fillRect(0, 0, 175, 119, guiSettings()->colors.guiColorBlack);
 
 		if (!_loading)
 			_screen->setScreenPalette(_screen->getPalette(0));
diff --git a/engines/kyra/engine/sprites_eob.cpp b/engines/kyra/engine/sprites_eob.cpp
index e90c63dcb9..f849a6b008 100644
--- a/engines/kyra/engine/sprites_eob.cpp
+++ b/engines/kyra/engine/sprites_eob.cpp
@@ -684,7 +684,6 @@ void EoBCoreEngine::drawTeleporter(int index) {
 	int16 y1 = telprtY[t];
 
 	for (int i = 0; i < 2; i++) {
-
 		int16 x2 = 0;
 		int16 y2 = 0;
 		int d = (t << 1) + i;
diff --git a/engines/kyra/gui/debugger.cpp b/engines/kyra/gui/debugger.cpp
index 75cc6733bf..b74aefe759 100644
--- a/engines/kyra/gui/debugger.cpp
+++ b/engines/kyra/gui/debugger.cpp
@@ -516,6 +516,11 @@ bool Debugger_EoB::cmdImportSaveFile(int argc, const char **argv) {
 }
 
 bool Debugger_EoB::cmdSaveOriginal(int argc, const char **argv) {
+	if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
+		debugPrintf("Command not supported for this version.\n");
+		return true;
+	}
+
 	if (!_vm->_runFlag) {
 		debugPrintf("This command doesn't work during intro or outro sequences,\nfrom the main menu or from the character generation.\n");
 		return true;
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 419387cd3e..2fc935284d 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -424,6 +424,10 @@ void EoBCoreEngine::gui_drawHorizontalBarGraph(int x, int y, int w, int h, int32
 }
 
 void EoBCoreEngine::gui_drawCharPortraitStatusFrame(int index) {
+	// Apparently the SegaCD version doesn't have these status frames.
+	if (_flags.platform == Common::kPlatformSegaCD)
+		return;
+
 	uint8 redGreenColor = (_partyEffectFlags & 0x20000) ? guiSettings()->colors.guiColorLightGreen : ((_configRenderMode == Common::kRenderCGA) ? 3 : guiSettings()->colors.guiColorLightRed);
 	int x = guiSettings()->charBoxCoords.facePosX_1[index & 1];
 	int y = guiSettings()->charBoxCoords.boxY[index >> 1];
@@ -1296,7 +1300,7 @@ int EoBCoreEngine::clickedSpellbookAbort(Button *button) {
 	_updateFlags = 0;
 	_screen->fillRect(64, 121, 175, 176, 0, 0);
 	_screen->fillRect(64, 121, 175, 176, 0, 2);
-	_screen->copyRegion(0, 0, 64, 121, 112, 56, Screen_EoB::kSpellbookBackupPage, 0, Screen::CR_NO_P_CHECK);
+	_screen->copyRegion(0, 0, 64, _flags.platform == Common::kPlatformSegaCD ? 120 : 121, 112, 56, Screen_EoB::kSpellbookBackupPage, 0, Screen::CR_NO_P_CHECK);
 	_screen->updateScreen();
 	gui_drawCompass(true);
 	gui_toggleButtons();
diff --git a/engines/kyra/gui/gui_eob_segacd.cpp b/engines/kyra/gui/gui_eob_segacd.cpp
index 9b321ead4e..30117d169f 100644
--- a/engines/kyra/gui/gui_eob_segacd.cpp
+++ b/engines/kyra/gui/gui_eob_segacd.cpp
@@ -65,7 +65,7 @@ void EoBEngine::gui_drawPlayField(bool refresh) {
 		*dst++ = (ix < 0) ? 0 : _addrTbl1[ix];
 	}
 
-	const uint16 ps[] = { 0xCE, 0xE0, 0x2FE, 0x310, 0x52E, 0x540 };
+	static const uint16 ps[6] = { 0xCE, 0xE0, 0x2FE, 0x310, 0x52E, 0x540 };
 
 	for (int i = 0; i < 4; ++i) {
 		dst = &_playFldPattern2[ps[i] >> 1];
@@ -82,6 +82,10 @@ void EoBEngine::gui_drawPlayField(bool refresh) {
 	str = _sres->resStreamEndian(9);
 	r->loadStreamToVRAM(str, 0xA4A0, false);
 	delete str;
+	str = _sres->resStreamEndian(10);
+	r->loadStreamToVRAM(str, 0x7920, false);
+	delete str;
+
 	/*
 // CAMP MENU
 str = _sres->resStreamEndian(8);
@@ -164,18 +168,23 @@ void EoBEngine::gui_printInventoryDigits(int x, int y, int val) {
 		EoBCoreEngine::gui_printInventoryDigits(x, y, val);
 		return;
 	}
-	_screen->drawShape(_screen->_curPage, _invSmallDigits[(val < 10) ? 22 + val : (val >= 100 ? 1 : 2 + val / 10)], x, y);
-	_screen->drawShape(_screen->_curPage, (val >= 10 && val < 100) ? _invSmallDigits[12 + (val % 10)] : 0, x, y);
+	_screen->drawShape(_screen->_curPage, _invSmallDigits[(val < 10) ? 22 + val : (val >= 100 ? 1 : 2 + val / 10)], x, y, 0);
+	_screen->drawShape(_screen->_curPage, (val >= 10 && val < 100) ? _invSmallDigits[12 + (val % 10)] : 0, x, y, 0);
 }
 
 void EoBEngine::gui_drawCharacterStatsPage() {
+	if (_flags.platform != Common::kPlatformSegaCD) {
+		EoBCoreEngine::gui_drawCharacterStatsPage();
+		return;
+	}
+
 	SegaRenderer *r = _screen->sega_getRenderer();
 	EoBCharacter *c = &_characters[_updateCharNum];
 
-	memset(_statsPattern2, 0, 792);
+	memset(_tempPattern, 0, 792);
 	for (int i = 0; i < 11; ++i) {
-		_statsPattern2[5 * 18 + i + 1] = 0x6555 + i;
-		_statsPattern2[6 * 18 + i + 1] = 0x6565 + i;
+		_tempPattern[5 * 18 + i + 1] = 0x6555 + i;
+		_tempPattern[6 * 18 + i + 1] = 0x6565 + i;
 	}
 
 	for (int i = 0; i < 4; i++)
@@ -206,7 +215,7 @@ void EoBEngine::gui_drawCharacterStatsPage() {
 		printStatsString(Common::String::format("%6d", c->experience[i]).c_str(), 7, 17 + i);
 	}
 
-	r->fillRectWithTiles(0, 22, 0, 18, 21, 0, true, true, _statsPattern2);
+	r->fillRectWithTiles(0, 22, 0, 18, 21, 0, true, true, _tempPattern);
 	r->render(Screen_EoB::kSegaRenderPage);
 
 	_screen->copyRegion(176, 40, 176, 40, 144, 128, Screen_EoB::kSegaRenderPage, 2, Screen::CR_NO_P_CHECK);
@@ -305,6 +314,51 @@ void EoBEngine::gui_displayMap() {
 	_totalPlaySecs += ((_system->getMillis() - startTime) / 1000);
 }
 
+void EoBEngine::gui_drawSpellbook() {
+	if (_flags.platform != Common::kPlatformSegaCD) {
+		EoBCoreEngine::gui_drawSpellbook();
+		return;
+	}
+
+	SegaRenderer *r = _screen->sega_getRenderer();
+	r->fillRectWithTiles(0, 10, 15, 12, 7, 0);
+	r->fillRectWithTiles(1, 10, 15, 12, 7, 0x6429);
+	memset(_tempPattern, 0, 168);
+	uint16 *dst = _tempPattern;
+
+	for (int i = 0; i < 6; ++i) {
+		dst[0] = 0x642B + 2 * i + (i == _openBookSpellLevel ? 0 : 12);
+		dst[1] = dst[0] + 1;
+		dst += 2;
+	}
+
+	for (int i = 0; i < 6; ++i) {
+		int d = _openBookAvailableSpells[_openBookSpellLevel * 10 + i];
+		if (d < 0)
+			continue;
+		printSpellbookString(&_tempPattern[(i + 1) * 12], _openBookSpellList[d], 0x63C9);
+	}
+
+	r->fillRectWithTiles(0, 10, 15, 12, 6, 0, true, false, _tempPattern);
+	r->render(Screen_EoB::kSegaRenderPage);
+
+	// The original SegaCD version actually doesn't disable the spell book after use but closes it instead.
+	if (!_closeSpellbookAfterUse) {
+		if (_characters[_openBookChar].disabledSlots & 4) {
+			static const uint8 xpos[] = { 0x44, 0x62, 0x80, 0x90 };
+			static const uint8 ypos[] = { 0x80, 0x90, 0xA0 };
+			for (int yc = 0; yc < 3; yc++) {
+				for (int xc = 0; xc < 4; xc++)
+					_screen->drawShape(Screen_EoB::kSegaRenderPage, _weaponSlotGrid, xpos[xc], ypos[yc], 0);
+			}
+		}
+	}
+
+	_screen->copyRegion(80, 120, 80, 120, 96, 56, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+	if (!_loading)
+		_screen->updateScreen();
+}
+
 void EoBEngine::gui_updateAnimations() {
 	if (_flags.platform != Common::kPlatformSegaCD)
 		return;
@@ -344,6 +398,14 @@ void EoBEngine::gui_updateAnimations() {
 			redrawCompass = true;
 		}
 	}
+	if (_updateFlags)
+		_compassTilesRestore = true;
+	else if (_compassTilesRestore) {
+		_screen->sega_getRenderer()->fillRectWithTiles(0, 10, 15, 12, 7, 0);
+		for (int i = 15; i < 22; ++i)
+			_screen->sega_getRenderer()->fillRectWithTiles(1, 10, i, 12, 1, 0x2000, true, true, &_playFldPattern2[i * 40 + 10]);
+		_compassTilesRestore = false;
+	}
 	if (redrawCompass) {
 		_screen->sega_getRenderer()->loadToVRAM(_compassData + (_compassAnimPhase & 0x0F) * 0x500, 0x500, 0xEE00);
 		_screen->sega_getRenderer()->render(Screen_EoB::kSegaRenderPage);
@@ -378,6 +440,8 @@ void EoBEngine::gui_updateAnimations() {
 		_screen->fillRect(0, 0, 175, 2, 0, _sceneDrawPage1);
 		_screen->copyBlockToPage(_sceneDrawPage1, 173, 0, 6, 120, _shakeBackBuffer1);
 		_screen->copyBlockToPage(_sceneDrawPage1, 0, 117, 179, 6, _shakeBackBuffer2);
+		if (_updateFlags)
+			_screen->copyRegion(64, 120, 64, 120, 112, 3, Screen_EoB::kSegaRenderPage, _sceneDrawPage1);
 		_screen->copyBlockToPage(_sceneDrawPage1, _sceneXoffset + _sceneShakeOffsetX, _sceneShakeOffsetY, 176, 120, _sceneWindowBuffer);
 
 		// For whatever reason the original shakes all types of shapes (decorations, doors, etc.) except the monsters and
@@ -430,7 +494,7 @@ void EoBEngine::makeNameShapes(int charId) {
 		if (!_characters[i].flags)
 			continue;
 		if (_characters[i].portrait < 0) {
-			_screen->sega_getRenderer()->loadToVRAM(in + 27648 + (-_characters[i].portrait - 1) * 224, 224, 0x3F00 + i * 0xE0);
+			_screen->sega_getRenderer()->loadToVRAM(in + 27424 - _characters[i].portrait * 224, 224, 0x3F00 + i * 0xE0);
 			_screen->sega_getRenderer()->fillRectWithTiles(0, 0, i << 1, 7, 1, 0x61F8 + i * 7, true);
 		} else {
 			_txt->printShadowedText(_characters[i].name, 0, i << 4, 0xFF, 0xCC);
@@ -480,11 +544,19 @@ void EoBEngine::makeFaceShapes(int charId) {
 }
 
 void EoBEngine::printStatsString(const char *str, int x, int y) {
-	uint16 *dst = &_statsPattern2[y * 18 + x];
+	uint16 *dst = &_tempPattern[y * 18 + x];
 	for (const uint8 *pos = (const uint8*)str; *pos; ++pos)
 		*dst++ = 0x6525 + _charTilesTable[*pos];
 }
 
+void EoBEngine::printSpellbookString(uint16 *dst, const char *str, uint16 ntbl) {
+	for (char c = *str++; c; c = *str++) {
+		if (c > 31 && c < 128)
+			*dst = ntbl + c - 32;
+		dst++;
+	}
+}
+
 void EoBEngine::drawMapButton(const char *str, int x, int y) {
 	_screen->sega_drawClippedLine(8, 9, x, y, 64, 14, 0x99);
 	_screen->sega_drawClippedLine(8, 9, x, y + 1, 63, 13, 0xBB);
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index 549efc7010..6093ccab0a 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -428,6 +428,8 @@ void EoBCoreEngine::initStaticResource() {
 	_bookNumbers = _staticres->loadStrings(kEoBBaseBookNumbers, temp);
 	_mageSpellList = _staticres->loadStrings(kEoBBaseMageSpellsList, _mageSpellListSize);
 	_clericSpellList = _staticres->loadStrings(kEoBBaseClericSpellsList, temp);
+	_mageSpellList2 = _staticres->loadStrings(_flags.platform == Common::kPlatformSegaCD ? kEoBBaseMageSpellsList2 : kEoBBaseMageSpellsList, temp);
+	_clericSpellList2 = _staticres->loadStrings(_flags.platform == Common::kPlatformSegaCD ? kEoBBaseClericSpellsList2 : kEoBBaseClericSpellsList, temp);
 	_spellNames = _staticres->loadStrings(kEoBBaseSpellNames, temp);
 
 	_magicStrings1 = _staticres->loadStrings(kEoBBaseMagicStrings1, temp);
@@ -681,21 +683,36 @@ void EoBCoreEngine::initButtonData() {
 
 	// The spellbook buttons in the table above are from EOB II. We make the necessary coordinates modifications for EOB I.
 	if (_flags.gameID == GI_EOB1) {
-		static const EoBGuiButtonDef eob1SpellbookButtonDefs[6] = {
+		static const EoBGuiButtonDef eob1SpellbookButtonDefs[7] = {
 			{ 2, 0, 0x1100, 71, 122, 20, 8, 0 },
 			{ 3, 0, 0x1100, 92, 122, 20, 8, 1 },
 			{ 4, 0, 0x1100, 113, 122, 20, 8, 2 },
 			{ 5, 0, 0x1100, 134, 122, 20, 8, 3 },
 			{ 6, 0, 0x1100, 155, 122, 20, 8, 4 },
-			{ 110, 0, 0x1100, 75, 168, 97, 6, 0 }
+			{ 110, 0, 0x1100, 75, 168, 97, 6, 0 },
+			{ 110, 0, 0x1100, 160, 120, 16, 8, 0 }
 		};
 
 		memcpy(&_buttonDefs[61], eob1SpellbookButtonDefs, 5 * sizeof(EoBGuiButtonDef));
-		memcpy(&_buttonDefs[88], &eob1SpellbookButtonDefs[5], sizeof(EoBGuiButtonDef));
-		for (int i = 67; i < 73; ++i)
+		memcpy(&_buttonDefs[88], &eob1SpellbookButtonDefs[_flags.platform == Common::kPlatformSegaCD ? 6 : 5], sizeof(EoBGuiButtonDef));
+		for (int i = 66; i < 72; ++i)
 			_buttonDefs[i].y++;
 		for (int i = 77; i < 79; ++i)
 			_buttonDefs[i].y++;
+
+		if (_flags.platform == Common::kPlatformSegaCD) {
+			for (int i = 0; i < 5; ++i) {
+				_buttonDefs[61 + i].x = 80 + (i << 4);
+				_buttonDefs[61 + i].y -= 2;
+				_buttonDefs[61 + i].w -= 4;
+			}
+			for (int i = 0; i < 6; ++i) {
+				_buttonDefs[66 + i].x = 80;
+				_buttonDefs[66 + i].y = (16 + i) << 3;
+				_buttonDefs[66 + i].w = 96;
+				_buttonDefs[66 + i].h = 8;
+			}
+		}
 	}
 
 	// Replace keycodes for EOB II FM-Towns
@@ -1321,7 +1338,7 @@ void EoBEngine::initStaticResource() {
 		const uint8 *e1 = _doorShapeEncodeDefs;
 		const uint8 *e2 = _doorSwitchShapeEncodeDefs;
 		const uint8 **doorShapesSrc = new const uint8*[30];
-		const uint8 **doorSwitchShapesSrc = new const uint8*[30];
+		const uint8 **doorSwitchShapesSrc = new const uint8*[15];
 
 		for (int i = 0; i < 5; ++i) {
 			const uint8 *shp = _screen->getCPagePtr(2);
@@ -1331,7 +1348,7 @@ void EoBEngine::initStaticResource() {
 				e1 += 4;
 			}
 			for (int ii = 0; ii < 3; ++ii) {
-				doorSwitchShapesSrc[i * 6 + ii] = doorSwitchShapesSrc[i * 6 + 3 + ii] = shp;
+				doorSwitchShapesSrc[i * 3 + ii] = shp;
 				shp += ((e2[0] * e2[1]) << 5);
 				e2 += 4;
 			}
@@ -1509,7 +1526,7 @@ const KyraRpgGUISettings EoBEngine::_guiSettingsAmigaMainMenu = {
 
 const KyraRpgGUISettings EoBEngine::_guiSettingsSegaCD = {
 	{ _dlgButtonPosX_Sega, _dlgButtonPosY_Sega, 0x66, 0xFF, 90, 14, 2, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
-	{ 135, 130, 132, 180, 0x00, 17, 23, 20, 184, 177, 180, 184, 177, 180, 15, 6, 0x31, 9, 2, 0x35, 4, 0x33, 12 },
+	{ 135, 130, 132, 180, 0x00, 17, 23, 20, 184, 177, 180, 184, 177, 180, 15, 6, 0x31, 9, 2, 0x35, 4, 0x33, 0x3C },
 	{	{ 184, 256, -1}, { 1, 57, 113 }, 64, 55,
 		{ 8, 80, -1 }, { 16, 72, 128 }, { 184, -1, -1 }, { 8, -1, -1 },
 		{ 40, 112, -1 }, { 16, 32, 72, 88, 128, 144 },
diff --git a/engines/kyra/sequence/seqplayer_eob_segacd.cpp b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
index a8d0283c2b..104e050942 100644
--- a/engines/kyra/sequence/seqplayer_eob_segacd.cpp
+++ b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
@@ -219,6 +219,7 @@ bool SegaSequencePlayer::play(int id) {
 		if (!(_playingID == 55 || _playingID == 56))
 			_vm->snd_stopSound();
 		_screen->clearPage(0);
+		_screen->sega_paletteOps(4, 0, 0);
 	}
 
 	_scrollManager->setVScrollTimers(0, 1, 0, 0, 1, 0);


Commit: db1fd2a546e439acf775b2d5f61ebd5574c3f317
    https://github.com/scummvm/scummvm/commit/db1fd2a546e439acf775b2d5f61ebd5574c3f317
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:15+02:00

Commit Message:
KYRA: (EOB/PC-98) - fix detect magic spell

Changed paths:
    engines/kyra/engine/eob.cpp


diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index d1d7a26695..197970ccaa 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -198,6 +198,12 @@ Common::Error EoBEngine::init() {
 void EoBEngine::loadItemsAndDecorationsShapes() {
 	if (_flags.platform != Common::kPlatformSegaCD) {
 		EoBCoreEngine::loadItemsAndDecorationsShapes();
+		if (_flags.platform == Common::kPlatformPC98) {
+			_blueItemIconShapes = new const uint8 * [_numItemIconShapes];
+			_screen->loadShapeSetBitmap("DETECT", 5, 3);
+			for (int i = 0; i < _numItemIconShapes; i++)
+				_blueItemIconShapes[i] = _screen->encodeShape((i % 0x14) << 1, (i / 0x14) << 4, 2, 0x10);
+		}
 		return;
 	}
 


Commit: 03a87dc6db0c352a9e5537f3477a2f768bd85010
    https://github.com/scummvm/scummvm/commit/03a87dc6db0c352a9e5537f3477a2f768bd85010
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:15+02:00

Commit Message:
KYRA: (EOB/SegaCD) - first part of camp menu

- fix preferences menu
- fix party resting

Changed paths:
  A engines/kyra/gui/gui_eob_segacd.h
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eob.h
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h
    engines/kyra/engine/sprites_eob.cpp
    engines/kyra/graphics/screen.cpp
    engines/kyra/graphics/screen_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.h
    engines/kyra/gui/gui_eob_segacd.cpp
    engines/kyra/resource/staticres_eob.cpp
    engines/kyra/sound/sound_segacd_eob.cpp
    engines/kyra/text/text_eob_segacd.cpp
    engines/kyra/text/text_eob_segacd.h


diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index 197970ccaa..ea50d27b62 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -23,6 +23,7 @@
 #ifdef ENABLE_EOB
 
 #include "kyra/engine/eob.h"
+#include "kyra/gui/gui_eob_segacd.h"
 #include "kyra/graphics/screen_eob_segacd.h"
 #include "kyra/resource/resource.h"
 #include "kyra/resource/resource_segacd.h"
@@ -34,6 +35,8 @@
 
 namespace Kyra {
 
+class GUI_EoB_SegaCD;
+
 EoBEngine::EoBEngine(OSystem *system, const GameFlags &flags)
 	: EoBCoreEngine(system, flags) {
 	_numSpells = 53;
@@ -154,6 +157,8 @@ Common::Error EoBEngine::init() {
 		assert(_seqPlayer);
 		_txt = new TextDisplayer_SegaCD(this, _screen);
 		assert(_txt);
+		_gui = new GUI_EoB_SegaCD(this);
+		assert(_gui);
 		_playFldPattern2 = new uint16[1040];
 		_tempPattern = new uint16[792];
 		_shakeBackBuffer1 = new uint8[120 * 6];
@@ -1018,7 +1023,7 @@ void EoBEngine::displayParchment(int id) {
 	int numPages = (id == 46) ? 3 : 1;
 	int curPage = (id == 46) ? 4 : id - 47;
 	for (int i = 0; i < numPages && !shouldQuit(); ++i) {
-		_screen->sega_copyToTextBuffer(data, 22464);
+		_screen->sega_loadTextBackground(data, 22464);
 		_txt->printShadowedText(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);
@@ -1027,10 +1032,8 @@ void EoBEngine::displayParchment(int id) {
 		_screen->sega_fadeToNeutral(1);
 
 		resetSkipFlag();
-		_allowSkip = true;
 		while (!(shouldQuit() || skipFlag()))
 			delay(20);
-		_allowSkip = false;
 		resetSkipFlag();
 
 		_screen->sega_fadeToBlack(1);
diff --git a/engines/kyra/engine/eob.h b/engines/kyra/engine/eob.h
index 5b8c0d7f56..9c48bae3b7 100644
--- a/engines/kyra/engine/eob.h
+++ b/engines/kyra/engine/eob.h
@@ -33,6 +33,7 @@ class SegaCDResource;
 class SegaSequencePlayer;
 class EoBEngine : public EoBCoreEngine {
 friend class GUI_EoB;
+friend class GUI_EoB_SegaCD;
 friend class EoBSeqPlayerCommon;
 friend class EoBIntroPlayer;
 friend class EoBPC98FinalePlayer;
@@ -192,8 +193,10 @@ private:
 	SegaCDResource *_sres;
 
 	// GUI
+	int clickedCamp(Button *button) override;
+
 	void gui_drawPlayField(bool refresh) override;
-	void gui_setupPlayFieldHelperPages() override;
+	void gui_setupPlayFieldHelperPages(bool keepText = false) override;
 	void gui_drawWeaponSlotStatus(int x, int y, int status) override;
 	void gui_printInventoryDigits(int x, int y, int val) override;
 	void gui_drawCharacterStatsPage() override;
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 616af2a1e9..74f8dacc67 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -52,6 +52,8 @@ EoBCoreEngine::EoBCoreEngine(OSystem *system, const GameFlags &flags) : KyraRpgE
 	_playFinale = false;
 	_runFlag = true;
 	_configMouse = _config2431 = true;
+	_mouseSpeed = _padSpeed = 1;
+	_inputMode = 0;
 	_loading = false;
 
 	_enableHiResDithering = false;
@@ -462,8 +464,11 @@ Common::Error EoBCoreEngine::init() {
 
 	setupKeyMap();
 
-	_gui = new GUI_EoB(this);
-	assert(_gui);
+	if (_flags.platform != Common::kPlatformSegaCD) {
+		_gui = new GUI_EoB(this);
+		assert(_gui);
+	}
+
 	if (_flags.platform != Common::kPlatformSegaCD) {
 		_txt = new TextDisplayer_rpg(this, _screen);
 		assert(_txt);
@@ -673,7 +678,7 @@ void EoBCoreEngine::readSettings() {
 	_configHpBarGraphs = ConfMan.getBool("hpbargraphs");
 	_configMouseBtSwap = ConfMan.getBool("mousebtswap");
 	_configSounds = ConfMan.getBool("sfx_mute") ? 0 : 1;	
-	_configMusic = (_flags.platform == Common::kPlatformPC98) ? (ConfMan.getBool("music_mute") ? 0 : 1) : (_configSounds ? 1 : 0);
+	_configMusic = (_flags.platform == Common::kPlatformPC98 || _flags.platform == Common::kPlatformSegaCD) ? (ConfMan.getBool("music_mute") ? 0 : 1) : (_configSounds ? 1 : 0);
 
 	if (_sound) {
 		_sound->enableMusic(_configNullSound ? false : _configMusic);
@@ -685,11 +690,11 @@ void EoBCoreEngine::writeSettings() {
 	ConfMan.setBool("hpbargraphs", _configHpBarGraphs);
 	ConfMan.setBool("mousebtswap", _configMouseBtSwap);
 	ConfMan.setBool("sfx_mute", _configSounds == 0);
-	if (_flags.platform == Common::kPlatformPC98)
+	if (_flags.platform == Common::kPlatformPC98 || _flags.platform == Common::kPlatformSegaCD)
 		ConfMan.setBool("music_mute", _configMusic == 0);
 
 	if (_sound) {
-		if (_flags.platform == Common::kPlatformPC98) {
+		if (_flags.platform == Common::kPlatformPC98 || _flags.platform == Common::kPlatformSegaCD) {
 			if (!_configMusic)
 				snd_playSong(0);
 		} else if (!_configSounds) {
@@ -1682,10 +1687,10 @@ int EoBCoreEngine::runDialogue(int dialogueTextId, int numStr, int loopButtonId,
 void EoBCoreEngine::restParty_displayWarning(const char *str) {
 	int od = _screen->curDimIndex();
 	_screen->setScreenDim(7);
-	Screen::FontId of = _screen->setFont(_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT);
+	Screen::FontId of = _screen->setFont(_conFont);
 	_screen->setCurPage(0);
 
-	_txt->printMessage(Common::String::format("\r%s\r", str).c_str());
+	_txt->printMessage(Common::String::format(_flags.platform == Common::kPlatformSegaCD ? "%s" : "\r%s\r", str).c_str());
 
 	_screen->setFont(of);
 	_screen->setScreenDim(od);
@@ -1699,7 +1704,12 @@ bool EoBCoreEngine::restParty_updateMonsters() {
 
 	for (int i = 0; i < 5; i++) {
 		_partyResting = true;
-		Screen::FontId of = _screen->setFont(_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT);
+
+		// The original SegaCD code does not update the monsters during resting. I presume to
+		// avoid graphical issues which I have (apparently successfully) tried to fix. At first
+		// I had disabled the monster updated just like the original, but it annoyed me, since
+		// it felt like a step backwards...
+		Screen::FontId of = _screen->setFont(_conFont);
 		int od = _screen->curDimIndex();
 		_screen->setScreenDim(7);
 		updateMonsters(0);
@@ -1707,6 +1717,7 @@ bool EoBCoreEngine::restParty_updateMonsters() {
 		timerProcessFlyingObjects(0);
 		_screen->setScreenDim(od);
 		_screen->setFont(of);
+
 		_partyResting = false;
 
 		for (int ii = 0; ii < 30; ii++) {
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index ec24cca0c8..d275ef8cd1 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -246,10 +246,11 @@ struct EoBMenuDef {
 	int8 numButtons;
 	int8 titleCol;
 };
+
 struct EoBMenuButtonDef {
 	int8 labelId;
 	int16 x;
-	int8 y;
+	int16 y;
 	uint8 width;
 	uint8 height;
 	int16 keyCode;
@@ -730,7 +731,7 @@ protected:
 
 	// Gui
 	virtual void gui_drawPlayField(bool refresh);
-	virtual void gui_setupPlayFieldHelperPages();
+	virtual void gui_setupPlayFieldHelperPages(bool keepText = false);
 	void gui_restorePlayField();
 	void gui_drawAllCharPortraitsWithStats();
 	void gui_drawCharPortraitWithStats(int index);
@@ -764,7 +765,7 @@ protected:
 	int clickedInventoryNextPage(Button *button);
 	int clickedPortraitRestore(Button *button);
 	int clickedCharPortraitDefault(Button *button);
-	int clickedCamp(Button *button);
+	virtual int clickedCamp(Button *button);
 	int clickedSceneDropPickupItem(Button *button);
 	int clickedCharPortrait2(Button *button);
 	int clickedWeaponSlot(Button *button);
@@ -1211,6 +1212,9 @@ protected:
 	int _prefMenuPlatformOffset;
 	bool _configMouse;
 	bool _config2431;
+	int _mouseSpeed;
+	int _padSpeed;
+	int _inputMode;
 
 	const char *const *_menuStringsMain;
 	const char *const *_menuStringsSaveLoad;
diff --git a/engines/kyra/engine/sprites_eob.cpp b/engines/kyra/engine/sprites_eob.cpp
index f849a6b008..05a643e54f 100644
--- a/engines/kyra/engine/sprites_eob.cpp
+++ b/engines/kyra/engine/sprites_eob.cpp
@@ -1050,6 +1050,15 @@ bool EoBCoreEngine::updateMonsterTryCloseAttack(EoBMonsterInPlay *m, int block)
 			for (int i = 0; i < 16 && m->curAttackFrame < 0; ++i) {
 				if (m->type != 4 && m->curAttackFrame == -1)
 					snd_updateEnvironmentalSfx(_monsterProps[m->type].sound1);
+				if (_flags.platform == Common::kPlatformSegaCD && _partyResting) {
+					// The original SegaCD code does not update the monsters during resting. I presume to
+					// avoid graphical issues which I try to fix here...
+					setLevelPalettes(_currentLevel);
+					_screen->sega_selectPalette(-1, 2, true);
+					gui_setupPlayFieldHelperPages(true);
+					gui_drawAllCharPortraitsWithStats();
+					_partyResting = false;
+				}
 				drawScene(1);
 				_flashShapeTimer = _system->getMillis() + _flashShapeTimerIntv1;
 			}
diff --git a/engines/kyra/graphics/screen.cpp b/engines/kyra/graphics/screen.cpp
index fdb913f98a..f31cfc4ea4 100644
--- a/engines/kyra/graphics/screen.cpp
+++ b/engines/kyra/graphics/screen.cpp
@@ -93,7 +93,6 @@ Screen::~Screen() {
 
 	delete _screenPalette;
 	delete _internFadePalette;
-	delete[] _textRenderBuffer;
 	delete[] _animBlockPtr;
 	delete[] _16bitPalette;
 	delete[] _16bitConversionPalette;
diff --git a/engines/kyra/graphics/screen_eob.cpp b/engines/kyra/graphics/screen_eob.cpp
index 25a507c476..2a2117598f 100644
--- a/engines/kyra/graphics/screen_eob.cpp
+++ b/engines/kyra/graphics/screen_eob.cpp
@@ -68,6 +68,7 @@ Screen_EoB::Screen_EoB(EoBCoreEngine *vm, OSystem *system) : Screen(vm, system,
 	_segaAnimator = 0;
 	_segaCustomPalettes = 0;
 	_palFaders = 0;
+	_defaultRenderBuffer = 0;
 	_specialColorReplace = false;
 	memset(_segaCurPalette, 0, sizeof(_segaCurPalette));
 }
@@ -84,6 +85,7 @@ Screen_EoB::~Screen_EoB() {
 	delete[] _cyclePalette;
 	delete[] _segaCustomPalettes;
 	delete[] _palFaders;
+	delete[] _defaultRenderBuffer;
 	delete _segaRenderer;
 	delete _segaAnimator;
 }
@@ -125,9 +127,10 @@ bool Screen_EoB::init() {
 			sega_initGraphics();
 			_segaCustomPalettes = new uint16[128];
 			_palFaders = new PaletteFader[4];
-			_textRenderBufferSize = SCREEN_W * _screenHeight;
-			_textRenderBuffer = new uint8[_textRenderBufferSize];
-			memset(_textRenderBuffer, 0, _textRenderBufferSize);
+			_defaultRenderBufferSize = SCREEN_W * _screenHeight;
+			_defaultRenderBuffer = new uint8[_defaultRenderBufferSize];
+			memset(_defaultRenderBuffer, 0, _defaultRenderBufferSize);
+			sega_setTextBuffer(0, 0);
 			memset(_segaCustomPalettes, 0, 128 * sizeof(uint16));
 		}
 
diff --git a/engines/kyra/graphics/screen_eob.h b/engines/kyra/graphics/screen_eob.h
index 9cfef9b863..b268a8eca6 100644
--- a/engines/kyra/graphics/screen_eob.h
+++ b/engines/kyra/graphics/screen_eob.h
@@ -144,10 +144,9 @@ public:
 	void sega_fadeToWhite(int delay) { sega_fadePalette(delay, 7); }
 	void sega_fadeToNeutral(int delay) { sega_fadePalette(delay, 0); }
 	void sega_paletteOps(int16 opPal, int16 par1, int16 par2);
+	void sega_setTextBuffer(uint8 *buffer, uint32 bufferSize);
 	void sega_clearTextBuffer(uint8 col);
-	void sega_clearTextBufferLine(uint16 y, uint16 lineHeight, uint16 pitch, uint8 col);
-	void sega_copyTextBufferLine(uint16 srcY, uint16 dstY, uint16 lineHeight, uint16 pitch);
-	void sega_copyToTextBuffer(const uint8 *src, uint16 size);
+	void sega_loadTextBackground(const uint8 *src, uint16 size);
 	void sega_drawTextBox(int pW, int pH, int x, int y, int w, int h, uint8 color1, uint8 color2);
 	void sega_loadTextBufferToVRAM(uint16 srcOffset, uint16 addr, int size);
 	void sega_gfxScale(uint8 *out, uint16 w, uint16 h, uint16 pitch, const uint8 *in, const uint16 *stampMap, const uint16 *traceVectors);
@@ -219,6 +218,8 @@ private:
 	SegaAnimator *_segaAnimator;
 	uint16 _segaCurPalette[64];
 	uint16 *_segaCustomPalettes;
+	uint8 *_defaultRenderBuffer;
+	int _defaultRenderBufferSize;
 };
 
 /**
diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index a827becfb6..506523787f 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -204,54 +204,21 @@ void Screen_EoB::sega_paletteOps(int16 op, int16 par1, int16 par2) {
 	}
 }
 
-void Screen_EoB::sega_clearTextBuffer(uint8 col) {
-	memset(_textRenderBuffer, col, _textRenderBufferSize);
-}
-
-void Screen_EoB::sega_clearTextBufferLine(uint16 y, uint16 lineHeight, uint16 pitch, uint8 col) {
-	uint32 *dst = (uint32*)(_textRenderBuffer + (((y >> 3) * pitch) << 5) + ((y & 7) << 2));
-	int ln = y;
-	uint32 c = col | (col << 8) | (col << 16) | (col << 24);
-	while (lineHeight--) {
-		uint32 *dst2 = dst;
-		for (uint16 w = pitch; w; --w) {
-			*dst = c;
-			dst += 8;
-		}
-		dst = dst2 + 1;
-		if (((++ln) & 7) == 0)
-			dst += ((pitch - 1) << 3);
+void Screen_EoB::sega_setTextBuffer(uint8 *buffer, uint32 bufferSize) {
+	if (!buffer) {
+		_textRenderBuffer = _defaultRenderBuffer;
+		_textRenderBufferSize = _defaultRenderBufferSize;
+	} else {
+		_textRenderBuffer = buffer;
+		_textRenderBufferSize = bufferSize;
 	}
 }
 
-
-void Screen_EoB::sega_copyTextBufferLine(uint16 srcY, uint16 dstY, uint16 lineHeight, uint16 pitch) {
-	uint32 *src = (uint32*)(_textRenderBuffer + (((srcY >> 3) * pitch) << 5) + ((srcY & 7) << 2));
-	uint32 *dst = (uint32*)(_textRenderBuffer + (((dstY >> 3) * pitch) << 5) + ((dstY & 7) << 2));
-	int src_ln = srcY;
-	int dst_ln = dstY;
-
-	while (lineHeight--) {
-		uint32 *src2 = src;
-		uint32 *dst2 = dst;
-
-		for (uint16 w = pitch; w; --w) {
-			*dst = *src;
-			src += 8;
-			dst += 8;
-		}
-
-		src = src2 + 1;
-		dst = dst2 + 1;
-
-		if (((++dst_ln) & 7) == 0)
-			dst += ((pitch - 1) << 3);
-		if (((++src_ln) & 7) == 0)
-			src += ((pitch - 1) << 3);
-	}
+void Screen_EoB::sega_clearTextBuffer(uint8 col) {
+	memset(_textRenderBuffer, col, _textRenderBufferSize);
 }
 
-void Screen_EoB::sega_copyToTextBuffer(const uint8 *src, uint16 size) {
+void Screen_EoB::sega_loadTextBackground(const uint8 *src, uint16 size) {
 	assert(size <= _textRenderBufferSize);
 	memcpy(_textRenderBuffer, src, size);
 }
@@ -540,7 +507,7 @@ void SegaRenderer::loadToVRAM(const void *data, uint16 dataSize, uint16 addr) {
 	checkUpdateDirtyRects(addr, dataSize);
 }
 
-void SegaRenderer::loadStreamToVRAM(Common::SeekableReadStreamEndian *in, uint16 addr, bool compressedData) {
+void SegaRenderer::loadStreamToVRAM(Common::SeekableReadStream *in, uint16 addr, bool compressedData) {
 	assert(in);
 	assert(addr <= 0xFFFF);
 
diff --git a/engines/kyra/graphics/screen_eob_segacd.h b/engines/kyra/graphics/screen_eob_segacd.h
index 05c5f41ba3..0dceb560c4 100644
--- a/engines/kyra/graphics/screen_eob_segacd.h
+++ b/engines/kyra/graphics/screen_eob_segacd.h
@@ -78,7 +78,7 @@ public:
 	void setVScrollMode(int mode);
 
 	void loadToVRAM(const void *data, uint16 dataSize, uint16 addr);
-	void loadStreamToVRAM(Common::SeekableReadStreamEndian *in, uint16 addr, bool compressedData = false);
+	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);
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 2fc935284d..fef224c395 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -73,7 +73,7 @@ void EoBCoreEngine::gui_drawPlayField(bool refresh) {
 	}
 }
 
-void EoBCoreEngine::gui_setupPlayFieldHelperPages() {
+void EoBCoreEngine::gui_setupPlayFieldHelperPages(bool) {
 	_screen->loadEoBBitmap("INVENT", _cgaMappingInv, 5, 3, 2);
 }
 
@@ -885,12 +885,20 @@ int EoBCoreEngine::clickedCamp(Button *button) {
 	// This ensures that all special rendering (EGA dithering, 16bit rendering, Japanese font rendering) will be visible on the thumbnail.
 	::createThumbnailFromScreen(&_thumbNail);
 
-	_screen->copyRegion(0, 120, 0, 0, 176, 24, 0, Screen_EoB::kCampMenuBackupPage, Screen::CR_NO_P_CHECK);
+	_screen->copyRegion(0, 120, 0, 0, 176, 48, 0, Screen_EoB::kCampMenuBackupPage, Screen::CR_NO_P_CHECK);
 
 	_gui->runCampMenu();
 
+	if (_flags.platform == Common::kPlatformSegaCD) {
+		setLevelPalettes(_currentLevel);
+		_screen->sega_selectPalette(-1, 2, true);
+		gui_setupPlayFieldHelperPages(true);
+		snd_playLevelScore();
+		gui_drawAllCharPortraitsWithStats();
+	}
+
 	_screen->fillRect(0, 0, 175, 143, 0, 2);
-	_screen->copyRegion(0, 0, 0, 120, 176, 24, Screen_EoB::kCampMenuBackupPage, 2, Screen::CR_NO_P_CHECK);
+	_screen->copyRegion(0, 0, 0, 120, 176, 48, Screen_EoB::kCampMenuBackupPage, 2, Screen::CR_NO_P_CHECK);
 	_screen->setScreenDim(cd);
 
 	_thumbNail.free();
@@ -2185,17 +2193,21 @@ void GUI_EoB::runCampMenu() {
 	Button *buttonList = 0;
 
 	for (bool runLoop = true; runLoop && !_vm->shouldQuit();) {
-		if (newMenu == 2)
-			updateOptionsStrings();
-
 		if (newMenu != -1) {
+			drawCampMenu();
+			if (newMenu == 2) {
+				updateOptionsStrings();
+				if (_vm->gameFlags().platform == Common::kPlatformSegaCD)
+					keepButtons = false;
+			}
 			if (!keepButtons) {
 				releaseButtons(buttonList);
 
-				_vm->_menuDefs[0].titleStrId = newMenu ? 1 : 56;
-				if (newMenu == 2)
+				if (_vm->_menuDefs[0].titleStrId != -1)
+					_vm->_menuDefs[0].titleStrId = newMenu ? 1 : 56;
+				if (newMenu == 2 && _vm->_menuDefs[2].titleStrId != -1)
 					_vm->_menuDefs[2].titleStrId = 57;
-				else if (newMenu == 1)
+				else if (newMenu == 1 && _vm->_menuDefs[1].titleStrId != -1)
 					_vm->_menuDefs[1].titleStrId = 58;
 
 				buttonList = initMenu(newMenu);
@@ -2204,6 +2216,9 @@ void GUI_EoB::runCampMenu() {
 					highlightButton = buttonList;
 					prevHighlightButton = 0;
 				}
+			} else if (_vm->gameFlags().platform == Common::kPlatformSegaCD && newMenu == 2) {
+				_screen->sega_getRenderer()->render(Screen_EoB::kSegaRenderPage);
+				_screen->copyRegion(0, 0, 0, 0, 176, 168, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
 			}
 
 			lastMenu = newMenu;
@@ -2222,6 +2237,8 @@ void GUI_EoB::runCampMenu() {
 		Button *clickedButton = _vm->gui_getButton(buttonList, inputFlag & 0x7FFF);
 
 		if (clickedButton) {
+			if (_vm->gameFlags().platform == Common::kPlatformSegaCD)
+				_vm->snd_playSoundEffect(0x81);
 			drawMenuButton(prevHighlightButton, false, false, true);
 			drawMenuButton(clickedButton, true, true, true);
 			_screen->updateScreen();
@@ -2261,7 +2278,7 @@ void GUI_EoB::runCampMenu() {
 				highlightButton = _vm->gui_getButton(buttonList, s);
 			}
 
-		} else if (inputFlag > 0x8000 && inputFlag < 0x8011) {
+		} else if (inputFlag > 0x8000 && inputFlag < 0x8012) {
 			int i = 0;
 			int cnt = 0;
 
@@ -2303,8 +2320,13 @@ void GUI_EoB::runCampMenu() {
 					displayTextBox(44);
 				// fall through
 
-			case 0x800C:
 			case 0x8010:
+				if (_vm->gameFlags().platform == Common::kPlatformSegaCD && inputFlag == 0x8010) {
+					_vm->_configMouse ^= true;
+					newMenu = 2;
+					break;
+				}
+			case 0x800C:			
 				if (lastMenu == 1 || lastMenu == 2)
 					newMenu = 0;
 				else if (inputFlag == _vm->_keyMap[Common::KEYCODE_ESCAPE])
@@ -2347,17 +2369,24 @@ void GUI_EoB::runCampMenu() {
 				break;
 
 			case 0x800B:
-				if (confirmDialogue(46))
-					_vm->quitGame();
-				newMenu = 0;
+				if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
+					_vm->_inputMode = (_vm->_inputMode + 1) % 3;
+					newMenu = 2;
+				} else {
+					if (confirmDialogue(46))
+						_vm->quitGame();
+					newMenu = 0;
+				}
 				break;
 
 			case 0x800D:
-				if (_vm->gameFlags().platform == Common::kPlatformPC98) {
+				if (_vm->gameFlags().platform == Common::kPlatformPC98 || _vm->gameFlags().platform == Common::kPlatformSegaCD) {
 					_vm->_configMusic ^= true;
 					_vm->writeSettings();
-					if (_vm->_configMusic)
+					if (_vm->_configMusic && _vm->gameFlags().platform == Common::kPlatformPC98)
 						_vm->snd_playLevelScore();
+					else if (_vm->_configMusic)
+						_vm->snd_playSong(11);
 					else
 						_vm->snd_playSong(0);
 				} else {
@@ -2369,7 +2398,7 @@ void GUI_EoB::runCampMenu() {
 				break;
 
 			case 0x800E:
-				if (_vm->gameFlags().platform == Common::kPlatformPC98)
+				if (_vm->gameFlags().platform == Common::kPlatformPC98 || _vm->gameFlags().platform == Common::kPlatformSegaCD)
 					_vm->_configSounds ^= true;
 				else
 					_vm->_configHpBarGraphs ^= true;
@@ -2390,6 +2419,14 @@ void GUI_EoB::runCampMenu() {
 				}
 				break;
 
+			case 0x8011:
+				if (_vm->_configMouse)
+					_vm->_mouseSpeed = (_vm->_mouseSpeed + 1) % 5;
+				else
+					_vm->_padSpeed = (_vm->_padSpeed + 1) % 5;
+				newMenu = 2;
+				break;
+
 			default:
 				break;
 			}
@@ -3644,7 +3681,8 @@ bool GUI_EoB::restParty() {
 	if (_vm->restParty_extraAbortCondition())
 		return true;
 
-	drawMenuButtonBox(_screen->_curDim->sx << 3, _screen->_curDim->sy, _screen->_curDim->w << 3, _screen->_curDim->h, false, false);
+	if (_vm->gameFlags().platform != Common::kPlatformSegaCD)
+		drawMenuButtonBox(_screen->_curDim->sx << 3, _screen->_curDim->sy, _screen->_curDim->w << 3, _screen->_curDim->h, false, false);
 
 	bool poisoned = false;
 	for (int i = 0; i < 6; i++) {
@@ -3681,8 +3719,12 @@ bool GUI_EoB::restParty() {
 		}
 	}
 
-	_screen->setClearScreenDim(7);
-	Screen::FontId of = _screen->setFont(_vm->_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT);
+	if (_vm->gameFlags().platform != Common::kPlatformSegaCD)
+		_screen->setClearScreenDim(7);
+	else
+		_screen->sega_clearTextBuffer(0);
+
+	Screen::FontId of = _screen->setFont(_vm->_conFont);
 
 	restParty_updateRestTime(hours, true);
 
@@ -4163,18 +4205,22 @@ Button *GUI_EoB::initMenu(int id) {
 		drawMenuButtonBox(dm->sx << 3, dm->sy, dm->w << 3, dm->h, false, false);
 	}
 
-	_screen->printShadedText(getMenuString(m->titleStrId), 5, 5, m->titleCol, 0, _vm->guiSettings()->colors.guiColorBlack);
-	_screen->setTextMarginRight(Screen::SCREEN_W);
+	if (m->titleStrId != -1) {
+		_screen->printShadedText(getMenuString(m->titleStrId), 5, 5, m->titleCol, 0, _vm->guiSettings()->colors.guiColorBlack);
+		_screen->setTextMarginRight(Screen::SCREEN_W);
+	}
 
 	Button *buttons = 0;
 	for (int i = 0; i < m->numButtons; i++) {
 		const EoBMenuButtonDef *df = &_vm->_menuButtonDefs[m->firstButtonStrId + i];
 		Button *b = new Button;
 		b->index = m->firstButtonStrId + i + 1;
-		if (id == 4 && _vm->game() == GI_EOB1)
-			b->index -= 14;
-		else if (id == 2)
-			b->index -= _vm->_prefMenuPlatformOffset;
+		if (_vm->gameFlags().platform != Common::kPlatformSegaCD) {
+			if (id == 4 && _vm->game() == GI_EOB1)
+				b->index -= 14;
+			else if (id == 2)
+				b->index -= _vm->_prefMenuPlatformOffset;
+		}
 
 		b->data0Val2 = 12;
 		b->data1Val2 = b->data2Val2 = 15;
@@ -4193,7 +4239,13 @@ Button *GUI_EoB::initMenu(int id) {
 		buttons = linkButton(buttons, b);
 	}
 
-	_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);
+	if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
+		_screen->sega_getRenderer()->render(Screen_EoB::kSegaRenderPage);
+		_screen->copyRegion(0, 0, 0, 0, 176, 168, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+	} 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);
+	}
+
 	_vm->gui_notifyButtonListChanged();
 	_screen->setCurPage(0);
 	_screen->updateScreen();
diff --git a/engines/kyra/gui/gui_eob.h b/engines/kyra/gui/gui_eob.h
index 0a045bfe16..da5451996c 100644
--- a/engines/kyra/gui/gui_eob.h
+++ b/engines/kyra/gui/gui_eob.h
@@ -59,8 +59,8 @@ public:
 	int simpleMenu_process(int sd, const char *const *strings, void *b, int32 menuItemsMask, int unk);
 
 	// Button based menus (camp menu, load menu)
-	void runCampMenu();
-	bool runLoadMenu(int x, int y, bool fromMainMenu = false);
+	virtual void runCampMenu();
+	virtual bool runLoadMenu(int x, int y, bool fromMainMenu = false);
 
 	bool confirmDialogue2(int dim, int id, int deflt);
 	void messageDialogue(int dim, int id, int buttonTextCol);
@@ -78,6 +78,13 @@ public:
 	// utilities for thumbnail creation
 	void createScreenThumbnail(Graphics::Surface &dst) override;
 
+protected:
+	const char *getMenuString(int id);
+	Button *initMenu(int id);
+	void releaseButtons(Button *list);
+
+	Screen_EoB *_screen;
+
 private:
 	int simpleMenu_getMenuItem(int index, int32 menuItemsMask, int itemOffset);
 	void simpleMenu_flashSelection(const char *str, int x, int y, int color1, int color2, int color3);
@@ -89,27 +96,25 @@ private:
 	void scribeScrollDialogue();
 	bool restParty();
 
-	bool confirmDialogue(int id);
+	virtual void drawCampMenu() {}
+	virtual bool confirmDialogue(int id);
 	int selectCharacterDialogue(int id);
-	void displayTextBox(int id);
+	virtual void displayTextBox(int id);
 
-	Button *initMenu(int id);
-	void drawMenuButton(Button *b, bool clicked, bool highlight, bool noFill);
+	virtual void drawMenuButton(Button *b, bool clicked, bool highlight, bool noFill);
 	void drawMenuButtonBox(int x, int y, int w, int h, bool clicked, bool noFill);
 	void drawTextBox(int dim, int id);
 	void drawSaveSlotButton(int slot, int redrawBox, int textCol);
 	void memorizePrayMenuPrintString(int spellId, int bookPageIndex, int spellType, bool noFill, bool highLight);
-	void updateOptionsStrings();
-	const char *getMenuString(int id);
+	virtual void updateOptionsStrings();
 
 	Button *linkButton(Button *list, Button *newbt);
-	void releaseButtons(Button *list);
 
 	void setupSaveMenuSlots();
 	int getHighlightSlot();
 	void sortSaveSlots() override;
 
-	void restParty_updateRestTime(int hours, bool init);
+	virtual void restParty_updateRestTime(int hours, bool init);
 
 	char **_menuStringsPrefsTemp;
 	char **_saveSlotStringsTemp;
@@ -119,7 +124,6 @@ private:
 	int16 _saveSlotY;
 
 	EoBCoreEngine *_vm;
-	Screen_EoB *_screen;
 
 	bool _pressFlag;
 
diff --git a/engines/kyra/gui/gui_eob_segacd.cpp b/engines/kyra/gui/gui_eob_segacd.cpp
index 30117d169f..3c43563d09 100644
--- a/engines/kyra/gui/gui_eob_segacd.cpp
+++ b/engines/kyra/gui/gui_eob_segacd.cpp
@@ -26,6 +26,7 @@
 #include "kyra/engine/eob.h"
 #include "kyra/graphics/screen_eob.h"
 #include "kyra/graphics/screen_eob_segacd.h"
+#include "kyra/gui/gui_eob_segacd.h"
 #include "kyra/resource/resource.h"
 #include "kyra/resource/resource_segacd.h"
 
@@ -33,6 +34,24 @@
 
 namespace Kyra {
 
+int EoBEngine::clickedCamp(Button *button) {
+	uint32 startTime = _system->getMillis();
+	gui_resetAnimations();
+
+	if (_flags.platform == Common::kPlatformSegaCD)
+		snd_playSong(11);
+
+	EoBCoreEngine::clickedCamp(button);
+
+	if (_flags.platform != Common::kPlatformSegaCD)
+		return button->arg;
+
+	gui_resetAnimations();
+	_totalPlaySecs += ((_system->getMillis() - startTime) / 1000);
+
+	return button->arg;
+}
+
 void EoBEngine::gui_drawPlayField(bool refresh) {
 	if (_flags.platform != Common::kPlatformSegaCD) {
 		EoBCoreEngine::gui_drawPlayField(refresh);
@@ -86,17 +105,6 @@ void EoBEngine::gui_drawPlayField(bool refresh) {
 	r->loadStreamToVRAM(str, 0x7920, false);
 	delete str;
 
-	/*
-// CAMP MENU
-str = _sres->resStreamEndian(8);
-_screen->sega_getRenderer()->loadStreamToVRAM(str, 0x20, true);
-delete str;
-_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 22, 15, 0);
-_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 22, 21, 0x4001, true);
-_screen->sega_getRenderer()->render(0);
-_screen->sega_selectPalette(40, 2, true);
-*/
-
 	gui_setupPlayFieldHelperPages();
 
 	if (refresh && !_sceneDrawPage2)
@@ -123,14 +131,17 @@ _screen->sega_selectPalette(40, 2, true);
 	_screen->sega_fadeToNeutral(1);
 }
 
-void EoBEngine::gui_setupPlayFieldHelperPages() {
+void EoBEngine::gui_setupPlayFieldHelperPages(bool keepText) {
 	if (_flags.platform != Common::kPlatformSegaCD) {
 		EoBCoreEngine::gui_setupPlayFieldHelperPages();
 		return;
 	}
 
-	_txt->clearDim(0);
+	if (!keepText)
+		_txt->clearDim(0);
+
 	SegaRenderer *r = _screen->sega_getRenderer();
+	r->fillRectWithTiles(0, 0, 0, 22, 21, 0);
 	r->fillRectWithTiles(0, 22, 0, 18, 21, 0);
 	r->fillRectWithTiles(1, 0, 0, 40, 26, 0x2000, true, false, _playFldPattern2);
 	r->fillRectWithTiles(0, 0, 21, 40, 5, 0x2000, true, false, _textFieldPattern);
@@ -565,7 +576,6 @@ void EoBEngine::drawMapButton(const char *str, int x, int y) {
 }
 
 void EoBEngine::drawMapPage(int level) {
-	int temp = 0;
 	_screen->sega_clearTextBuffer(0);
 	int cs = _screen->setFontStyles(_screen->_currentFont, (_flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat) | Font::kStyleNarrow1);
 	_txt->printShadowedText(_mapStrings3[level - 1], 0, 0, 0xCC, 0, 48, 16, 0, false);
@@ -628,6 +638,213 @@ void EoBEngine::drawDialogueButtons() {
 	_screen->sega_getRenderer()->render(0);
 }
 
+GUI_EoB_SegaCD::GUI_EoB_SegaCD(EoBEngine *vm) : GUI_EoB(vm), _vm(vm) {
+	_vm->_sres->loadContainer("ITEM");
+	uint8 *cm = _vm->_sres->resData(8, 0);
+	uint8 *cmdec = new uint8[47925];
+	uint16 decodeSize = READ_LE_UINT16(cm + 2);
+	_screen->decodeBIN(cm + 4, cmdec, decodeSize);
+	_campMenu = cmdec;
+	delete[] cm;
+}
+
+GUI_EoB_SegaCD::~GUI_EoB_SegaCD() {
+	delete[] _campMenu;
+}
+
+void GUI_EoB_SegaCD::drawCampMenu() {
+	_screen->sega_getRenderer()->loadToVRAM(_campMenu, 14784, 0x20);
+	_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 22, 21, 0);
+	_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 22, 21, 0x4001, true);
+	_screen->sega_selectPalette(40, 2, true);
+}
+
+bool GUI_EoB_SegaCD::confirmDialogue(int id) {
+	_screen->sega_clearTextBuffer(0);
+
+	int cs = _vm->gameFlags().lang == Common::JA_JPN ? Font::kStyleFixedWidth : (Font::kStyleForceTwoByte | Font::kStyleFat);
+	if (id == 47) {
+		cs |= Font::kStyleNarrow2;
+		_screen->_charSpacing = 1;
+	}
+	cs = _screen->setFontStyles(_screen->_currentFont, cs);
+	_vm->_txt->printShadowedText(getMenuString(id), 0, 3, 0xFF, 0xCC, 160, 40, 0, false);
+	_screen->_charSpacing = 0;
+	_screen->setFontStyles(_screen->_currentFont, cs);
+
+	_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(Screen_EoB::kSegaRenderPage);
+	_screen->copyRegion(0, 0, 0, 0, 176, 128, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+	_screen->updateScreen();
+
+	Button *buttonList = initMenu(5);
+
+	int newHighlight = 0;
+	int lastHighlight = -1;
+	bool result = false;
+
+	for (bool runLoop = true; runLoop && !_vm->shouldQuit();) {
+		if (newHighlight != lastHighlight) {
+			if (lastHighlight != -1)
+				drawMenuButton(_vm->gui_getButton(buttonList, lastHighlight + 33), false, false, true);
+			drawMenuButton(_vm->gui_getButton(buttonList, newHighlight + 33), false, true, true);
+			_screen->updateScreen();
+			lastHighlight = newHighlight;
+		}
+
+		int inputFlag = _vm->checkInput(buttonList, false, 0) & 0x80FF;
+		_vm->removeInputTop();
+
+		if (inputFlag == _vm->_keyMap[Common::KEYCODE_KP5] || inputFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inputFlag == _vm->_keyMap[Common::KEYCODE_RETURN]) {
+			result = lastHighlight == 0;
+			inputFlag = 0x8012 + lastHighlight;
+			runLoop = false;
+		} else if (inputFlag == _vm->_keyMap[Common::KEYCODE_KP4] || inputFlag == _vm->_keyMap[Common::KEYCODE_LEFT] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP6] || inputFlag == _vm->_keyMap[Common::KEYCODE_RIGHT]) {
+			newHighlight ^= 1;
+		} else if (inputFlag == 0x8012) {
+			result = true;
+			runLoop = false;
+		} else if (inputFlag == 0x8013) {
+			result = false;
+			runLoop = false;
+		} else {
+			Common::Point p = _vm->getMousePos();
+			for (Button *b = buttonList; b; b = b->nextButton) {
+				if ((b->arg & 2) && _vm->posWithinRect(p.x, p.y, b->x, b->y, b->x + b->width, b->y + b->height))
+					newHighlight = b->index - 18;
+			}
+		}
+
+		if (!runLoop) {
+			Button *b = _vm->gui_getButton(buttonList, lastHighlight + 18);
+			drawMenuButton(b, true, true, true);
+			_screen->updateScreen();
+			_vm->_system->delayMillis(80);
+			drawMenuButton(b, false, true, true);
+			_screen->updateScreen();
+		}
+	}
+
+	releaseButtons(buttonList);
+
+	return result;
+}
+
+void GUI_EoB_SegaCD::displayTextBox(int id) {
+	_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 22, 20, 0);
+	_screen->sega_clearTextBuffer(0);
+	int cs = _screen->setFontStyles(_screen->_currentFont, _vm->gameFlags().lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat);
+	_vm->_txt->printShadowedText(getMenuString(id), 0, 0, 0xFF, 0xCC, 160, 40, 0, false);
+	_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(Screen_EoB::kSegaRenderPage);
+	_screen->copyRegion(0, 0, 0, 0, 176, 168, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+	_screen->updateScreen();
+
+	_vm->resetSkipFlag();
+	while (!(_vm->shouldQuit() || _vm->skipFlag()))
+		_vm->delay(20);
+	_vm->resetSkipFlag();
+}
+
+void GUI_EoB_SegaCD::drawMenuButton(Button *b, bool clicked, bool highlight, bool noFill) {
+	if (!b)
+		return;
+	drawButtonIntern(b->index - 1, clicked ? 1 : 0);
+	drawButtonIntern(b->index - 1, 2);
+}
+
+void GUI_EoB_SegaCD::drawButtonIntern(int id, int op) {
+	assert(id < 22);
+	const SegaMenuButton &b = _menuButtons[id];
+	if (b.w == 0)
+		return;
+
+	if (op == 2) {
+		_screen->sega_getRenderer()->fillRectWithTiles(0, b.x, b.y, b.w, b.h, 0x4000 + b.nameTbl, true);
+	} else {
+		_screen->sega_getRenderer()->loadToVRAM(&_campMenu[(0x1CE + b.nameTbl2 + op * b.w * b.h) << 5], (b.w * b.h) << 5, b.nameTbl << 5);
+	}
+
+	if (op != 2) {
+		_screen->sega_getRenderer()->render(Screen_EoB::kSegaRenderPage);
+		_screen->copyRegion(b.x << 3, b.y << 3, b.x << 3, b.y << 3, b.w << 3, b.h << 3, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+	}
+}
+
+void GUI_EoB_SegaCD::updateOptionsStrings() {
+	uint16 ntblInputMode[3] = { 0x34C, 0x360, 0x30C };
+	int speed = _vm->_configMouse ? _vm->_mouseSpeed : _vm->_padSpeed;
+
+	SegaRenderer *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);	
+	r->loadToVRAM(&_campMenu[(0x1CE + ntblInputMode[_vm->_inputMode]) << 5], 0x280, 0x49A0);
+	r->loadToVRAM(&_campMenu[(0x444 + speed * 12) << 5], 0xC0, 0x48E0);
+
+	r->fillRectWithTiles(0, 15, 5, 3, 2, 0x4247, true);
+	r->fillRectWithTiles(0, 8, 5, 6, 2, 0x4217, true);
+	r->fillRectWithTiles(0, 8, 8, 6, 2, 0x4223, true);
+	r->fillRectWithTiles(0, 8, 11, 6, 2, 0x422F, true);
+	r->fillRectWithTiles(0, 8, 14, 10, 2, 0x424D, true);
+}
+
+void GUI_EoB_SegaCD::restParty_updateRestTime(int hours, bool init) {
+	SegaRenderer *r = _screen->sega_getRenderer();
+	if (init)
+		r->fillRectWithTiles(0, 1, 4, 20, 17, 0);
+	_screen->sega_clearTextBuffer(0);
+
+	int cs = _screen->setFontStyles(_screen->_currentFont, _vm->gameFlags().lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat);
+	_vm->_txt->printShadowedText(getMenuString(42), 0, 0, 0xFF, 0xCC, 160, 48, 0, false);
+	_vm->_txt->printShadowedText(_vm->_menuStringsRest2[3], 0, 16, 0xFF, 0xCC, 160, 48, 0, false);
+	_vm->_txt->printShadowedText(Common::String::format("%3d", hours).c_str(), 117, 16, 0xFF, 0xCC, 160, 48, 0, false);
+	_screen->setFontStyles(_screen->_currentFont, cs);
+
+	_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(Screen_EoB::kSegaRenderPage);
+	_screen->copyRegion(0, 0, 0, 0, 176, 128, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+	_screen->updateScreen();
+	_vm->delay(160);
+}
+
+const GUI_EoB_SegaCD::SegaMenuButton GUI_EoB_SegaCD::_menuButtons[22] = {
+	{ 0x01e7, 0x0000, 0x0001, 0x0005, 0x000a, 0x0002 },
+	{ 0x01fb, 0x0028, 0x000b, 0x0005, 0x000a, 0x0002 },
+	{ 0x020f, 0x0050, 0x000b, 0x0008, 0x000a, 0x0002 },
+	{ 0x0223, 0x0078, 0x000b, 0x000b, 0x000a, 0x0002 },
+	{ 0x0237, 0x00a0, 0x0001, 0x000e, 0x000a, 0x0002 }, // opt
+	{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, // dummy
+	{ 0x01cf, 0x01c8, 0x000f, 0x0012, 0x0006, 0x0002 },
+
+	{ 0x025f, 0x0118, 0x0001, 0x000b, 0x000a, 0x0002 }, // load
+	{ 0x024b, 0x00c8, 0x0001, 0x0008, 0x000a, 0x0002 }, // save
+	{ 0x0273, 0x0140, 0x000b, 0x000e, 0x000a, 0x0002 }, // drop
+
+	{ 0x020b, 0x0198, 0x0001, 0x000e, 0x0006, 0x0002 },
+	{ 0x01cf, 0x01c8, 0x000f, 0x0012, 0x0006, 0x0002 },
+	{ 0x01f3, 0x01e0, 0x0001, 0x0008, 0x0006, 0x0002 },
+	{ 0x01ff, 0x01f8, 0x0001, 0x000b, 0x0006, 0x0002 },
+	{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, // dummy
+	{ 0x01e7, 0x0210, 0x0001, 0x0005, 0x0006, 0x0002 },
+	{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, // dummy
+
+	{ 0x01CF, 0x0168, 0x0003, 0x000A, 0x0006, 0x0002 }, // yes
+	{ 0x01db, 0x0180, 0x000d, 0x000a, 0x0006, 0x0002 }, // no
+
+	{ 0x01db, 0x01b0, 0x0001, 0x0012, 0x0006, 0x0002 },
+	{ 0x01cf, 0x01c8, 0x000f, 0x0012, 0x0006, 0x0002 },
+
+
+	{ 0x024d, 0x030c, 0x0001, 0x0011, 0x000a, 0x0002 }
+};
+
 } // End of namespace Kyra
 
 #endif // ENABLE_EOB
diff --git a/engines/kyra/gui/gui_eob_segacd.h b/engines/kyra/gui/gui_eob_segacd.h
new file mode 100644
index 0000000000..4a3731cc23
--- /dev/null
+++ b/engines/kyra/gui/gui_eob_segacd.h
@@ -0,0 +1,69 @@
+/* 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.
+ *
+ */
+
+#ifdef ENABLE_EOB
+
+#ifndef KYRA_GUI_EOB_SEGACD_H
+#define KYRA_GUI_EOB_SEGACD_H
+
+#include "kyra/gui/gui_eob.h"
+
+#ifdef ENABLE_EOB
+
+namespace Kyra {
+
+class GUI_EoB_SegaCD : public GUI_EoB {
+public:
+	GUI_EoB_SegaCD(EoBEngine *vm);
+	~GUI_EoB_SegaCD() override;
+
+private:
+	void drawCampMenu() override;
+	bool confirmDialogue(int id) override;
+	void displayTextBox(int id) override;
+	void drawMenuButton(Button *b, bool clicked, bool highlight, bool noFill) override;
+	void drawButtonIntern(int id, int op);
+	void updateOptionsStrings() override;
+	void restParty_updateRestTime(int hours, bool init) override;
+
+	const uint8 *_campMenu;
+	EoBEngine *_vm;
+
+	struct SegaMenuButton {
+		uint16 nameTbl;
+		uint16 nameTbl2;
+		int16 x;
+		int16 y;
+		uint16 w;
+		uint16 h;
+	};
+
+	static const SegaMenuButton _menuButtons[22];
+};
+
+} // End of namespace Kyra
+
+#endif // ENABLE_EOB
+
+#endif
+
+#endif // ENABLE_EOB || ENABLE_LOL
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index 6093ccab0a..95b13d3803 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -826,7 +826,7 @@ void EoBCoreEngine::initButtonData() {
 }
 
 void EoBCoreEngine::initMenus() {
-	static const EoBMenuButtonDef buttonDefs[] = {
+	static const EoBMenuButtonDef buttonDefsDefault[] = {
 		{  2,   12,  20, 158,  14,  20,  3  },
 		{  3,   12,  37, 158,  14,  52,  3  },
 		{  4,   12,  54, 158,  14,  26,  3  },
@@ -884,9 +884,48 @@ void EoBCoreEngine::initMenus() {
 		{  8,  128, 122,  40,  14,  19,  7  }
 	};
 
-	_menuButtonDefs = buttonDefs;
+	static const EoBMenuButtonDef buttonDefsSegaCD[] = {
+		{   0,   8,  40,  80,  16,  20,  3  },
+		{   0,   88, 40,  80,  16,  52,  3  },
+		{   0,   88, 64,  80,  16,  26,  3  },
+		{   0,   88, 88,  80,  16,  32,  3  },
+		{   0,   8, 112,  80,  16,   0,  3  },
 
-	static const EoBMenuDef menuDefs[] = {
+		{   0,   0,   0,   0,   0,   0,  0  },
+
+		{   0,  120,144,  48,  16,  19,  7  },
+
+		
+		{   0,   8,  88,  80,  16,   0,  3  }, //load
+		{   0,   8,  64,  80,  16,   0,  3  }, //save
+		{   0,   88,112,  80,  16,   0,  3  }, //drop
+
+		
+
+
+
+		{   0,   8, 112,  48,  16,   0,  3  },
+		{   0,  120,144,  48,  16,  19,  7  },
+		{   0,   8,  64,  48,  16,   0,  3  },
+		{   0,   8,  88,  48,  16,   0,  3  },
+		{   0,   0,   0,   0,   0,   0,  0  },
+		{   0,   8,  40,  48,  16,   0,  3  },
+		{   0, 120,  40,  24,  16,   0,  3  },
+		
+		{   0,  24,  80,  48,  16,  48,  3  },
+		{   0, 104,  80,  48,  16,  19,  3  },
+		
+		 
+
+		{   0,   88,112,  48,  16,   0,  3  },
+		{   0,  120, 40,  24,  16,   0,  3  },
+		{   0,   8,  40,  48,  16,   0,  3  },
+		{   0,   8, 136,  80,  16,   0,  3  }
+	};
+
+	_menuButtonDefs = (_flags.platform == Common::kPlatformSegaCD) ? buttonDefsSegaCD : buttonDefsDefault;
+
+	static const EoBMenuDef menuDefsDefault[7] = {
 		{  1, 10,  0, 7,  9 },
 		{  1, 10,  7, 5,  9 },
 		{  1, 10, 12, 3,  9 },
@@ -896,30 +935,43 @@ void EoBCoreEngine::initMenus() {
 		{ 48, 10, 34, 2,  9 }
 	};
 
-	delete[] _menuDefs;
-	_menuDefs = new EoBMenuDef[ARRAYSIZE(menuDefs)];
-	memcpy(_menuDefs, menuDefs, sizeof(menuDefs));
+	static const EoBMenuDef menuDefsSegaCD[6] = {
+		{  -1, 0,  0, 10, -1 },
+		{  -1, 0,  0,  0, -1 },
+		{  -1, 0, 10,  7, -1 },
+		{  -1, 0,  0,  0, -1 },
+		{  -1, 0,  0,  0, -1 },
+		{  -1, 0, 17,  2, -1 }
+	};
 
-	if (_flags.gameID == GI_EOB1) {
-		// assign EOB 1 style memorize/pray menu
-		_menuDefs[4].numButtons = 8;
-		_menuDefs[4].firstButtonStrId = 36;
-	}
+	delete[] _menuDefs;
+	if (_flags.platform == Common::kPlatformSegaCD) {
+		_menuDefs = new EoBMenuDef[ARRAYSIZE(menuDefsSegaCD)];
+		memcpy(_menuDefs, menuDefsSegaCD, sizeof(menuDefsSegaCD));
+	} else {
+		_menuDefs = new EoBMenuDef[ARRAYSIZE(menuDefsDefault)];
+		memcpy(_menuDefs, menuDefsDefault, sizeof(menuDefsDefault));
+		if (_flags.gameID == GI_EOB1) {
+			// assign EOB 1 style memorize/pray menu
+			_menuDefs[4].numButtons = 8;
+			_menuDefs[4].firstButtonStrId = 36;
+		}
 
-	if (_flags.platform == Common::kPlatformFMTowns) {
-		// assign FM-Towns style options menu
-		_menuDefs[2].numButtons = 4;
-		_menuDefs[2].firstButtonStrId = 44;
-		_prefMenuPlatformOffset = 32;
-	} else if (_flags.platform == Common::kPlatformPC98) {
-		// assign PC-98 style options menu
-		_menuDefs[2].numButtons = 4;
-		_menuDefs[2].firstButtonStrId = 48;
-		_prefMenuPlatformOffset = 36;
-	} else if (_flags.platform == Common::kPlatformAmiga) {
-		// assign Amiga text colors
-		_menuDefs[0].titleCol = _menuDefs[1].titleCol = _menuDefs[2].titleCol = _menuDefs[4].titleCol = _menuDefs[6].titleCol = guiSettings()->colors.guiColorLightBlue;
-		_menuDefs[3].titleCol = _menuDefs[5].titleCol = guiSettings()->colors.guiColorWhite;
+		if (_flags.platform == Common::kPlatformFMTowns) {
+			// assign FM-Towns style options menu
+			_menuDefs[2].numButtons = 4;
+			_menuDefs[2].firstButtonStrId = 44;
+			_prefMenuPlatformOffset = 32;
+		} else if (_flags.platform == Common::kPlatformPC98) {
+			// assign PC-98 style options menu
+			_menuDefs[2].numButtons = 4;
+			_menuDefs[2].firstButtonStrId = 48;
+			_prefMenuPlatformOffset = 36;
+		} else if (_flags.platform == Common::kPlatformAmiga) {
+			// assign Amiga text colors
+			_menuDefs[0].titleCol = _menuDefs[1].titleCol = _menuDefs[2].titleCol = _menuDefs[4].titleCol = _menuDefs[6].titleCol = guiSettings()->colors.guiColorLightBlue;
+			_menuDefs[3].titleCol = _menuDefs[5].titleCol = guiSettings()->colors.guiColorWhite;
+		}
 	}
 }
 
diff --git a/engines/kyra/sound/sound_segacd_eob.cpp b/engines/kyra/sound/sound_segacd_eob.cpp
index 6115e90fa8..cb41e2fcb8 100644
--- a/engines/kyra/sound/sound_segacd_eob.cpp
+++ b/engines/kyra/sound/sound_segacd_eob.cpp
@@ -72,9 +72,14 @@ void SoundSegaCD_EoB::loadSfxFile(Common::String file) {
 }
 
 void SoundSegaCD_EoB::playTrack(uint8 track) {
-	if (!_musicEnabled || !_ready)
+	if (!_ready)
 		return;
 
+	if (!_musicEnabled) {
+		haltTrack();
+		return;
+	}
+
 	int loop = track >> 6;
 	track &= 0x7F;
 
@@ -83,7 +88,7 @@ void SoundSegaCD_EoB::playTrack(uint8 track) {
 }
 
 void SoundSegaCD_EoB::haltTrack() {
-	if (!_musicEnabled || !_ready)
+	if (!_ready)
 		return;
 	g_system->getAudioCDManager()->stop();
 }
diff --git a/engines/kyra/text/text_eob_segacd.cpp b/engines/kyra/text/text_eob_segacd.cpp
index a89b193520..646eddc262 100644
--- a/engines/kyra/text/text_eob_segacd.cpp
+++ b/engines/kyra/text/text_eob_segacd.cpp
@@ -32,9 +32,13 @@ namespace Kyra {
 TextDisplayer_SegaCD::TextDisplayer_SegaCD(EoBEngine *engine, Screen_EoB *scr) : TextDisplayer_rpg(engine, scr), _engine(engine), _screen(scr), _renderer(scr->sega_getRenderer()),
 	_curDim(0), _textColor(0xFF), _curPosY(0), _curPosX(0) {
 	assert(_renderer);
+	_msgRenderBufferSize = 320 * 48;
+	_msgRenderBuffer = new uint8[_msgRenderBufferSize];
+	memset(_msgRenderBuffer, 0, _msgRenderBufferSize);
 }
 
 TextDisplayer_SegaCD::~TextDisplayer_SegaCD() {
+	delete[] _msgRenderBuffer;
 }
 
 void TextDisplayer_SegaCD::printDialogueText(int id, const char *string1, const char *string2) {
@@ -92,7 +96,7 @@ void TextDisplayer_SegaCD::printShadowedText(const char *str, int x, int y, int
 			_screen->sega_loadTextBufferToVRAM(i * (pitchW >> 3), (s->unkC & 0x7FF) << 5, (pitchW * pitchH) >> 1);
 	} else {
 		_screen->sega_loadTextBufferToVRAM(0, (s->unkC & 0x7FF) << 5, (pitchW * pitchH) >> 1);
-	}	
+	}
 }
 
 int TextDisplayer_SegaCD::clearDim(int dim) {
@@ -102,10 +106,12 @@ int TextDisplayer_SegaCD::clearDim(int dim) {
 	const ScreenDim *s = &_dimTable[dim];
 	_renderer->memsetVRAM((s->unkC & 0x7FF) << 5, s->unkA, (s->w * s->h) >> 1);
 	_screen->sega_clearTextBuffer(s->unkA);
+	memset(_msgRenderBuffer, 0, _msgRenderBufferSize);
 	return res;
 }
 
 void TextDisplayer_SegaCD::displayText(char *str, ...) {
+	_screen->sega_setTextBuffer(_msgRenderBuffer, _msgRenderBufferSize);
 	int cs = _screen->setFontStyles(Screen::FID_8_FNT, _vm->gameFlags().lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleFat | Font::kStyleForceTwoByte);
 	char tmp[3] = "  ";
 	int posX = _curPosX;
@@ -166,6 +172,7 @@ void TextDisplayer_SegaCD::displayText(char *str, ...) {
 	_renderer->render(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);
 }
 
 uint8 TextDisplayer_SegaCD::fetchCharacter(char *dest, const char *&src) {
@@ -183,8 +190,51 @@ uint8 TextDisplayer_SegaCD::fetchCharacter(char *dest, const char *&src) {
 }
 
 void TextDisplayer_SegaCD::linefeed() {
-	_screen->sega_copyTextBufferLine(_screen->getFontHeight(), 0, (_dimTable[_curDim].h & ~7) - _screen->getFontHeight(), _dimTable[_curDim].w >> 3);
-	_screen->sega_clearTextBufferLine(_screen->getFontHeight(), _screen->getFontHeight(), _dimTable[_curDim].w >> 3, _dimTable[_curDim].unkA);
+	copyTextBufferLine(_screen->getFontHeight(), 0, (_dimTable[_curDim].h & ~7) - _screen->getFontHeight(), _dimTable[_curDim].w >> 3);
+	clearTextBufferLine(_screen->getFontHeight(), _screen->getFontHeight(), _dimTable[_curDim].w >> 3, _dimTable[_curDim].unkA);
+}
+
+void TextDisplayer_SegaCD::clearTextBufferLine(uint16 y, uint16 lineHeight, uint16 pitch, uint8 col) {
+	uint32 *dst = (uint32*)(_msgRenderBuffer + (((y >> 3) * pitch) << 5) + ((y & 7) << 2));
+	int ln = y;
+	uint32 c = col | (col << 8) | (col << 16) | (col << 24);
+	while (lineHeight--) {
+		uint32 *dst2 = dst;
+		for (uint16 w = pitch; w; --w) {
+			*dst = c;
+			dst += 8;
+		}
+		dst = dst2 + 1;
+		if (((++ln) & 7) == 0)
+			dst += ((pitch - 1) << 3);
+	}
+}
+
+
+void TextDisplayer_SegaCD::copyTextBufferLine(uint16 srcY, uint16 dstY, uint16 lineHeight, uint16 pitch) {
+	uint32 *src = (uint32*)(_msgRenderBuffer + (((srcY >> 3) * pitch) << 5) + ((srcY & 7) << 2));
+	uint32 *dst = (uint32*)(_msgRenderBuffer + (((dstY >> 3) * pitch) << 5) + ((dstY & 7) << 2));
+	int src_ln = srcY;
+	int dst_ln = dstY;
+
+	while (lineHeight--) {
+		uint32 *src2 = src;
+		uint32 *dst2 = dst;
+
+		for (uint16 w = pitch; w; --w) {
+			*dst = *src;
+			src += 8;
+			dst += 8;
+		}
+
+		src = src2 + 1;
+		dst = dst2 + 1;
+
+		if (((++dst_ln) & 7) == 0)
+			dst += ((pitch - 1) << 3);
+		if (((++src_ln) & 7) == 0)
+			src += ((pitch - 1) << 3);
+	}
 }
 
 const ScreenDim TextDisplayer_SegaCD::_dimTable[6] = {
diff --git a/engines/kyra/text/text_eob_segacd.h b/engines/kyra/text/text_eob_segacd.h
index 309ce62290..1b67fd39f0 100644
--- a/engines/kyra/text/text_eob_segacd.h
+++ b/engines/kyra/text/text_eob_segacd.h
@@ -47,9 +47,14 @@ private:
 	uint8 fetchCharacter(char *dest, const char *&src);
 	void linefeed();
 
+	void clearTextBufferLine(uint16 y, uint16 lineHeight, uint16 pitch, uint8 col);
+	void copyTextBufferLine(uint16 srcY, uint16 dstY, uint16 lineHeight, uint16 pitch);
+
 	Screen_EoB *_screen;
 	SegaRenderer *_renderer;
 	EoBEngine *_engine;
+	uint8 *_msgRenderBuffer;
+	uint32 _msgRenderBufferSize;
 
 	int _curDim;
 	int _curPosY;


Commit: dc7de3b2a571fca283c24313c4923c16f963b57d
    https://github.com/scummvm/scummvm/commit/dc7de3b2a571fca283c24313c4923c16f963b57d
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:15+02:00

Commit Message:
KYRA: (EOB/SegaCD) - finish camp menu

(memorize/pray for spell menus, etc.)

Changed paths:
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eob.h
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h
    engines/kyra/graphics/screen.h
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/gui/gui_eob.h
    engines/kyra/gui/gui_eob_segacd.cpp
    engines/kyra/gui/gui_eob_segacd.h
    engines/kyra/resource/staticres_eob.cpp
    engines/kyra/sequence/sequences_eob.cpp


diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index ea50d27b62..2ef578fa78 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -67,7 +67,7 @@ EoBEngine::EoBEngine(OSystem *system, const GameFlags &flags)
 	_shakeBackBuffer1 = _shakeBackBuffer2 = 0;
 	_compassDirection2 = _compassAnimDest = _compassAnimPhase = _compassAnimStep = _compassAnimDelayCounter = 0;
 	_compassAnimSwitch = _compassAnimDone = _compassTilesRestore = false;
-	_redGrid = _charTilesTable = 0;
+	_redGrid = _charTilesTable = _scrYellow = 0;
 	_compassData = 0;
 	_mapStrings1 = _mapStrings2 = _mapStrings3 = 0;
 
@@ -85,6 +85,7 @@ EoBEngine::~EoBEngine() {
 	releaseShpArr(_weaponSlotShapes, 6);
 	releaseShpArr(_invSmallDigits, 32);
 
+	delete[] _scrYellow;
 	delete[] _redGrid;
 	delete[] _doorShapesSrc;
 	delete[] _doorSwitchShapesSrc;
@@ -160,7 +161,7 @@ Common::Error EoBEngine::init() {
 		_gui = new GUI_EoB_SegaCD(this);
 		assert(_gui);
 		_playFldPattern2 = new uint16[1040];
-		_tempPattern = new uint16[792];
+		_tempPattern = new uint16[924];
 		_shakeBackBuffer1 = new uint8[120 * 6];
 		_shakeBackBuffer2 = new uint8[179 * 6];
 		_compassData = new uint8[0x5000];
@@ -247,6 +248,15 @@ void EoBEngine::loadItemsAndDecorationsShapes() {
 	for (int i = 0; i < 6; ++i)
 		_teleporterShapes[i] = _sparkShapes[(i + 1) >> 1];
 
+	uint8 *cmdec = new uint8[47925];
+	uint8 *scrYellow = new uint8[4992];
+	in = _sres->resData(8, 0);
+	_screen->decodeBIN(in + 4, cmdec, READ_LE_UINT16(in + 2));
+	memcpy(scrYellow, &cmdec[0x87C0], 4992);
+	delete[] in;
+	delete[] cmdec;
+	_scrYellow = scrYellow;
+
 	int cp = _screen->setCurPage(Screen_EoB::kSegaInitShapesPage);
 	for (int i = 0; i < 4; ++i)
 		_screen->sega_getRenderer()->loadToVRAM(_redGridTile, 8, 0x52A0 + i * 8);
@@ -256,7 +266,7 @@ void EoBEngine::loadItemsAndDecorationsShapes() {
 	_screen->drawShape(Screen_EoB::kSegaInitShapesPage, _weaponSlotShapes[1], 0, 16, 0);
 	_weaponSlotGrid = _screen->encodeShape(0, 0, 4, 16);
 	_disabledCharGrid = _screen->encodeShape(0, 0, 4, 32);
-	_blackBoxSmallGrid = _screen->encodeShape(0, 0, 2, 8);
+	_blackBoxSmallGrid = _screen->encodeShape(0, 0, 2, guiSettings()->charBoxCoords.facePosY_1[0] - guiSettings()->charBoxCoords.boxY[0] - 1);
 	_blackBoxWideGrid = _screen->encodeShape(0, 0, 4, 8);
 	_redGrid = _screen->encodeShape(0, 32, 4, 32);
 	_screen->clearPage(Screen_EoB::kSegaInitShapesPage);
@@ -303,13 +313,29 @@ void EoBEngine::startupLoad() {
 	_sound->loadSoundFile(0);
 	_screen->selectPC98Palette(0, _screen->getPalette(0));
 
-	if (_flags.platform == Common::kPlatformSegaCD) {
-		_screen->sega_selectPalette(4, 0);
-		_screen->sega_selectPalette(8, 2);
-		_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 40, 28, 0x2000);
-		_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 28, 0x2000);
-		_txt->clearDim(0);
-	}
+	if (_flags.platform != Common::kPlatformSegaCD)
+		return;
+
+	_screen->sega_fadeToBlack(1);
+	_screen->sega_selectPalette(4, 0);
+	_screen->sega_selectPalette(40, 2);
+	_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 40, 28, 0x2000);
+	_screen->sega_getRenderer()->fillRectWithTiles(1, 0, 0, 40, 28, 0x2000);
+	_txt->clearDim(0);
+	_screen->clearPage(0);
+	_screen->sega_drawClippedLine(20, 18, 0, 0, 160, 144, 0xEE);
+	_screen->sega_drawClippedLine(20, 18, 0, 1, 159, 143, 0xAA);
+	_screen->sega_drawClippedLine(20, 18, 1, 1, 158, 142, 0xBB);
+	_screen->sega_loadTextBufferToVRAM(0, 0x20, 11520);
+	_screen->sega_getRenderer()->fillRectWithTiles(1, 10, 4, 20, 18, 0x4001, true);
+	_screen->sega_fadeToNeutral(1);
+}
+
+void EoBEngine::startupLoad2() {
+	if (_flags.platform != Common::kPlatformSegaCD)
+		return;
+	_screen->sega_fadeToBlack(1);
+	seq_segaOpeningCredits(true);
 }
 
 void EoBEngine::drawNpcScene(int npcIndex) {
@@ -1174,9 +1200,9 @@ bool EoBEngine::checkPartyStatusExtra() {
 			if (checkInput(0, false, 0) & 0xFF)
 				break;
 		}
+		_screen->copyPage(Screen_EoB::kDefeatMsgBackupPage, 0);
 	}
-
-	_screen->copyPage(Screen_EoB::kDefeatMsgBackupPage, 0);
+	
 	_eventList.clear();
 	_screen->setScreenDim(cd);
 	_txt->removePageBreakFlag();
diff --git a/engines/kyra/engine/eob.h b/engines/kyra/engine/eob.h
index 9c48bae3b7..2ee41e15e6 100644
--- a/engines/kyra/engine/eob.h
+++ b/engines/kyra/engine/eob.h
@@ -82,6 +82,7 @@ private:
 	// Main loop
 	void startupNew() override;
 	void startupLoad() override;
+	void startupLoad2() override;
 
 	// Intro/Outro/Sequence Playback
 	enum IntroPart {
@@ -94,7 +95,7 @@ private:
 	void seq_playFinale() override;
 	void seq_xdeath() override;
 
-	void seq_segaOpeningCredits();
+	void seq_segaOpeningCredits(bool jumpToTitle);
 	void seq_segaFinalCredits();
 	void seq_segaShowStats();
 
@@ -170,6 +171,7 @@ private:
 	void turnUndeadAutoHit() override;
 
 	const char *const *_turnUndeadString;
+	const uint8 *_scrYellow;
 
 	// Sound
 	void snd_loadAmigaSounds(int level, int) override;
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 74f8dacc67..494cb6a5df 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -638,8 +638,9 @@ Common::Error EoBCoreEngine::go() {
 		if (action == -1) {
 			// load game
 			startupLoad();
-			repeatLoop = _gui->runLoadMenu(72, 14, true);
-				
+			repeatLoop = _gui->runLoadMenu(_flags.platform == Common::kPlatformSegaCD ? 80 : 72, _flags.platform == Common::kPlatformSegaCD ? 16 : 14, true);
+			if (!repeatLoop)
+				startupLoad2();
 		} else if (action == -2 || action == -4) {
 			// new game
 			repeatLoop = startCharacterGeneration(action == -4);
@@ -772,12 +773,22 @@ bool EoBCoreEngine::checkPartyStatus(bool handleDeath) {
 	if (checkPartyStatusExtra()) {
 		Screen::FontId of = _screen->setFont(_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT);
 		gui_updateControls();
-		if (_gui->runLoadMenu(0, 0)) {
+		int x = 0;
+		int y = 0;
+		if (_flags.platform == Common::kPlatformSegaCD) {
+			startupLoad();
+			x = 80;
+			y = 16;
+		}
+		if (_gui->runLoadMenu(x, y)) {
 			_screen->setFont(of);
 			return true;
 		}
 	}
 
+	if (_flags.platform == Common::kPlatformSegaCD)
+		_screen->sega_fadeToBlack(1);
+
 	quitGame();
 	return false;
 }
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index d275ef8cd1..de2fd8326a 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -340,6 +340,7 @@ protected:
 	// Main loop
 	virtual void startupNew();
 	virtual void startupLoad() = 0;
+	virtual void startupLoad2() {}
 	void runLoop();
 	void update() override { screen()->updateScreen(); }
 	bool checkPartyStatus(bool handleDeath);
diff --git a/engines/kyra/graphics/screen.h b/engines/kyra/graphics/screen.h
index e9fbdbee6b..f2b8d111cb 100644
--- a/engines/kyra/graphics/screen.h
+++ b/engines/kyra/graphics/screen.h
@@ -105,9 +105,8 @@ public:
 	virtual int getCharWidth(uint16 c) const = 0;
 
 	/**
-	 * Gets the height of a specific character. The only font that requires an
-	 * implemenation for this is the SegaCD one. For all other font this is a
-	 * fixed value.
+	 * Gets the height of a specific character. The only font that needs this
+	 * is the SegaCD one. For all other fonts this is a fixed value.
 	 */
 	virtual int getCharHeight(uint16 c) const { return getHeight(); }
 
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index fef224c395..558f5d5576 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -1088,6 +1088,7 @@ int EoBCoreEngine::clickedSpellbookTab(Button *button) {
 int EoBCoreEngine::clickedSpellbookList(Button *button) {
 	int listIndex = button->arg;
 	bool spellLevelAvailable = false;
+	int bbrk = _flags.platform == Common::kPlatformSegaCD ? 6 : 9;
 
 	if (listIndex == 6) {
 		for (int i = 0; i < 10; i++) {
@@ -1107,8 +1108,8 @@ int EoBCoreEngine::clickedSpellbookList(Button *button) {
 
 		do {
 			_openBookSpellSelectedItem += v;
-			int s = (_openBookSpellSelectedItem >= 0) ? _openBookSpellSelectedItem : 9;
-			_openBookSpellSelectedItem = (s <= 9) ? s : 0;
+			int s = (_openBookSpellSelectedItem >= 0) ? _openBookSpellSelectedItem : bbrk;
+			_openBookSpellSelectedItem = (s <= bbrk) ? s : 0;
 		} while (_openBookAvailableSpells[_openBookSpellLevel * 10 + _openBookSpellSelectedItem] <= 0 && _openBookSpellSelectedItem != 9);
 
 		if (_openBookSpellSelectedItem >= 6) {
@@ -1499,7 +1500,7 @@ void EoBCoreEngine::gui_processInventorySlotClick(int slot) {
 	}
 }
 
-GUI_EoB::GUI_EoB(EoBCoreEngine *vm) : GUI(vm), _vm(vm), _screen(vm->_screen) {
+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];
 	memset(_menuStringsPrefsTemp, 0, 4 * sizeof(char *));
 
@@ -1539,9 +1540,24 @@ GUI_EoB::GUI_EoB(EoBCoreEngine *vm) : GUI(vm), _vm(vm), _screen(vm->_screen) {
 		_highLightColorTable = _highlightColorTableEGA;
 	else if (_vm->game() == GI_EOB1 && _vm->gameFlags().platform == Common::kPlatformPC98)
 		_highLightColorTable = _highlightColorTablePC98;
+	else if (_vm->gameFlags().platform == Common::kPlatformSegaCD)
+		_highLightColorTable = _highlightColorTableSegaCD;
 	else
 		_highLightColorTable = _highlightColorTableVGA;
 
+	EoBRect16 *highlightFrames = new EoBRect16[20];
+	memcpy(highlightFrames, _highlightFramesDefault, 20 * sizeof(EoBRect16));
+
+	if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
+		for (int i = 0; i < 6; ++i) {
+			highlightFrames[i].x1 = _vm->guiSettings()->charBoxCoords.boxX[i & 1];
+			highlightFrames[i].y1 = _vm->guiSettings()->charBoxCoords.boxY[i >> 1];
+			highlightFrames[i].x2 = _vm->guiSettings()->charBoxCoords.boxX[i & 1] + _vm->guiSettings()->charBoxCoords.boxWidth - 1;
+			highlightFrames[i].y2 = _vm->guiSettings()->charBoxCoords.boxY[i >> 1] + _vm->guiSettings()->charBoxCoords.boxHeight - 1;
+		}
+	}
+
+	_highlightFrames = highlightFrames;
 	_updateBoxIndex = -1;
 	_highLightBoxTimer = 0;
 	_updateBoxColorIndex = 0;
@@ -1563,8 +1579,8 @@ GUI_EoB::~GUI_EoB() {
 	}
 
 	delete[] _saveSlotIdTemp;
-
 	delete[] _numAssignedSpellsOfType;
+	delete[] _highlightFrames;
 }
 
 void GUI_EoB::processButton(Button *button) {
@@ -2339,13 +2355,13 @@ void GUI_EoB::runCampMenu() {
 				if (runLoadMenu(0, 0))
 					runLoop = false;
 				else
-					newMenu = 1;
+					newMenu = _vm->gameFlags().platform == Common::kPlatformSegaCD ? 0 : 1;
 				break;
 
 			case 0x8009:
 				if (runSaveMenu(0, 0))
 					displayTextBox(14);
-				newMenu = 1;
+				newMenu = _vm->gameFlags().platform == Common::kPlatformSegaCD ? 0 : 1;
 				break;
 
 			case 0x800A:
@@ -2355,12 +2371,15 @@ void GUI_EoB::runCampMenu() {
 				}
 
 				if (cnt > 4) {
-					_vm->dropCharacter(selectCharacterDialogue(53));
-					_vm->gui_drawPlayField(false);
-					_screen->copyRegion(0, 120, 0, 0, 176, 24, 0, Screen_EoB::kCampMenuBackupPage, Screen::CR_NO_P_CHECK);
-					Screen::FontId cfn = _screen->setFont(_vm->_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT);
-					_vm->gui_drawAllCharPortraitsWithStats();
-					_screen->setFont(cfn);
+					i = selectCharacterDialogue(53);
+					if (i > 0) {
+						_vm->dropCharacter(i);
+						_vm->gui_drawPlayField(false);
+						_screen->copyRegion(0, 120, 0, 0, 176, 24, 0, Screen_EoB::kCampMenuBackupPage, Screen::CR_NO_P_CHECK);
+						Screen::FontId cfn = _screen->setFont(_vm->_conFont);
+						_vm->gui_drawAllCharPortraitsWithStats();
+						_screen->setFont(cfn);
+					}
 				} else {
 					displayTextBox(45);
 				}
@@ -2475,10 +2494,10 @@ bool GUI_EoB::runLoadMenu(int x, int y, bool fromMainMenu) {
 		updateSaveSlotsList(_vm->_targetName);
 		
 		_vm->useMainMenuGUISettings(fromMainMenu);
-		int slot = selectSaveSlotDialogue(x, y, 1);
+		int slot = selectSaveSlotDialog(x, y, 1);
 		_vm->useMainMenuGUISettings(false);
 
-		if (slot > 5) {
+		if (slot > _numSlotsVisible - 1) {
 			runLoop = result = false;
 		} else if (slot >= 0) {
 			if (_saveSlotIdTemp[slot] == -1) {
@@ -2998,7 +3017,7 @@ Common::String GUI_EoB::transferTargetMenu(Common::Array<Common::String> &target
 
 	int slot = 0;
 	do {
-		slot = selectSaveSlotDialogue(72, 14, 2);
+		slot = selectSaveSlotDialog(72, 14, 2);
 		if (slot == 6)
 			break;
 	} while (_saveSlotIdTemp[slot] == -1);
@@ -3024,7 +3043,7 @@ bool GUI_EoB::transferFileMenu(Common::String &targetName, Common::String &selec
 
 	int slot = 0;
 	do {
-		slot = selectSaveSlotDialogue(72, 14, 4);
+		slot = selectSaveSlotDialog(72, 14, 4);
 		if (slot == 6)
 			break;
 
@@ -3072,11 +3091,11 @@ bool GUI_EoB::runSaveMenu(int x, int y) {
 
 	for (bool runLoop = true; runLoop && !_vm->shouldQuit();) {
 		updateSaveSlotsList(_vm->_targetName);
-		int slot = selectSaveSlotDialogue(x, y, 0);
-		if (slot > 5) {
+		int slot = selectSaveSlotDialog(x, y, 0);
+		if (slot > _numSlotsVisible - 1) {
 			runLoop = result = false;
 		} else if (slot >= 0) {
-			bool useSlot = (_saveSlotIdTemp[slot] == -1);
+			bool useSlot = (_saveSlotIdTemp[slot] == -1 || _vm->gameFlags().platform == Common::kPlatformSegaCD);
 			if (useSlot)
 				_saveSlotStringsTemp[slot][0] = 0;
 			else
@@ -3091,7 +3110,8 @@ bool GUI_EoB::runSaveMenu(int x, int y) {
 			_screen->set16bitShadingLevel(4);
 
 			for (int in = -1; in == -1 && !_vm->shouldQuit();) {
-				_screen->fillRect(fx - 2, fy, fx + 160, fy + 8, _vm->guiSettings()->colors.fill);
+				if (_vm->gameFlags().platform != Common::kPlatformSegaCD)
+					_screen->fillRect(fx - 2, fy, fx + 160, fy + 8, _vm->guiSettings()->colors.fill);
 				if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
 					TimeDate td;
 					_vm->_system->getTimeAndDate(td);					
@@ -3099,6 +3119,9 @@ bool GUI_EoB::runSaveMenu(int x, int y) {
 					in = strlen(_saveSlotStringsTemp[slot]);
 					of = _vm->screen()->setFont(Screen::FID_6_FNT);
 					y++;
+				} else if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
+					Common::strlcpy(_saveSlotStringsTemp[slot], Common::String::format("%s\r FLOOR %-2u %u:%02u", _vm->_characters[0].name, _vm->_currentLevel + 1, _vm->_totalPlaySecs / 3600, _vm->_totalPlaySecs / 60).c_str(), 25);
+					in = strlen(_saveSlotStringsTemp[slot]);
 				} else {
 					in = getTextInput(_saveSlotStringsTemp[slot], x + 1, fy, 19, _vm->guiSettings()->colors.guiColorBlue, 0, _vm->guiSettings()->colors.guiColorDarkRed);
 				}
@@ -3117,10 +3140,14 @@ bool GUI_EoB::runSaveMenu(int x, int y) {
 				continue;
 			}
 
-			_screen->fillRect(fx - 2, fy, fx + 160, fy + 8, _vm->guiSettings()->colors.fill);
-			_screen->printShadedText(_saveSlotStringsTemp[slot], (x + 1) << 3, fy, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
-			_screen->set16bitShadingLevel(0);
-			_screen->setFont(of);
+			if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
+				drawSaveSlotButton(slot, 2, true);
+			} else {
+				_screen->fillRect(fx - 2, fy, fx + 160, fy + 8, _vm->guiSettings()->colors.fill);
+				_screen->printShadedText(_saveSlotStringsTemp[slot], (x + 1) << 3, fy, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+				_screen->set16bitShadingLevel(0);
+				_screen->setFont(of);
+			}
 			_screen->updateScreen();
 
 			Graphics::Surface thumb;
@@ -3141,21 +3168,11 @@ bool GUI_EoB::runSaveMenu(int x, int y) {
 	return result;
 }
 
-int GUI_EoB::selectSaveSlotDialogue(int x, int y, int id) {
+int GUI_EoB::selectSaveSlotDialog(int x, int y, int id) {
 	_saveSlotX = _saveSlotY = 0;
-	int col1 = (_vm->_configRenderMode == Common::kRenderCGA) ? 1 : _vm->guiSettings()->colors.guiColorWhite;
-	_screen->setCurPage(2);
-
 	_savegameOffset = 0;
-
-	drawMenuButtonBox(0, 0, 176, 144, false, false);
-	const char *title = (id < 2) ? _vm->_saveLoadStrings[2 + id] : _vm->_transferStringsScummVM[id - 1];
-	_screen->printShadedText(title, 52, 5, col1, 0, _vm->guiSettings()->colors.guiColorBlack);
-
-	_screen->copyRegion(0, 0, x, y, 176, 144, 2, 0, Screen::CR_NO_P_CHECK);
-	_screen->fillRect(0, 0, 175, 143, 0, 2);
-
-	_screen->setCurPage(0);
+	
+	drawSaveSlotDialog(x, y, id);
 	_screen->updateScreen();
 
 	_saveSlotX = x;
@@ -3168,17 +3185,20 @@ int GUI_EoB::selectSaveSlotDialogue(int x, int y, int id) {
 	for (bool runLoop = true; runLoop && !_vm->shouldQuit();) {
 		int inputFlag = _vm->checkInput(0, false, 0) & 0x8FF;
 		_vm->removeInputTop();
+		bool clickedButton = false;
 
 		if (inputFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inputFlag == _vm->_keyMap[Common::KEYCODE_RETURN]) {
-			runLoop = false;
+			runLoop = (_vm->gameFlags().platform == Common::kPlatformSegaCD && _saveSlotIdTemp[newHighlight] == -1 && id == 1);
+			clickedButton = true;
 		} else if (inputFlag == _vm->_keyMap[Common::KEYCODE_ESCAPE]) {
-			newHighlight = 6;
+			newHighlight = _numSlotsVisible;
 			runLoop = false;
+			clickedButton = true;
 		} else if (inputFlag == _vm->_keyMap[Common::KEYCODE_DOWN] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP2]) {
-			if (++newHighlight > 5) {
-				newHighlight = 5;
-				if (++_savegameOffset > 984)
-					_savegameOffset = 984;
+			if (++newHighlight > (_numSlotsVisible - 1)) {
+				newHighlight = _numSlotsVisible - 1;
+				if (++_savegameOffset > (990 - _numSlotsVisible))
+					_savegameOffset = (990 - _numSlotsVisible);
 				else
 					lastOffset = -1;
 			}
@@ -3191,20 +3211,20 @@ int GUI_EoB::selectSaveSlotDialogue(int x, int y, int id) {
 					lastOffset = -1;
 			}
 		} else if (inputFlag == _vm->_keyMap[Common::KEYCODE_PAGEDOWN] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP1]) {
-			_savegameOffset += 6;
-			if (_savegameOffset > 984)
-				_savegameOffset = 984;
+			_savegameOffset += _numSlotsVisible;
+			if (_savegameOffset > (990 - _numSlotsVisible))
+				_savegameOffset = (990 - _numSlotsVisible);
 			else
 				lastOffset = -1;
 		} else if (inputFlag == _vm->_keyMap[Common::KEYCODE_PAGEUP] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP7]) {
-			_savegameOffset -= 6;
+			_savegameOffset -= _numSlotsVisible;
 			if (_savegameOffset < 0)
 				_savegameOffset = 0;
 			else
 				lastOffset = -1;
 		} else if (inputFlag == 205) {
-			if (++_savegameOffset > 984)
-				_savegameOffset = 984;
+			if (++_savegameOffset > (990 - _numSlotsVisible))
+				_savegameOffset = (990 - _numSlotsVisible);
 			else
 				lastOffset = -1;
 		} else if (inputFlag == 203) {
@@ -3216,29 +3236,35 @@ int GUI_EoB::selectSaveSlotDialogue(int x, int y, int id) {
 			slot = getHighlightSlot();
 			if (slot != -1) {
 				newHighlight = slot;
-				if (inputFlag == 199)
-					runLoop = false;
+				if (inputFlag == 199) {
+					runLoop = (_vm->gameFlags().platform == Common::kPlatformSegaCD && _saveSlotIdTemp[newHighlight] == -1 && id == 1);
+					clickedButton = true;
+				}
 			}
 		}
 
 		if (lastOffset != _savegameOffset) {
 			lastHighlight = -1;
 			setupSaveMenuSlots();
-			for (int i = 0; i < 7; i++)
-				drawSaveSlotButton(i, 1, col1);
+			for (int i = 0; i < _numSlotsVisible + 1; i++)
+				drawSaveSlotButton(i, 1, false);
 			lastOffset = _savegameOffset;
 		}
 
 		if (lastHighlight != newHighlight) {
-			drawSaveSlotButton(lastHighlight, 0, col1);
-			drawSaveSlotButton(newHighlight, 0, _vm->guiSettings()->colors.guiColorLightRed);
+			drawSaveSlotButton(lastHighlight, 0, false);
+			drawSaveSlotButton(newHighlight, 0, true);
 
 			// Display highlighted slot index in the bottom left corner to avoid people getting lost with the 990 save slots
+			int sli = (newHighlight == _numSlotsVisible) ? _savegameOffset : (_savegameOffset + newHighlight);
 			if (_vm->_flags.platform == Common::kPlatformSegaCD) {
-
+				_screen->sega_clearTextBuffer(0);
+				_vm->_txt->printShadowedText(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(Screen_EoB::kSegaRenderPage);
+				_screen->copyRegion(_saveSlotX + 8, _saveSlotY + 152, _saveSlotX + 8, _saveSlotY + 152, 56, 8, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
 			} else {
 				Screen::FontId of = _screen->setFont(Screen::FID_6_FNT);
-				int sli = (newHighlight == 6) ? _savegameOffset : (_savegameOffset + newHighlight);
 				_screen->set16bitShadingLevel(4);
 				_screen->printText(Common::String::format("%03d/989", sli).c_str(), _saveSlotX + 5, _saveSlotY + 135, _vm->guiSettings()->colors.frame2, _vm->guiSettings()->colors.fill);
 				_screen->set16bitShadingLevel(0);
@@ -3248,17 +3274,29 @@ int GUI_EoB::selectSaveSlotDialogue(int x, int y, int id) {
 			_screen->updateScreen();
 			lastHighlight = newHighlight;
 		}
-	}
 
-	drawSaveSlotButton(newHighlight, 2, _vm->guiSettings()->colors.guiColorLightRed);
-	_screen->updateScreen();
-	_vm->_system->delayMillis(80);
-	drawSaveSlotButton(newHighlight, 1, _vm->guiSettings()->colors.guiColorLightRed);
-	_screen->updateScreen();
+		if (clickedButton) {
+			drawSaveSlotButton(newHighlight, 2, true);
+			_screen->updateScreen();
+			_vm->_system->delayMillis(80);
+			drawSaveSlotButton(newHighlight, 1, true);
+			_screen->updateScreen();
+		}
+	}
 
 	return newHighlight;
 }
 
+void GUI_EoB::drawSaveSlotDialog(int x, int y, int id) {
+	_screen->setCurPage(2);
+	drawMenuButtonBox(0, 0, 176, 144, false, false);
+	const char* title = (id < 2) ? _vm->_saveLoadStrings[2 + id] : _vm->_transferStringsScummVM[id - 1];
+	_screen->printShadedText(title, 52, 5, (_vm->_configRenderMode == Common::kRenderCGA) ? 1 : _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+	_screen->copyRegion(0, 0, x, y, 176, 144, 2, 0, Screen::CR_NO_P_CHECK);
+	_screen->fillRect(0, 0, 175, 143, 0, 2);
+	_screen->setCurPage(0);
+}
+
 void GUI_EoB::runMemorizePrayMenu(int charIndex, int spellType) {
 	if (charIndex == -1)
 		return;
@@ -3359,14 +3397,19 @@ void GUI_EoB::runMemorizePrayMenu(int charIndex, int spellType) {
 		}
 	}
 
+	initMemorizePrayMenu();
 	Button *buttonList = initMenu(4);
 
 	int lastHighLightText = -1;
 	int lastHighLightButton = -1;
 	int newHighLightButton = 0;
 	int newHighLightText = 0;
+	int buttonStart = (_vm->gameFlags().platform == Common::kPlatformSegaCD) ? 0x801B : 0x8017;
+	int listY = (_vm->gameFlags().platform == Common::kPlatformSegaCD) ? 80 : 50;
+	int listEntryH = (_vm->gameFlags().platform == Common::kPlatformSegaCD) ? 8 : 9;
 	bool updateDesc = true;
 	bool updateList = true;
+	bool highLightClicked = (_vm->gameFlags().platform == Common::kPlatformSegaCD);
 
 	for (bool runLoop = true; runLoop && !_vm->shouldQuit();) {
 		updateBoxFrameHighLight(charIndex);
@@ -3380,12 +3423,13 @@ void GUI_EoB::runMemorizePrayMenu(int charIndex, int spellType) {
 
 		if (lastHighLightButton != newHighLightButton) {
 			if (lastHighLightButton >= 0)
-				drawMenuButton(_vm->gui_getButton(buttonList, lastHighLightButton + 26), false, false, true);
-			drawMenuButton(_vm->gui_getButton(buttonList, newHighLightButton + 26), false, true, true);
+				drawMenuButton(_vm->gui_getButton(buttonList, lastHighLightButton + ((buttonStart & 0xFF) + 3)), false, false, true);
+			drawMenuButton(_vm->gui_getButton(buttonList, newHighLightButton + ((buttonStart & 0xFF) + 3)), highLightClicked, true, true);
 			newHighLightText = 0;
 			lastHighLightText = -1;
 			lastHighLightButton = newHighLightButton;
 			updateDesc = updateList = true;
+			highLightClicked = false;
 		}
 
 		if (updateList) {
@@ -3395,15 +3439,28 @@ void GUI_EoB::runMemorizePrayMenu(int charIndex, int spellType) {
 				memorizePrayMenuPrintString(menuSpellMap[lastHighLightButton * 11 + ii], ii - 1, spellType, false, false);
 
 			_screen->setCurPage(0);
-			_screen->copyRegion(0, 50, 0, 50, 176, 72, 2, 0, Screen::CR_NO_P_CHECK);
+			if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
+				_screen->sega_getRenderer()->render(Screen_EoB::kSegaRenderPage);
+				_screen->copyRegion(0, 80, 0, 80, 176, 72, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+			} else {
+				_screen->copyRegion(0, 50, 0, 50, 176, 72, 2, 0, Screen::CR_NO_P_CHECK);
+			}
 			lastHighLightText = -1;
 		}
-
+		
 		if (updateDesc) {
 			updateDesc = false;
-			_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);
-			_screen->set16bitShadingLevel(0);
+			if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
+				_screen->sega_clearTextBuffer(0);
+				_vm->_txt->printShadowedText(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(Screen_EoB::kSegaRenderPage);
+				_screen->copyRegion(8, 64, 8, 64, 160, 16, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+			} 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);
+				_screen->set16bitShadingLevel(0);
+			}
 		}
 
 		if (newHighLightText < 0)
@@ -3422,15 +3479,15 @@ void GUI_EoB::runMemorizePrayMenu(int charIndex, int spellType) {
 		_vm->removeInputTop();
 
 		if (inputFlag == _vm->_keyMap[Common::KEYCODE_KP6] || inputFlag == _vm->_keyMap[Common::KEYCODE_RIGHT]) {
-			inputFlag = 0x801A + ((lastHighLightButton + 1) % _numVisPages);
+			inputFlag = buttonStart + 3 + ((lastHighLightButton + 1) % _numVisPages);
 		} else if (inputFlag == _vm->_keyMap[Common::KEYCODE_KP4] || inputFlag == _vm->_keyMap[Common::KEYCODE_LEFT]) {
-			inputFlag = lastHighLightButton ? 0x8019 + lastHighLightButton : 0x8019 + _numVisPages;
+			inputFlag = lastHighLightButton ? buttonStart + 2 + lastHighLightButton : buttonStart + 2 + _numVisPages;
 		} else if (inputFlag == _vm->_keyMap[Common::KEYCODE_ESCAPE]) {
-			inputFlag = 0x8018;
+			inputFlag = buttonStart + 1;
 		} else {
 			Common::Point p = _vm->getMousePos();
-			if (_vm->posWithinRect(p.x, p.y, 8, 50, 168, 122)) {
-				newHighLightText = (p.y - 50) / 9;
+			if (_vm->posWithinRect(p.x, p.y, 8, listY, 168, listY + 72)) {
+				newHighLightText = (p.y - listY) / listEntryH;
 				if (menuSpellMap[lastHighLightButton * 11] - 1 < newHighLightText)
 					newHighLightText = menuSpellMap[lastHighLightButton * 11] - 1;
 			}
@@ -3440,12 +3497,16 @@ void GUI_EoB::runMemorizePrayMenu(int charIndex, int spellType) {
 			b = _vm->gui_getButton(buttonList, inputFlag & 0x7FFF);
 			drawMenuButton(b, true, true, true);
 			_screen->updateScreen();
-			_vm->_system->delayMillis(80);
-			drawMenuButton(b, false, false, true);
-			_screen->updateScreen();
+			if (_vm->gameFlags().platform == Common::kPlatformSegaCD && inputFlag >= buttonStart + 3) {
+				highLightClicked = true;
+			} else {
+				_vm->_system->delayMillis(80);
+				drawMenuButton(b, false, false, true);
+				_screen->updateScreen();
+			}
 		}
 
-		if (inputFlag == 0x8019 || inputFlag == _vm->_keyMap[Common::KEYCODE_KP_PLUS] || inputFlag == _vm->_keyMap[Common::KEYCODE_PLUS] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP5] || inputFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inputFlag == _vm->_keyMap[Common::KEYCODE_RETURN]) {
+		if (inputFlag == buttonStart + 2 || inputFlag == _vm->_keyMap[Common::KEYCODE_KP_PLUS] || inputFlag == _vm->_keyMap[Common::KEYCODE_PLUS] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP5] || inputFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inputFlag == _vm->_keyMap[Common::KEYCODE_RETURN]) {
 			if (np[lastHighLightButton] > numAssignedSpellsPerBookPage[lastHighLightButton] && lastHighLightText != -1) {
 				_numAssignedSpellsOfType[menuSpellMap[lastHighLightButton * 11 + lastHighLightText + 1] * 2 - 2]++;
 				numAssignedSpellsPerBookPage[lastHighLightButton]++;
@@ -3469,7 +3530,7 @@ void GUI_EoB::runMemorizePrayMenu(int charIndex, int spellType) {
 			newHighLightText = menuSpellMap[lastHighLightButton * 11] - 1;
 		} else if (inputFlag == _vm->_keyMap[Common::KEYCODE_HOME] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP7]) {
 			newHighLightText = 0;
-		} else if (inputFlag == 0x8017) {
+		} else if (inputFlag == buttonStart) {
 			if (numAssignedSpellsPerBookPage[lastHighLightButton]) {
 				for (int i = 1; i <= menuSpellMap[lastHighLightButton * 11]; i++) {
 					numAssignedSpellsPerBookPage[lastHighLightButton] -= _numAssignedSpellsOfType[menuSpellMap[lastHighLightButton * 11 + i] * 2 - 2];
@@ -3479,21 +3540,21 @@ void GUI_EoB::runMemorizePrayMenu(int charIndex, int spellType) {
 				updateDesc = updateList = true;
 			}
 
-		} else if (inputFlag == 0x8018) {
+		} else if (inputFlag == buttonStart + 1) {
 			_vm->gui_drawAllCharPortraitsWithStats();
 			runLoop = false;
 
 		} else if (inputFlag & 0x8000) {
-			newHighLightButton = inputFlag - 0x801A;
+			newHighLightButton = inputFlag - (buttonStart + 3);
 			if (newHighLightButton == lastHighLightButton)
-				drawMenuButton(_vm->gui_getButton(buttonList, inputFlag & 0x7FFF), false, true, true);
+				drawMenuButton(_vm->gui_getButton(buttonList, inputFlag & 0x7FFF), _vm->gameFlags().platform == Common::kPlatformSegaCD, true, true);
 		}
 	}
 
 	releaseButtons(buttonList);
 	updateBoxFrameHighLight(-1);
 
-	Screen::FontId of = _screen->setFont(_vm->_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT);
+	Screen::FontId of = _screen->setFont(_vm->_conFont);
 	_vm->gui_drawCharPortraitWithStats(charIndex);
 	_screen->setFont(of);
 
@@ -4011,6 +4072,8 @@ int GUI_EoB::selectCharacterDialogue(int id) {
 	uint8 flags = (id == 26) ? (_vm->game() == GI_EOB1 ? 0x04 : 0x14) : 0x02;
 	_vm->removeInputTop();
 
+	int buttonStart = _vm->gameFlags().platform == Common::kPlatformSegaCD ? 0x8014 : 0x8010;
+
 	_charSelectRedraw = false;
 	bool starvedUnconscious = false;
 	int count = 0;
@@ -4044,21 +4107,21 @@ int GUI_EoB::selectCharacterDialogue(int id) {
 		else if (id == 49)
 			eid = 52;
 
-		displayTextBox(eid);
+		displayTextBox(eid, 0x55);
 		return -1;
 	}
 
-	static const uint16 selX[] = { 184, 256, 184, 256, 184, 256 };
-	static const uint8 selY[] = { 2, 2, 54, 54, 106, 106};
-
 	for (int i = 0; i < 6; i++) {
 		if (found[i] != -1 || !_vm->testCharacter(i, 1))
 			continue;
 
-		_screen->drawShape(0, _vm->_blackBoxSmallGrid, selX[i], selY[i], 0);
-		_screen->drawShape(0, _vm->_blackBoxSmallGrid, selX[i] + 16, selY[i], 0);
-		_screen->drawShape(0, _vm->_blackBoxSmallGrid, selX[i] + 32, selY[i], 0);
-		_screen->drawShape(0, _vm->_blackBoxSmallGrid, selX[i] + 48, selY[i], 0);
+		int selX = _vm->guiSettings()->charBoxCoords.boxX[i & 1];
+		int selY = _vm->guiSettings()->charBoxCoords.boxY[i >> 1];
+
+		_screen->drawShape(0, _vm->_blackBoxSmallGrid, selX, selY, 0);
+		_screen->drawShape(0, _vm->_blackBoxSmallGrid, selX + 16, selY, 0);
+		_screen->drawShape(0, _vm->_blackBoxSmallGrid, selX + 32, selY, 0);
+		_screen->drawShape(0, _vm->_blackBoxSmallGrid, selX + 48, selY, 0);
 		_charSelectRedraw = true;
 	}
 	_screen->updateScreen();
@@ -4087,7 +4150,7 @@ int GUI_EoB::selectCharacterDialogue(int id) {
 		}
 	}
 
-	Screen::FontId of = _screen->setFont(_vm->_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT);
+	Screen::FontId of = _screen->setFont(_vm->_conFont);
 
 	while (result == -2 && !_vm->shouldQuit()) {
 		int inputFlag = _vm->checkInput(buttonList, false, 0);
@@ -4119,18 +4182,18 @@ int GUI_EoB::selectCharacterDialogue(int id) {
 			if (hlCur >= 0)
 				result = hlCur;
 
-		} else if (inputFlag == _vm->_keyMap[Common::KEYCODE_ESCAPE] || inputFlag == 0x8010) {
-			_screen->setFont(_vm->_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT);
+		} else if (inputFlag == _vm->_keyMap[Common::KEYCODE_ESCAPE] || inputFlag == buttonStart) {
+			_screen->setFont(_vm->_invFont3);
 			drawMenuButton(buttonList, true, true, true);
 			_screen->updateScreen();
 			_vm->_system->delayMillis(80);
 			drawMenuButton(buttonList, false, false, true);
 			_screen->updateScreen();
-			_screen->setFont(_vm->_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT);
+			_screen->setFont(_vm->_conFont);
 			result = -1;
 
-		} else if (inputFlag > 0x8010 && inputFlag < 0x8017) {
-			result = inputFlag - 0x8011;
+		} else if (inputFlag > buttonStart && inputFlag < buttonStart + 7) {
+			result = inputFlag - (buttonStart + 1);
 			if (found[result])
 				result = -2;
 		}
@@ -4140,7 +4203,7 @@ int GUI_EoB::selectCharacterDialogue(int id) {
 	if (hlCur >= 0)
 		_vm->gui_drawCharPortraitWithStats(hlCur);
 
-	_screen->setFont(_vm->_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT);
+	_screen->setFont(_vm->_invFont3);
 
 	if (result != -1 && id != 53) {
 		if (flags & 4) {
@@ -4165,7 +4228,7 @@ int GUI_EoB::selectCharacterDialogue(int id) {
 	return result;
 }
 
-void GUI_EoB::displayTextBox(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);
@@ -4206,7 +4269,10 @@ Button *GUI_EoB::initMenu(int id) {
 	}
 
 	if (m->titleStrId != -1) {
-		_screen->printShadedText(getMenuString(m->titleStrId), 5, 5, m->titleCol, 0, _vm->guiSettings()->colors.guiColorBlack);
+		if (_vm->gameFlags().platform == Common::kPlatformSegaCD)
+			displayTextBox(m->titleStrId, 0x55, false);
+		else
+			_screen->printShadedText(getMenuString(m->titleStrId), 5, 5, m->titleCol, 0, _vm->guiSettings()->colors.guiColorBlack);
 		_screen->setTextMarginRight(Screen::SCREEN_W);
 	}
 
@@ -4316,7 +4382,7 @@ void GUI_EoB::drawTextBox(int dim, int id) {
 	_screen->setFont(of);
 }
 
-void GUI_EoB::drawSaveSlotButton(int slot, int redrawBox, int textCol) {
+void GUI_EoB::drawSaveSlotButton(int slot, int redrawBox, bool highlight) {
 	if (slot < 0)
 		return;
 
@@ -4324,7 +4390,7 @@ void GUI_EoB::drawSaveSlotButton(int slot, int redrawBox, int textCol) {
 	int y = _saveSlotY + slot * 17 + 20;
 	int w = 167;
 	char slotString[26];
-	Common::strlcpy(slotString, slot < 6 ? _saveSlotStringsTemp[slot] : _vm->_saveLoadStrings[0], _vm->gameFlags().platform == Common::kPlatformFMTowns ? 25 : 20);
+	Common::strlcpy(slotString, slot < _numSlotsVisible ? _saveSlotStringsTemp[slot] : _vm->_saveLoadStrings[0], _vm->gameFlags().platform == Common::kPlatformFMTowns ? 25 : 20);
 	
 	if (slot >= 6) {
 		x = _saveSlotX + 118;
@@ -4341,7 +4407,7 @@ void GUI_EoB::drawSaveSlotButton(int slot, int redrawBox, int textCol) {
 		y++;
 	}
 
-	_screen->printShadedText(slotString, x + 4, y + 3, textCol, 0, _vm->guiSettings()->colors.guiColorBlack);
+	_screen->printShadedText(slotString, x + 4, y + 3, highlight ? _vm->guiSettings()->colors.guiColorLightRed : (_vm->_configRenderMode == Common::kRenderCGA ? 1 : _vm->guiSettings()->colors.guiColorWhite), 0, _vm->guiSettings()->colors.guiColorBlack);
 	_vm->screen()->setFont(fnt);
 }
 
@@ -4467,7 +4533,7 @@ void GUI_EoB::releaseButtons(Button *list) {
 }
 
 void GUI_EoB::setupSaveMenuSlots() {
-	for (int i = 0; i < 6; ++i) {
+	for (int i = 0; i < _numSlotsVisible; ++i) {
 		if (_savegameOffset + i < _savegameListSize) {
 			if (_savegameList[i + _savegameOffset]) {
 				Common::strlcpy(_saveSlotStringsTemp[i], _savegameList[i + _savegameOffset], 25);
@@ -4526,7 +4592,7 @@ void GUI_EoB::restParty_updateRestTime(int hours, bool init) {
 	_screen->setFont(of);
 }
 
-const EoBRect16 GUI_EoB::_highlightFrames[] = {
+const EoBRect16 GUI_EoB::_highlightFramesDefault[] = {
 	{ 0x00B7, 0x0001, 0x00F7, 0x0034 },
 	{ 0x00FF, 0x0001, 0x013F, 0x0034 },
 	{ 0x00B7, 0x0035, 0x00F7, 0x0068 },
@@ -4557,6 +4623,8 @@ const uint8 GUI_EoB::_highlightColorTableAmiga[] = { 0x13, 0x0B, 0x12, 0x0A, 0x1
 
 const uint8 GUI_EoB::_highlightColorTablePC98[] = { 0x0C, 0x0D, 0x0E, 0x0F, 0x0E, 0x0D, 0x00 };
 
+const uint8 GUI_EoB::_highlightColorTableSegaCD[] = { 0x3D, 0x3D, 0x3D, 0x3E, 0x3E, 0x3E, 0x3F, 0x3F, 0x3F, 0x3E, 0x3E, 0x3E, 0x00 };
+
 } // End of namespace Kyra
 
 #endif // ENABLE_EOB
diff --git a/engines/kyra/gui/gui_eob.h b/engines/kyra/gui/gui_eob.h
index da5451996c..cf84be54c5 100644
--- a/engines/kyra/gui/gui_eob.h
+++ b/engines/kyra/gui/gui_eob.h
@@ -83,6 +83,11 @@ protected:
 	Button *initMenu(int id);
 	void releaseButtons(Button *list);
 
+	int8 *_numAssignedSpellsOfType;
+	char** _saveSlotStringsTemp;
+	int16 _saveSlotX;
+	int16 _saveSlotY;
+
 	Screen_EoB *_screen;
 
 private:
@@ -91,37 +96,37 @@ private:
 	void simpleMenu_initMenuItemsMask(int menuId, int maxItem, int32 menuItemsMask, int unk);
 
 	bool runSaveMenu(int x, int y);
-	int selectSaveSlotDialogue(int x, int y, int id);
+	int selectSaveSlotDialog(int x, int y, int id);
+	virtual void drawSaveSlotDialog(int x, int y, int id);
 	void runMemorizePrayMenu(int charIndex, int spellType);
 	void scribeScrollDialogue();
 	bool restParty();
 
 	virtual void drawCampMenu() {}
+	virtual void initMemorizePrayMenu() {}
 	virtual bool confirmDialogue(int id);
 	int selectCharacterDialogue(int id);
-	virtual void displayTextBox(int id);
+	virtual void displayTextBox(int id, int textColor = 0xFF, bool wait = true);
 
 	virtual void drawMenuButton(Button *b, bool clicked, bool highlight, bool noFill);
 	void drawMenuButtonBox(int x, int y, int w, int h, bool clicked, bool noFill);
 	void drawTextBox(int dim, int id);
-	void drawSaveSlotButton(int slot, int redrawBox, int textCol);
-	void memorizePrayMenuPrintString(int spellId, int bookPageIndex, int spellType, bool noFill, bool highLight);
+	virtual void drawSaveSlotButton(int slot, int redrawBox, bool highlight);
+	virtual void memorizePrayMenuPrintString(int spellId, int bookPageIndex, int spellType, bool noFill, bool highLight);
 	virtual void updateOptionsStrings();
 
 	Button *linkButton(Button *list, Button *newbt);
 
 	void setupSaveMenuSlots();
-	int getHighlightSlot();
+	virtual int getHighlightSlot();
 	void sortSaveSlots() override;
 
 	virtual void restParty_updateRestTime(int hours, bool init);
 
 	char **_menuStringsPrefsTemp;
-	char **_saveSlotStringsTemp;
 	int16 *_saveSlotIdTemp;
 	int _savegameOffset;
-	int16 _saveSlotX;
-	int16 _saveSlotY;
+	const int _numSlotsVisible;
 
 	EoBCoreEngine *_vm;
 
@@ -141,7 +146,6 @@ private:
 
 	uint8 _numPages;
 	uint8 _numVisPages;
-	int8 *_numAssignedSpellsOfType;
 	uint32 _clericSpellAvltyFlags;
 	uint32 _paladinSpellAvltyFlags;
 	bool _needRest;
@@ -155,11 +159,13 @@ private:
 	const uint8 *_highLightColorTable;
 	uint32 _highLightBoxTimer;
 
-	static const EoBRect16 _highlightFrames[];
+	const EoBRect16 *_highlightFrames;
+	static const EoBRect16 _highlightFramesDefault[];
 	static const uint8 _highlightColorTableVGA[];
 	static const uint8 _highlightColorTableEGA[];
 	static const uint8 _highlightColorTableAmiga[];
 	static const uint8 _highlightColorTablePC98[];
+	static const uint8 _highlightColorTableSegaCD[];
 
 	// FM-Towns specific
 	int checkKatakanaSelection();
diff --git a/engines/kyra/gui/gui_eob_segacd.cpp b/engines/kyra/gui/gui_eob_segacd.cpp
index 3c43563d09..d0118d56e8 100644
--- a/engines/kyra/gui/gui_eob_segacd.cpp
+++ b/engines/kyra/gui/gui_eob_segacd.cpp
@@ -141,6 +141,7 @@ void EoBEngine::gui_setupPlayFieldHelperPages(bool keepText) {
 		_txt->clearDim(0);
 
 	SegaRenderer *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);
 	r->fillRectWithTiles(1, 0, 0, 40, 26, 0x2000, true, false, _playFldPattern2);
@@ -347,7 +348,7 @@ void EoBEngine::gui_drawSpellbook() {
 		int d = _openBookAvailableSpells[_openBookSpellLevel * 10 + i];
 		if (d < 0)
 			continue;
-		printSpellbookString(&_tempPattern[(i + 1) * 12], _openBookSpellList[d], 0x63C9);
+		printSpellbookString(&_tempPattern[(i + 1) * 12], _openBookSpellList[d], (i == _openBookSpellSelectedItem) ? 0x6223 : 0x63C9);
 	}
 
 	r->fillRectWithTiles(0, 10, 15, 12, 6, 0, true, false, _tempPattern);
@@ -561,6 +562,7 @@ void EoBEngine::printStatsString(const char *str, int x, int y) {
 }
 
 void EoBEngine::printSpellbookString(uint16 *dst, const char *str, uint16 ntbl) {
+	assert(str);
 	for (char c = *str++; c; c = *str++) {
 		if (c > 31 && c < 128)
 			*dst = ntbl + c - 32;
@@ -646,10 +648,19 @@ GUI_EoB_SegaCD::GUI_EoB_SegaCD(EoBEngine *vm) : GUI_EoB(vm), _vm(vm) {
 	_screen->decodeBIN(cm + 4, cmdec, decodeSize);
 	_campMenu = cmdec;
 	delete[] cm;
+
+	const EoBMenuButtonDef* df = &_vm->_menuButtonDefs[6];
+	_saveLoadCancelButton = new Button();
+	_saveLoadCancelButton->index = 7;
+	_saveLoadCancelButton->width = df->width;
+	_saveLoadCancelButton->height = df->height;
+	_saveLoadCancelButton->flags = df->flags;
+	_saveLoadCancelButton->extButtonDef = df;
 }
 
 GUI_EoB_SegaCD::~GUI_EoB_SegaCD() {
 	delete[] _campMenu;
+	delete _saveLoadCancelButton;
 }
 
 void GUI_EoB_SegaCD::drawCampMenu() {
@@ -659,6 +670,38 @@ void GUI_EoB_SegaCD::drawCampMenu() {
 	_screen->sega_selectPalette(40, 2, true);
 }
 
+void GUI_EoB_SegaCD::initMemorizePrayMenu() {
+	_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 22, 21, 0);
+	_screen->sega_getRenderer()->fillRectWithTiles(0, 1, 8, 20, 2, 0x62AB, true);
+	_screen->sega_getRenderer()->fillRectWithTiles(0, 1, 4, 20, 4, 0x6283, true);
+	_screen->sega_getRenderer()->memsetVRAM(0x5060, 0, 2560);
+	_screen->sega_getRenderer()->memsetVRAM(0x5560, 0, 1280);
+	_screen->sega_getRenderer()->loadToVRAM(&_campMenu[0x87C0], 4992, 0x3CE0);
+	_screen->sega_clearTextBuffer(0);
+	_vm->_txt->printShadowedText(getMenuString(37), 0, 2, 0xFF, 0xCC, 160, 16, 0, false);
+	_screen->sega_loadTextBufferToVRAM(0, 0x5060, 2560);
+	_screen->sega_getRenderer()->render(Screen_EoB::kSegaRenderPage);
+	_screen->copyRegion(8, 32, 8, 32, 160, 16, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+}
+
+void GUI_EoB_SegaCD::drawSaveSlotDialog(int x, int y, int id) {
+	_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 22, 21, 0);
+	_screen->sega_getRenderer()->fillRectWithTiles(0, (x >> 3) + 1, (y >> 3) + (y ? 3 : 4), 20, 2, 0x6283, true);
+	_screen->sega_getRenderer()->fillRectWithTiles(0, (x >> 3) + (x ? 5 : 6), (y >> 3) + (y ? 6 : 7), 15, 10, 0x62AB, true);
+	_screen->sega_getRenderer()->fillRectWithTiles(0, (x >> 3) + 1, (y >> 3) + 19, 7, 1, 0x6002, true);
+	_screen->sega_getRenderer()->loadToVRAM(&_campMenu[0x87C0], 4992, 0x3CE0);
+	_screen->sega_getRenderer()->memsetVRAM(0x5560, 0, 4480);
+	_screen->sega_clearTextBuffer(0);
+	_saveLoadCancelButton->x = ((const EoBMenuButtonDef*)_saveLoadCancelButton->extButtonDef)->x + x - (x ? 8 : 0);
+	_saveLoadCancelButton->y = ((const EoBMenuButtonDef*)_saveLoadCancelButton->extButtonDef)->y + y;
+	int cs = _screen->setFontStyles(_screen->_currentFont, _vm->gameFlags().lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat);
+	_vm->_txt->printShadowedText(_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(Screen_EoB::kSegaRenderPage);
+	_screen->copyRegion(x, y + 8, x, y + 8, 176, 168, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+}
+
 bool GUI_EoB_SegaCD::confirmDialogue(int id) {
 	_screen->sega_clearTextBuffer(0);
 
@@ -732,17 +775,22 @@ bool GUI_EoB_SegaCD::confirmDialogue(int id) {
 	return result;
 }
 
-void GUI_EoB_SegaCD::displayTextBox(int id) {
+void GUI_EoB_SegaCD::displayTextBox(int id, int textColor, bool wait) {
 	_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 22, 20, 0);
 	_screen->sega_clearTextBuffer(0);
-	int cs = _screen->setFontStyles(_screen->_currentFont, _vm->gameFlags().lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat);
-	_vm->_txt->printShadowedText(getMenuString(id), 0, 0, 0xFF, 0xCC, 160, 40, 0, false);
+	int cs = _vm->gameFlags().lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat;
+	if (id == 23 || id == 26 || id == 49)
+		cs |= Font::kStyleNarrow2;
+	cs = _screen->setFontStyles(_screen->_currentFont, cs);
+	_vm->_txt->printShadowedText(getMenuString(id), 0, 0, textColor, 0xCC, 160, 40, 0, false);
 	_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(Screen_EoB::kSegaRenderPage);
 	_screen->copyRegion(0, 0, 0, 0, 176, 168, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
 	_screen->updateScreen();
+	if (!wait)
+		return;
 
 	_vm->resetSkipFlag();
 	while (!(_vm->shouldQuit() || _vm->skipFlag()))
@@ -753,26 +801,66 @@ void GUI_EoB_SegaCD::displayTextBox(int id) {
 void GUI_EoB_SegaCD::drawMenuButton(Button *b, bool clicked, bool highlight, bool noFill) {
 	if (!b)
 		return;
-	drawButtonIntern(b->index - 1, clicked ? 1 : 0);
-	drawButtonIntern(b->index - 1, 2);
+
+	const MenuButtonTiles &t = _menuButtonTiles[b->index - 1];
+	if (!t.nameTbl)
+		return;
+
+	_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(Screen_EoB::kSegaRenderPage);
+	_screen->copyRegion(b->x, b->y, b->x, b->y, b->width, b->height, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
 }
 
-void GUI_EoB_SegaCD::drawButtonIntern(int id, int op) {
-	assert(id < 22);
-	const SegaMenuButton &b = _menuButtons[id];
-	if (b.w == 0)
+void GUI_EoB_SegaCD::drawSaveSlotButton(int slot, int redrawBox, bool highlight) {
+	if (slot < 0)
 		return;
 
-	if (op == 2) {
-		_screen->sega_getRenderer()->fillRectWithTiles(0, b.x, b.y, b.w, b.h, 0x4000 + b.nameTbl, true);
-	} else {
-		_screen->sega_getRenderer()->loadToVRAM(&_campMenu[(0x1CE + b.nameTbl2 + op * b.w * b.h) << 5], (b.w * b.h) << 5, b.nameTbl << 5);
+	if (slot == 5) {
+		drawMenuButton(_saveLoadCancelButton, redrawBox == 2, false, false);
+		return;
 	}
 
-	if (op != 2) {
-		_screen->sega_getRenderer()->render(Screen_EoB::kSegaRenderPage);
-		_screen->copyRegion(b.x << 3, b.y << 3, b.x << 3, b.y << 3, b.w << 3, b.h << 3, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+	_screen->sega_getRenderer()->fillRectWithTiles(0, (_saveSlotX >> 3) + (_saveSlotX ? 1 : 2), (_saveSlotY >> 3) + (_saveSlotY ? 6 : 7) + (slot << 1), 3, 2, 0x41E7 + slot * 12 + (redrawBox == 2 ? 6 : 0), true);
+	_screen->sega_clearTextBuffer(0);
+	_vm->_txt->printShadowedText(slot < 5 ? _saveSlotStringsTemp[slot] : _vm->_saveLoadStrings[0], 0, (slot << 4) + (slot < 5 ? 0 : 2), highlight ? 0x55 : 0xFF, 0xCC, 121, 80, 0, false);
+	_screen->sega_loadTextBufferToVRAM(0, 0x5560, 4800);
+	_screen->sega_getRenderer()->render(Screen_EoB::kSegaRenderPage);
+	_screen->copyRegion(_saveSlotX + (_saveSlotX ? 8 : 16), _saveSlotY + (_saveSlotY ? 48 : 56) + (slot << 4), _saveSlotX + (_saveSlotX ? 8 : 16), _saveSlotY + (_saveSlotY ? 48 : 56) + (slot << 4), 168, 16, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+}
+
+int GUI_EoB_SegaCD::getHighlightSlot() {
+	int res = -1;
+	Common::Point p = _vm->getMousePos();
+
+	for (int i = 0; i < 5; i++) {
+		int y = _saveSlotY + i * 16 + (_saveSlotY ? 48 : 56);
+		if (_vm->posWithinRect(p.x, p.y, _saveSlotX + (_saveSlotX ? 8 : 16), y, _saveSlotX + 167, y + 15)) {
+			res = i;
+			break;
+		}
+	}
+
+	if (_vm->posWithinRect(p.x, p.y, _saveLoadCancelButton->x, _saveLoadCancelButton->y, _saveLoadCancelButton->x + _saveLoadCancelButton->width - 1, _saveLoadCancelButton->y + _saveLoadCancelButton->height - 1))
+		res = 5;
+
+	return res;
+}
+
+void GUI_EoB_SegaCD::memorizePrayMenuPrintString(int spellId, int bookPageIndex, int spellType, bool noFill, bool highLight) {
+	if (bookPageIndex < 0)
+		return;
+
+	if (spellId) {
+		memset(_vm->_tempPattern, 0, 924);
+		Common::String s = Common::String::format(_vm->_menuStringsMgc[0], spellType ? _vm->_clericSpellList[spellId] : _vm->_mageSpellList[spellId], _numAssignedSpellsOfType[spellId * 2 - 2]);
+		_vm->printSpellbookString(_vm->_tempPattern, s.c_str(), highLight ? 0x6223 : 0x63C9);
+		_screen->sega_getRenderer()->fillRectWithTiles(0, 1, 10 + bookPageIndex, 20, 1, 0, true, true, _vm->_tempPattern);
+	} else {
+		_screen->sega_getRenderer()->fillRectWithTiles(0, 1, 10 + bookPageIndex, 20, 1, 0);
 	}
+	_screen->sega_getRenderer()->render(Screen_EoB::kSegaRenderPage);
+	_screen->copyRegion(8, 80 + bookPageIndex * 8, 8, 80 + bookPageIndex * 8, 160, 8, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
 }
 
 void GUI_EoB_SegaCD::updateOptionsStrings() {
@@ -814,35 +902,18 @@ void GUI_EoB_SegaCD::restParty_updateRestTime(int hours, bool init) {
 	_vm->delay(160);
 }
 
-const GUI_EoB_SegaCD::SegaMenuButton GUI_EoB_SegaCD::_menuButtons[22] = {
-	{ 0x01e7, 0x0000, 0x0001, 0x0005, 0x000a, 0x0002 },
-	{ 0x01fb, 0x0028, 0x000b, 0x0005, 0x000a, 0x0002 },
-	{ 0x020f, 0x0050, 0x000b, 0x0008, 0x000a, 0x0002 },
-	{ 0x0223, 0x0078, 0x000b, 0x000b, 0x000a, 0x0002 },
-	{ 0x0237, 0x00a0, 0x0001, 0x000e, 0x000a, 0x0002 }, // opt
-	{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, // dummy
-	{ 0x01cf, 0x01c8, 0x000f, 0x0012, 0x0006, 0x0002 },
-
-	{ 0x025f, 0x0118, 0x0001, 0x000b, 0x000a, 0x0002 }, // load
-	{ 0x024b, 0x00c8, 0x0001, 0x0008, 0x000a, 0x0002 }, // save
-	{ 0x0273, 0x0140, 0x000b, 0x000e, 0x000a, 0x0002 }, // drop
-
-	{ 0x020b, 0x0198, 0x0001, 0x000e, 0x0006, 0x0002 },
-	{ 0x01cf, 0x01c8, 0x000f, 0x0012, 0x0006, 0x0002 },
-	{ 0x01f3, 0x01e0, 0x0001, 0x0008, 0x0006, 0x0002 },
-	{ 0x01ff, 0x01f8, 0x0001, 0x000b, 0x0006, 0x0002 },
-	{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, // dummy
-	{ 0x01e7, 0x0210, 0x0001, 0x0005, 0x0006, 0x0002 },
-	{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, // dummy
-
-	{ 0x01CF, 0x0168, 0x0003, 0x000A, 0x0006, 0x0002 }, // yes
-	{ 0x01db, 0x0180, 0x000d, 0x000a, 0x0006, 0x0002 }, // no
-
-	{ 0x01db, 0x01b0, 0x0001, 0x0012, 0x0006, 0x0002 },
-	{ 0x01cf, 0x01c8, 0x000f, 0x0012, 0x0006, 0x0002 },
-
-
-	{ 0x024d, 0x030c, 0x0001, 0x0011, 0x000a, 0x0002 }
+const GUI_EoB_SegaCD::MenuButtonTiles GUI_EoB_SegaCD::_menuButtonTiles[35] = {
+	{ 0x01e7, 0x0000 },	{ 0x01fb, 0x0028 },	{ 0x020f, 0x0050 }, { 0x0223, 0x0078 },
+	{ 0x0237, 0x00a0 }, { 0x0000, 0x0000 },	{ 0x01cf, 0x01c8 },	{ 0x025f, 0x0118 },
+	{ 0x024b, 0x00c8 }, { 0x0273, 0x0140 },	{ 0x020b, 0x0198 },	{ 0x01cf, 0x01c8 },
+	{ 0x01f3, 0x01e0 }, { 0x01ff, 0x01f8 },	{ 0x0000, 0x0000 },	{ 0x01e7, 0x0210 },
+	{ 0x0000, 0x0000 },	{ 0x01CF, 0x0168 }, { 0x01db, 0x0180 }, { 0x01cf, 0x01c8 },
+	{ 0x0000, 0x0000 }, { 0x0000, 0x0000 }, { 0x0000, 0x0000 }, { 0x0000, 0x0000 },
+	{ 0x0000, 0x0000 },	{ 0x0000, 0x0000 },	{ 0x01db, 0x01b0 }, { 0x01cf, 0x01c8 },
+	{ 0x0000, 0x0000 }, { 0x01e7, 0x0270 },	{ 0x01f3, 0x027C }, { 0x01ff, 0x0288 },
+	{ 0x020b, 0x0294 },	{ 0x0217, 0x02A0 },
+	
+	{ 0x024d, 0x030c }
 };
 
 } // End of namespace Kyra
diff --git a/engines/kyra/gui/gui_eob_segacd.h b/engines/kyra/gui/gui_eob_segacd.h
index 4a3731cc23..4dea584fc3 100644
--- a/engines/kyra/gui/gui_eob_segacd.h
+++ b/engines/kyra/gui/gui_eob_segacd.h
@@ -38,26 +38,27 @@ public:
 
 private:
 	void drawCampMenu() override;
+	void initMemorizePrayMenu() override;
+	void drawSaveSlotDialog(int x, int y, int id) override;
 	bool confirmDialogue(int id) override;
-	void displayTextBox(int id) override;
+	void displayTextBox(int id, int textColor, bool wait) override;
 	void drawMenuButton(Button *b, bool clicked, bool highlight, bool noFill) override;
-	void drawButtonIntern(int id, int op);
+	void drawSaveSlotButton(int slot, int redrawBox, bool highlight) override;
+	int getHighlightSlot() override;
+	void memorizePrayMenuPrintString(int spellId, int bookPageIndex, int spellType, bool noFill, bool highLight) override;
 	void updateOptionsStrings() override;
 	void restParty_updateRestTime(int hours, bool init) override;
 
 	const uint8 *_campMenu;
+	Button* _saveLoadCancelButton;
 	EoBEngine *_vm;
 
-	struct SegaMenuButton {
+	struct MenuButtonTiles {
 		uint16 nameTbl;
-		uint16 nameTbl2;
-		int16 x;
-		int16 y;
-		uint16 w;
-		uint16 h;
+		uint16 srcOffs;
 	};
 
-	static const SegaMenuButton _menuButtons[22];
+	static const MenuButtonTiles _menuButtonTiles[35];
 };
 
 } // End of namespace Kyra
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index 95b13d3803..b21d598992 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -524,14 +524,16 @@ void EoBCoreEngine::initStaticResource() {
 	// Hard code the following strings, since EOB I doesn't have them in the original.
 	// EOB I doesn't have load and save menus, because there is only one single
 	// save slot. Instead of emulating this we provide a menu similiar to EOB II.
+	// EOB I actually has save/load menus. I supply the strings here, too... 
 
-	static const char *const saveLoadStrings[6][4] = {
+	static const char *const saveLoadStrings[7][4] = {
 		{   "Cancel",   "Empty Slot",		"Save Game",    "Load Game"     },
 		{   "Abbr.",    "Leerer Slot",		"Speichern",    "  Laden"       },
 		{	" < < ",	"Posizione Vuota",	"Salva",		"Carica"	    },
 		{	"Anular",	"Sin Uso",			"Grabar",		"Cargar"	    },
 		{   0,          0,					0,					0			},
-		{	0,          0,					0,					0			}
+		{	0,          0,					0,					0			},
+		{   "Cancel",   "\x82""d""\x82\x8d\x82\x90\x82\x94\x82\x99\x81""@""\x82\x92\x82\x85\x82\x87\x82\x89\x82\x8f\x82\x8e",		"Select save area",    "Select load data"     }
 	};
 
 	static const char *const errorSlotEmptyString[6] = {
@@ -544,31 +546,38 @@ void EoBCoreEngine::initStaticResource() {
 	};
 	
 	switch (_flags.lang) {
-		case Common::EN_ANY:
+	case Common::EN_ANY: {
+		if (_flags.platform == Common::kPlatformSegaCD) {
+			_saveLoadStrings = saveLoadStrings[6];
+			_errorSlotEmptyString = errorSlotEmptyString[5];
+		} else {
 			_saveLoadStrings = saveLoadStrings[0];
 			_errorSlotEmptyString = errorSlotEmptyString[0];
-			break;
-		case Common::DE_DEU:
-			_saveLoadStrings = saveLoadStrings[1];
-			_errorSlotEmptyString = errorSlotEmptyString[1];
-			break;
-		case Common::IT_ITA:
-			_saveLoadStrings = saveLoadStrings[2];
-			_errorSlotEmptyString = errorSlotEmptyString[2];
-			break;
-		case Common::ES_ESP:
-			_saveLoadStrings = saveLoadStrings[3];
-			_errorSlotEmptyString = errorSlotEmptyString[3];
-			break;
-		case Common::JA_JPN:
-			// EOB II FM-Towns uses English here.
-			// Only the empty slot warning is in Japanese.
-			_saveLoadStrings = saveLoadStrings[0];
-			_errorSlotEmptyString = errorSlotEmptyString[4];
-			break;
-		default:
-			_saveLoadStrings = saveLoadStrings[5];
-			_errorSlotEmptyString = errorSlotEmptyString[5];
+		}
+		break;
+	}
+	case Common::DE_DEU:
+		_saveLoadStrings = saveLoadStrings[1];
+		_errorSlotEmptyString = errorSlotEmptyString[1];
+		break;
+	case Common::IT_ITA:
+		_saveLoadStrings = saveLoadStrings[2];
+		_errorSlotEmptyString = errorSlotEmptyString[2];
+		break;
+	case Common::ES_ESP:
+		_saveLoadStrings = saveLoadStrings[3];
+		_errorSlotEmptyString = errorSlotEmptyString[3];
+		break;
+	case Common::JA_JPN:
+		// EOB II FM-Towns uses English here.
+		// Only the empty slot warning is in Japanese.
+		_saveLoadStrings = saveLoadStrings[0];
+		_errorSlotEmptyString = errorSlotEmptyString[4];
+		break;
+	default:
+		_saveLoadStrings = saveLoadStrings[5];
+		_errorSlotEmptyString = errorSlotEmptyString[5];
+		break;
 	}
 
 	_menuOkString = "OK";
@@ -886,39 +895,42 @@ void EoBCoreEngine::initMenus() {
 
 	static const EoBMenuButtonDef buttonDefsSegaCD[] = {
 		{   0,   8,  40,  80,  16,  20,  3  },
-		{   0,   88, 40,  80,  16,  52,  3  },
-		{   0,   88, 64,  80,  16,  26,  3  },
-		{   0,   88, 88,  80,  16,  32,  3  },
+		{   0,  88,  40,  80,  16,  52,  3  },
+		{   0,  88,  64,  80,  16,  26,  3  },
+		{   0,  88,  88,  80,  16,  32,  3  },
 		{   0,   8, 112,  80,  16,   0,  3  },
-
 		{   0,   0,   0,   0,   0,   0,  0  },
-
-		{   0,  120,144,  48,  16,  19,  7  },
-
-		
-		{   0,   8,  88,  80,  16,   0,  3  }, //load
-		{   0,   8,  64,  80,  16,   0,  3  }, //save
-		{   0,   88,112,  80,  16,   0,  3  }, //drop
-
-		
-
-
-
+		{   0, 120, 144,  48,  16,  19,  7  },
+		{   0,   8,  88,  80,  16,   0,  3  },
+		{   0,   8,  64,  80,  16,   0,  3  },
+		{   0,  88, 112,  80,  16,   0,  3  },
 		{   0,   8, 112,  48,  16,   0,  3  },
-		{   0,  120,144,  48,  16,  19,  7  },
+		{   0, 120, 144,  48,  16,  19,  7  },
 		{   0,   8,  64,  48,  16,   0,  3  },
 		{   0,   8,  88,  48,  16,   0,  3  },
 		{   0,   0,   0,   0,   0,   0,  0  },
 		{   0,   8,  40,  48,  16,   0,  3  },
-		{   0, 120,  40,  24,  16,   0,  3  },
-		
+		{   0, 120,  40,  24,  16,   0,  3  },		
 		{   0,  24,  80,  48,  16,  48,  3  },
 		{   0, 104,  80,  48,  16,  19,  3  },
-		
-		 
+		{   0, 120, 144,  48,  16,  19,  5  },
+		{   0, 184,   2,  63,  50, 112,  0  },
+		{   0, 256,   2,  63,  50, 113,  0  },
+		{   0, 184,  58,  63,  50, 114,  0  },
+		{   0, 256,  58,  63,  50, 115,  0  },
+		{   0, 184, 114,  63,  50, 116,  0  },
+		{   0, 256, 114,  63,  50, 117,  0  },
+		{  36,   8, 144,  48,  16,  48,  5  },
+		{  8,  120, 144,  48,  16,  19,  5  },
+		{  0,    0,  50, 168,  72,  61,  0  },
+		{  31,   8,  48,  24,  16,   2,  5  },
+		{  32,  40,  48,  24,  16,   3,  5  },
+		{  33,  72,  48,  24,  16,   4,  5  },
+		{  34, 104,  48,  24,  16,   5,  5  },
+		{  35, 136,  48,  24,  16,   6,  5  },
 
-		{   0,   88,112,  48,  16,   0,  3  },
-		{   0,  120, 40,  24,  16,   0,  3  },
+		{   0,  88, 112,  48,  16,   0,  3  },
+		{   0, 120,  40,  24,  16,   0,  3  },
 		{   0,   8,  40,  48,  16,   0,  3  },
 		{   0,   8, 136,  80,  16,   0,  3  }
 	};
@@ -936,12 +948,12 @@ void EoBCoreEngine::initMenus() {
 	};
 
 	static const EoBMenuDef menuDefsSegaCD[6] = {
-		{  -1, 0,  0, 10, -1 },
-		{  -1, 0,  0,  0, -1 },
-		{  -1, 0, 10,  7, -1 },
-		{  -1, 0,  0,  0, -1 },
-		{  -1, 0,  0,  0, -1 },
-		{  -1, 0, 17,  2, -1 }
+		{  -1, 0,  0, 10,   -1 },
+		{  -1, 0,  0,  0,   -1 },
+		{  -1, 0, 10,  7,   -1 },
+		{   0, 0, 19,  7, 0x55 },
+		{  -1, 0, 26,  8,   -1 },
+		{  -1, 0, 17,  2,   -1 }
 	};
 
 	delete[] _menuDefs;
diff --git a/engines/kyra/sequence/sequences_eob.cpp b/engines/kyra/sequence/sequences_eob.cpp
index e86c1ecaf7..afe49971f7 100644
--- a/engines/kyra/sequence/sequences_eob.cpp
+++ b/engines/kyra/sequence/sequences_eob.cpp
@@ -2270,7 +2270,7 @@ int EoBEngine::mainMenuLoop() {
 void EoBEngine::seq_playIntro(int part) {
 	if (_flags.platform == Common::kPlatformSegaCD) {
 		if (part == kOnlyCredits)
-			seq_segaOpeningCredits();
+			seq_segaOpeningCredits(false);
 		else
 			seq_segaPlaySequence(53, true);
 	} else {
@@ -2430,7 +2430,7 @@ void EoBEngine::seq_xdeath() {
 	for (int iii = 0; iii < 228; ++iii) \
 		((int16*)scrollTable)[iii << 1] = ((int16*)scrollTable)[(iii << 1) + 1] = (iii & 1) ? -step : step;
 
-void EoBEngine::seq_segaOpeningCredits() {
+void EoBEngine::seq_segaOpeningCredits(bool jumpToTitle) {
 	uint16 *scrollTable = new uint16[0x200];
 	memset(scrollTable, 0, 0x200 * sizeof(uint16));
 	SegaRenderer *r = _screen->sega_getRenderer();
@@ -2459,9 +2459,10 @@ void EoBEngine::seq_segaOpeningCredits() {
 	_allowSkip = true;
 	resetSkipFlag();
 
-	_screen->sega_fadeToNeutral(3);
+	if (!jumpToTitle)
+		_screen->sega_fadeToNeutral(3);
 
-	for (int i = 0; i < 8 && !(shouldQuit() || skipFlag()); ++i) {
+	for (int i = jumpToTitle ? 8 : 0; i < 8 && !(shouldQuit() || skipFlag()); ++i) {
 		updateScrollState(scrollTable, 320);
 		r->loadToVRAM(scrollTable, 0x400, 0xD800);
 		_screen->sega_selectPalette(i == 3 ? 59 : 50, 0, true);
@@ -2526,10 +2527,10 @@ void EoBEngine::seq_segaOpeningCredits() {
 	r->fillRectWithTiles(1, 0, 0, 40, 28, 1, true);
 	r->fillRectWithTiles(0, 0, 0, 40, 28, 0);
 	r->render(0);
-	if (!(shouldQuit() || skipFlag()))
+	if (!(jumpToTitle || shouldQuit() || skipFlag()))
 		_screen->sega_fadeToNeutral(3);
 
-	while (!(shouldQuit() || skipFlag()))
+	while (!(jumpToTitle || shouldQuit() || skipFlag()))
 		delay(20);
 
 	_allowSkip = false;
@@ -2771,6 +2772,7 @@ bool EoBEngine::seq_segaPlaySequence(int sequenceId, bool setupScreen) {
 	if (_flags.platform != Common::kPlatformSegaCD)
 		return true;
 
+	uint32 startTime = _system->getMillis();
 	_allowSkip = true;
 	resetSkipFlag();
 
@@ -2786,6 +2788,8 @@ bool EoBEngine::seq_segaPlaySequence(int sequenceId, bool setupScreen) {
 	if (setupScreen)
 		seq_segaRestoreAfterSequence();
 
+	_totalPlaySecs += ((_system->getMillis() - startTime) / 1000);
+
 	return true;
 }
 


Commit: b5b969638356f7dd2374812a8983803d2dfbe8dd
    https://github.com/scummvm/scummvm/commit/b5b969638356f7dd2374812a8983803d2dfbe8dd
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:17+02:00

Commit Message:
KYRA: (EOB/SegaCD) - improve renderer

Changed paths:
    engines/kyra/engine/eob.cpp
    engines/kyra/graphics/screen_eob_segacd.cpp
    engines/kyra/graphics/screen_eob_segacd.h


diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index 2ef578fa78..81e0b2cb95 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, true); \
+	_screen->sega_getRenderer()->render(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); \
diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index 506523787f..27e73a4046 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -344,7 +344,7 @@ void Screen_EoB::sega_encodeShapesFromSprites(const uint8 **dst, const uint8 *sr
 		}
 		
 		_segaAnimator->update();
-		_segaRenderer->render(Screen_EoB::kSegaInitShapesPage, true);
+		_segaRenderer->render(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);
@@ -429,7 +429,7 @@ SegaRenderer::SegaRenderer(Screen_EoB *screen) : _screen(screen), _drChain(0), _
 }
 
 SegaRenderer::~SegaRenderer() {
-	clearDirtyRects();
+	//clearDirtyRects();
 	delete[] _vram;
 	delete[] _vsram;
 	delete[] _spriteMask;
@@ -504,7 +504,7 @@ void SegaRenderer::loadToVRAM(const void *data, uint16 dataSize, uint16 addr) {
 	assert(data);
 	assert(addr + dataSize <= 0x10000);
 	memcpy(_vram + addr, data, dataSize);
-	checkUpdateDirtyRects(addr, dataSize);
+	//checkUpdateDirtyRects(addr, dataSize);
 }
 
 void SegaRenderer::loadStreamToVRAM(Common::SeekableReadStream *in, uint16 addr, bool compressedData) {
@@ -526,7 +526,7 @@ void SegaRenderer::loadStreamToVRAM(Common::SeekableReadStream *in, uint16 addr,
 		assert(in->size() < 0x10000 - addr);
 		in->read(dst, in->size());
 	}
-	addDirtyRect(0, 0, _screenW, _screenH);
+	//addDirtyRect(0, 0, _screenW, _screenH);
 }
 
 void SegaRenderer::memsetVRAM(int addr, uint8 val, int len) {
@@ -537,7 +537,7 @@ void SegaRenderer::memsetVRAM(int addr, uint8 val, int len) {
 
 void SegaRenderer::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;
-	addDirtyRect(x << 3, y << 3, w << 3, h << 3);
+	//addDirtyRect(x << 3, y << 3, w << 3, h << 3);
 	if (y & 0x8000) {
 		y &= ~0x8000;
 		addr = 0xE000;
@@ -598,19 +598,19 @@ void SegaRenderer::writeUint16VSRAM(int addr, uint16 value) {
 	assert(addr < 80);
 	assert(!(addr & 1));
 	_vsram[addr >> 1] = value;
-	checkUpdateDirtyRects(addr, 2);
+	//checkUpdateDirtyRects(addr, 2);
 }
 
 void SegaRenderer::writeUint8VRAM(int addr, uint8 value) {
 	assert(addr < 0x10000);
 	_vram[addr] = value;
-	checkUpdateDirtyRects(addr, 1);
+	//checkUpdateDirtyRects(addr, 1);
 }
 
 void SegaRenderer::writeUint16VRAM(int addr, uint16 value) {
 	assert(addr < 0x10000);
 	*((uint16*)(_vram + addr)) = value;
-	checkUpdateDirtyRects(addr, 2);
+	//checkUpdateDirtyRects(addr, 2);
 }
 
 void SegaRenderer::clearPlanes() {
@@ -620,21 +620,23 @@ void SegaRenderer::clearPlanes() {
 	}
 }
 
-void SegaRenderer::render(int destPageNum, bool spritesOnly) {
-	uint8 *renderBuffer = _screen->getPagePtr(destPageNum);
-	memset(renderBuffer, 0, _screenW * _screenH);
-
-	if (destPageNum == 0)
-		sendDirtyRectsToScreen();
+void SegaRenderer::render(int destPageNum, 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;
 
-	// It is assumed that no dirty rects are needed if the rendering takes place to any other page than page 0.
-	// So if you'd render to e. g. page 2 and afterwards to page 0 your dirty rects would be lost. This is
-	// intentional for now, since I assume that this will be in harmony with the actual usage of the renderer.
-	clearDirtyRects();
+	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, 0, 0, _blocksW, _blocksH);
+		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) {
@@ -642,19 +644,19 @@ void SegaRenderer::render(int destPageNum, bool spritesOnly) {
 		// kind of replaces plane A in the space that is covered by it.
 		if (_planes[kWindowPlane].nameTableSize) {
 			SegaPlane *p = &_planes[kWindowPlane];
-			renderPlanePart(kPlaneA, renderBuffer, 0, 0, p->blockX, _blocksH);
-			renderPlanePart(kPlaneA, renderBuffer, 0, 0, _blocksW, p->blockY);
-			renderPlanePart(kPlaneA, renderBuffer, p->blockX + p->w, 0, _blocksW, _blocksH);
-			renderPlanePart(kPlaneA, renderBuffer, 0, p->blockY + p->h, _blocksW, _blocksH);
+			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, 0, 0, _blocksW, _blocksH);
+			renderPlanePart(kPlaneA, renderBuffer, renderBlockX, renderBlockY, renderBlockX + renderBlockWidth, renderBlockY + renderBlockHeight);
 		}
 	}
 
 	// Window Plane
 	if (_planes[kWindowPlane].nameTableSize && !spritesOnly) {
 		SegaPlane *p = &_planes[kWindowPlane];
-		renderPlanePart(kWindowPlane, renderBuffer, p->blockX, p->blockY, p->blockX + p->w, p->blockY + p->h);
+		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
@@ -681,6 +683,20 @@ void SegaRenderer::render(int destPageNum, bool spritesOnly) {
 		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;
 
@@ -730,14 +746,14 @@ void SegaRenderer::renderPlanePart(int plane, uint8 *dstBuffer, int x1, int y1,
 
 			int ty = (vscrNt + y) % p->mod;
 
-			renderPlaneTile(dst, x, &p->nameTable[ty * _pitch + x1], vscrPxStart, vscrPxEnd, hScrollTableIndex, _pitch);
+			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 + x1], vscrPxStart, vscrPxEnd, hScrollTableIndex, _pitch);
+				renderPlaneTile(dst + dstOffs, x, &p->nameTable[ty * _pitch], vscrPxStart, vscrPxEnd, hScrollTableIndex, _pitch);
 			}
 			dst += 8;
 		}
@@ -745,7 +761,7 @@ void SegaRenderer::renderPlanePart(int plane, uint8 *dstBuffer, int x1, int y1,
 	}
 }
 
-void SegaRenderer::renderPlaneTile(uint8 *dst, int destX, const uint16 *nameTable, int vScrollLSBStart, int vScrollLSBEnd, int hScrollTableIndex, uint16 pitch) {
+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;
@@ -757,7 +773,7 @@ void SegaRenderer::renderPlaneTile(uint8 *dst, int destX, const uint16 *nameTabl
 			hscrNt >>= 3;
 		}
 
-		const uint16 *pNt = &nameTable[(destX + hscrNt) % pitch];
+		const uint16 *pNt = &ntblLine[(ntblX + hscrNt) % pitch];
 		if (pNt < (const uint16*)(&_vram[0x10000])) {
 			uint16 nt = *pNt;
 			uint16 pal = ((nt >> 13) & 3) << 4;
@@ -775,7 +791,7 @@ void SegaRenderer::renderPlaneTile(uint8 *dst, int destX, const uint16 *nameTabl
 
 		if (hscrPx) {
 			dst += (8 - hscrPx);
-			pNt = &nameTable[(destX + hscrNt + 1) % pitch];
+			pNt = &ntblLine[(ntblX + hscrNt + 1) % pitch];
 			if (pNt < (const uint16*)(&_vram[0x10000])) {
 				uint16 nt = *pNt;
 				uint16 pal = ((nt >> 13) & 3) << 4;
@@ -925,7 +941,7 @@ template<bool hflip> void SegaRenderer::renderLineFragment(uint8 *dst, uint8 *ma
 
 #undef mRenderLineFragment
 
-void SegaRenderer::checkUpdateDirtyRects(int addr, int len) {
+/*void SegaRenderer::checkUpdateDirtyRects(int addr, int len) {
 	//void *tbl[] = { _vram, _hScrollTable, , _planes[kPlaneA].nameTable, _planes[kPlaneB].nameTable, _planes[kWindowPlane].nameTable };
 	addDirtyRect(0, 0, _screenW, _screenH);
 	/*uint8 *addE = &_vram[addr];
@@ -941,6 +957,7 @@ void SegaRenderer::checkUpdateDirtyRects(int addr, int len) {
 	}*/
 
 	//if (addr >= _hScrollTable && addr < _hScrollTable + 0x400);
+/*
 }
 
 void SegaRenderer::addDirtyRect(int x, int y, int w, int h) {
@@ -965,7 +982,7 @@ void SegaRenderer::clearDirtyRects() {
 		delete _drChain;
 		_drChain = e;
 	}
-}
+}*/
 
 void SegaRenderer::initPrioRenderTask(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal, bool hflip) {
 	_prioChainEnd = new PrioTileRenderObj(_prioChainEnd, dst, mask, src, start, end, pal, hflip);
diff --git a/engines/kyra/graphics/screen_eob_segacd.h b/engines/kyra/graphics/screen_eob_segacd.h
index 0dceb560c4..d6390d823e 100644
--- a/engines/kyra/graphics/screen_eob_segacd.h
+++ b/engines/kyra/graphics/screen_eob_segacd.h
@@ -85,8 +85,10 @@ public:
 	void writeUint8VRAM(int addr, uint8 value);
 	void writeUint16VRAM(int addr, uint16 value);
 	void clearPlanes();
-	
-	void render(int destPageNum, bool spritesOnly = false);
+
+	//void renderScreen();
+	//void renderArea(int destPageNum, int renderLeft, int renderTop, int renderWidth, int renderHeight, bool spritesOnly = false);
+	void render(int destPageNum, int renderBlockX = -1, int renderBlockY = -1, int renderBlockWidth = -1, int renderBlockHeight = -1, bool spritesOnly = false);
 
 private:
 	void renderPlanePart(int plane, uint8 *dstBuffer, int x1, int y1, int x2, int y2);
@@ -101,11 +103,11 @@ private:
 	const renderFuncD *_renderLineFragmentD;
 #else
 	template<bool hflip> void renderLineFragment(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal);
-#endif
+#endif/*
 	void checkUpdateDirtyRects(int addr, int len);
 	void addDirtyRect(int x, int y, int w, int h);
 	void sendDirtyRectsToScreen();
-	void clearDirtyRects();
+	void clearDirtyRects();*/
 
 	void initPrioRenderTask(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal, bool hflip);
 	void clearPrioChain();


Commit: 2ebf16cf137a767bb27ae0aec969face521ab88b
    https://github.com/scummvm/scummvm/commit/2ebf16cf137a767bb27ae0aec969face521ab88b
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:18+02:00

Commit Message:
KYRA: (EOB/SegaCD) - adapt code to updated renderer

Changed paths:
    engines/kyra/engine/eob.cpp
    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/sequences_darkmoon.cpp
    engines/kyra/sequence/sequences_eob.cpp


diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index 81e0b2cb95..af6e79b1d9 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -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);
+	_screen->sega_getRenderer()->render(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);
diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index 27e73a4046..2b379dd0f5 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -386,7 +386,7 @@ void Screen_EoB::sega_encodeShapesFromSprites(const uint8 **dst, const uint8 *sr
 }
 #endif
 
-SegaRenderer::SegaRenderer(Screen_EoB *screen) : _screen(screen), _drChain(0), _prioChainStart(0), _prioChainEnd(0), _pitch(64), _hScrollMode(0), _hScrollTable(0), _vScrollMode(0), _spriteTable(0), _numSpritesMax(0), _spriteMask(0)
+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
@@ -429,7 +429,6 @@ SegaRenderer::SegaRenderer(Screen_EoB *screen) : _screen(screen), _drChain(0), _
 }
 
 SegaRenderer::~SegaRenderer() {
-	//clearDirtyRects();
 	delete[] _vram;
 	delete[] _vsram;
 	delete[] _spriteMask;
@@ -504,7 +503,6 @@ void SegaRenderer::loadToVRAM(const void *data, uint16 dataSize, uint16 addr) {
 	assert(data);
 	assert(addr + dataSize <= 0x10000);
 	memcpy(_vram + addr, data, dataSize);
-	//checkUpdateDirtyRects(addr, dataSize);
 }
 
 void SegaRenderer::loadStreamToVRAM(Common::SeekableReadStream *in, uint16 addr, bool compressedData) {
@@ -526,7 +524,6 @@ void SegaRenderer::loadStreamToVRAM(Common::SeekableReadStream *in, uint16 addr,
 		assert(in->size() < 0x10000 - addr);
 		in->read(dst, in->size());
 	}
-	//addDirtyRect(0, 0, _screenW, _screenH);
 }
 
 void SegaRenderer::memsetVRAM(int addr, uint8 val, int len) {
@@ -537,7 +534,6 @@ void SegaRenderer::memsetVRAM(int addr, uint8 val, int len) {
 
 void SegaRenderer::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;
-	//addDirtyRect(x << 3, y << 3, w << 3, h << 3);
 	if (y & 0x8000) {
 		y &= ~0x8000;
 		addr = 0xE000;
@@ -598,19 +594,16 @@ void SegaRenderer::writeUint16VSRAM(int addr, uint16 value) {
 	assert(addr < 80);
 	assert(!(addr & 1));
 	_vsram[addr >> 1] = value;
-	//checkUpdateDirtyRects(addr, 2);
 }
 
 void SegaRenderer::writeUint8VRAM(int addr, uint8 value) {
 	assert(addr < 0x10000);
 	_vram[addr] = value;
-	//checkUpdateDirtyRects(addr, 1);
 }
 
 void SegaRenderer::writeUint16VRAM(int addr, uint16 value) {
 	assert(addr < 0x10000);
 	*((uint16*)(_vram + addr)) = value;
-	//checkUpdateDirtyRects(addr, 2);
 }
 
 void SegaRenderer::clearPlanes() {
diff --git a/engines/kyra/graphics/screen_eob_segacd.h b/engines/kyra/graphics/screen_eob_segacd.h
index d6390d823e..72b575fccb 100644
--- a/engines/kyra/graphics/screen_eob_segacd.h
+++ b/engines/kyra/graphics/screen_eob_segacd.h
@@ -86,10 +86,7 @@ public:
 	void writeUint16VRAM(int addr, uint16 value);
 	void clearPlanes();
 
-	//void renderScreen();
-	//void renderArea(int destPageNum, int renderLeft, int renderTop, int renderWidth, int renderHeight, bool spritesOnly = false);
-	void render(int destPageNum, int renderBlockX = -1, int renderBlockY = -1, int renderBlockWidth = -1, int renderBlockHeight = -1, bool spritesOnly = false);
-
+	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);
@@ -103,11 +100,7 @@ private:
 	const renderFuncD *_renderLineFragmentD;
 #else
 	template<bool hflip> void renderLineFragment(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal);
-#endif/*
-	void checkUpdateDirtyRects(int addr, int len);
-	void addDirtyRect(int x, int y, int w, int h);
-	void sendDirtyRectsToScreen();
-	void clearDirtyRects();*/
+#endif
 
 	void initPrioRenderTask(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal, bool hflip);
 	void clearPrioChain();
@@ -131,12 +124,6 @@ private:
 	uint16 _pitch;
 	uint16 _numSpritesMax;
 
-	struct DRChainEntry {
-		DRChainEntry(DRChainEntry *chain, int x, int y, int w, int h) : _next(chain), _rect(x, y, x + w, y + h) {}
-		Common::Rect _rect;
-		DRChainEntry *_next;
-	} *_drChain;
-
 	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) {
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 558f5d5576..5359105e81 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -2090,9 +2090,6 @@ void GUI_EoB::simpleMenu_setup(int sd, int maxItem, const char *const *strings,
 		}
 	}
 
-	if (_vm->gameFlags().platform == Common::kPlatformSegaCD)
-		_screen->sega_getRenderer()->render(0);
-	_screen->updateScreen();
 	_menuLineSpacing = lineSpacing;
 	_menuLastInFlags = 0;
 	_vm->removeInputTop();
@@ -2141,14 +2138,12 @@ int GUI_EoB::simpleMenu_process(int sd, const char *const *strings, void *b, int
 
 	if (newItem != currentItem) {
 		if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
-			_vm->_txt->printShadowedText(strings[simpleMenu_getMenuItem(currentItem, menuItemsMask, itemOffset)], 4, 2 + currentItem * lineH, 0xFF, 0x11);
-			_vm->_txt->printShadowedText(strings[simpleMenu_getMenuItem(newItem, menuItemsMask, itemOffset)], 4, 2 + newItem * lineH, 0x55, 0x11);
-			_screen->sega_getRenderer()->render(0);
+			_vm->_txt->printShadedText(strings[simpleMenu_getMenuItem(currentItem, menuItemsMask, itemOffset)], 4, (sd == 8 ? 2 : 20) + currentItem * lineH, 0xFF, sd == 8 ? 0x11 : 0x99);
+			_vm->_txt->printShadedText(strings[simpleMenu_getMenuItem(newItem, menuItemsMask, itemOffset)], 4, (sd == 8 ? 2 : 20) + newItem * lineH, 0x55, sd == 8 ? 0x11 : 0x99);
 		} else {
 			_screen->printText(strings[simpleMenu_getMenuItem(currentItem, menuItemsMask, itemOffset)], x, y + currentItem * lineH, (_vm->_configRenderMode == Common::kRenderCGA) ? 1 : _vm->guiSettings()->colors.guiColorWhite, 0);
 			_screen->printText(strings[simpleMenu_getMenuItem(newItem, menuItemsMask, itemOffset)], x, y + newItem * lineH, _vm->guiSettings()->colors.guiColorLightRed, 0);
 		}
-		_screen->updateScreen();
 	}
 
 	if (result != -1) {
@@ -2233,8 +2228,7 @@ void GUI_EoB::runCampMenu() {
 					prevHighlightButton = 0;
 				}
 			} else if (_vm->gameFlags().platform == Common::kPlatformSegaCD && newMenu == 2) {
-				_screen->sega_getRenderer()->render(Screen_EoB::kSegaRenderPage);
-				_screen->copyRegion(0, 0, 0, 0, 176, 168, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+				_screen->sega_getRenderer()->render(0, 0, 0, 22, 21);
 			}
 
 			lastMenu = newMenu;
@@ -3439,12 +3433,10 @@ void GUI_EoB::runMemorizePrayMenu(int charIndex, int spellType) {
 				memorizePrayMenuPrintString(menuSpellMap[lastHighLightButton * 11 + ii], ii - 1, spellType, false, false);
 
 			_screen->setCurPage(0);
-			if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
-				_screen->sega_getRenderer()->render(Screen_EoB::kSegaRenderPage);
-				_screen->copyRegion(0, 80, 0, 80, 176, 72, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
-			} else {
+			if (_vm->gameFlags().platform == Common::kPlatformSegaCD)
+				_screen->sega_getRenderer()->render(0, 0, 10, 22, 9);
+			else
 				_screen->copyRegion(0, 50, 0, 50, 176, 72, 2, 0, Screen::CR_NO_P_CHECK);
-			}
 			lastHighLightText = -1;
 		}
 		
@@ -3454,8 +3446,7 @@ void GUI_EoB::runMemorizePrayMenu(int charIndex, int spellType) {
 				_screen->sega_clearTextBuffer(0);
 				_vm->_txt->printShadowedText(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(Screen_EoB::kSegaRenderPage);
-				_screen->copyRegion(8, 64, 8, 64, 160, 16, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+				_screen->sega_getRenderer()->render(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);
@@ -4305,12 +4296,10 @@ Button *GUI_EoB::initMenu(int id) {
 		buttons = linkButton(buttons, b);
 	}
 
-	if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
-		_screen->sega_getRenderer()->render(Screen_EoB::kSegaRenderPage);
-		_screen->copyRegion(0, 0, 0, 0, 176, 168, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
-	} else {
+	if (_vm->gameFlags().platform == Common::kPlatformSegaCD)
+		_screen->sega_getRenderer()->render(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);
-	}
 
 	_vm->gui_notifyButtonListChanged();
 	_screen->setCurPage(0);
diff --git a/engines/kyra/gui/gui_eob_segacd.cpp b/engines/kyra/gui/gui_eob_segacd.cpp
index d0118d56e8..3badadaa13 100644
--- a/engines/kyra/gui/gui_eob_segacd.cpp
+++ b/engines/kyra/gui/gui_eob_segacd.cpp
@@ -228,9 +228,7 @@ void EoBEngine::gui_drawCharacterStatsPage() {
 	}
 
 	r->fillRectWithTiles(0, 22, 0, 18, 21, 0, true, true, _tempPattern);
-	r->render(Screen_EoB::kSegaRenderPage);
-
-	_screen->copyRegion(176, 40, 176, 40, 144, 128, Screen_EoB::kSegaRenderPage, 2, Screen::CR_NO_P_CHECK);
+	r->render(2, 22, 5, 18, 16);
 }
 
 void EoBEngine::gui_displayMap() {
@@ -352,7 +350,7 @@ void EoBEngine::gui_drawSpellbook() {
 	}
 
 	r->fillRectWithTiles(0, 10, 15, 12, 6, 0, true, false, _tempPattern);
-	r->render(Screen_EoB::kSegaRenderPage);
+	r->render(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) {
@@ -420,8 +418,7 @@ void EoBEngine::gui_updateAnimations() {
 	}
 	if (redrawCompass) {
 		_screen->sega_getRenderer()->loadToVRAM(_compassData + (_compassAnimPhase & 0x0F) * 0x500, 0x500, 0xEE00);
-		_screen->sega_getRenderer()->render(Screen_EoB::kSegaRenderPage);
-		_screen->copyRegion(88, 120, 88, 120, 80, 48, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+		_screen->sega_getRenderer()->render(0, 11, 15, 10, 6);
 		updScreen = true;
 	}
 
@@ -514,11 +511,7 @@ void EoBEngine::makeNameShapes(int charId) {
 	}
 	delete[] in;
 
-	_screen->sega_getRenderer()->render(_screen->_curPage);
-	_screen->sega_getRenderer()->render(0);
-	_screen->updateScreen();
-	_screen->sega_fadeToNeutral(0);
-
+	_screen->sega_getRenderer()->render(_screen->_curPage, 0, 0, 8, 12);
 	for (int i = first; i <= last; ++i) {
 		if (!_characters[i].flags)
 			continue;
@@ -680,8 +673,7 @@ void GUI_EoB_SegaCD::initMemorizePrayMenu() {
 	_screen->sega_clearTextBuffer(0);
 	_vm->_txt->printShadowedText(getMenuString(37), 0, 2, 0xFF, 0xCC, 160, 16, 0, false);
 	_screen->sega_loadTextBufferToVRAM(0, 0x5060, 2560);
-	_screen->sega_getRenderer()->render(Screen_EoB::kSegaRenderPage);
-	_screen->copyRegion(8, 32, 8, 32, 160, 16, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+	_screen->sega_getRenderer()->render(0, 1, 4, 20, 2);
 }
 
 void GUI_EoB_SegaCD::drawSaveSlotDialog(int x, int y, int id) {
@@ -698,8 +690,7 @@ void GUI_EoB_SegaCD::drawSaveSlotDialog(int x, int y, int id) {
 	_vm->_txt->printShadowedText(_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(Screen_EoB::kSegaRenderPage);
-	_screen->copyRegion(x, y + 8, x, y + 8, 176, 168, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+	_screen->sega_getRenderer()->render(0, x >> 3, (y >> 3) + 1, 22, 21);
 }
 
 bool GUI_EoB_SegaCD::confirmDialogue(int id) {
@@ -718,8 +709,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(Screen_EoB::kSegaRenderPage);
-	_screen->copyRegion(0, 0, 0, 0, 176, 128, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+	_screen->sega_getRenderer()->render(0, 1, 0, 22, 20);
 	_screen->updateScreen();
 
 	Button *buttonList = initMenu(5);
@@ -786,8 +776,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(Screen_EoB::kSegaRenderPage);
-	_screen->copyRegion(0, 0, 0, 0, 176, 168, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+	_screen->sega_getRenderer()->render(0, 0, 0, 22, 20);
 	_screen->updateScreen();
 	if (!wait)
 		return;
@@ -808,8 +797,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(Screen_EoB::kSegaRenderPage);
-	_screen->copyRegion(b->x, b->y, b->x, b->y, b->width, b->height, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+	_screen->sega_getRenderer()->render(0, b->x >> 3, b->y >> 3, b->width >> 3, b->height >> 3);
 }
 
 void GUI_EoB_SegaCD::drawSaveSlotButton(int slot, int redrawBox, bool highlight) {
@@ -825,8 +813,7 @@ void GUI_EoB_SegaCD::drawSaveSlotButton(int slot, int redrawBox, bool highlight)
 	_screen->sega_clearTextBuffer(0);
 	_vm->_txt->printShadowedText(slot < 5 ? _saveSlotStringsTemp[slot] : _vm->_saveLoadStrings[0], 0, (slot << 4) + (slot < 5 ? 0 : 2), highlight ? 0x55 : 0xFF, 0xCC, 121, 80, 0, false);
 	_screen->sega_loadTextBufferToVRAM(0, 0x5560, 4800);
-	_screen->sega_getRenderer()->render(Screen_EoB::kSegaRenderPage);
-	_screen->copyRegion(_saveSlotX + (_saveSlotX ? 8 : 16), _saveSlotY + (_saveSlotY ? 48 : 56) + (slot << 4), _saveSlotX + (_saveSlotX ? 8 : 16), _saveSlotY + (_saveSlotY ? 48 : 56) + (slot << 4), 168, 16, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+	_screen->sega_getRenderer()->render(0, (_saveSlotX >> 3) + (_saveSlotX ? 1 : 2), (_saveSlotY >> 3) + (_saveSlotY ? 6 : 7) + (slot << 1), 21, 2);
 }
 
 int GUI_EoB_SegaCD::getHighlightSlot() {
@@ -859,8 +846,7 @@ 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(Screen_EoB::kSegaRenderPage);
-	_screen->copyRegion(8, 80 + bookPageIndex * 8, 8, 80 + bookPageIndex * 8, 160, 8, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+	_screen->sega_getRenderer()->render(0, 1, 10 + bookPageIndex, 20, 1);
 }
 
 void GUI_EoB_SegaCD::updateOptionsStrings() {
@@ -896,8 +882,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(Screen_EoB::kSegaRenderPage);
-	_screen->copyRegion(0, 0, 0, 0, 176, 128, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+	r->render(0, 0, 0, 22, 16);
 	_screen->updateScreen();
 	_vm->delay(160);
 }
diff --git a/engines/kyra/sequence/sequences_darkmoon.cpp b/engines/kyra/sequence/sequences_darkmoon.cpp
index 68e754c485..79ba647858 100644
--- a/engines/kyra/sequence/sequences_darkmoon.cpp
+++ b/engines/kyra/sequence/sequences_darkmoon.cpp
@@ -207,9 +207,12 @@ int DarkMoonEngine::mainMenuLoop() {
 	do {
 		_screen->setScreenDim(6);
 		_gui->simpleMenu_setup(6, 0, _mainMenuStrings, -1, 0, 0);
+		_screen->updateScreen();
 
-		while (sel == -1 && !shouldQuit())
+		while (sel == -1 && !shouldQuit()) {
 			sel = _gui->simpleMenu_process(6, _mainMenuStrings, 0, -1, 0);
+			_screen->updateScreen();
+		}
 	} while ((sel < 0 || sel > 5) && !shouldQuit());
 
 	if (_flags.platform == Common::kPlatformFMTowns && sel == 2) {
@@ -225,8 +228,11 @@ void DarkMoonEngine::townsUtilitiesMenu() {
 	int sel = -1;
 	do {
 		_gui->simpleMenu_setup(8, 0, _utilMenuStrings, -1, 0, 0);
-		while (sel == -1 && !shouldQuit())
+		_screen->updateScreen();
+		while (sel == -1 && !shouldQuit()) {
 			sel = _gui->simpleMenu_process(8, _utilMenuStrings, 0, -1, 0);
+			_screen->updateScreen();
+		}
 		if (sel == 0) {
 			_config2431 ^= true;
 			sel = -1;
diff --git a/engines/kyra/sequence/sequences_eob.cpp b/engines/kyra/sequence/sequences_eob.cpp
index afe49971f7..751efe7736 100644
--- a/engines/kyra/sequence/sequences_eob.cpp
+++ b/engines/kyra/sequence/sequences_eob.cpp
@@ -2259,9 +2259,16 @@ int EoBEngine::mainMenuLoop() {
 	do {
 		_screen->setScreenDim(28);
 		_gui->simpleMenu_setup(8, 0, _mainMenuStrings, -1, 0, 0);
+		if (_flags.platform == Common::kPlatformSegaCD)
+			_screen->sega_getRenderer()->render(0);
+		_screen->updateScreen();
 
-		while (sel == -1 && !shouldQuit())
+		while (sel == -1 && !shouldQuit()) {
 			sel = _gui->simpleMenu_process(8, _mainMenuStrings, 0, -1, 0);
+			if (_flags.platform == Common::kPlatformSegaCD)
+				_screen->sega_getRenderer()->render(0, 6, 20, 26, 5);
+			_screen->updateScreen();
+		}
 	} while ((sel < 0 || sel > 5) && !shouldQuit());
 
 	return sel + 1;


Commit: 87ec24fbfd4fadefadc00adf364637f8b49cb052
    https://github.com/scummvm/scummvm/commit/87ec24fbfd4fadefadc00adf364637f8b49cb052
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:18+02:00

Commit Message:
KYRA: (EOB/SegaCD) - update character generator

Changed paths:
    engines/kyra/engine/chargen.cpp
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eob.h
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h
    engines/kyra/graphics/screen.cpp
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/gui/gui_eob.h
    engines/kyra/gui/gui_eob_segacd.cpp
    engines/kyra/gui/gui_eob_segacd.h
    engines/kyra/resource/staticres_eob.cpp
    engines/kyra/sequence/seqplayer_eob_segacd.cpp
    engines/kyra/sequence/sequences_eob.cpp
    engines/kyra/text/text_eob_segacd.cpp
    engines/kyra/text/text_eob_segacd.h
    engines/kyra/text/text_rpg.h


diff --git a/engines/kyra/engine/chargen.cpp b/engines/kyra/engine/chargen.cpp
index 9ad2a0d98a..cf7c5e16ad 100644
--- a/engines/kyra/engine/chargen.cpp
+++ b/engines/kyra/engine/chargen.cpp
@@ -23,6 +23,7 @@
 #ifdef ENABLE_EOB
 
 #include "kyra/engine/eobcommon.h"
+#include "kyra/graphics/screen_eob_segacd.h"
 #include "kyra/resource/resource.h"
 #include "kyra/sound/sound_intern.h"
 
@@ -52,8 +53,8 @@ private:
 	void initButtonsFromList(int first, int numButtons);
 	void initButton(int index, int x, int y, int w, int h, int keyCode);
 	void checkForCompleteParty();
-	void toggleSpecialButton(int index, int bodyCustom, int pageNum);
-	void processSpecialButton(int index);
+	void drawButton(int index, int buttonState, int pageNum);
+	void processButtonClick(int index);
 	int viewDeleteCharacter();
 	void createPartyMember();
 	int raceSexMenu();
@@ -110,6 +111,7 @@ private:
 	static const uint16 _chargenButtonKeyCodesFMTOWNS[];
 	static const CreatePartyModButton _chargenModButtons[];
 	static const EoBRect8 _chargenButtonBodyCoords[];
+	static const uint8 _chargenSegaButtonCoords[];
 	static const int16 _chargenBoxX[];
 	static const int16 _chargenBoxY[];
 	static const int16 _chargenNameFieldX[];
@@ -123,12 +125,14 @@ private:
 	EoBCharacter *_characters;
 	const uint8 **_faceShapes;
 
+	uint8 *_wndBackgrnd;
+
 	EoBCoreEngine *_vm;
 	Screen_EoB *_screen;
 };
 
 CharacterGenerator::CharacterGenerator(EoBCoreEngine *vm, Screen_EoB *screen) : _vm(vm), _screen(screen),
-	_characters(0), _faceShapes(0), _chargenMagicShapes(0), _chargenMagicShapeTimer(0),
+	_characters(0), _faceShapes(0), _chargenMagicShapes(0), _chargenMagicShapeTimer(0), _wndBackgrnd(0),
 	_updateBoxShapesIndex(0), _lastUpdateBoxShapesIndex(0), _magicShapesBox(6), _activeBox(0) {
 
 	_chargenStatStrings = _vm->_chargenStatStrings;
@@ -164,6 +168,13 @@ CharacterGenerator::CharacterGenerator(EoBCoreEngine *vm, Screen_EoB *screen) :
 		}
 	}
 
+	if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
+		// Adjust modify menu buttons for SegaCD
+		for (int i = 31; i < 38; ++i)
+			chargenButtonDefs[i].y += 8;
+		chargenButtonDefs[37].x--;
+	}
+
 	_chargenButtonDefs = chargenButtonDefs;
 }
 
@@ -180,13 +191,15 @@ CharacterGenerator::~CharacterGenerator() {
 		delete[] _chargenButtonLabels[i];
 
 	delete[] _chargenButtonDefs;
+	delete[] _wndBackgrnd;
+	_vm->_wndBackgrnd = 0;
 
 	_screen->clearPage(2);
 }
 
 bool CharacterGenerator::start(EoBCharacter *characters, const uint8 ***faceShapes, bool defaultParty) {
 	if (!characters || !faceShapes) {
-		warning("CharacterGenerator::start: Called without character data");
+		warning("CharacterGenerator::start(): Called without character data");
 		return true;
 	}
 
@@ -204,7 +217,8 @@ bool CharacterGenerator::start(EoBCharacter *characters, const uint8 ***faceShap
 		return false;
 
 	if (!_vm->shouldQuit()) {
-		processSpecialButton(15);
+		if (!defaultParty)
+			processButtonClick(15);
 		finish();
 	}
 
@@ -241,29 +255,76 @@ void CharacterGenerator::init(bool defaultParty) {
 	if (defaultParty)
 		return;
 
-	_screen->loadEoBBitmap((_vm->game() == GI_EOB1 && _vm->_flags.lang == Common::ES_ESP) ? "CCARGEN" : "CHARGEN", _vm->_cgaMappingDefault, 5, 3, 0);
-	_screen->selectPC98Palette(4, _screen->getPalette(0));
-
-	if (_vm->gameFlags().platform == Common::kPlatformAmiga || (_vm->game() == GI_EOB1 && _vm->gameFlags().platform == Common::kPlatformPC98))
-		_screen->fadeFromBlack(32);
-
-	_screen->loadShapeSetBitmap((_vm->game() == GI_EOB1 && _vm->_flags.lang == Common::ES_ESP) ? "CCARGENB" : "CHARGENB", 5, 3);
 	if (_chargenMagicShapes) {
 		for (int i = 0; i < 10; i++)
 			delete[] _chargenMagicShapes[i];
 		delete[] _chargenMagicShapes;
 	}
 
-	_chargenMagicShapes = new uint8 * [10];
-	for (int i = 0; i < 10; i++)
-		_chargenMagicShapes[i] = _screen->encodeShape(i << 2, 0, 4, 32, true, _vm->_cgaMappingDefault);
+	if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
+		_screen->sega_fadeToBlack(0);
+		_screen->sega_selectPalette(13, 0);
+		_screen->sega_selectPalette(14, 1);
+		_screen->sega_selectPalette(15, 2);
+		_screen->sega_selectPalette(15, 3);
+		_vm->_txt->clearDim(1);
+
+		Common::SeekableReadStream *in = _vm->resource()->createReadStream("CHARGEN");
+		_screen->sega_getRenderer()->loadStreamToVRAM(in, 0x20);
+		_wndBackgrnd = new uint8[10240];
+		_vm->_wndBackgrnd = _wndBackgrnd;
+		for (int i = 0; i < 16; ++i) {
+			in->seek(((8 + i) * 40 + 18) << 5);
+			in->read(&_wndBackgrnd[i * 640], 640);
+		}
+		delete in;
 
-	for (int i = 0; i < 17; i++) {
-		const CreatePartyModButton *c = &_chargenModButtons[i];
-		_chargenButtonLabels[i] = c->labelW ? _screen->encodeShape(c->encodeLabelX, c->encodeLabelY, c->labelW, c->labelH, true, _vm->_cgaMappingDefault) : 0;
+		uint8 *cgb = _vm->resource()->fileData("CGBUTTON", 0);
+		_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->_curPage = 2;
+		_chargenMagicShapes = new uint8*[10];
+		for (int i = 0; i < 10; i++)
+			_chargenMagicShapes[i] = _screen->encodeShape(i << 2, 0, 4, 32);
+		_screen->_curPage = 0;
+
+		_screen->sega_getRenderer()->loadToVRAM(&cgb[0], 0x1900, 0x8220);
+		_screen->sega_getRenderer()->loadToVRAM(&cgb[0x1900], 0x8E0, 0xB080);
+		delete[] cgb;
+		
+		_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);
+
+		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);
+
+		_screen->sega_fadeToNeutral(4);
+
+	} else {
+		_screen->loadEoBBitmap((_vm->game() == GI_EOB1 && _vm->_flags.lang == Common::ES_ESP) ? "CCARGEN" : "CHARGEN", _vm->_cgaMappingDefault, 5, 3, 0);
+		_screen->selectPC98Palette(4, _screen->getPalette(0));
+
+		if (_vm->gameFlags().platform == Common::kPlatformAmiga || (_vm->game() == GI_EOB1 && _vm->gameFlags().platform == Common::kPlatformPC98))
+			_screen->fadeFromBlack(32);
+
+		_screen->loadShapeSetBitmap((_vm->game() == GI_EOB1 && _vm->_flags.lang == Common::ES_ESP) ? "CCARGENB" : "CHARGENB", 5, 3);
+		_chargenMagicShapes = new uint8*[10];
+		for (int i = 0; i < 10; i++)
+			_chargenMagicShapes[i] = _screen->encodeShape(i << 2, 0, 4, 32, true, _vm->_cgaMappingDefault);
+
+		for (int i = 0; i < 17; i++) {
+			const CreatePartyModButton *c = &_chargenModButtons[i];
+			_chargenButtonLabels[i] = c->labelW?_screen->encodeShape(c->encodeLabelX, c->encodeLabelY, c->labelW, c->labelH, true, _vm->_cgaMappingDefault):0;
+		}
+
+		_screen->convertPage(3, 2, _vm->_cgaMappingDefault);
 	}
 
-	_screen->convertPage(3, 2, _vm->_cgaMappingDefault);
+	_screen->setScreenDim(2);
 	_screen->_curPage = 0;
 	_screen->convertToHiColor(2);
 	_screen->shadeRect(142, 63, 306, 193, 4);
@@ -272,12 +333,10 @@ void CharacterGenerator::init(bool defaultParty) {
 }
 
 bool CharacterGenerator::createCustomParty(const uint8 ***faceShapes) {
-	_screen->setScreenDim(2);
-
 	checkForCompleteParty();
 	initButtonsFromList(0, 5);
 
-	_vm->snd_playSong(_vm->game() == GI_EOB1 ? (_vm->gameFlags().platform == Common::kPlatformPC98 ? 1 : 20) : 13);
+	_vm->snd_playSong(_vm->game() == GI_EOB1 ? (_vm->gameFlags().platform == Common::kPlatformPC98 ? 1 : (_vm->gameFlags().platform == Common::kPlatformSegaCD ? 8: 20)) : 13);
 	_activeBox = 0;
 
 	for (bool loop = true; loop && (!_vm->shouldQuit());) {
@@ -410,9 +469,18 @@ 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;
-	_screen->printShadedText(_chargenStrings1[8], x, 16, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+	int cs = 0;
+
+	if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
+		_screen->sega_loadTextBackground(_wndBackgrnd, 10240);
+		_screen->sega_getRenderer()->fillRectWithTiles(0, 18, 8, 20, 16, 0);
+		cs = _screen->setFontStyles(_screen->_currentFont, _vm->gameFlags().lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat);
+		_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->copyRegion(160, 0, 144, 64, 160, 128, 2, 0, Screen::CR_NO_P_CHECK);
+	}
 	_screen->setCurPage(cp);
-	_screen->copyRegion(160, 0, 144, 64, 160, 128, 2, 0, Screen::CR_NO_P_CHECK);
 
 	int numChars = 0;
 	for (int i = 0; i < 4; i++) {
@@ -421,24 +489,53 @@ void CharacterGenerator::checkForCompleteParty() {
 	}
 
 	if (numChars == 4) {
-		_screen->setCurPage(2);
-		_screen->printShadedText(_chargenStrings1[0], x, 61, _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);
-		toggleSpecialButton(15, 0, 0);
+		if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
+			_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->setCurPage(0);
+			_screen->copyRegion(168, 61, 152, 125, 136, 40, 2, 0, Screen::CR_NO_P_CHECK);
+		}
+		drawButton(15, 0, 0);
 	} else {
-		toggleSpecialButton(14, 0, 0);
+		drawButton(14, 0, 0);
+	}
+
+	if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
+		_screen->setFontStyles(_screen->_currentFont, cs);
+		_screen->sega_getRenderer()->render(0, 18, 8, 20, 16);
 	}
 
 	_screen->updateScreen();
 }
 
-void CharacterGenerator::toggleSpecialButton(int index, int bodyCustom, int pageNum) {
+void CharacterGenerator::drawButton(int index, int buttonState, int pageNum) {
 	if (index >= 17)
 		return;
 
+	if (_vm->_flags.platform == Common::kPlatformSegaCD && index > 3) {
+		static const int8 buttonOrder[] = { -1, -1, -1, -1, 9, 10, 8, 6, 7, 11, 4, 5, 2, 3, -2, 0, 1 };
+		index = buttonOrder[index];
+		if (index < 0) {
+			if (index == -2) {
+				// The original SegaCD version doesn't remove the 'PLAY' button here. We do it nonetheless,
+				// 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);
+			}
+			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->updateScreen();
+		return;
+	}
+
 	const CreatePartyModButton *c = &_chargenModButtons[index];
-	const EoBRect8 *p = &_chargenButtonBodyCoords[c->bodyIndex + bodyCustom];
+	const EoBRect8 *p = &_chargenButtonBodyCoords[c->bodyIndex + buttonState];
 
 	int x2 = 20;
 	int y2 = 0;
@@ -459,12 +556,12 @@ void CharacterGenerator::toggleSpecialButton(int index, int bodyCustom, int page
 	_screen->updateScreen();
 }
 
-void CharacterGenerator::processSpecialButton(int index) {
-	toggleSpecialButton(index, 1, 0);
+void CharacterGenerator::processButtonClick(int index) {
+	drawButton(index, 1, 0);
 	if (!(_vm->game() == GI_EOB1 && _vm->_flags.platform == Common::kPlatformPC98))
 		_vm->snd_playSoundEffect(76);
 	_vm->_system->delayMillis(80);
-	toggleSpecialButton(index, 0, 0);
+	drawButton(index, 0, 0);
 }
 
 int CharacterGenerator::viewDeleteCharacter() {
@@ -482,7 +579,7 @@ int CharacterGenerator::viewDeleteCharacter() {
 		_vm->removeInputTop();
 
 		if (inputFlag == _vm->_keyMap[Common::KEYCODE_RETURN] || inputFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP5] || inputFlag == _vm->_keyMap[Common::KEYCODE_ESCAPE]) {
-			processSpecialButton(9);
+			processButtonClick(9);
 			res = 0;
 			loop = false;
 		} else if (inputFlag == _vm->_keyMap[Common::KEYCODE_LEFT] || inputFlag == _vm->_keyMap[Common::KEYCODE_RIGHT]) {
@@ -497,15 +594,20 @@ int CharacterGenerator::viewDeleteCharacter() {
 				res = 1;
 				loop = false;
 			} else if (inputFlag == 5) {
-				processSpecialButton(9);
+				processButtonClick(9);
 				res = 0;
 				loop = false;
 			} else if (inputFlag == 6) {
 				if (_characters[_activeBox].name[0]) {
-					processSpecialButton(16);
+					processButtonClick(16);
 					_characters[_activeBox].name[0] = 0;
 					_characters[_activeBox].faceShape = 0;
-					processNameInput(_activeBox, _vm->guiSettings()->colors.guiColorBlack);
+					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);
+					} else {
+						processNameInput(_activeBox, _vm->guiSettings()->colors.guiColorBlack);
+					}
 					processFaceMenuSelection(_activeBox + 50);
 				}
 			} else {
@@ -566,26 +668,47 @@ void CharacterGenerator::createPartyMember() {
 		for (_characters[_activeBox].name[0] = 0; _characters[_activeBox].name[0] == 0 && !_vm->shouldQuit();) {
 			processFaceMenuSelection(_chargenMinStats[6]);
 			printStats(_activeBox, 0);
-			_screen->printShadedText(_chargenStrings2[11], 149, 100, _vm->guiSettings()->colors.guiColorLightBlue, 0, _vm->guiSettings()->colors.guiColorBlack);
-			if (!_vm->shouldQuit()) {
-				_vm->_gui->getTextInput(_characters[_activeBox].name, 24, 100, 10, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorDarkRed);
-				processNameInput(_activeBox, _vm->guiSettings()->colors.guiColorBlue);
+			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);
+				if (!_vm->shouldQuit())
+					_vm->_gui->getTextInput(_characters[_activeBox].name, (_chargenBoxX[_activeBox] >> 3) - 1, _chargenBoxY[_activeBox] + 41, 7, 0xFF, 0x00, 0xFF);
+			} else {
+				_screen->printShadedText(_chargenStrings2[11], 149, 100, _vm->guiSettings()->colors.guiColorLightBlue, 0, _vm->guiSettings()->colors.guiColorBlack);
+				if (!_vm->shouldQuit()) {
+					_vm->_gui->getTextInput(_characters[_activeBox].name, 24, 100, 10, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorDarkRed);
+					processNameInput(_activeBox, _vm->guiSettings()->colors.guiColorBlue);
+				}
 			}
 		}
 	}
 }
 
 int CharacterGenerator::raceSexMenu() {
-	_screen->drawBox(_chargenBoxX[_activeBox], _chargenBoxY[_activeBox], _chargenBoxX[_activeBox] + 32, _chargenBoxY[_activeBox] + 33, 12);
+	_screen->drawBox(_chargenBoxX[_activeBox], _chargenBoxY[_activeBox], _chargenBoxX[_activeBox] + 32, _chargenBoxY[_activeBox] + 33, _vm->guiSettings()->colors.guiColorBlack);
 	_screen->copyRegion(0, 0, 144, 64, 160, 128, 2, 0, Screen::CR_NO_P_CHECK);
-	_screen->printShadedText(_chargenStrings2[8], 147, 67, _vm->guiSettings()->colors.guiColorLightBlue, 0, _vm->guiSettings()->colors.guiColorBlack);
+
+	if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
+		_screen->sega_loadTextBackground(_wndBackgrnd, 10240);
+		_screen->sega_getRenderer()->fillRectWithTiles(0, 18, 8, 20, 16, 0);
+		_vm->_txt->printShadedText(_chargenStrings2[8], 0, 0, -1, 0x99);
+	} else {
+		_screen->printShadedText(_chargenStrings2[8], 147, 67, _vm->guiSettings()->colors.guiColorLightBlue, 0, _vm->guiSettings()->colors.guiColorBlack);
+	}
 	_vm->removeInputTop();
 
 	_vm->_gui->simpleMenu_setup(1, 0, _chargenRaceSexStrings, -1, 0, 0);
+	if (_vm->_flags.platform == Common::kPlatformSegaCD)
+		_screen->sega_getRenderer()->render(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->updateScreen();
 		updateMagicShapes();
 	}
 
@@ -605,12 +728,20 @@ int CharacterGenerator::classMenu(int raceSex) {
 	updateMagicShapes();
 
 	_screen->copyRegion(0, 0, 144, 64, 160, 128, 2, 0, Screen::CR_NO_P_CHECK);
-	_screen->printShadedText(_chargenStrings2[9], 147, 67, _vm->guiSettings()->colors.guiColorLightBlue, 0, _vm->guiSettings()->colors.guiColorBlack);
-
-	toggleSpecialButton(5, 0, 0);
+	if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
+		_screen->sega_loadTextBackground(_wndBackgrnd, 10240);
+		_screen->sega_getRenderer()->fillRectWithTiles(0, 18, 8, 20, 16, 0);
+		_vm->_txt->printShadedText(_chargenStrings2[9], 0, 0, -1, 0x99);
+	} else {
+		_screen->printShadedText(_chargenStrings2[9], 147, 67, _vm->guiSettings()->colors.guiColorLightBlue, 0, _vm->guiSettings()->colors.guiColorBlack);
+	}
+	drawButton(5, 0, 0);
 
 	itemsMask &= _classMenuMasks[raceSex / 2];
 	_vm->_gui->simpleMenu_setup(2, 15, _chargenClassStrings, itemsMask, 0, 0);
+	if (_vm->_flags.platform == Common::kPlatformSegaCD)
+		_screen->sega_getRenderer()->render(0, 18, 8, 20, 16);
+	_screen->updateScreen();
 
 	_vm->_mouseX = _vm->_mouseY = 0;
 	int16 res = -1;
@@ -629,13 +760,16 @@ int CharacterGenerator::classMenu(int raceSex) {
 				_vm->removeInputTop();
 		} 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->updateScreen();
 		}
 	}
 
 	_vm->removeInputTop();
 
 	if (res == _vm->_keyMap[Common::KEYCODE_ESCAPE])
-		processSpecialButton(5);
+		processButtonClick(5);
 
 	return res;
 }
@@ -653,12 +787,21 @@ int CharacterGenerator::alignmentMenu(int cClass) {
 	updateMagicShapes();
 
 	_screen->copyRegion(0, 0, 144, 64, 160, 128, 2, 0, Screen::CR_NO_P_CHECK);
-	_screen->printShadedText(_chargenStrings2[10], 147, 67, _vm->guiSettings()->colors.guiColorLightBlue, 0, _vm->guiSettings()->colors.guiColorBlack);
+	if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
+		_screen->sega_loadTextBackground(_wndBackgrnd, 10240);
+		_screen->sega_getRenderer()->fillRectWithTiles(0, 18, 8, 20, 16, 0);
+		_vm->_txt->printShadedText(_chargenStrings2[10], 0, 0, -1, 0x99);
+	} else {
+		_screen->printShadedText(_chargenStrings2[10], 147, 67, _vm->guiSettings()->colors.guiColorLightBlue, 0, _vm->guiSettings()->colors.guiColorBlack);
+	}
 
-	toggleSpecialButton(5, 0, 0);
+	drawButton(5, 0, 0);
 
 	itemsMask &= _alignmentMenuMasks[cClass];
 	_vm->_gui->simpleMenu_setup(3, 9, _chargenAlignmentStrings, itemsMask, 0, 0);
+	if (_vm->_flags.platform == Common::kPlatformSegaCD)
+		_screen->sega_getRenderer()->render(0, 18, 8, 20, 16);
+	_screen->updateScreen();
 
 	_vm->_mouseX = _vm->_mouseY = 0;
 	int16 res = -1;
@@ -677,13 +820,16 @@ int CharacterGenerator::alignmentMenu(int cClass) {
 				_vm->removeInputTop();
 		} 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->updateScreen();
 		}
 	}
 
 	_vm->removeInputTop();
 
 	if (res == _vm->_keyMap[Common::KEYCODE_ESCAPE])
-		processSpecialButton(5);
+		processButtonClick(5);
 
 	return res;
 }
@@ -834,20 +980,20 @@ void CharacterGenerator::statsAndFacesMenu() {
 		_vm->removeInputTop();
 
 		if (in == 0x8001) {
-			processSpecialButton(4);
+			processButtonClick(4);
 			updateMagicShapes();
 			generateStats(_activeBox);
 			in = -1;
 		} else if (in == 0x8002) {
-			processSpecialButton(7);
+			processButtonClick(7);
 			modifyMenu();
 			in = -1;
 		} else if (in == 0x8003) {
-			processSpecialButton(8);
+			processButtonClick(8);
 			faceSelectMenu();
 			in = -1;
 		} else if (in == 0x8004 || in == _vm->_keyMap[Common::KEYCODE_KP5]) {
-			processSpecialButton(6);
+			processButtonClick(6);
 			in = 1;
 		} else {
 			in = 0;
@@ -874,8 +1020,8 @@ void CharacterGenerator::faceSelectMenu() {
 	int8 shp = charSex ? 26 : 0;
 
 	printStats(_activeBox, 4);
-	toggleSpecialButton(12, 0, 0);
-	toggleSpecialButton(13, 0, 0);
+	drawButton(12, 0, 0);
+	drawButton(13, 0, 0);
 	_vm->_gui->updateBoxFrameHighLight(-1);
 
 	shp = getNextFreeFaceShape(shp, charSex, 1, _chargenSelectedPortraits);
@@ -903,13 +1049,13 @@ void CharacterGenerator::faceSelectMenu() {
 			_vm->_gui->updateBoxFrameHighLight(box + 10);
 
 			if (in == 0x8002 || in == _vm->_keyMap[Common::KEYCODE_RIGHT]) {
-				processSpecialButton(13);
+				processButtonClick(13);
 				in = 2;
 			} else if (in > 0x8002 && in < 0x8007) {
 				box = (in & 7) - 3;
 				in = 3;
 			} else if (in == 0x8001 || in == _vm->_keyMap[Common::KEYCODE_LEFT]) {
-				processSpecialButton(12);
+				processButtonClick(12);
 				in = 1;
 			} else if (in == _vm->_keyMap[Common::KEYCODE_RETURN] || in == _vm->_keyMap[Common::KEYCODE_KP5]) {
 				in = 3;
@@ -970,61 +1116,77 @@ void CharacterGenerator::processFaceMenuSelection(int index) {
 	if (index <= 48)
 		_screen->drawShape(0, _characters[_activeBox].faceShape, _chargenBoxX[_activeBox], _chargenBoxY[_activeBox] + 1, 0);
 	else
-		toggleSpecialButton(index - 50, 0, 0);
+		drawButton(index - 50, 0, 0);
 }
 
 void CharacterGenerator::printStats(int index, int mode) {
-	_screen->copyRegion(0, 0, 160, 0, 160, 128, 2, 2, Screen::CR_NO_P_CHECK);
-	_screen->_curPage = 2;
-
 	EoBCharacter *c = &_characters[index];
 
-	if (mode != 4)
-		_screen->drawShape(2, c->faceShape, 224, 2, 0);
-
-	_screen->printShadedText(c->name, 160 + ((160 - _screen->getTextWidth(c->name)) / 2), 35, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
-	_screen->printShadedText(_chargenRaceSexStrings[c->raceSex], 160 + ((160 - _screen->getTextWidth(_chargenRaceSexStrings[c->raceSex])) / 2), 45, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
-	_screen->printShadedText(_chargenClassStrings[c->cClass], 160 + ((160 - _screen->getTextWidth(_chargenClassStrings[c->cClass])) / 2), 54, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
-
-	for (int i = 0; i < 6; i++)
-		_screen->printShadedText(_chargenStatStrings[i], 163, (i + 8) << 3, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+	if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
+		_screen->sega_loadTextBackground(_wndBackgrnd, 10240);
+		_screen->sega_getRenderer()->fillRectWithTiles(0, 18, 8, 20, 16, 0);
+	} else {
+		_screen->copyRegion(0, 0, 160, 0, 160, 128, 2, 2, Screen::CR_NO_P_CHECK);
+		_screen->_curPage = 2;
+		if (mode != 4)
+			_screen->drawShape(2, c->faceShape, 224, 2, 0);
+	}
 
-	_screen->printShadedText(_chargenStrings1[2], 248, 64, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+	Common::String str1 = Common::String::format(_chargenStrings1[3], _vm->getCharStrength(c->strengthCur, c->strengthExtCur, _vm->gameFlags().platform == Common::kPlatformSegaCD).c_str(), c->intelligenceCur, c->wisdomCur, c->dexterityCur, c->constitutionCur, c->charismaCur);
+	Common::String str2 = Common::String::format(_chargenStrings1[4], c->armorClass, c->hitPointsMax);
+	const char *lvlStr = c->level[2] ? _chargenStrings1[7] : (c->level[1] ? _chargenStrings1[6] : _chargenStrings1[5]);
+	Common::String str3 = Common::String::format(lvlStr, c->level[0], c->level[1], c->level[2]);
 
-	Common::String str = Common::String::format(_chargenStrings1[3], _vm->getCharStrength(c->strengthCur, c->strengthExtCur).c_str(), c->intelligenceCur, c->wisdomCur, c->dexterityCur, c->constitutionCur, c->charismaCur);
-	_screen->printShadedText(str.c_str(), 192, 64, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+	if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
+		_vm->_txt->printShadedText(_chargenStatStrings[0], 0, 72);
+		_vm->_txt->printShadedText(_chargenRaceSexStrings[c->raceSex], 80 - _screen->getTextWidth(_chargenRaceSexStrings[c->raceSex]) / 2, 48);
+		_vm->_txt->printShadedText(_chargenClassStrings[c->cClass], 80 - _screen->getTextWidth(_chargenClassStrings[c->cClass]) / 2, 56);
+		_vm->_txt->printShadedText(str1.c_str(), 32, 72);
+		_vm->_txt->printShadedText(str2.c_str(), 112, 72);
+		_vm->_txt->printShadedText(str3.c_str(), 120, 88);
+	} else {
+		_screen->printShadedText(c->name, 160 + ((160 - _screen->getTextWidth(c->name)) / 2), 35, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+		_screen->printShadedText(_chargenRaceSexStrings[c->raceSex], 160 + ((160 - _screen->getTextWidth(_chargenRaceSexStrings[c->raceSex])) / 2), 45, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+		_screen->printShadedText(_chargenClassStrings[c->cClass], 160 + ((160 - _screen->getTextWidth(_chargenClassStrings[c->cClass])) / 2), 54, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
 
-	str = Common::String::format(_chargenStrings1[4], c->armorClass, c->hitPointsMax);
-	_screen->printShadedText(str.c_str(), 280, 64, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+		for (int i = 0; i < 6; i++)
+			_screen->printShadedText(_chargenStatStrings[i], 163, (i + 8) << 3, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
 
-	const char *lvlStr = c->level[2] ? _chargenStrings1[7] : (c->level[1] ? _chargenStrings1[6] : _chargenStrings1[5]);
-	str = Common::String::format(lvlStr, c->level[0], c->level[1], c->level[2]);
-	_screen->printShadedText(str.c_str(), 280, 80, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+		_screen->printShadedText(_chargenStrings1[2], 248, 64, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+		_screen->printShadedText(str1.c_str(), 192, 64, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);		
+		_screen->printShadedText(str2.c_str(), 280, 64, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+		_screen->printShadedText(str3.c_str(), 280, 80, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+	}
 
 	switch (mode) {
 	case 1:
-		toggleSpecialButton(4, 0, 2);
-		toggleSpecialButton(7, 0, 2);
-		toggleSpecialButton(8, 0, 2);
-		toggleSpecialButton(6, 0, 2);
+		drawButton(4, 0, 2);
+		drawButton(7, 0, 2);
+		drawButton(8, 0, 2);
+		drawButton(6, 0, 2);
 		break;
 
 	case 2:
-		toggleSpecialButton(16, 0, 2);
-		toggleSpecialButton(9, 0, 2);
+		drawButton(16, 0, 2);
+		drawButton(9, 0, 2);
 		break;
 
 	case 3:
-		toggleSpecialButton(10, 0, 2);
-		toggleSpecialButton(11, 0, 2);
-		toggleSpecialButton(9, 0, 2);
+		drawButton(10, 0, 2);
+		drawButton(11, 0, 2);
+		drawButton(9, 0, 2);
 		break;
 
 	default:
 		break;
 	}
 
-	_screen->copyRegion(160, 0, 144, 64, 160, 128, 2, 0, Screen::CR_NO_P_CHECK);
+	if (_vm->_flags.platform == Common::kPlatformSegaCD) {
+		_screen->sega_getRenderer()->render(0, 18, 8, 20, 16);
+		if (mode != 4)
+			_screen->drawShape(0, c->faceShape, 208, 66, 0);
+	} else
+		_screen->copyRegion(160, 0, 144, 64, 160, 128, 2, 0, Screen::CR_NO_P_CHECK);
 
 	if (mode != 3)
 		_screen->updateScreen();
@@ -1033,9 +1195,9 @@ void CharacterGenerator::printStats(int index, int mode) {
 }
 
 void CharacterGenerator::processNameInput(int index, int textColor) {
-	Screen::FontId of = _screen->setFont(_vm->_flags.use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT);
+	Screen::FontId of = _screen->setFont(_vm->_conFont);
 	_screen->fillRect(_chargenNameFieldX[index], _chargenNameFieldY[index], _chargenNameFieldX[index] + 59, _chargenNameFieldY[index] + 5, _vm->guiSettings()->colors.guiColorBlack);
-	_screen->setFont(_vm->_flags.use16ColorMode ? Screen::FID_SJIS_SMALL_FNT : Screen::FID_6_FNT);
+	_screen->setFont(_vm->_invFont1);
 	int xOffs = (60 - _screen->getTextWidth(_characters[index].name)) >> 1;
 	_screen->printText(_characters[index].name, _chargenNameFieldX[index] + xOffs, _chargenNameFieldY[index], textColor, 0);
 	_screen->updateScreen();
@@ -1067,10 +1229,15 @@ int CharacterGenerator::modifyStat(int index, int8 *stat1, int8 *stat2) {
 	printStats(_activeBox, 3);
 	_vm->removeInputTop();
 
-	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);
-	_screen->printShadedText(statStr.c_str(), b->x + 32, b->y, _vm->guiSettings()->colors.guiColorLightRed, 0, _vm->guiSettings()->colors.guiColorBlack);
+	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);
+	} 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);
+		_screen->printShadedText(statStr.c_str(), b->x + 32, b->y, _vm->guiSettings()->colors.guiColorLightRed, 0, _vm->guiSettings()->colors.guiColorBlack);
+	}
 	_screen->updateScreen();
 
 	EoBCharacter *c = &_characters[_activeBox];
@@ -1090,11 +1257,11 @@ int CharacterGenerator::modifyStat(int index, int8 *stat1, int8 *stat2) {
 		_vm->removeInputTop();
 
 		if (inputFlag == _vm->_keyMap[Common::KEYCODE_LEFT] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP4] || inputFlag == _vm->_keyMap[Common::KEYCODE_MINUS] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP_MINUS] || inputFlag == 0x8009) {
-			processSpecialButton(11);
+			processButtonClick(11);
 			v1--;
 
 		} else if (inputFlag == _vm->_keyMap[Common::KEYCODE_RIGHT] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP6] || inputFlag == _vm->_keyMap[Common::KEYCODE_PLUS] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP_PLUS] || inputFlag == 0x8008) {
-			processSpecialButton(10);
+			processButtonClick(10);
 			v1++;
 
 		} else if (inputFlag == _vm->_keyMap[Common::KEYCODE_UP] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP8]) {
@@ -1106,7 +1273,7 @@ int CharacterGenerator::modifyStat(int index, int8 *stat1, int8 *stat2) {
 			loop = false;
 
 		} else if (inputFlag == _vm->_keyMap[Common::KEYCODE_o] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP5] || inputFlag == _vm->_keyMap[Common::KEYCODE_ESCAPE] || inputFlag == 0x800A) {
-			processSpecialButton(9);
+			processButtonClick(9);
 			loop = false;
 			ci = -2;
 
@@ -1146,40 +1313,50 @@ int CharacterGenerator::modifyStat(int index, int8 *stat1, int8 *stat2) {
 		if (index == 6)
 			_characters[_activeBox].hitPointsMax = v1;
 
-		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);
-		_screen->printShadedText(statStr.c_str(), b->x + 32, b->y, _vm->guiSettings()->colors.guiColorLightRed, 0, _vm->guiSettings()->colors.guiColorBlack);
-		_screen->updateScreen();
+		bool hpChanged = false;
+		bool acChanged = false;
 
 		if (index == 4) {
 			int oldVal = c->hitPointsCur;
 			_chargenMaxStats[6] = getMaxHp(c->cClass, c->constitutionCur, c->level[0], c->level[1], c->level[2]);
 			_chargenMinStats[6] = getMinHp(c->cClass, c->constitutionCur, c->level[0], c->level[1], c->level[2]);
 			c->hitPointsMax = c->hitPointsCur = CLIP<int16>(c->hitPointsCur, _chargenMinStats[6], _chargenMaxStats[6]);
-
-			if (c->hitPointsCur != oldVal) {
-				statStr = Common::String::format("%d", c->hitPointsCur);
-				_screen->copyRegion(120, 72, 264, 136, 40, 8, 2, 0, Screen::CR_NO_P_CHECK);
-				_screen->printShadedText(statStr.c_str(), 264, 136, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
-				_screen->updateScreen();
-			}
-
+			hpChanged = c->hitPointsCur != oldVal;
 		} else if (index == 3) {
 			int oldVal = c->armorClass;
 			c->armorClass = _vm->getDexterityArmorClassModifier(v1) + 10;
+			acChanged = c->armorClass != oldVal;
+		}
 
-			if (c->armorClass != oldVal) {
+		if (_vm->_flags.platform == Common::kPlatformSegaCD) {
+			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);
+		} 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);
+			_screen->printShadedText(statStr.c_str(), b->x + 32, b->y, _vm->guiSettings()->colors.guiColorLightRed, 0, _vm->guiSettings()->colors.guiColorBlack);
+			if (hpChanged) {
+				statStr = Common::String::format("%d", c->hitPointsCur);
+				_screen->copyRegion(120, 72, 264, 136, 40, 8, 2, 0, Screen::CR_NO_P_CHECK);
+				_screen->printShadedText(statStr.c_str(), 264, 136, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+			} else if (acChanged) {
 				statStr = Common::String::format("%d", c->armorClass);
 				_screen->copyRegion(120, 64, 264, 128, 40, 8, 2, 0, Screen::CR_NO_P_CHECK);
 				_screen->printShadedText(statStr.c_str(), 264, 128, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
-				_screen->updateScreen();
 			}
 		}
 
+		_screen->updateScreen();
+
 		if (loop == false) {
-			statStr = index ? Common::String::format("%d", *s1) : _vm->getCharStrength(*s1, *s2);
-			_screen->printText(statStr.c_str(), b->x + 32, b->y, _vm->guiSettings()->colors.guiColorWhite, 0);
+			if (_vm->_flags.platform == Common::kPlatformSegaCD) {
+				printStats(_activeBox, 3);
+			} else {
+				Common::String statStr = index ? Common::String::format("%d", *s1) : _vm->getCharStrength(*s1, *s2);
+				_screen->printText(statStr.c_str(), b->x + 32, b->y, _vm->guiSettings()->colors.guiColorWhite, 0);
+			}
 			_screen->updateScreen();
 		}
 	}
@@ -1521,6 +1698,21 @@ const EoBRect8 CharacterGenerator::_chargenButtonBodyCoords[] = {
 	{ 0x14, 0x90, 0x0B, 0x10 }
 };
 
+const uint8 CharacterGenerator::_chargenSegaButtonCoords[] {
+	0x03, 0x17, 0x0b, 0x02, 0x00,
+	0x1c, 0x16, 0x05, 0x02, 0x2c,
+	0x12, 0x08, 0x04, 0x02, 0x40,
+	0x12, 0x0a, 0x04, 0x02, 0x50,
+	0x1b, 0x16, 0x03, 0x02, 0x60,
+	0x1e, 0x16, 0x03, 0x02, 0x6c,
+	0x21, 0x14, 0x05, 0x02, 0x78,
+	0x1c, 0x16, 0x05, 0x02, 0x8c,
+	0x21, 0x16, 0x05, 0x02, 0xa0,
+	0x1c, 0x14, 0x05, 0x02, 0xb4,
+	0x21, 0x16, 0x05, 0x02, 0xc8,
+	0x21, 0x16, 0x05, 0x02, 0xdc
+};
+
 const int16 CharacterGenerator::_chargenBoxX[] = { 0x10, 0x50, 0x10, 0x50 };
 const int16 CharacterGenerator::_chargenBoxY[] = { 0x3F, 0x3F, 0x7F, 0x7F };
 const int16 CharacterGenerator::_chargenNameFieldX[] = { 0x02, 0x42, 0x02, 0x42 };
diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index af6e79b1d9..2246f79e24 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -131,6 +131,7 @@ Common::Error EoBEngine::init() {
 	} else if (_flags.platform == Common::kPlatformSegaCD) {
 		_screen->modifyScreenDim(27, 0x00, 0x02, 0x11, 0x03);
 		_screen->modifyScreenDim(28, 0x07, 0xA0, 0x17, 0x24);
+		_screen->modifyScreenDim(2, 0x12, 0x44, 0x14, 0x80);
 	} else {
 		_screen->modifyScreenDim(12, 0x01, 0x04, 0x14, 0xA0);
 	}
@@ -331,7 +332,7 @@ void EoBEngine::startupLoad() {
 	_screen->sega_fadeToNeutral(1);
 }
 
-void EoBEngine::startupLoad2() {
+void EoBEngine::startupReset() {
 	if (_flags.platform != Common::kPlatformSegaCD)
 		return;
 	_screen->sega_fadeToBlack(1);
@@ -1050,7 +1051,7 @@ void EoBEngine::displayParchment(int id) {
 	int curPage = (id == 46) ? 4 : id - 47;
 	for (int i = 0; i < numPages && !shouldQuit(); ++i) {
 		_screen->sega_loadTextBackground(data, 22464);
-		_txt->printShadowedText(strings[curPage++], 16, 16, 0x22, 0, 208, 216, 16, false);
+		_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);
@@ -1143,9 +1144,9 @@ bool EoBEngine::checkPartyStatusExtra() {
 
 		int cs = _screen->setFontStyles(_screen->_currentFont, _flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat);
 		_screen->sega_clearTextBuffer(0);
-		_txt->printShadowedText(_menuStringsDefeat[0], 12, 0, 0xff, 0xcc, 304, 48, 0, false);
-		_txt->printShadowedText(_menuStringsDefeat[1], 20, 16, 0xff, 0xcc, 304, 48, 0, false);
-		_txt->printShadowedText(_menuStringsDefeat[2], 20, 32, 0xff, 0xcc, 304, 48, 0, false);
+		_txt->printShadedText(_menuStringsDefeat[0], 12, 0, 0xff, 0xcc, 304, 48, 0, false);
+		_txt->printShadedText(_menuStringsDefeat[1], 20, 16, 0xff, 0xcc, 304, 48, 0, false);
+		_txt->printShadedText(_menuStringsDefeat[2], 20, 32, 0xff, 0xcc, 304, 48, 0, false);
 		_screen->setFontStyles(_screen->_currentFont, cs);
 		_screen->sega_loadTextBufferToVRAM(0, 0xA3A0, 7296);
 		r->fillRectWithTiles(0, 1, 20, 38, 6, 0x651D, true);
diff --git a/engines/kyra/engine/eob.h b/engines/kyra/engine/eob.h
index 2ee41e15e6..a099829ff3 100644
--- a/engines/kyra/engine/eob.h
+++ b/engines/kyra/engine/eob.h
@@ -82,7 +82,7 @@ private:
 	// Main loop
 	void startupNew() override;
 	void startupLoad() override;
-	void startupLoad2() override;
+	void startupReset() override;
 
 	// Intro/Outro/Sequence Playback
 	enum IntroPart {
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 494cb6a5df..f3f413947b 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -243,6 +243,7 @@ EoBCoreEngine::EoBCoreEngine(OSystem *system, const GameFlags &flags) : KyraRpgE
 	_lastVIntTick = _lastSecTick = _totalPlaySecs = _totalEnemiesKilled = _totalSteps = 0;
 	_levelMaps = 0;
 	_closeSpellbookAfterUse = false;
+	_wndBackgrnd = 0;
 
 	memset(_cgaMappingLevel, 0, sizeof(_cgaMappingLevel));
 	memset(_expRequirementTables, 0, sizeof(_expRequirementTables));
@@ -640,12 +641,14 @@ Common::Error EoBCoreEngine::go() {
 			startupLoad();
 			repeatLoop = _gui->runLoadMenu(_flags.platform == Common::kPlatformSegaCD ? 80 : 72, _flags.platform == Common::kPlatformSegaCD ? 16 : 14, true);
 			if (!repeatLoop)
-				startupLoad2();
+				startupReset();
 		} else if (action == -2 || action == -4) {
 			// new game
 			repeatLoop = startCharacterGeneration(action == -4);
 			if (repeatLoop && !shouldQuit())
 				startupNew();
+			else
+				startupReset();
 		} else if (action == -3) {
 			// transfer party
 			repeatLoop = startPartyTransfer();
@@ -1077,13 +1080,13 @@ int EoBCoreEngine::getModifiedHpLimits(int hpModifier, int constModifier, int le
 	return res;
 }
 
-Common::String EoBCoreEngine::getCharStrength(int str, int strExt) {
+Common::String EoBCoreEngine::getCharStrength(int str, int strExt, bool twoDigitsPadding) {
 	if (strExt) {
 		if (strExt == 100)
 			strExt = 0;
-		_strenghtStr = Common::String::format("%d/%02d", str, strExt);
+		_strenghtStr = Common::String::format(twoDigitsPadding ? "%02d/%02d" : "%d/%02d", str, strExt);
 	} else {
-		_strenghtStr = Common::String::format("%d", str);
+		_strenghtStr = Common::String::format(twoDigitsPadding ? "%02d" : "%d", str);
 	}
 
 	return _strenghtStr;
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index de2fd8326a..73ee51b07d 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -340,7 +340,7 @@ protected:
 	// Main loop
 	virtual void startupNew();
 	virtual void startupLoad() = 0;
-	virtual void startupLoad2() {}
+	virtual void startupReset() {}
 	void runLoop();
 	void update() override { screen()->updateScreen(); }
 	bool checkPartyStatus(bool handleDeath);
@@ -353,6 +353,7 @@ protected:
 	bool startPartyTransfer();
 
 	const uint8 **_faceShapes;
+	const uint8 *_wndBackgrnd;
 
 	static const int8 _characterClassType[];
 	static const uint8 _hpIncrPerLevel[];
@@ -403,7 +404,7 @@ protected:
 	int getClassAndConstHitpointsModifier(int cclass, int constitution);
 	int getCharacterClassType(int cclass, int levelIndex);
 	int getModifiedHpLimits(int hpModifier, int constModifier, int level, bool mode);
-	Common::String getCharStrength(int str, int strExt);
+	Common::String getCharStrength(int str, int strExt, bool twoDigitsPadding = false);
 	int testCharacter(int16 index, int flags);
 	int getNextValidCharIndex(int curCharIndex, int searchStep);
 
diff --git a/engines/kyra/graphics/screen.cpp b/engines/kyra/graphics/screen.cpp
index f31cfc4ea4..e9abfdbfd5 100644
--- a/engines/kyra/graphics/screen.cpp
+++ b/engines/kyra/graphics/screen.cpp
@@ -1395,7 +1395,7 @@ int Screen::getTextWidth(const char *str, bool nextWordOnly) {
 
 		uint c = fetchChar(str);
 
-		if (c == 0 || (nextWordOnly && (c == 2 || c == 6 || c == 32 || c == 0x4081))) {
+		if (c == 0 || (nextWordOnly && (c == 2 || c == 6 || c == 13 || c == 32 || c == 0x4081))) {
 			break;
 		} else if (c == '\r') {
 			if (curLineLen > maxLineLen)
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 5359105e81..8acdce2ca5 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -1533,6 +1533,7 @@ GUI_EoB::GUI_EoB(EoBCoreEngine *vm) : GUI(vm), _vm(vm), _screen(vm->_screen), _n
 	memset(_numAssignedSpellsOfType, 0, 72);
 
 	_charSelectRedraw = false;
+	_clickableCharactersPage = 0;
 
 	if (_vm->gameFlags().platform == Common::kPlatformAmiga)
 		_highLightColorTable = _highlightColorTableAmiga;
@@ -2080,9 +2081,9 @@ void GUI_EoB::simpleMenu_setup(int sd, int maxItem, const char *const *strings,
 
 	for (int i = 0; i < _menuNumItems; i++) {
 		int item = simpleMenu_getMenuItem(i, menuItemsMask, itemOffset);
-		int ty = i * (lineSpacing + _screen->getFontHeight());
+		int ty = i * (lineSpacing + _screen->getCharHeight(' '));
 		if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
-			_vm->_txt->printShadowedText(strings[item], 4, 2 + ty, item == v ? 0x55 : 0xff, 0x11);
+			_vm->_txt->printShadedText(strings[item], 4, (sd == 8 ? 2 : 20) + ty, item == v ? 0x55 : 0xff, sd == 8 ? 0x11 : 0x99);
 		} else {
 			_screen->printShadedText(strings[item], x, y + ty, (_vm->_configRenderMode == Common::kRenderCGA) ? 1 : _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
 			if (item == v)
@@ -2101,7 +2102,7 @@ int GUI_EoB::simpleMenu_process(int sd, const char *const *strings, void *b, int
 	int currentItem = _menuCur % _menuNumItems;
 	int newItem = currentItem;
 	int result = -1;
-	int lineH = (_menuLineSpacing + _screen->getFontHeight());
+	int lineH = (_menuLineSpacing + _screen->getCharHeight(' '));
 	int lineS1 = _menuLineSpacing >> 1;
 	int x = (_screen->_curDim->sx + dm->sx) << 3;
 	int y = _screen->_curDim->sy + dm->sy;
@@ -2110,9 +2111,9 @@ int GUI_EoB::simpleMenu_process(int sd, const char *const *strings, void *b, int
 	_vm->removeInputTop();
 	Common::Point mousePos = _vm->getMousePos();
 
-	int x1 = (_screen->_curDim->sx << 3) + (dm->sx * _screen->getFontWidth());
+	int x1 = (_screen->_curDim->sx << 3) + (dm->sx * _screen->getCharWidth('W'));
 	int y1 = _screen->_curDim->sy + dm->sy - lineS1;
-	int x2 = x1 + (dm->w * _screen->getFontWidth()) - 1;
+	int x2 = x1 + (dm->w * _screen->getCharWidth('W')) - 1;
 	int y2 = y1 + _menuNumItems * lineH - 1;
 	if (_vm->posWithinRect(mousePos.x, mousePos.y, x1, y1, x2, y2))
 		newItem = (mousePos.y - y1) / lineH;
@@ -2681,7 +2682,7 @@ void GUI_EoB::updateBoxFrameHighLight(int box) {
 	} else {
 		if (_updateBoxIndex != -1) {
 			const EoBRect16 *r = &_highlightFrames[_updateBoxIndex];
-			_screen->drawBox(r->x1, r->y1, r->x2, r->y2, 12);
+			_screen->drawBox(r->x1, r->y1, r->x2, r->y2, _vm->guiSettings()->colors.guiColorBlack);
 			_screen->updateScreen();
 		}
 
@@ -2700,6 +2701,9 @@ int GUI_EoB::getTextInput(char *dest, int x, int y, int destMaxLen, int textColo
 	uint8 cursorState = 1;
 	char sufx[3] = " \0";
 
+	uint8 *segaCharBuf = new uint8[destMaxLen << 5];
+	memset(segaCharBuf, 0, destMaxLen << 5);
+
 	int len = strlen(dest);
 	if (len > destMaxLen) {
 		len = destMaxLen;
@@ -2720,7 +2724,7 @@ int GUI_EoB::getTextInput(char *dest, int x, int y, int destMaxLen, int textColo
 	_screen->printText(sufx, (x + pos) << 3, y, textColor1, cursorColor);
 
 	_menuCur = -1;
-	printKatakanaOptions(0);
+	printClickableCharacters(0);
 
 	int bytesPerChar = (_vm->_flags.platform == Common::kPlatformFMTowns) ? 2 : 1;
 	int in = 0;
@@ -2731,7 +2735,14 @@ int GUI_EoB::getTextInput(char *dest, int x, int y, int destMaxLen, int textColo
 
 		while (!in && !_vm->shouldQuit()) {
 			if (next <= _vm->_system->getMillis()) {
-				if (cursorState) {
+				if (_vm->_flags.platform == Common::kPlatformSegaCD) {
+					memset(segaCharBuf, cursorState ? 0 : cursorColor, 32);
+					_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_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);
 					_screen->printShadedText(sufx, (x + pos) << 3, y, textColor1, textColor2, _vm->guiSettings()->colors.guiColorBlack);
 				} else {
@@ -2745,7 +2756,7 @@ int GUI_EoB::getTextInput(char *dest, int x, int y, int destMaxLen, int textColo
 			}
 
 			_vm->updateInput();
-			in = checkKatakanaSelection();
+			in = checkClickableCharactersSelection();
 
 			for (Common::List<KyraEngine_v1::Event>::const_iterator evt = _vm->_eventList.begin(); evt != _vm->_eventList.end(); ++evt) {
 				if (evt->event.type == Common::EVENT_KEYDOWN) {
@@ -2800,8 +2811,8 @@ int GUI_EoB::getTextInput(char *dest, int x, int y, int destMaxLen, int textColo
 
 		} else if ((in > 31 && in < 126) || (in == 0x89)) {
 			if (!(in == 32 && pos == 0)) {
-				// EOBI/PC-98 is the only version that allows small characters
-				if (in >= 97 && in <= 122 && !_vm->_flags.use16ColorMode)
+				// The SegaCD and PC-98 versions of EOB I are the only versions that allow small characters
+				if (in >= 97 && in <= 122 && !(_vm->_flags.gameID == GI_EOB1 && (_vm->_flags.platform == Common::kPlatformPC98 || _vm->_flags.platform == Common::kPlatformSegaCD)))
 					in -= 32;
 
 				if (pos < len) {
@@ -2848,9 +2859,18 @@ int GUI_EoB::getTextInput(char *dest, int x, int y, int destMaxLen, int textColo
 			}
 		}
 
-		_screen->copyRegion(0, 191, (x - 1) << 3, y, (destMaxLen + 2) << 3, 9, 2, 0, Screen::CR_NO_P_CHECK);
-		_screen->printShadedText(dest, x << 3, y, textColor1, textColor2, _vm->guiSettings()->colors.guiColorBlack);
-		
+		if (_vm->_flags.platform == Common::kPlatformSegaCD) {
+			memset(segaCharBuf, 0, destMaxLen << 5);
+			_screen->sega_setTextBuffer(segaCharBuf, destMaxLen << 5);
+			_vm->_txt->printShadedText(dest, 0, 0, textColor1, 0, -1, -1, 0, false);
+			_screen->sega_loadTextBufferToVRAM(0, ((y >> 3) * 40 + x + 1) << 5, destMaxLen << 5);
+			_screen->sega_getRenderer()->render(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);
+			_screen->printShadedText(dest, x << 3, y, textColor1, textColor2, _vm->guiSettings()->colors.guiColorBlack);
+		}
+
 		if (_vm->_flags.platform == Common::kPlatformFMTowns) {
 			if (pos < len) {
 				sufx[0] = dest[pos * bytesPerChar];
@@ -2863,20 +2883,30 @@ int GUI_EoB::getTextInput(char *dest, int x, int y, int destMaxLen, int textColo
 			sufx[0] = (pos < len) ? dest[pos] : 32;
 		}
 
-		if (cursorState)
+		if (_vm->_flags.platform == Common::kPlatformSegaCD) {
+			memset(segaCharBuf, 0, 32);
+			_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_setTextBuffer(0, 0);
+		} else if (cursorState) {
 			_screen->printText(sufx, (x + pos) << 3, y, textColor1, cursorColor);
-		else
+		} else {
 			_screen->printShadedText(sufx, (x + pos) << 3, y, textColor1, textColor2, _vm->guiSettings()->colors.guiColorBlack);
+		}
 		_screen->updateScreen();
 
 	} while (_keyPressed.keycode != Common::KEYCODE_RETURN && _keyPressed.keycode != Common::KEYCODE_ESCAPE && !_vm->shouldQuit());
 
+	delete[] segaCharBuf;
+
 	lolKeymap->setEnabled(true);
 
 	return _keyPressed.keycode == Common::KEYCODE_ESCAPE ? -1 : len;
 }
 
-int GUI_EoB::checkKatakanaSelection() {
+int GUI_EoB::checkClickableCharactersSelection() {
 	if (_vm->_flags.platform != Common::kPlatformFMTowns)
 		return 0;
 
@@ -2893,8 +2923,8 @@ int GUI_EoB::checkKatakanaSelection() {
 			int lineOffs = (y - 112) >> 4;
 			int column = (x - 152) >> 2;
 
-			_csjis[0] = _vm->_textInputCharacterLines[_currentKanaPage * 4 + lineOffs][column];
-			_csjis[1] = _vm->_textInputCharacterLines[_currentKanaPage * 4 + lineOffs][column + 1];
+			_csjis[0] = _vm->_textInputCharacterLines[_clickableCharactersPage * 4 + lineOffs][column];
+			_csjis[1] = _vm->_textInputCharacterLines[_clickableCharactersPage * 4 + lineOffs][column + 1];
 
 			if (_csjis[0] != '\x81' || _csjis[1] != '\x40') {
 				highlight = lineOffs << 8 | column;
@@ -2930,8 +2960,8 @@ int GUI_EoB::checkKatakanaSelection() {
 			_screen->printShadedText(_vm->_textInputSelectStrings[_menuCur & 3], kanaSelXCrds[_menuCur & 3], 176, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
 		} else {
 			char osjis[3];
-			osjis[0] = _vm->_textInputCharacterLines[_currentKanaPage * 4 + (_menuCur >> 8)][_menuCur & 0xFF];
-			osjis[1] = _vm->_textInputCharacterLines[_currentKanaPage * 4 + (_menuCur >> 8)][(_menuCur & 0xFF) + 1];
+			osjis[0] = _vm->_textInputCharacterLines[_clickableCharactersPage * 4 + (_menuCur >> 8)][_menuCur & 0xFF];
+			osjis[1] = _vm->_textInputCharacterLines[_clickableCharactersPage * 4 + (_menuCur >> 8)][(_menuCur & 0xFF) + 1];
 			osjis[2] = 0;
 			_screen->printShadedText(osjis, 152 + ((_menuCur & 0xFF) << 2), 112 + ((_menuCur >> 4) & ~0x0F), _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
 		}
@@ -2943,7 +2973,7 @@ int GUI_EoB::checkKatakanaSelection() {
 		if (highlight & 0x400) {
 			switch (highlight & 3) {
 			case 0:
-				printKatakanaOptions((_currentKanaPage + 1) % 3);
+				printClickableCharacters((_clickableCharactersPage + 1) % 3);
 				break;
 			case 1:
 				_keyPressed.keycode = Common::KEYCODE_RETURN;
@@ -2964,11 +2994,11 @@ int GUI_EoB::checkKatakanaSelection() {
 	return in;
 }
 
-void GUI_EoB::printKatakanaOptions(int page) {
+void GUI_EoB::printClickableCharacters(int page) {
 	if (_vm->_flags.platform != Common::kPlatformFMTowns)
 		return;
 
-	_currentKanaPage = page;
+	_clickableCharactersPage = page;
 	_screen->copyRegion(160, 44, 144, 108, 160, 84, 2, 0, Screen::CR_NO_P_CHECK);
 	for (int i = 0; i < 4; i++)
 		_screen->printShadedText(_vm->_textInputCharacterLines[page * 4 + i], 152, (i << 4) + 112, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
@@ -3253,7 +3283,7 @@ int GUI_EoB::selectSaveSlotDialog(int x, int y, int id) {
 			int sli = (newHighlight == _numSlotsVisible) ? _savegameOffset : (_savegameOffset + newHighlight);
 			if (_vm->_flags.platform == Common::kPlatformSegaCD) {
 				_screen->sega_clearTextBuffer(0);
-				_vm->_txt->printShadowedText(Common::String::format("%03d/989", sli).c_str(), 0, 0, 0xFF, 0xCC, -1, -1, 0, false);
+				_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(Screen_EoB::kSegaRenderPage);
 				_screen->copyRegion(_saveSlotX + 8, _saveSlotY + 152, _saveSlotX + 8, _saveSlotY + 152, 56, 8, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
@@ -3444,7 +3474,7 @@ void GUI_EoB::runMemorizePrayMenu(int charIndex, int spellType) {
 			updateDesc = false;
 			if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
 				_screen->sega_clearTextBuffer(0);
-				_vm->_txt->printShadowedText(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);
+				_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);
 			} else {
diff --git a/engines/kyra/gui/gui_eob.h b/engines/kyra/gui/gui_eob.h
index cf84be54c5..d93cdeb077 100644
--- a/engines/kyra/gui/gui_eob.h
+++ b/engines/kyra/gui/gui_eob.h
@@ -55,8 +55,8 @@ public:
 	int processButtonList(Button *buttonList, uint16 inputFlags, int8 mouseWheel) override;
 
 	// Non button based menu handling (main menu, character generation)
-	void simpleMenu_setup(int sd, int maxItem, const char *const *strings, int32 menuItemsMask, int unk, int lineSpacing);
-	int simpleMenu_process(int sd, const char *const *strings, void *b, int32 menuItemsMask, int unk);
+	void simpleMenu_setup(int sd, int maxItem, const char *const *strings, int32 menuItemsMask, int itemOffset, int lineSpacing);
+	int simpleMenu_process(int sd, const char *const *strings, void *b, int32 menuItemsMask, int itemOffset);
 
 	// Button based menus (camp menu, load menu)
 	virtual void runCampMenu();
@@ -87,6 +87,10 @@ protected:
 	char** _saveSlotStringsTemp;
 	int16 _saveSlotX;
 	int16 _saveSlotY;
+	int _menuCur;
+
+	int _clickableCharactersPage;;
+	char _csjis[3];
 
 	Screen_EoB *_screen;
 
@@ -150,7 +154,6 @@ private:
 	uint32 _paladinSpellAvltyFlags;
 	bool _needRest;
 
-	int _menuCur;
 	int _menuNumItems;
 	bool _charSelectRedraw;
 
@@ -167,12 +170,9 @@ private:
 	static const uint8 _highlightColorTablePC98[];
 	static const uint8 _highlightColorTableSegaCD[];
 
-	// FM-Towns specific
-	int checkKatakanaSelection();
-	void printKatakanaOptions(int page);
-
-	int _currentKanaPage;
-	char _csjis[3];
+	// FM-Towns / SegaCD specific
+	virtual int checkClickableCharactersSelection();
+	virtual void printClickableCharacters(int page);
 };
 
 } // End of namespace Kyra
diff --git a/engines/kyra/gui/gui_eob_segacd.cpp b/engines/kyra/gui/gui_eob_segacd.cpp
index 3badadaa13..b8f002979f 100644
--- a/engines/kyra/gui/gui_eob_segacd.cpp
+++ b/engines/kyra/gui/gui_eob_segacd.cpp
@@ -273,7 +273,7 @@ void EoBEngine::gui_displayMap() {
 	_screen->sega_clearTextBuffer(0);
 
 	_screen->sega_clearTextBuffer(0);
-	_txt->printShadowedText(_mapStrings2[_currentLevel - 1], 0, 0, 0xFF, 0, 64, 16, 0, false);
+	_txt->printShadedText(_mapStrings2[_currentLevel - 1], 0, 0, 0xFF, 0, 64, 16, 0, false);
 	_screen->sega_loadTextBufferToVRAM(0, 0x7C20, 512);
 	r->fillRectWithTiles(0, 31, 16, 8, 2, 0x63E1, true);
 
@@ -506,7 +506,7 @@ void EoBEngine::makeNameShapes(int charId) {
 			_screen->sega_getRenderer()->loadToVRAM(in + 27424 - _characters[i].portrait * 224, 224, 0x3F00 + i * 0xE0);
 			_screen->sega_getRenderer()->fillRectWithTiles(0, 0, i << 1, 7, 1, 0x61F8 + i * 7, true);
 		} else {
-			_txt->printShadowedText(_characters[i].name, 0, i << 4, 0xFF, 0xCC);
+			_txt->printShadedText(_characters[i].name, 0, i << 4, 0xFF, 0xCC);
 		}
 	}
 	delete[] in;
@@ -567,13 +567,13 @@ void EoBEngine::drawMapButton(const char *str, int x, int y) {
 	_screen->sega_drawClippedLine(8, 9, x, y, 64, 14, 0x99);
 	_screen->sega_drawClippedLine(8, 9, x, y + 1, 63, 13, 0xBB);
 	_screen->sega_drawClippedLine(8, 9, x + 1, y + 1, 62, 12, 0xAA);
-	_txt->printShadowedText(str, x + 14, y + 1, 0xFF, 0xCC, 64, 72, 0, false);
+	_txt->printShadedText(str, x + 14, y + 1, 0xFF, 0xCC, 64, 72, 0, false);
 }
 
 void EoBEngine::drawMapPage(int level) {
 	_screen->sega_clearTextBuffer(0);
 	int cs = _screen->setFontStyles(_screen->_currentFont, (_flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat) | Font::kStyleNarrow1);
-	_txt->printShadowedText(_mapStrings3[level - 1], 0, 0, 0xCC, 0, 48, 16, 0, false);
+	_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();
@@ -624,7 +624,7 @@ void EoBEngine::drawDialogueButtons() {
 		_screen->sega_drawClippedLine(38, 6, _dialogueButtonPosX[i], _dialogueButtonPosY[i], 90, 14, 0x99);
 		_screen->sega_drawClippedLine(38, 6, _dialogueButtonPosX[i], _dialogueButtonPosY[i] + 1, 89, 13, 0xBB);
 		_screen->sega_drawClippedLine(38, 6, _dialogueButtonPosX[i] + 1, _dialogueButtonPosY[i] + 1, 88, 12, 0xAA);
-		_txt->printShadowedText(_dialogueButtonString[i], _dialogueButtonPosX[i] + (_dialogueButtonWidth >> 1) - MIN<int>(_dialogueButtonWidth, _screen->getTextWidth(_dialogueButtonString[i])) / 2,
+		_txt->printShadedText(_dialogueButtonString[i], _dialogueButtonPosX[i] + (_dialogueButtonWidth >> 1) - MIN<int>(_dialogueButtonWidth, _screen->getTextWidth(_dialogueButtonString[i])) / 2,
 			_dialogueButtonPosY[i] + 1, _dialogueHighlightedButton == i ? _dialogueButtonLabelColor1 : _dialogueButtonLabelColor2, 0xEE, 304, 48, 0, false);
 		_screen->setFontStyles(_screen->_currentFont, cs);
 	}
@@ -671,7 +671,7 @@ void GUI_EoB_SegaCD::initMemorizePrayMenu() {
 	_screen->sega_getRenderer()->memsetVRAM(0x5560, 0, 1280);
 	_screen->sega_getRenderer()->loadToVRAM(&_campMenu[0x87C0], 4992, 0x3CE0);
 	_screen->sega_clearTextBuffer(0);
-	_vm->_txt->printShadowedText(getMenuString(37), 0, 2, 0xFF, 0xCC, 160, 16, 0, false);
+	_vm->_txt->printShadedText(getMenuString(37), 0, 2, 0xFF, 0xCC, 160, 16, 0, false);
 	_screen->sega_loadTextBufferToVRAM(0, 0x5060, 2560);
 	_screen->sega_getRenderer()->render(0, 1, 4, 20, 2);
 }
@@ -687,7 +687,7 @@ void GUI_EoB_SegaCD::drawSaveSlotDialog(int x, int y, int id) {
 	_saveLoadCancelButton->x = ((const EoBMenuButtonDef*)_saveLoadCancelButton->extButtonDef)->x + x - (x ? 8 : 0);
 	_saveLoadCancelButton->y = ((const EoBMenuButtonDef*)_saveLoadCancelButton->extButtonDef)->y + y;
 	int cs = _screen->setFontStyles(_screen->_currentFont, _vm->gameFlags().lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat);
-	_vm->_txt->printShadowedText(_vm->_saveLoadStrings[2 + id], 0, 3, 0xFF, 0xCC, 160, 16, 0, false);
+	_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);
@@ -702,7 +702,7 @@ bool GUI_EoB_SegaCD::confirmDialogue(int id) {
 		_screen->_charSpacing = 1;
 	}
 	cs = _screen->setFontStyles(_screen->_currentFont, cs);
-	_vm->_txt->printShadowedText(getMenuString(id), 0, 3, 0xFF, 0xCC, 160, 40, 0, false);
+	_vm->_txt->printShadedText(getMenuString(id), 0, 3, 0xFF, 0xCC, 160, 40, 0, false);
 	_screen->_charSpacing = 0;
 	_screen->setFontStyles(_screen->_currentFont, cs);
 
@@ -772,7 +772,7 @@ void GUI_EoB_SegaCD::displayTextBox(int id, int textColor, bool wait) {
 	if (id == 23 || id == 26 || id == 49)
 		cs |= Font::kStyleNarrow2;
 	cs = _screen->setFontStyles(_screen->_currentFont, cs);
-	_vm->_txt->printShadowedText(getMenuString(id), 0, 0, textColor, 0xCC, 160, 40, 0, false);
+	_vm->_txt->printShadedText(getMenuString(id), 0, 0, textColor, 0xCC, 160, 40, 0, false);
 	_screen->sega_loadTextBufferToVRAM(0, 0x5060, 3200);
 	_screen->setFontStyles(_screen->_currentFont, cs);
 	_screen->sega_getRenderer()->fillRectWithTiles(0, 1, 6, 20, 5, 0x6283, true);
@@ -811,7 +811,7 @@ void GUI_EoB_SegaCD::drawSaveSlotButton(int slot, int redrawBox, bool highlight)
 
 	_screen->sega_getRenderer()->fillRectWithTiles(0, (_saveSlotX >> 3) + (_saveSlotX ? 1 : 2), (_saveSlotY >> 3) + (_saveSlotY ? 6 : 7) + (slot << 1), 3, 2, 0x41E7 + slot * 12 + (redrawBox == 2 ? 6 : 0), true);
 	_screen->sega_clearTextBuffer(0);
-	_vm->_txt->printShadowedText(slot < 5 ? _saveSlotStringsTemp[slot] : _vm->_saveLoadStrings[0], 0, (slot << 4) + (slot < 5 ? 0 : 2), highlight ? 0x55 : 0xFF, 0xCC, 121, 80, 0, false);
+	_vm->_txt->printShadedText(slot < 5 ? _saveSlotStringsTemp[slot] : _vm->_saveLoadStrings[0], 0, (slot << 4) + (slot < 5 ? 0 : 2), highlight ? 0x55 : 0xFF, 0xCC, 121, 80, 0, false);
 	_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);
 }
@@ -874,9 +874,9 @@ void GUI_EoB_SegaCD::restParty_updateRestTime(int hours, bool init) {
 	_screen->sega_clearTextBuffer(0);
 
 	int cs = _screen->setFontStyles(_screen->_currentFont, _vm->gameFlags().lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat);
-	_vm->_txt->printShadowedText(getMenuString(42), 0, 0, 0xFF, 0xCC, 160, 48, 0, false);
-	_vm->_txt->printShadowedText(_vm->_menuStringsRest2[3], 0, 16, 0xFF, 0xCC, 160, 48, 0, false);
-	_vm->_txt->printShadowedText(Common::String::format("%3d", hours).c_str(), 117, 16, 0xFF, 0xCC, 160, 48, 0, false);
+	_vm->_txt->printShadedText(getMenuString(42), 0, 0, 0xFF, 0xCC, 160, 48, 0, false);
+	_vm->_txt->printShadedText(_vm->_menuStringsRest2[3], 0, 16, 0xFF, 0xCC, 160, 48, 0, false);
+	_vm->_txt->printShadedText(Common::String::format("%3d", hours).c_str(), 117, 16, 0xFF, 0xCC, 160, 48, 0, false);
 	_screen->setFontStyles(_screen->_currentFont, cs);
 
 	_screen->sega_loadTextBufferToVRAM(0, 0x5060, 5120);
@@ -887,6 +887,106 @@ void GUI_EoB_SegaCD::restParty_updateRestTime(int hours, bool init) {
 	_vm->delay(160);
 }
 
+int GUI_EoB_SegaCD::checkClickableCharactersSelection() {
+	Common::Point mousePos = _vm->getMousePos();
+	int highlight = -1;
+
+	for (int i = 0; i < 60; ++i) {
+		int x = (i % 12) * 12 + 152;
+		int y = (i / 12) * 12 + 96;
+		if (!_vm->posWithinRect(mousePos.x, mousePos.y, x, y, x + 11, y + 7))
+			continue;
+		highlight = i;
+		break;
+	}
+
+	if (highlight == -1) {
+		for (int i = 0; i < 3; ++i) {
+			int x = 200 + i * 36;
+			if (!_vm->posWithinRect(mousePos.x, mousePos.y, x, 164, x + _screen->getTextWidth(_vm->_textInputSelectStrings[i]) - 1, 171))
+				continue;
+			highlight = 200 + i;
+			break;
+		}
+	}
+
+	if (highlight != _menuCur) {
+		printClickableCharacters(_clickableCharactersPage);
+		if (highlight != -1)
+			printClickableCharacter(highlight, 0x55);
+		_screen->sega_getRenderer()->render(0, 18, 10, 20, 14);
+		_menuCur = highlight;
+	}
+
+	_csjis[0] = _csjis[1] = _csjis[2] = 0;
+	int in = 0;
+	for (Common::List<KyraEngine_v1::Event>::const_iterator evt = _vm->_eventList.begin(); evt != _vm->_eventList.end(); ++evt) {
+		if (evt->event.type == Common::EVENT_LBUTTONDOWN)
+			in = 1;
+	}
+
+	if (in && highlight != -1) {
+		_menuCur = -1;
+		switch (highlight) {
+		case 200:
+			printClickableCharacters(_clickableCharactersPage ^ 1);
+			break;
+		case 201:
+			_keyPressed.keycode = Common::KEYCODE_BACKSPACE;
+			break;
+		case 202:
+			_keyPressed.keycode = Common::KEYCODE_RETURN;
+			break;
+		default:
+			_csjis[0] = fetchClickableCharacter(highlight);
+			return _csjis[0];
+		}
+	}
+
+	return in;
+}
+
+void GUI_EoB_SegaCD::printClickableCharacters(int page) {
+	if (_clickableCharactersPage != page) {
+		_clickableCharactersPage = page;
+		assert(_vm->_wndBackgrnd);
+		_screen->sega_loadTextBackground(_vm->_wndBackgrnd, 10240);
+	}
+	for (int i = 0; i < 60; ++i)
+		printClickableCharacter(i, 0xFF);
+	for (int i = 200; i < 203; ++i)
+		printClickableCharacter(i, 0xFF);
+	_screen->sega_getRenderer()->render(0, 18, 10, 20, 14);
+}
+
+void GUI_EoB_SegaCD::printClickableCharacter(int id, int col) {
+	char ch[3] = "\0\0";
+
+	if (id < 60) {
+		ch[0] = fetchClickableCharacter(id);
+		_vm->_txt->printShadedText(ch, (id % 12) * 12 + 12, (id / 12) * 12 + 32, col);
+	} else if (id >= 200) {
+		id -= 200;
+		_vm->_txt->printShadedText(_vm->_textInputSelectStrings[id], 60 + id * 36, 100, col);
+	}
+}
+
+char GUI_EoB_SegaCD::fetchClickableCharacter(int id) const {
+	if (id >= 200)
+		return (char)id;
+	if (id >= 60)
+		return 0;
+
+	char c = _vm->_textInputCharacterLines[_clickableCharactersPage][id];
+	if (_clickableCharactersPage) {
+		if (c > 159 && c < 192)
+			c -= 32;
+		else if (c > 191 && c < 224)
+			c += 32;
+	}
+	return c;
+}
+
 const GUI_EoB_SegaCD::MenuButtonTiles GUI_EoB_SegaCD::_menuButtonTiles[35] = {
 	{ 0x01e7, 0x0000 },	{ 0x01fb, 0x0028 },	{ 0x020f, 0x0050 }, { 0x0223, 0x0078 },
 	{ 0x0237, 0x00a0 }, { 0x0000, 0x0000 },	{ 0x01cf, 0x01c8 },	{ 0x025f, 0x0118 },
diff --git a/engines/kyra/gui/gui_eob_segacd.h b/engines/kyra/gui/gui_eob_segacd.h
index 4dea584fc3..2800a81ed0 100644
--- a/engines/kyra/gui/gui_eob_segacd.h
+++ b/engines/kyra/gui/gui_eob_segacd.h
@@ -49,6 +49,11 @@ private:
 	void updateOptionsStrings() override;
 	void restParty_updateRestTime(int hours, bool init) override;
 
+	int checkClickableCharactersSelection() override;
+	void printClickableCharacters(int page) override;
+	void printClickableCharacter(int id, int col);
+	char fetchClickableCharacter(int id) const;
+
 	const uint8 *_campMenu;
 	Button* _saveLoadCancelButton;
 	EoBEngine *_vm;
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index b21d598992..a0599fc504 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -524,7 +524,8 @@ void EoBCoreEngine::initStaticResource() {
 	// Hard code the following strings, since EOB I doesn't have them in the original.
 	// EOB I doesn't have load and save menus, because there is only one single
 	// save slot. Instead of emulating this we provide a menu similiar to EOB II.
-	// EOB I actually has save/load menus. I supply the strings here, too... 
+	// EOB I SegaCD actually has save/load menus with more than 1 slot if there is
+	// a RAM cart present). I supply the strings here, too... 
 
 	static const char *const saveLoadStrings[7][4] = {
 		{   "Cancel",   "Empty Slot",		"Save Game",    "Load Game"     },
diff --git a/engines/kyra/sequence/seqplayer_eob_segacd.cpp b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
index 104e050942..9302c9c023 100644
--- a/engines/kyra/sequence/seqplayer_eob_segacd.cpp
+++ b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
@@ -418,7 +418,7 @@ void SegaSequencePlayer::s_displayTextJp(const uint8 *pos) {
 		y = 16;
 	}
 
-	_vm->_txt->printShadowedText(str, x, y, -1, 0xEE);
+	_vm->_txt->printShadedText(str, x, y, -1, 0xEE);
 }
 
 void SegaSequencePlayer::s_fadeToNeutral(const uint8 *pos) {
@@ -608,7 +608,7 @@ void SegaSequencePlayer::s_displayTextEn(const uint8 *pos) {
 
 	if (_playingID >= 55) {
 		int cs = _screen->setFontStyles(_screen->_currentFont, Font::kStyleForceTwoByte | Font::kStyleFat);
-		_vm->_txt->printShadowedText(str, 0, 0, -1, 0xEE);
+		_vm->_txt->printShadedText(str, 0, 0, -1, 0xEE);
 		_screen->setFontStyles(_screen->_currentFont, cs);
 	} else {
 		int x = 0;
@@ -619,7 +619,7 @@ void SegaSequencePlayer::s_displayTextEn(const uint8 *pos) {
 			y = 16;
 		}
 
-		_vm->_txt->printShadowedText(str, x, y, -1, 0xEE);
+		_vm->_txt->printShadedText(str, x, y, -1, 0xEE);
 	}
 }
 
diff --git a/engines/kyra/sequence/sequences_eob.cpp b/engines/kyra/sequence/sequences_eob.cpp
index 751efe7736..f47a1f4df3 100644
--- a/engines/kyra/sequence/sequences_eob.cpp
+++ b/engines/kyra/sequence/sequences_eob.cpp
@@ -161,7 +161,6 @@ private:
 	const uint8 _palCycleDelay;
 };
 
-
 class EoBAmigaFinalePlayer : public EoBSeqPlayerCommon {
 public:
 	EoBAmigaFinalePlayer(EoBEngine *vm, Screen_EoB *screen);
@@ -2174,7 +2173,7 @@ int EoBEngine::mainMenu() {
 				_screen->sega_getRenderer()->fillRectWithTiles(1, 7, 25, 25, 1, 0x4E3, true);
 				_screen->sega_getRenderer()->fillRectWithTiles(1, 6, 21, 1, 5, 0);
 				_screen->setFontStyles(_screen->_currentFont, Font::kStyleNarrow1);
-				_txt->printShadowedText(versionString.c_str(), 200 - versionString.size() * 8, _ttlCfg->versionStrYOffs, 0x88);
+				_txt->printShadedText(versionString.c_str(), 200 - versionString.size() * 8, _ttlCfg->versionStrYOffs, 0x88);
 				_screen->setFontStyles(_screen->_currentFont, _flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat);
 			} else {
 				_screen->_curPage = 2;
@@ -2513,6 +2512,7 @@ void EoBEngine::seq_segaOpeningCredits(bool jumpToTitle) {
 		delay(500);
 	}
 
+	delete[] scrollTable;
 	_screen->sega_fadeToBlack(0);
 	r->setPlaneTableLocation(SegaRenderer::kPlaneA, 0xC000);
 	r->setupPlaneAB(512, 512);
@@ -2619,7 +2619,7 @@ void EoBEngine::seq_segaFinalCredits() {
 					pos++;
 
 				_screen->setFontStyles(_screen->_currentFont, styles);
-				_txt->printShadowedText(pos, 120 - (_screen->getTextWidth(pos) >> 1), 0, 0xFF, 0xCC, -1, -1, 0, false);
+				_txt->printShadedText(pos, 120 - (_screen->getTextWidth(pos) >> 1), 0, 0xFF, 0xCC, -1, -1, 0, false);
 				curStr++;
 			}
 		} else {
@@ -2667,17 +2667,17 @@ void EoBEngine::seq_segaShowStats() {
 	int styles = _flags.lang == Common::JA_JPN ? Font::kStyleFixedWidth : Font::kStyleForceTwoByte | Font::kStyleFat;
 	int cs = _screen->setFontStyles(_screen->_currentFont, styles);
 
-	_txt->printShadowedText(_finBonusStrings[2], 90, 8, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadedText(_finBonusStrings[2], 90, 8, 0xFF, 0x00, -1, -1, 0, false);
 
 	styles |= Font::kStyleNarrow2;
 	_screen->setFontStyles(_screen->_currentFont, styles);
 
-	_txt->printShadowedText(_finBonusStrings[3], 48, 28, 0xFF, 0x00, -1, -1, 0, false);
-	_txt->printShadowedText(_finBonusStrings[4], 48, 40, 0xFF, 0x00, -1, -1, 0, false);
-	_txt->printShadowedText(_finBonusStrings[5], 48, 52, 0xFF, 0x00, -1, -1, 0, false);
-	_txt->printShadowedText(_finBonusStrings[6], 48, 64, 0xFF, 0x00, -1, -1, 0, false);
-	_txt->printShadowedText(_finBonusStrings[7], 48, 76, 0xFF, 0x00, -1, -1, 0, false);
-	_txt->printShadowedText(_finBonusStrings[8], 48, 88, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadedText(_finBonusStrings[3], 48, 28, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadedText(_finBonusStrings[4], 48, 40, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadedText(_finBonusStrings[5], 48, 52, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadedText(_finBonusStrings[6], 48, 64, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadedText(_finBonusStrings[7], 48, 76, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadedText(_finBonusStrings[8], 48, 88, 0xFF, 0x00, -1, -1, 0, false);
 
 	styles &= ~(Font::kStyleNarrow2);
 	_screen->setFontStyles(_screen->_currentFont, styles);
@@ -2690,12 +2690,12 @@ void EoBEngine::seq_segaShowStats() {
 			++specialSearches;
 	}
 
-	_txt->printShadowedText(Common::String::format("%u:%02u:%02u", _totalPlaySecs / 3600, _totalPlaySecs / 60, _totalPlaySecs).c_str(), 148, 28, 0xFF, 0x00, -1, -1, 0, false);
-	_txt->printShadowedText(Common::String::format("%u", _totalEnemiesKilled).c_str(), 148, 40, 0xFF, 0x00, -1, -1, 0, false);
-	_txt->printShadowedText(Common::String::format("%u", _totalSteps).c_str(), 148, 52, 0xFF, 0x00, -1, -1, 0, false);
-	_txt->printShadowedText(Common::String::format("%u(%u%%)", partyArrows, partyArrows * 100 / 26).c_str(), 148, 64, 0xFF, 0x00, -1, -1, 0, false);
-	_txt->printShadowedText(Common::String::format("%u(%u%%)", numMaps, numMaps * 100 / 12).c_str(), 148, 76, 0xFF, 0x00, -1, -1, 0, false);
-	_txt->printShadowedText(Common::String::format("%u(%u%%)", specialSearches, specialSearches * 100 / 12).c_str(), 148, 88, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadedText(Common::String::format("%u:%02u:%02u", _totalPlaySecs / 3600, _totalPlaySecs / 60, _totalPlaySecs).c_str(), 148, 28, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadedText(Common::String::format("%u", _totalEnemiesKilled).c_str(), 148, 40, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadedText(Common::String::format("%u", _totalSteps).c_str(), 148, 52, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadedText(Common::String::format("%u(%u%%)", partyArrows, partyArrows * 100 / 26).c_str(), 148, 64, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadedText(Common::String::format("%u(%u%%)", numMaps, numMaps * 100 / 12).c_str(), 148, 76, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadedText(Common::String::format("%u(%u%%)", specialSearches, specialSearches * 100 / 12).c_str(), 148, 88, 0xFF, 0x00, -1, -1, 0, false);
 
 	if (checkScriptFlags(0x1FFE)) {
 		const char pwgen[] = "A15BZFQ3CDXYEKNM279GHIUSJLR84P6T";
@@ -2709,9 +2709,9 @@ void EoBEngine::seq_segaShowStats() {
 		}
 		password[5] = pwgen[v];
 
-		_txt->printShadowedText(_finBonusStrings[0], 30, 108, 0x22, 0x00, -1, -1, 0, false);
-		_txt->printShadowedText(_finBonusStrings[1], 30, 132, 0x22, 0x00, -1, -1, 0, false);
-		_txt->printShadowedText(password, 140, 156, 0xFF, 0x00, -1, -1, 0, false);
+		_txt->printShadedText(_finBonusStrings[0], 30, 108, 0x22, 0x00, -1, -1, 0, false);
+		_txt->printShadedText(_finBonusStrings[1], 30, 132, 0x22, 0x00, -1, -1, 0, false);
+		_txt->printShadedText(password, 140, 156, 0xFF, 0x00, -1, -1, 0, false);
 	}
 
 	_screen->sega_loadTextBufferToVRAM(0, 32, 28160);
diff --git a/engines/kyra/text/text_eob_segacd.cpp b/engines/kyra/text/text_eob_segacd.cpp
index 646eddc262..aa8bbe6f8b 100644
--- a/engines/kyra/text/text_eob_segacd.cpp
+++ b/engines/kyra/text/text_eob_segacd.cpp
@@ -56,13 +56,13 @@ void TextDisplayer_SegaCD::printDialogueText(const char *str, bool wait) {
 	clearDim(_curDim);
 
 	if (wait) {
-		printShadowedText(str, 32, 12);
+		printShadedText(str, 32, 12);
 		_engine->resetSkipFlag();
 		_renderer->render(0);
 		_screen->updateScreen();
 		_engine->delay(500);
 	} else {
-		printShadowedText(str, 0, 0);
+		printShadedText(str, 0, 0);
 		_renderer->render(0);
 		_screen->updateScreen();
 	}
@@ -70,7 +70,7 @@ void TextDisplayer_SegaCD::printDialogueText(const char *str, bool wait) {
 	_screen->setFontStyles(Screen::FID_8_FNT, cs);
 }
 
-void TextDisplayer_SegaCD::printShadowedText(const char *str, int x, int y, int textColor, int shadowColor, int pitchW, int pitchH, int marginRight, bool screenUpdate) {
+void TextDisplayer_SegaCD::printShadedText(const char *str, int x, int y, int textColor, int shadowColor, int pitchW, int pitchH, int marginRight, bool screenUpdate) {
 	const ScreenDim *s = &_dimTable[_curDim];
 	if (x == -1)
 		x = s->sx;
@@ -92,8 +92,8 @@ void TextDisplayer_SegaCD::printShadowedText(const char *str, int x, int y, int
 		return;
 
 	if (s->unkE) {
-		for (int i = 0; i < pitchH >> 3; ++i)
-			_screen->sega_loadTextBufferToVRAM(i * (pitchW >> 3), (s->unkC & 0x7FF) << 5, (pitchW * pitchH) >> 1);
+		for (int i = 0; i < (pitchH >> 3); ++i)
+			_screen->sega_loadTextBufferToVRAM(i * (pitchW << 2), ((s->unkC & 0x7FF) + i * s->unkE) << 5, pitchW << 2);
 	} else {
 		_screen->sega_loadTextBufferToVRAM(0, (s->unkC & 0x7FF) << 5, (pitchW * pitchH) >> 1);
 	}
@@ -157,14 +157,14 @@ void TextDisplayer_SegaCD::displayText(char *str, ...) {
 				}
 			}			
 
-			printShadowedText(tmp, _curPosX, _curPosY, _textColor);
+			printShadedText(tmp, _curPosX, _curPosY, _textColor);
 			_curPosX += _screen->getTextWidth(tmp);
 			updated = true;
 		}		
 	}
 
 	if (!updated)
-		printShadowedText("", _curPosX, _curPosY, _textColor);
+		printShadedText("", _curPosX, _curPosY, _textColor);
 
 	if (tc != -1)
 		SWAP(_textColor, tc);
@@ -239,7 +239,7 @@ void TextDisplayer_SegaCD::copyTextBufferLine(uint16 srcY, uint16 dstY, uint16 l
 
 const ScreenDim TextDisplayer_SegaCD::_dimTable[6] = {
 	{ 0x0001, 0x0017, 0x0118, 0x0018, 0xff, 0x44, 0x2597, 0x0000 },
-	{ 0x0012, 0x0009, 0x00a0, 0x0080, 0xff, 0x00, 0x0153, 0x0028 },
+	{ 0x0012, 0x0009, 0x00a0, 0x0080, 0xff, 0x99, 0x0153, 0x0028 },
 	{ 0x0001, 0x0014, 0x0130, 0x0030, 0xff, 0xee, 0xe51c, 0x0000 },
 	{ 0x0001, 0x0017, 0x00D0, 0x0030, 0xff, 0x00, 0x0461, 0x0000 },
 	{ 0x0000, 0x0000, 0x00F0, 0x0100, 0xff, 0x00, 0x600A, 0x0000 },
diff --git a/engines/kyra/text/text_eob_segacd.h b/engines/kyra/text/text_eob_segacd.h
index 1b67fd39f0..6ac8b639e9 100644
--- a/engines/kyra/text/text_eob_segacd.h
+++ b/engines/kyra/text/text_eob_segacd.h
@@ -39,7 +39,7 @@ public:
 
 	void printDialogueText(int id, const char *string1, const char *string2) override;
 	void printDialogueText(const char *str, bool wait = false) override;
-	void printShadowedText(const char *str, int x = -1, int y = -1, int textColor = -1, int shadowColor = -1, int pitchW = -1, int pitchH = -1, int marginRight = 0, bool screenUpdate = true) override;
+	void printShadedText(const char *str, int x = -1, int y = -1, int textColor = -1, int shadowColor = -1, int pitchW = -1, int pitchH = -1, int marginRight = 0, bool screenUpdate = true) override;
 	int clearDim(int dim) override;
 
 private:
diff --git a/engines/kyra/text/text_rpg.h b/engines/kyra/text/text_rpg.h
index d4740f8a6a..76674a9236 100644
--- a/engines/kyra/text/text_rpg.h
+++ b/engines/kyra/text/text_rpg.h
@@ -42,7 +42,7 @@ public:
 	virtual void printDialogueText(int stringId, const char *pageBreakString, const char *pageBreakString2 = 0);
 	virtual void printDialogueText(const char *str, bool wait = false);
 	void printMessage(const char *str, int textColor = -1, ...);
-	virtual void printShadowedText(const char *str, int x = -1, int y = -1, int textColor = -1, int shadowColor = -1, int pitchW = -1, int pitchH = -1, int marginRight = 0, bool screenUpdate = true) {}
+	virtual void printShadedText(const char *str, int x = -1, int y = -1, int textColor = -1, int shadowColor = -1, int pitchW = -1, int pitchH = -1, int marginRight = 0, bool screenUpdate = true) {}
 
 	virtual int clearDim(int dim);
 	void clearCurDim();


Commit: d8bae9196cd5a7f54f7709fd6eb4d461c21423c2
    https://github.com/scummvm/scummvm/commit/d8bae9196cd5a7f54f7709fd6eb4d461c21423c2
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:20+02:00

Commit Message:
KYRA: (EOB/SegaCD) - fix save dialog glitch

Changed paths:
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/gui/gui_eob_segacd.cpp


diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 8acdce2ca5..a4d51743ef 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -3285,8 +3285,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(Screen_EoB::kSegaRenderPage);
-				_screen->copyRegion(_saveSlotX + 8, _saveSlotY + 152, _saveSlotX + 8, _saveSlotY + 152, 56, 8, Screen_EoB::kSegaRenderPage, 0, Screen::CR_NO_P_CHECK);
+				_screen->sega_getRenderer()->render(0, (_saveSlotX + 8) >> 3, (_saveSlotY + 152) >> 3, 7, 1);
 			} else {
 				Screen::FontId of = _screen->setFont(Screen::FID_6_FNT);
 				_screen->set16bitShadingLevel(4);
diff --git a/engines/kyra/gui/gui_eob_segacd.cpp b/engines/kyra/gui/gui_eob_segacd.cpp
index b8f002979f..a56eaf151f 100644
--- a/engines/kyra/gui/gui_eob_segacd.cpp
+++ b/engines/kyra/gui/gui_eob_segacd.cpp
@@ -776,7 +776,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, 0, 22, 20);
+	_screen->sega_getRenderer()->render(0, 0, 1, 22, 19);
 	_screen->updateScreen();
 	if (!wait)
 		return;


Commit: e190feda072d449e3ad69f747fc70337dffc68b1
    https://github.com/scummvm/scummvm/commit/e190feda072d449e3ad69f747fc70337dffc68b1
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:20+02:00

Commit Message:
KYRA: (EOB/SegaCD) - fix save slot naming glitch

Changed paths:
    engines/kyra/gui/gui_eob.cpp


diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index a4d51743ef..ec5e43bad6 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -3144,7 +3144,7 @@ bool GUI_EoB::runSaveMenu(int x, int y) {
 					of = _vm->screen()->setFont(Screen::FID_6_FNT);
 					y++;
 				} else if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
-					Common::strlcpy(_saveSlotStringsTemp[slot], Common::String::format("%s\r FLOOR %-2u %u:%02u", _vm->_characters[0].name, _vm->_currentLevel + 1, _vm->_totalPlaySecs / 3600, _vm->_totalPlaySecs / 60).c_str(), 25);
+					Common::strlcpy(_saveSlotStringsTemp[slot], Common::String::format("%s\r FLOOR %-2u %u:%02u", _vm->_characters[0].name, _vm->_currentLevel, _vm->_totalPlaySecs / 3600, _vm->_totalPlaySecs / 60).c_str(), 25);
 					in = strlen(_saveSlotStringsTemp[slot]);
 				} else {
 					in = getTextInput(_saveSlotStringsTemp[slot], x + 1, fy, 19, _vm->guiSettings()->colors.guiColorBlue, 0, _vm->guiSettings()->colors.guiColorDarkRed);


Commit: 9c1e8c99c69df01710122166547378289280ecfd
    https://github.com/scummvm/scummvm/commit/9c1e8c99c69df01710122166547378289280ecfd
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:20+02:00

Commit Message:
KYRA: fix various warnings

(gcc warnings, valgrind 'conditional jump depends on uninitialized data and some valid cppcheck warnings)

Changed paths:
    engines/kyra/engine/chargen.cpp
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/items_eob.cpp
    engines/kyra/engine/items_lol.cpp
    engines/kyra/graphics/screen_eob_segacd.cpp
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/gui/gui_eob.h
    engines/kyra/gui/gui_eob_segacd.cpp
    engines/kyra/sequence/sequences_hof.cpp
    engines/kyra/sound/drivers/audiomaster2.cpp
    engines/kyra/text/text_eob_segacd.cpp


diff --git a/engines/kyra/engine/chargen.cpp b/engines/kyra/engine/chargen.cpp
index cf7c5e16ad..a5071d73d9 100644
--- a/engines/kyra/engine/chargen.cpp
+++ b/engines/kyra/engine/chargen.cpp
@@ -111,7 +111,7 @@ private:
 	static const uint16 _chargenButtonKeyCodesFMTOWNS[];
 	static const CreatePartyModButton _chargenModButtons[];
 	static const EoBRect8 _chargenButtonBodyCoords[];
-	static const uint8 _chargenSegaButtonCoords[];
+	static const uint8 _chargenSegaButtonCoords[60];
 	static const int16 _chargenBoxX[];
 	static const int16 _chargenBoxY[];
 	static const int16 _chargenNameFieldX[];
@@ -1698,7 +1698,7 @@ const EoBRect8 CharacterGenerator::_chargenButtonBodyCoords[] = {
 	{ 0x14, 0x90, 0x0B, 0x10 }
 };
 
-const uint8 CharacterGenerator::_chargenSegaButtonCoords[] {
+const uint8 CharacterGenerator::_chargenSegaButtonCoords[60] = {
 	0x03, 0x17, 0x0b, 0x02, 0x00,
 	0x1c, 0x16, 0x05, 0x02, 0x2c,
 	0x12, 0x08, 0x04, 0x02, 0x40,
diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index 2246f79e24..566c420fcf 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -162,10 +162,15 @@ Common::Error EoBEngine::init() {
 		_gui = new GUI_EoB_SegaCD(this);
 		assert(_gui);
 		_playFldPattern2 = new uint16[1040];
+		memset(_playFldPattern2, 0, 1040 * sizeof(uint16));
 		_tempPattern = new uint16[924];
+		memset(_tempPattern, 0, 924 * sizeof(uint16));
 		_shakeBackBuffer1 = new uint8[120 * 6];
+		memset(_shakeBackBuffer1, 0, 120 * 6);
 		_shakeBackBuffer2 = new uint8[179 * 6];
+		memset(_shakeBackBuffer2, 0, 179 * 6);
 		_compassData = new uint8[0x5000];
+		memset(_compassData, 0, 0x5000);
 		_closeSpellbookAfterUse = false;
 	}
 
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index f3f413947b..1188db577e 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -468,12 +468,10 @@ Common::Error EoBCoreEngine::init() {
 	if (_flags.platform != Common::kPlatformSegaCD) {
 		_gui = new GUI_EoB(this);
 		assert(_gui);
-	}
-
-	if (_flags.platform != Common::kPlatformSegaCD) {
 		_txt = new TextDisplayer_rpg(this, _screen);
 		assert(_txt);
 	}
+
 	_inf = new EoBInfProcessor(this, _screen);
 	assert(_inf);
 	setDebugger(new Debugger_EoB(this));
diff --git a/engines/kyra/engine/items_eob.cpp b/engines/kyra/engine/items_eob.cpp
index 63abe0d2e1..07ebe9d4f6 100644
--- a/engines/kyra/engine/items_eob.cpp
+++ b/engines/kyra/engine/items_eob.cpp
@@ -78,7 +78,7 @@ void EoBCoreEngine::loadItemDefs() {
 			_items[i].value = (int8)*pos++;
 		}
 		_numItems += (temp / 14);
-		_items[22].nameUnid = _items[27].nameUnid = _items[28].nameUnid = _items[29].nameUnid = _items[29].nameUnid = 96;
+		_items[22].nameUnid = _items[27].nameUnid = _items[28].nameUnid = _items[29].nameUnid = _items[59].nameUnid = 96;
 	}
 
 	if (_itemNamesStatic) {
diff --git a/engines/kyra/engine/items_lol.cpp b/engines/kyra/engine/items_lol.cpp
index 446650d6e1..a31fd03a22 100644
--- a/engines/kyra/engine/items_lol.cpp
+++ b/engines/kyra/engine/items_lol.cpp
@@ -121,7 +121,7 @@ void LoLEngine::giveCredits(int credits, int redraw) {
 
 		if (redraw) {
 			gui_drawMoneyBox(6);
-			if (credits)
+			// if (credits)
 				delay(_tickLength, 1);
 		}
 		credits -= t;
diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index 2b379dd0f5..601279599f 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -507,8 +507,6 @@ void SegaRenderer::loadToVRAM(const void *data, uint16 dataSize, uint16 addr) {
 
 void SegaRenderer::loadStreamToVRAM(Common::SeekableReadStream *in, uint16 addr, bool compressedData) {
 	assert(in);
-	assert(addr <= 0xFFFF);
-
 	uint8 *dst = _vram + addr;
 
 	if (compressedData) {
@@ -934,49 +932,6 @@ template<bool hflip> void SegaRenderer::renderLineFragment(uint8 *dst, uint8 *ma
 
 #undef mRenderLineFragment
 
-/*void SegaRenderer::checkUpdateDirtyRects(int addr, int len) {
-	//void *tbl[] = { _vram, _hScrollTable, , _planes[kPlaneA].nameTable, _planes[kPlaneB].nameTable, _planes[kWindowPlane].nameTable };
-	addDirtyRect(0, 0, _screenW, _screenH);
-	/*uint8 *addE = &_vram[addr];
-	int x = 0;
-	int y = 0;
-	int w = 0;
-	int h = 0;
-
-	if (addE >= (uint8*)_planes[kPlaneA].nameTable && addE < (uint8*)_planes[kPlaneA].nameTable + _planes[kPlaneA].nameTableSize) {
-		x = ((addE - (uint8*)_planes[kPlaneA].nameTable) >> 1) % _pitch;
-		y = ((addE - (uint8*)_planes[kPlaneA].nameTable) >> 1) / _pitch;
-		w = len > _pitch;
-	}*/
-
-	//if (addr >= _hScrollTable && addr < _hScrollTable + 0x400);
-/*
-}
-
-void SegaRenderer::addDirtyRect(int x, int y, int w, int h) {
-	_drChain = new DRChainEntry(_drChain, x, y, w, h);
-}
-
-void SegaRenderer::sendDirtyRectsToScreen() {
-	for (DRChainEntry *e = _drChain; e; e = e->_next) {
-		int w = e->_rect.width();
-		int h = e->_rect.height();
-		if (w == _screenW && h == _screenH) {
-			_screen->_forceFullUpdate = true;
-			return;
-		}
-		_screen->addDirtyRect(e->_rect.left, e->_rect.top, w, h);
-	}
-}
-
-void SegaRenderer::clearDirtyRects() {
-	while (_drChain) {
-		DRChainEntry *e = _drChain->_next;
-		delete _drChain;
-		_drChain = e;
-	}
-}*/
-
 void SegaRenderer::initPrioRenderTask(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal, bool hflip) {
 	_prioChainEnd = new PrioTileRenderObj(_prioChainEnd, dst, mask, src, start, end, pal, hflip);
 	if (!_prioChainStart)
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index ec5e43bad6..eddc258dfc 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -2337,6 +2337,8 @@ void GUI_EoB::runCampMenu() {
 					newMenu = 2;
 					break;
 				}
+				// fall through
+
 			case 0x800C:			
 				if (lastMenu == 1 || lastMenu == 2)
 					newMenu = 0;
diff --git a/engines/kyra/gui/gui_eob.h b/engines/kyra/gui/gui_eob.h
index d93cdeb077..caf1dffdf3 100644
--- a/engines/kyra/gui/gui_eob.h
+++ b/engines/kyra/gui/gui_eob.h
@@ -89,7 +89,7 @@ protected:
 	int16 _saveSlotY;
 	int _menuCur;
 
-	int _clickableCharactersPage;;
+	int _clickableCharactersPage;
 	char _csjis[3];
 
 	Screen_EoB *_screen;
diff --git a/engines/kyra/gui/gui_eob_segacd.cpp b/engines/kyra/gui/gui_eob_segacd.cpp
index a56eaf151f..a7f955d97b 100644
--- a/engines/kyra/gui/gui_eob_segacd.cpp
+++ b/engines/kyra/gui/gui_eob_segacd.cpp
@@ -556,7 +556,7 @@ void EoBEngine::printStatsString(const char *str, int x, int y) {
 
 void EoBEngine::printSpellbookString(uint16 *dst, const char *str, uint16 ntbl) {
 	assert(str);
-	for (char c = *str++; c; c = *str++) {
+	for (uint8 c = (uint8)*str++; c; c = (uint8)*str++) {
 		if (c > 31 && c < 128)
 			*dst = ntbl + c - 32;
 		dst++;
@@ -977,14 +977,14 @@ char GUI_EoB_SegaCD::fetchClickableCharacter(int id) const {
 	if (id >= 60)
 		return 0;
 
-	char c = _vm->_textInputCharacterLines[_clickableCharactersPage][id];
+	uint8 c = (uint8)_vm->_textInputCharacterLines[_clickableCharactersPage][id];
 	if (_clickableCharactersPage) {
 		if (c > 159 && c < 192)
 			c -= 32;
 		else if (c > 191 && c < 224)
 			c += 32;
 	}
-	return c;
+	return (char)c;
 }
 
 const GUI_EoB_SegaCD::MenuButtonTiles GUI_EoB_SegaCD::_menuButtonTiles[35] = {
diff --git a/engines/kyra/sequence/sequences_hof.cpp b/engines/kyra/sequence/sequences_hof.cpp
index 3235df6d85..2bbb240949 100644
--- a/engines/kyra/sequence/sequences_hof.cpp
+++ b/engines/kyra/sequence/sequences_hof.cpp
@@ -376,6 +376,10 @@ SeqPlayer_HOF::SeqPlayer_HOF(KyraEngine_v1 *vm, Screen_v2 *screen, OSystem *syst
 	_countDownRemainder = 0;
 	_countDownLastUpdate = 0;
 
+	_talkieFinaleExtraFlag = false;
+	_target = kHoF;
+	_firstScene = _loopStartScene = 0;
+
 	int tempSize = 0;
 	_vm->resource()->unloadAllPakFiles();
 	_vm->resource()->loadPakFile(StaticResource::staticDataFilename());
diff --git a/engines/kyra/sound/drivers/audiomaster2.cpp b/engines/kyra/sound/drivers/audiomaster2.cpp
index 3e13a61015..fb3ccbbdd8 100644
--- a/engines/kyra/sound/drivers/audiomaster2.cpp
+++ b/engines/kyra/sound/drivers/audiomaster2.cpp
@@ -497,11 +497,8 @@ void SoundResource::interrupt(AudioMaster2IOManager *io) {
 	setupSoundEffect(unit, io->_sync, io->_tempo);
 }
 
-SoundResource8SVX::SoundResource8SVX(AudioMaster2ResourceManager *res) : SoundResource(res, 4) {
-	_numSamplesOnce = _numSamplesRepeat = _numSamplesPerCycle = _trackVolume = _dataSize = 0;
-	_rate = 0;
-	_numBlocks = _format = 0;
-	_data = 0;
+SoundResource8SVX::SoundResource8SVX(AudioMaster2ResourceManager *res) : SoundResource(res, 4),
+	_numSamplesOnce(0), _numSamplesRepeat(0), _numSamplesPerCycle(0), _trackVolume(0), _dataSize(0), _rate(0), _numBlocks(0), _format(0), _data(0) {
 }
 
 SoundResource8SVX::~SoundResource8SVX() {
diff --git a/engines/kyra/text/text_eob_segacd.cpp b/engines/kyra/text/text_eob_segacd.cpp
index aa8bbe6f8b..bffb5b3902 100644
--- a/engines/kyra/text/text_eob_segacd.cpp
+++ b/engines/kyra/text/text_eob_segacd.cpp
@@ -176,14 +176,14 @@ void TextDisplayer_SegaCD::displayText(char *str, ...) {
 }
 
 uint8 TextDisplayer_SegaCD::fetchCharacter(char *dest, const char *&src) {
-	char c = *src++;
+	uint8 c = (uint8)*src++;
 
-	if (c <= '\r') {
+	if (c <= (uint8)'\r') {
 		dest[0] = '\0';
-		return (uint8)c;
+		return c;
 	}
 		
-	dest[0] = c;
+	dest[0] = (char)c;
 	dest[1] = (c <= 0x7F || (c >= 0xA1 && c <= 0xDF)) ? '\0' : *src++;
 
 	return 0;


Commit: 1f11262c8a196bcec8117aa74eecb1e23d8c7981
    https://github.com/scummvm/scummvm/commit/1f11262c8a196bcec8117aa74eecb1e23d8c7981
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:20+02:00

Commit Message:
KYRA: (EOB/SegaCD) - fix scribe scroll dialog

Changed paths:
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/gui/gui_eob.h
    engines/kyra/gui/gui_eob_segacd.cpp
    engines/kyra/gui/gui_eob_segacd.h
    engines/kyra/resource/staticres_eob.cpp


diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index eddc258dfc..ce667a2ba1 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -3627,6 +3627,16 @@ void GUI_EoB::scribeScrollDialogue() {
 	int16 *menuItems = new int16[6];
 	int numScrolls = 0;
 
+	uint8 yOffs= 50;
+	uint8 lw = 9;
+	uint8 inputCodeOffs = 0;
+
+	if (_vm->_flags.platform == Common::kPlatformSegaCD) {
+		yOffs = 80;
+		lw = 8;
+		inputCodeOffs = 4;
+	}
+
 	for (int i = 0; i < 32; i++) {
 		for (int ii = 0; ii < 6; ii++) {
 			scrollInvSlot[i] = _vm->checkInventoryForItem(ii, 34, i + 1) + 1;
@@ -3641,7 +3651,6 @@ void GUI_EoB::scribeScrollDialogue() {
 	if (numScrolls) {
 		int csel = selectCharacterDialogue(49);
 		if (csel != -1) {
-
 			EoBCharacter *c = &_vm->_characters[csel];
 			int s = 0;
 
@@ -3674,10 +3683,11 @@ void GUI_EoB::scribeScrollDialogue() {
 							break;
 
 						releaseButtons(buttonList);
+						initScribeScrollMenu();
 						buttonList = initMenu(6);
 
 						for (int i = 0; i < s; i++)
-							_screen->printShadedText(_vm->_mageSpellList[menuItems[i]], 8, 9 * i + 50, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+							printScribeScrollSpellString(menuItems, i, false);
 
 						redraw = false;
 						lastHighLight = -1;
@@ -3686,9 +3696,9 @@ void GUI_EoB::scribeScrollDialogue() {
 
 					if (lastHighLight != newHighLight) {
 						if (lastHighLight >= 0)
-							_screen->printText(_vm->_mageSpellList[menuItems[lastHighLight]], 8, 9 * lastHighLight + 50, _vm->guiSettings()->colors.guiColorWhite, 0);
+							printScribeScrollSpellString(menuItems, lastHighLight, false);
+						printScribeScrollSpellString(menuItems, newHighLight, true);
 						lastHighLight = newHighLight;
-						_screen->printText(_vm->_mageSpellList[menuItems[lastHighLight]], 8, 9 * lastHighLight + 50, _vm->guiSettings()->colors.guiColorLightRed, 0);
 						_screen->updateScreen();
 					}
 
@@ -3697,16 +3707,16 @@ void GUI_EoB::scribeScrollDialogue() {
 
 					if (inputFlag == 0) {
 						Common::Point p = _vm->getMousePos();
-						if (_vm->posWithinRect(p.x, p.y, 8, 50, 176, s * 9 + 49))
-							newHighLight = (p.y - 50) / 9;
+						if (_vm->posWithinRect(p.x, p.y, 8, yOffs, 176, s * lw + yOffs - 1))
+							newHighLight = (p.y - yOffs) / lw;
 					} else if (inputFlag == _vm->_keyMap[Common::KEYCODE_KP2] || inputFlag == _vm->_keyMap[Common::KEYCODE_DOWN]) {
 						newHighLight = (newHighLight + 1) % s;
 					} else if (inputFlag == _vm->_keyMap[Common::KEYCODE_KP8] || inputFlag == _vm->_keyMap[Common::KEYCODE_UP]) {
 						newHighLight = (newHighLight + s - 1) % s;
-					} else if (inputFlag == 0x8023 || inputFlag == _vm->_keyMap[Common::KEYCODE_ESCAPE]) {
+					} else if (inputFlag == (0x8023 + inputCodeOffs) || inputFlag == _vm->_keyMap[Common::KEYCODE_ESCAPE]) {
 						s = 0;
-					} else if (inputFlag == 0x8024) {
-						newHighLight = (_vm->_mouseY - 50) / 9;
+					} else if (inputFlag == 0x8024 + inputCodeOffs) {
+						newHighLight = (_vm->_mouseY - yOffs) / lw;
 						if (newHighLight >= 0 && newHighLight < s) {
 							inputFlag = _vm->_keyMap[Common::KEYCODE_SPACE];
 						} else {
@@ -4028,6 +4038,14 @@ bool GUI_EoB::restParty() {
 	return res;
 }
 
+void GUI_EoB::printScribeScrollSpellString(const int16 *menuItems, int id, bool highlight) {
+	assert(menuItems);
+	if (highlight)
+		_screen->printText(_vm->_mageSpellList[menuItems[id]], 8, 9 * id + 50, _vm->guiSettings()->colors.guiColorLightRed, 0);
+	else
+		_screen->printShadedText(_vm->_mageSpellList[menuItems[id]], 8, 9 * id + 50, _vm->guiSettings()->colors.guiColorWhite, 0, _vm->guiSettings()->colors.guiColorBlack);
+}
+
 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);
diff --git a/engines/kyra/gui/gui_eob.h b/engines/kyra/gui/gui_eob.h
index caf1dffdf3..3ce2fe34cd 100644
--- a/engines/kyra/gui/gui_eob.h
+++ b/engines/kyra/gui/gui_eob.h
@@ -108,6 +108,8 @@ private:
 
 	virtual void drawCampMenu() {}
 	virtual void initMemorizePrayMenu() {}
+	virtual void initScribeScrollMenu() {}
+	virtual void printScribeScrollSpellString(const int16 *menuItems, int id, bool highlight);
 	virtual bool confirmDialogue(int id);
 	int selectCharacterDialogue(int id);
 	virtual void displayTextBox(int id, int textColor = 0xFF, bool wait = true);
diff --git a/engines/kyra/gui/gui_eob_segacd.cpp b/engines/kyra/gui/gui_eob_segacd.cpp
index a7f955d97b..37d8f8a48e 100644
--- a/engines/kyra/gui/gui_eob_segacd.cpp
+++ b/engines/kyra/gui/gui_eob_segacd.cpp
@@ -676,6 +676,25 @@ void GUI_EoB_SegaCD::initMemorizePrayMenu() {
 	_screen->sega_getRenderer()->render(0, 1, 4, 20, 2);
 }
 
+void GUI_EoB_SegaCD::initScribeScrollMenu() {
+	_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 22, 21, 0);
+	_screen->sega_getRenderer()->fillRectWithTiles(0, 1, 4, 20, 4, 0x6283, true);
+	_screen->sega_getRenderer()->loadToVRAM(&_campMenu[0x87C0], 4992, 0x3CE0);
+	_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);
+}
+
+void GUI_EoB_SegaCD::printScribeScrollSpellString(const int16 *menuItems, int id, bool highlight) {
+	assert(menuItems);
+	uint16 buf[22];
+	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);
+}
+
 void GUI_EoB_SegaCD::drawSaveSlotDialog(int x, int y, int id) {
 	_screen->sega_getRenderer()->fillRectWithTiles(0, 0, 0, 22, 21, 0);
 	_screen->sega_getRenderer()->fillRectWithTiles(0, (x >> 3) + 1, (y >> 3) + (y ? 3 : 4), 20, 2, 0x6283, true);
@@ -987,7 +1006,7 @@ char GUI_EoB_SegaCD::fetchClickableCharacter(int id) const {
 	return (char)c;
 }
 
-const GUI_EoB_SegaCD::MenuButtonTiles GUI_EoB_SegaCD::_menuButtonTiles[35] = {
+const GUI_EoB_SegaCD::MenuButtonTiles GUI_EoB_SegaCD::_menuButtonTiles[40] = {
 	{ 0x01e7, 0x0000 },	{ 0x01fb, 0x0028 },	{ 0x020f, 0x0050 }, { 0x0223, 0x0078 },
 	{ 0x0237, 0x00a0 }, { 0x0000, 0x0000 },	{ 0x01cf, 0x01c8 },	{ 0x025f, 0x0118 },
 	{ 0x024b, 0x00c8 }, { 0x0273, 0x0140 },	{ 0x020b, 0x0198 },	{ 0x01cf, 0x01c8 },
@@ -996,9 +1015,8 @@ const GUI_EoB_SegaCD::MenuButtonTiles GUI_EoB_SegaCD::_menuButtonTiles[35] = {
 	{ 0x0000, 0x0000 }, { 0x0000, 0x0000 }, { 0x0000, 0x0000 }, { 0x0000, 0x0000 },
 	{ 0x0000, 0x0000 },	{ 0x0000, 0x0000 },	{ 0x01db, 0x01b0 }, { 0x01cf, 0x01c8 },
 	{ 0x0000, 0x0000 }, { 0x01e7, 0x0270 },	{ 0x01f3, 0x027C }, { 0x01ff, 0x0288 },
-	{ 0x020b, 0x0294 },	{ 0x0217, 0x02A0 },
-	
-	{ 0x024d, 0x030c }
+	{ 0x020b, 0x0294 },	{ 0x0217, 0x02A0 },	{ 0x024d, 0x030c },	{ 0x0000, 0x0000 },
+	{ 0x0000, 0x0000 }, { 0x0000, 0x0000 }, { 0x01CF, 0x01C8 }, { 0x0000, 0x0000 }
 };
 
 } // End of namespace Kyra
diff --git a/engines/kyra/gui/gui_eob_segacd.h b/engines/kyra/gui/gui_eob_segacd.h
index 2800a81ed0..2c6c685b04 100644
--- a/engines/kyra/gui/gui_eob_segacd.h
+++ b/engines/kyra/gui/gui_eob_segacd.h
@@ -39,6 +39,8 @@ public:
 private:
 	void drawCampMenu() override;
 	void initMemorizePrayMenu() override;
+	void initScribeScrollMenu() override;
+	void printScribeScrollSpellString(const int16 *menuItems, int id, bool highlight) override;
 	void drawSaveSlotDialog(int x, int y, int id) override;
 	bool confirmDialogue(int id) override;
 	void displayTextBox(int id, int textColor, bool wait) override;
@@ -63,7 +65,7 @@ private:
 		uint16 srcOffs;
 	};
 
-	static const MenuButtonTiles _menuButtonTiles[35];
+	static const MenuButtonTiles _menuButtonTiles[40];
 };
 
 } // End of namespace Kyra
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index a0599fc504..6b9637d769 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -683,7 +683,7 @@ void EoBCoreEngine::initButtonData() {
 		{ 0, 0, 0x1100, 146, 168, 32, 10, 0 },
 		{ 0, 0, 0x1100, 296, 56, 16, 16, 27 },
 
-		{ 101, 96, 0x1100, 248, 152, 64, 14, -1 },
+		{ 101, 96, 0x1100, 248, 152, 64, 14, 65535 },
 		{ 103, 98, 0x1100, 248, 168, 64, 14, 1 },
 		{ 110, 0, 0x1100, 248, 184, 64, 14, 2 }
 	};
@@ -929,11 +929,12 @@ void EoBCoreEngine::initMenus() {
 		{  33,  72,  48,  24,  16,   4,  5  },
 		{  34, 104,  48,  24,  16,   5,  5  },
 		{  35, 136,  48,  24,  16,   6,  5  },
-
 		{   0,  88, 112,  48,  16,   0,  3  },
 		{   0, 120,  40,  24,  16,   0,  3  },
 		{   0,   8,  40,  48,  16,   0,  3  },
-		{   0,   8, 136,  80,  16,   0,  3  }
+		{   0,   8, 136,  80,  16,   0,  3  },
+		{   0, 120, 144,  48,  16,   0,  3  },
+		{   0,  24,  80,  80,  48,   0,  3  }
 	};
 
 	_menuButtonDefs = (_flags.platform == Common::kPlatformSegaCD) ? buttonDefsSegaCD : buttonDefsDefault;
@@ -948,13 +949,14 @@ void EoBCoreEngine::initMenus() {
 		{ 48, 10, 34, 2,  9 }
 	};
 
-	static const EoBMenuDef menuDefsSegaCD[6] = {
+	static const EoBMenuDef menuDefsSegaCD[7] = {
 		{  -1, 0,  0, 10,   -1 },
 		{  -1, 0,  0,  0,   -1 },
 		{  -1, 0, 10,  7,   -1 },
 		{   0, 0, 19,  7, 0x55 },
 		{  -1, 0, 26,  8,   -1 },
-		{  -1, 0, 17,  2,   -1 }
+		{  -1, 0, 17,  2,   -1 },
+		{  -1, 0, 38,  2,   -1 }
 	};
 
 	delete[] _menuDefs;


Commit: 1303dfc8b7aee1019eac0aad5e81f719f79aea4b
    https://github.com/scummvm/scummvm/commit/1303dfc8b7aee1019eac0aad5e81f719f79aea4b
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:20+02:00

Commit Message:
KYRA: (EOB/SegaCD) - fix text color glitch

Changed paths:
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/text/text_eob_segacd.cpp


diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 1188db577e..9f0a0b2556 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -1547,7 +1547,7 @@ void EoBCoreEngine::setupDialogueButtons(int presetfirst, int numStr, va_list &a
 	Screen::FontId of = _screen->setFont((_flags.gameID == GI_EOB2 && _flags.platform == Common::kPlatformFMTowns) ? Screen::FID_8_FNT : _screen->_currentFont);
 
 	for (int i = 0; i < numStr; i++) {
-		const char *s = va_arg(args, const char *);
+		const char *s = va_arg(args, const char*);
 		if (s)
 			_dialogueButtonString[i] = s;
 		else
diff --git a/engines/kyra/text/text_eob_segacd.cpp b/engines/kyra/text/text_eob_segacd.cpp
index bffb5b3902..f190d12062 100644
--- a/engines/kyra/text/text_eob_segacd.cpp
+++ b/engines/kyra/text/text_eob_segacd.cpp
@@ -134,7 +134,7 @@ void TextDisplayer_SegaCD::displayText(char *str, ...) {
 		}
 
 		if (cmd == 6) {
-			_textColor = *pos++;
+			_textColor = (uint8)*pos++;
 		} else if (cmd == 2) {
 			pos++;
 		} else if (cmd == 13) {


Commit: fb9f6ba4a5a36e3c230e10a034c97e79a22859fa
    https://github.com/scummvm/scummvm/commit/fb9f6ba4a5a36e3c230e10a034c97e79a22859fa
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:20+02:00

Commit Message:
KYRA: (EOB) - allow return to launcher from gameover screen

Changed paths:
    engines/kyra/engine/eobcommon.cpp


diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 9f0a0b2556..f1f6724b0a 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -790,7 +790,9 @@ bool EoBCoreEngine::checkPartyStatus(bool handleDeath) {
 	if (_flags.platform == Common::kPlatformSegaCD)
 		_screen->sega_fadeToBlack(1);
 
-	quitGame();
+	if (!shouldQuit())
+		quitGame();
+
 	return false;
 }
 


Commit: f2b02a77ce5099c41b1bc2d7cf7ae47e7c61a0ea
    https://github.com/scummvm/scummvm/commit/f2b02a77ce5099c41b1bc2d7cf7ae47e7c61a0ea
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:21+02:00

Commit Message:
KYRA: (EOB/SegaCD) - improve magic animations

Changed paths:
    engines/kyra/engine/eobcommon.h
    engines/kyra/engine/magic_eob.cpp
    engines/kyra/gui/gui_eob.cpp


diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index 73ee51b07d..e5ab2a82aa 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -736,7 +736,7 @@ protected:
 	virtual void gui_setupPlayFieldHelperPages(bool keepText = false);
 	void gui_restorePlayField();
 	void gui_drawAllCharPortraitsWithStats();
-	void gui_drawCharPortraitWithStats(int index);
+	void gui_drawCharPortraitWithStats(int index, bool screenUpdt = true);
 	void gui_drawFaceShape(int index);
 	void gui_drawWeaponSlot(int charIndex, int slot);
 	virtual void gui_drawWeaponSlotStatus(int x, int y, int status);
diff --git a/engines/kyra/engine/magic_eob.cpp b/engines/kyra/engine/magic_eob.cpp
index 134371972a..f92097fd98 100644
--- a/engines/kyra/engine/magic_eob.cpp
+++ b/engines/kyra/engine/magic_eob.cpp
@@ -399,15 +399,15 @@ void EoBCoreEngine::sparkEffectDefensive(int charIndex) {
 	if (_flags.gameID == GI_EOB1 && _flags.platform == Common::kPlatformAmiga)
 		snd_playSoundEffect(104);
 
-	for (int i = 0; i < 8; i++) {
+	for (int i = 0; i < 32; i++) {
 		for (int ii = first; ii <= last; ii++) {
 			if (!testCharacter(ii, 1) || (_currentControlMode && ii != _updateCharNum))
 				continue;
 
-			gui_drawCharPortraitWithStats(ii);
+			gui_drawCharPortraitWithStats(ii, false);
 
 			for (int iii = 0; iii < 4; iii++) {
-				int shpIndex = ((_sparkEffectDefSteps[i] & _sparkEffectDefSubSteps[iii]) >> _sparkEffectDefShift[iii]);
+				int shpIndex = ((_sparkEffectDefSteps[i >> 2] & _sparkEffectDefSubSteps[iii]) >> _sparkEffectDefShift[iii]);
 				if (!shpIndex)
 					continue;
 				int x = _sparkEffectDefAdd[iii * 2] - 8;
@@ -420,10 +420,11 @@ void EoBCoreEngine::sparkEffectDefensive(int charIndex) {
 					y += _sparkEffectDefY[ii];
 				}
 				_screen->drawShape(0, _sparkShapes[shpIndex - 1], x, y, 0);
-				_screen->updateScreen();
 			}
 		}
-		delay(2 * _tickLength);
+		updateAnimTimers();
+		_screen->updateScreen();
+		delay(_tickLength >> 1);
 	}
 
 	for (int i = first; i < last; i++)
@@ -438,18 +439,31 @@ void EoBCoreEngine::sparkEffectOffensive() {
 	for (int i = 0; i < 16; i++)
 		_screen->copyRegionToBuffer(0, _sparkEffectOfX[i], _sparkEffectOfY[i], 16, 16, &_spellAnimBuffer[i << sh]);
 
-	for (int i = 0; i < 11; i++) {
-		for (int ii = 0; ii < 16; ii++)
-			_screen->copyBlockToPage(2, _sparkEffectOfX[ii], _sparkEffectOfY[ii], 16, 16, &_spellAnimBuffer[ii << sh]);
+	for (int i = 0; i < 44; i++) {
+		bool sceneShake = _sceneShakeCountdown;
+		updateAnimTimers();
+		if (sceneShake) {
+			_screen->copyRegion(0, 0, 0, 0, 176, 120, 0, 2, Screen::CR_NO_P_CHECK);
+			if (!_sceneShakeCountdown) {
+				for (int i = 0; i < 16; i++)
+					_screen->copyRegionToBuffer(0, _sparkEffectOfX[i], _sparkEffectOfY[i], 16, 16, &_spellAnimBuffer[i << sh]);
+			}
+		}
+
+		if (!sceneShake) {
+			for (int ii = 0; ii < 16; ii++)
+				_screen->copyBlockToPage(2, _sparkEffectOfX[ii], _sparkEffectOfY[ii], 16, 16, &_spellAnimBuffer[ii << sh]);
+		}
 
 		for (int ii = 0; ii < 16; ii++) {
-			int shpIndex = (_sparkEffectOfFlags1[i] & _sparkEffectOfFlags2[ii]) >> _sparkEffectOfShift[ii];
+			int shpIndex = (_sparkEffectOfFlags1[i >> 2] & _sparkEffectOfFlags2[ii]) >> _sparkEffectOfShift[ii];
 			if (shpIndex)
 				_screen->drawShape(2, _sparkShapes[shpIndex - 1], _sparkEffectOfX[ii], _sparkEffectOfY[ii], 0);
 		}
-		delay(2 * _tickLength);
+
 		_screen->copyRegion(0, 0, 0, 0, 176, 120, 2, 0, Screen::CR_NO_P_CHECK);
 		_screen->updateScreen();
+		delay(_tickLength >> 1);
 	}
 
 	for (int i = 0; i < 16; i++)
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index ce667a2ba1..15dce0b813 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -89,7 +89,7 @@ void EoBCoreEngine::gui_drawAllCharPortraitsWithStats() {
 		gui_drawCharPortraitWithStats(i);
 }
 
-void EoBCoreEngine::gui_drawCharPortraitWithStats(int index) {
+void EoBCoreEngine::gui_drawCharPortraitWithStats(int index, bool screenUpdt) {
 	if (!testCharacter(index, 1))
 		return;
 
@@ -154,7 +154,8 @@ void EoBCoreEngine::gui_drawCharPortraitWithStats(int index) {
 				_screen->drawShape(0, _redSplatShape, guiSettings()->charBoxCoords.boxX[index & 1] + guiSettings()->charBoxCoords.redSplatOffsetX, y2 + guiSettings()->charBoxCoords.redSplatOffsetY, 0);
 				gui_printInventoryDigits(guiSettings()->charBoxCoords.boxX[index & 1] + guiSettings()->charBoxCoords.redSplatOffsetX + 12, y2 + guiSettings()->charBoxCoords.redSplatOffsetY + 10, c->damageTaken);
 			}
-			_screen->updateScreen();
+			if (screenUpdt)
+				_screen->updateScreen();
 		}
 	} else if ((_currentControlMode == 1 || _currentControlMode == 2) && index == _updateCharNum) {
 		_screen->copyRegion(176, 0, 0, 0, 144, 168, 2, 2, Screen::CR_NO_P_CHECK);


Commit: 5e276286ae03d6dd8648c915bc72e21577d4b875
    https://github.com/scummvm/scummvm/commit/5e276286ae03d6dd8648c915bc72e21577d4b875
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:21+02:00

Commit Message:
AUDIO/GUI: add SegaCD sound settings

Changed paths:
    audio/mididrv.cpp
    audio/mididrv.h
    audio/softsynth/fmtowns_pc98/towns_pc98_plugins.cpp
    base/plugins.cpp
    common/gui_options.cpp
    common/gui_options.h
    engines/kyra/detection_tables.h
    engines/kyra/engine/eobcommon.cpp
    gui/options.cpp


diff --git a/audio/mididrv.cpp b/audio/mididrv.cpp
index 74d1b88823..02ca01058e 100644
--- a/audio/mididrv.cpp
+++ b/audio/mididrv.cpp
@@ -109,6 +109,7 @@ static const struct {
 	{ MT_APPLEIIGS,	GUIO_MIDIAPPLEIIGS },
 	{ MT_TOWNS,		GUIO_MIDITOWNS },
 	{ MT_PC98,		GUIO_MIDIPC98 },
+	{ MT_SEGACD,	GUIO_MIDISEGACD },
 	{ MT_GM,		GUIO_MIDIGM },
 	{ MT_MT32,		GUIO_MIDIMT32 },
 	{ 0,			0 },
@@ -229,6 +230,11 @@ MidiDriver::DeviceHandle MidiDriver::detectDevice(int flags) {
 			reslt = hdl;
 		break;
 
+	case MT_SEGACD:
+	if (flags & MDT_SEGACD)
+		reslt = hdl;
+	break;
+
 	case MT_GM:
 	case MT_GS:
 	case MT_MT32:
@@ -363,6 +369,9 @@ MidiDriver::DeviceHandle MidiDriver::detectDevice(int flags) {
 		} else if (flags & MDT_PC98) {
 			tp = MT_PC98;
 			flags &= ~MDT_PC98;
+		} else if (flags & MDT_SEGACD) {
+			tp = MT_SEGACD;
+			flags &= ~MDT_SEGACD;
 		} else if (flags & MDT_ADLIB) {
 			tp = MT_ADLIB;
 			flags &= ~MDT_ADLIB;
diff --git a/audio/mididrv.h b/audio/mididrv.h
index 199bce8084..6f37125cd7 100644
--- a/audio/mididrv.h
+++ b/audio/mididrv.h
@@ -47,6 +47,7 @@ enum MusicType {
 	MT_APPLEIIGS,		// Apple IIGS
 	MT_TOWNS,			// FM-TOWNS
 	MT_PC98,			// PC98
+	MT_SEGACD,			// SegaCD
 	MT_GM,				// General MIDI
 	MT_MT32,			// MT-32
 	MT_GS				// Roland GS
@@ -77,11 +78,12 @@ enum MidiDriverFlags {
 	MDT_AMIGA       = 1 << 5,
 	MDT_APPLEIIGS   = 1 << 6,
 	MDT_TOWNS       = 1 << 7,		// FM-TOWNS: Maps to MT_TOWNS
-	MDT_PC98        = 1 << 8,		// FM-TOWNS: Maps to MT_PC98
-	MDT_MIDI        = 1 << 9,		// Real MIDI
-	MDT_PREFER_MT32 = 1 << 10,		// MT-32 output is preferred
-	MDT_PREFER_GM   = 1 << 11,		// GM output is preferred
-	MDT_PREFER_FLUID= 1 << 12		// FluidSynth driver is preferred
+	MDT_PC98        = 1 << 8,		// PC-98: Maps to MT_PC98
+	MDT_SEGACD		= 1 << 9,
+	MDT_MIDI        = 1 << 10,		// Real MIDI
+	MDT_PREFER_MT32 = 1 << 11,		// MT-32 output is preferred
+	MDT_PREFER_GM   = 1 << 12,		// GM output is preferred
+	MDT_PREFER_FLUID= 1 << 13		// FluidSynth driver is preferred
 };
 
 /**
diff --git a/audio/softsynth/fmtowns_pc98/towns_pc98_plugins.cpp b/audio/softsynth/fmtowns_pc98/towns_pc98_plugins.cpp
index 41034846eb..5a1d606653 100644
--- a/audio/softsynth/fmtowns_pc98/towns_pc98_plugins.cpp
+++ b/audio/softsynth/fmtowns_pc98/towns_pc98_plugins.cpp
@@ -76,10 +76,36 @@ Common::Error PC98EmuMusicPlugin::createInstance(MidiDriver **mididriver, MidiDr
 	return Common::kUnknownError;
 }
 
+class SegaCDSoundPlugin : public MusicPluginObject {
+public:
+	const char *getName() const {
+		return _s("SegaCD Audio");
+	}
+
+	const char *getId() const {
+		return "segacd";
+	}
+
+	MusicDevices getDevices() const;
+	Common::Error createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle = 0) const;
+};
+
+MusicDevices SegaCDSoundPlugin::getDevices() const {
+	MusicDevices devices;
+	devices.push_back(MusicDevice(this, "", MT_SEGACD));
+	return devices;
+}
+
+Common::Error SegaCDSoundPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const {
+	*mididriver = 0;
+	return Common::kUnknownError;
+}
+
 //#if PLUGIN_ENABLED_DYNAMIC(TOWNS)
 	//REGISTER_PLUGIN_DYNAMIC(TOWNS, PLUGIN_TYPE_MUSIC, TownsEmuMusicPlugin);
-	//REGISTER_PLUGIN_DYNAMIC(TOWNS, PLUGIN_TYPE_MUSIC, TownsEmuMusicPlugin);
+	//REGISTER_PLUGIN_DYNAMIC(PC98, PLUGIN_TYPE_MUSIC, PC98EmuMusicPlugin);
 //#else
 	REGISTER_PLUGIN_STATIC(TOWNS, PLUGIN_TYPE_MUSIC, TownsEmuMusicPlugin);
 	REGISTER_PLUGIN_STATIC(PC98, PLUGIN_TYPE_MUSIC, PC98EmuMusicPlugin);
+	REGISTER_PLUGIN_STATIC(SEGACD, PLUGIN_TYPE_MUSIC, SegaCDSoundPlugin);
 //#endif
diff --git a/base/plugins.cpp b/base/plugins.cpp
index c3ee99e6b6..b02d79267f 100644
--- a/base/plugins.cpp
+++ b/base/plugins.cpp
@@ -137,6 +137,7 @@ public:
 		LINK_PLUGIN(APPLEIIGS)
 		LINK_PLUGIN(TOWNS)
 		LINK_PLUGIN(PC98)
+		LINK_PLUGIN(SEGACD)
 		#if defined(USE_TIMIDITY)
 		LINK_PLUGIN(TIMIDITY)
 		#endif
diff --git a/common/gui_options.cpp b/common/gui_options.cpp
index 2786701c85..e568230705 100644
--- a/common/gui_options.cpp
+++ b/common/gui_options.cpp
@@ -53,6 +53,7 @@ const struct GameOpt {
 	{ GUIO_MIDIAPPLEIIGS,"midiAppleIIgs" },
 	{ GUIO_MIDITOWNS,    "midiTowns" },
 	{ GUIO_MIDIPC98,     "midiPC98" },
+	{ GUIO_MIDISEGACD,   "midiSegaCD" },
 	{ GUIO_MIDIMT32,     "midiMt32" },
 	{ GUIO_MIDIGM,       "midiGM" },
 
diff --git a/common/gui_options.h b/common/gui_options.h
index 00d08cfea5..eca75e6877 100644
--- a/common/gui_options.h
+++ b/common/gui_options.h
@@ -41,6 +41,7 @@
 #define GUIO_MIDIAPPLEIIGS   "\015"
 #define GUIO_MIDITOWNS       "\016"
 #define GUIO_MIDIPC98        "\017"
+#define GUIO_MIDISEGACD      "\018"
 #define GUIO_MIDIMT32        "\020"
 #define GUIO_MIDIGM          "\021"
 
diff --git a/engines/kyra/detection_tables.h b/engines/kyra/detection_tables.h
index a7962b46a1..f2458f550d 100644
--- a/engines/kyra/detection_tables.h
+++ b/engines/kyra/detection_tables.h
@@ -1840,7 +1840,7 @@ const KYRAGameDescription adGameDescs[] = {
 			Common::EN_ANY,
 			Common::kPlatformSegaCD,
 			ADGF_NO_FLAGS,
-			GUIO3(GUIO_NOSPEECH, GUIO_NOMIDI, GAMEOPTION_EOB_MOUSESWAP)
+			GUIO3(GUIO_NOSPEECH, GUIO_MIDISEGACD, GAMEOPTION_EOB_MOUSESWAP)
 		},
 		EOB_FLAGS
 	},
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index f1f6724b0a..664b4fe32b 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -434,7 +434,7 @@ Common::Error EoBCoreEngine::init() {
 		_sound = new SoundAmiga_EoB(this, _mixer);
 		break;
 	case Common::kPlatformSegaCD:
-		dev = MidiDriver::detectDevice(/*MDT_SEGACD*/MDT_TOWNS);
+		dev = MidiDriver::detectDevice(MDT_SEGACD);
 		_sound = new SoundSegaCD_EoB(this, _mixer);
 		break;
 	default:
diff --git a/gui/options.cpp b/gui/options.cpp
index 2c95f7d774..485d9999d7 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -1284,8 +1284,8 @@ void OptionsDialog::addAudioControls(GuiObject *boss, const Common::String &pref
 		for (MusicDevices::iterator d = i.begin(); d != i.end(); ++d) {
 			Common::String deviceGuiOption = MidiDriver::musicType2GUIO(d->getMusicType());
 
-			if ((_domain == Common::ConfigManager::kApplicationDomain && d->getMusicType() != MT_TOWNS  // global dialog - skip useless FM-Towns, C64, Amiga, AppleIIGS options there
-				 && d->getMusicType() != MT_C64 && d->getMusicType() != MT_AMIGA && d->getMusicType() != MT_APPLEIIGS && d->getMusicType() != MT_PC98)
+			if ((_domain == Common::ConfigManager::kApplicationDomain && d->getMusicType() != MT_TOWNS  // global dialog - skip useless FM-Towns, C64, Amiga, AppleIIGS and SegaCD options there
+				 && d->getMusicType() != MT_C64 && d->getMusicType() != MT_AMIGA && d->getMusicType() != MT_APPLEIIGS && d->getMusicType() != MT_PC98 && d->getMusicType() != MT_SEGACD)
 				|| (_domain != Common::ConfigManager::kApplicationDomain && !hasMidiDefined) // No flags are specified
 				|| (_guioptions.contains(deviceGuiOption)) // flag is present
 				// HACK/FIXME: For now we have to show GM devices, even when the game only has GUIO_MIDIMT32 set,


Commit: b379b529388f20ddc04a41a0a20437f2009c5a62
    https://github.com/scummvm/scummvm/commit/b379b529388f20ddc04a41a0a20437f2009c5a62
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:21+02:00

Commit Message:
AUDIO: preparations for SegaCD sound driver

Changed paths:
  A audio/softsynth/fmtowns_pc98/pcm_common.cpp
  A audio/softsynth/fmtowns_pc98/pcm_common.h
  A audio/softsynth/fmtowns_pc98/sega_audio.cpp
  A audio/softsynth/fmtowns_pc98/sega_audio.h
    audio/module.mk
    audio/softsynth/fmtowns_pc98/pc98_audio.cpp
    audio/softsynth/fmtowns_pc98/towns_audio.cpp


diff --git a/audio/module.mk b/audio/module.mk
index 8a7531e9ef..11a5aac7e2 100644
--- a/audio/module.mk
+++ b/audio/module.mk
@@ -47,6 +47,8 @@ MODULE_OBJS := \
 	softsynth/opl/dosbox.o \
 	softsynth/opl/mame.o \
 	softsynth/fmtowns_pc98/pc98_audio.o \
+	softsynth/fmtowns_pc98/pcm_common.o \
+	softsynth/fmtowns_pc98/sega_audio.o \
 	softsynth/fmtowns_pc98/towns_audio.o \
 	softsynth/fmtowns_pc98/towns_euphony.o \
 	softsynth/fmtowns_pc98/towns_pc98_driver.o \
diff --git a/audio/softsynth/fmtowns_pc98/pc98_audio.cpp b/audio/softsynth/fmtowns_pc98/pc98_audio.cpp
index 144e9834b0..edf9353485 100644
--- a/audio/softsynth/fmtowns_pc98/pc98_audio.cpp
+++ b/audio/softsynth/fmtowns_pc98/pc98_audio.cpp
@@ -24,7 +24,7 @@
 #include "audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h"
 #include "common/mutex.h"
 
-class PC98AudioCoreInternal : public TownsPC98_FmSynth {
+class PC98AudioCoreInternal final : public TownsPC98_FmSynth {
 private:
 	PC98AudioCoreInternal(Audio::Mixer *mixer, PC98AudioCore *owner, PC98AudioPluginDriver *driver, PC98AudioPluginDriver::EmuType type);
 public:
diff --git a/audio/softsynth/fmtowns_pc98/pcm_common.cpp b/audio/softsynth/fmtowns_pc98/pcm_common.cpp
new file mode 100644
index 0000000000..bd197944ef
--- /dev/null
+++ b/audio/softsynth/fmtowns_pc98/pcm_common.cpp
@@ -0,0 +1,168 @@
+/* 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 "audio/softsynth/fmtowns_pc98/pcm_common.h"
+#include "audio/mixer.h"
+
+PCMChannel_Base::PCMChannel_Base() : _vol(0), _data(0), _dataEnd(0), _loopLen(0), _pos(0), _loopStart(0), _step(0), _panLeft(7), _panRight(7), _activeOutput(false)  {
+}
+
+PCMChannel_Base::~PCMChannel_Base() {
+	clear();
+}
+
+void PCMChannel_Base::clear() {
+	_vol = 0;
+	_data = 0;
+	_dataEnd = 0;
+	_loopLen = 0;
+	_pos = 0;
+	_loopStart = 0;
+	_step = 0;
+	_panLeft = _panRight = 7;
+	_activeOutput = false;
+}
+
+void PCMChannel_Base::updateOutput() {
+	if (!isPlaying())
+		return;
+
+	_pos += _step;
+
+	if (_pos >= _dataEnd) {
+		if (_loopStart != _dataEnd) {
+			_pos = _loopStart;
+			_dataEnd = _loopStart + _loopLen;
+		} else {
+			_pos = 0;
+			stopInternal();
+		}
+	}
+}
+
+int32 PCMChannel_Base::currentSampleLeft() {
+	return (isActive() && _panLeft) ? (((_data[_pos >> 11] * _vol) * _panLeft) >> 3) : 0;
+}
+
+int32 PCMChannel_Base::currentSampleRight() {
+	return (isActive() && _panRight) ? (((_data[_pos >> 11] * _vol) * _panRight) >> 3) : 0;
+}
+
+bool PCMChannel_Base::isActive() const {
+	return _activeOutput;
+}
+
+void PCMChannel_Base::activate() {
+	_activeOutput = true;
+}
+
+void PCMChannel_Base::deactivate() {
+	_activeOutput = false;
+}
+
+void PCMChannel_Base::setData(const int8 *data, uint32 dataEnd, uint32 dataStart) {
+	_data = data;
+	_dataEnd = dataEnd;
+	_pos = dataStart;
+}
+
+void PCMChannel_Base::setVolume(uint8 vol) {
+	_vol = vol;
+}
+
+void PCMChannel_Base::setPanPos(uint8 pan) {
+	_panLeft = pan & 0x0f;
+	_panRight = pan >> 4;
+}
+
+void PCMChannel_Base::setupLoop(uint32 loopStart, uint32 loopLen) {
+	_loopStart = loopStart << 11;
+	_loopLen = loopLen << 11;
+}
+
+void PCMChannel_Base::setRate(uint16 rate) {
+	_step = rate;
+}
+
+PCMDevice_Base::PCMDevice_Base(int samplingRate, int deviceVolume, int numChannels) : _numChannels(numChannels), _deviceVolume(deviceVolume), _intRate((7670454 << 8) / samplingRate),
+	_extRate(72 << 8), _timer(0), _musicVolume(Audio::Mixer::kMaxMixerVolume), _sfxVolume(Audio::Mixer::kMaxMixerVolume), _pcmSfxChanMask(0) {
+	_channels = new PCMChannel_Base*[numChannels];
+}
+
+PCMDevice_Base::~PCMDevice_Base() {
+	delete[] _channels;
+}
+
+void PCMDevice_Base::assignChannel(uint8 id, PCMChannel_Base *const chan) {
+	assert(id < _numChannels);
+	_channels[id] = chan;
+}
+
+void PCMDevice_Base::setMusicVolume(uint16 vol) {
+	_musicVolume = vol;
+}
+
+void PCMDevice_Base::setSfxVolume(uint16 vol) {
+	_sfxVolume = vol;
+}
+
+void PCMDevice_Base::setSfxChanMask(int mask) {
+	_pcmSfxChanMask = mask;
+}
+
+void PCMDevice_Base::readBuffer(int32 *buffer, uint32 bufferSize) {
+	for (uint32 i = 0; i < bufferSize; i++) {
+		_timer += _extRate;
+		while (_timer >= _intRate) {
+			_timer -= _intRate;
+
+			for (int ii = 0; ii < 8; ii++)
+				_channels[ii]->updateOutput();
+		}
+
+		int32 finOutL = 0;
+		int32 finOutR = 0;
+
+		for (int ii = 0; ii < _numChannels; ii++) {
+			if (_channels[ii]->isActive()) {
+				int32 oL = _channels[ii]->currentSampleLeft();
+				int32 oR = _channels[ii]->currentSampleRight();
+				if ((1 << ii) & (~_pcmSfxChanMask)) {
+					oL = (oL * _musicVolume) / Audio::Mixer::kMaxMixerVolume;
+					oR = (oR * _musicVolume) / Audio::Mixer::kMaxMixerVolume;
+				}
+				if ((1 << ii) & _pcmSfxChanMask) {
+					oL = (oL * _sfxVolume) / Audio::Mixer::kMaxMixerVolume;
+					oR = (oR * _sfxVolume) / Audio::Mixer::kMaxMixerVolume;
+				}
+				finOutL += oL;
+				finOutR += oR;
+
+				if (!_channels[ii]->isPlaying())
+					_channels[ii]->deactivate();
+			}
+		}
+
+		buffer[i << 1] += ((finOutL * _deviceVolume) >> 7);
+		buffer[(i << 1) + 1] += ((finOutR * _deviceVolume) >> 7);
+	}
+}
diff --git a/audio/softsynth/fmtowns_pc98/pcm_common.h b/audio/softsynth/fmtowns_pc98/pcm_common.h
new file mode 100644
index 0000000000..98d71c027d
--- /dev/null
+++ b/audio/softsynth/fmtowns_pc98/pcm_common.h
@@ -0,0 +1,96 @@
+/* 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 PCM_COMMON_H
+#define PCM_COMMON_H
+
+#include "common/scummsys.h"
+
+// The SegaCD and the FM-Towns have (almost) the same PCM sound chip. And while each platform has low-level driver
+// stuff going on top of that it still makes sense to identify and abstract some commmon code.
+
+class PCMChannel_Base {
+public:
+	PCMChannel_Base();
+	virtual ~PCMChannel_Base();
+
+	virtual void clear();
+
+	void updateOutput();
+	int32 currentSampleLeft();
+	int32 currentSampleRight();
+
+	virtual bool isPlaying() const = 0;
+	bool isActive() const;
+	void activate();
+	void deactivate();
+
+protected:
+	void setData(const int8 *data, uint32 dataEnd, uint32 dataStart = 0);
+	void setVolume(uint8 vol);
+	void setPanPos(uint8 setPanPos);
+	void setupLoop(uint32 loopStart, uint32 loopLen);
+	void setRate(uint16 rate);
+
+private:
+	virtual void stopInternal() = 0;
+
+	uint8 _panLeft;
+	uint8 _panRight;
+	uint8 _vol;
+	bool _activeOutput;
+
+	uint32 _loopStart;
+	uint32 _loopLen;
+	uint32 _dataEnd;
+	uint32 _pos;
+	uint16 _step;
+	const int8 *_data;
+};
+
+class PCMDevice_Base {
+public:
+	PCMDevice_Base(int samplingRate, int deviceVolume, int numChannels);
+	~PCMDevice_Base();
+
+	void assignChannel(uint8 id, PCMChannel_Base *const chan);
+	void setMusicVolume(uint16 vol);
+	void setSfxVolume(uint16 vol);
+	void setSfxChanMask(int mask);
+
+	void readBuffer(int32 *buffer, uint32 bufferSize);
+
+private:
+	const uint32 _intRate;
+	const uint32 _extRate;
+	const int _deviceVolume;
+	uint32 _timer;
+
+	uint16 _musicVolume;
+	uint16 _sfxVolume;
+	int _pcmSfxChanMask;
+
+	PCMChannel_Base **_channels;
+	const int _numChannels;
+};
+
+#endif
diff --git a/audio/softsynth/fmtowns_pc98/sega_audio.cpp b/audio/softsynth/fmtowns_pc98/sega_audio.cpp
new file mode 100644
index 0000000000..5baaf6d3d4
--- /dev/null
+++ b/audio/softsynth/fmtowns_pc98/sega_audio.cpp
@@ -0,0 +1,513 @@
+/* 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 "audio/softsynth/fmtowns_pc98/sega_audio.h"
+#include "audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h"
+#include "audio/softsynth/fmtowns_pc98/pcm_common.h"
+#include "common/mutex.h"
+
+class SegaPCMChannel final : public PCMChannel_Base {
+public:
+	SegaPCMChannel() : PCMChannel_Base(), _playing(false) {}
+	~SegaPCMChannel() override {}
+
+	void play(const int8 *data, uint16 dataSize, uint16 startAddress, uint16 loopStart, uint16 loopLen, uint16 pitch, uint8 pan, uint8 vol);
+	void stop();
+	bool isPlaying() const override;
+
+private:
+	void stopInternal() override;
+	bool _playing;
+};
+
+class SegaPSG {
+public:
+	SegaPSG(int samplingRate, int deviceVolume);
+	~SegaPSG() {}
+
+	void write(uint8 val);
+
+	void setMusicVolume(uint16 vol);
+	void setSfxVolume(uint16 vol);
+	void setSfxChanMask(int mask);
+
+	void readBuffer(int32 *buffer, uint32 bufferSize);
+
+private:
+	struct Channel {
+		Channel() : freq(0), curSample(0), counter(0), out(0) {}
+		uint16 freq;
+		int16 curSample;
+		int16 out;
+		uint16 counter;
+	};
+
+	Channel _channels[3];
+	uint8 _nfb;
+	uint8 _nfs;
+	uint8 _nat;
+	int _cr;
+
+	int16 _attnTable[16];
+
+	uint16 _musicVolume;
+	uint16 _sfxVolume;
+	int _sfxChanMask;
+
+	const uint32 _extRate;
+	const uint32 _intRate;
+	uint32 _timer;
+	const uint16 _deviceVolume;
+	const uint8 _numChannels;
+};
+
+class SegaAudioInterfaceInternal final : public TownsPC98_FmSynth {
+private:
+	SegaAudioInterfaceInternal(Audio::Mixer *mixer, SegaAudioInterface *owner, SegaAudioPluginDriver *driver);
+public:
+	~SegaAudioInterfaceInternal();
+
+	static SegaAudioInterfaceInternal *addNewRef(Audio::Mixer *mixer, SegaAudioInterface *owner, SegaAudioPluginDriver *driver);
+	static void releaseRef(SegaAudioInterface *owner);
+
+	bool init();
+
+	void loadPCMData(uint16 address, const uint8 *data, uint16 dataLen);
+	void playPCMChannel(uint8 channel, uint8 dataStart, uint16 loopStart, uint16 pitch, uint8 pan, uint8 vol);
+	void stopPCMChannel(uint8 channel);
+
+	void psgWrite(uint8 data);
+
+	void setMusicVolume(int volume);
+	void setSoundEffectVolume(int volume);
+	// Defines the channels used as sound effect channels for the purpose of ScummVM GUI volume control.
+	// The first 6 bits are 6 fm channels. The next 3 bits are psg channels. The bits that follow represent pcm channels.
+	void setSoundEffectChanMask(int mask);
+
+	Common::Mutex &mutex();
+	int mixerThreadLockCounter() const;
+
+private:
+	bool assignPluginDriver(SegaAudioInterface *owner, SegaAudioPluginDriver *driver, bool externalMutexHandling = false);
+	void removePluginDriver(SegaAudioInterface *owner);
+
+	void nextTickEx(int32 *buffer, uint32 bufferSize) override;
+
+	void timerCallbackA();
+	void timerCallbackB();
+
+	uint16 pcmCountSamples(uint16 address) const;
+
+	int8 *_pcmBanks;
+
+	uint16 _musicVolume;
+	uint16 _sfxVolume;
+
+	SegaPCMChannel **_pcmChan;
+	SegaPSG *_psgDev;
+	PCMDevice_Base *_pcmDev;
+
+	SegaAudioPluginDriver *_drv;
+	void *_drvOwner;
+	bool _ready;
+
+	static SegaAudioInterfaceInternal *_refInstance;
+	static int _refCount;
+};
+
+SegaAudioInterfaceInternal *SegaAudioInterfaceInternal::_refInstance = 0;
+int SegaAudioInterfaceInternal::_refCount = 0;
+
+SegaAudioInterfaceInternal::SegaAudioInterfaceInternal(Audio::Mixer *mixer, SegaAudioInterface *owner, SegaAudioPluginDriver *driver) :TownsPC98_FmSynth(mixer, TownsPC98_FmSynth::kTypeTowns),
+	_drv(driver), _drvOwner(owner),	_musicVolume(Audio::Mixer::kMaxMixerVolume), _sfxVolume(Audio::Mixer::kMaxMixerVolume), _pcmBanks(0), _pcmDev(0), _psgDev(0), _pcmChan(0), _ready(false) {
+}
+
+SegaAudioInterfaceInternal::~SegaAudioInterfaceInternal() {
+	deinit();
+	Common::StackLock lock(_mutex);
+	_ready = false;
+
+	if (_pcmChan) {
+		for (int i = 0; i < 8; ++i)
+			delete _pcmChan[i];
+		delete[] _pcmChan;
+	}
+	delete _pcmDev;
+	delete _psgDev;
+	delete[] _pcmBanks;
+}
+
+SegaAudioInterfaceInternal *SegaAudioInterfaceInternal::addNewRef(Audio::Mixer *mixer, SegaAudioInterface *owner, SegaAudioPluginDriver *driver) {
+	_refCount++;
+	if (_refCount == 1 && _refInstance == 0)
+		_refInstance = new SegaAudioInterfaceInternal(mixer, owner, driver);
+	else if (_refCount < 2 || _refInstance == 0)
+		error("SegaAudioInterfaceInternal::addNewRef(): Internal reference management failure");
+	else if (!_refInstance->assignPluginDriver(owner, driver))
+		error("SegaAudioInterfaceInternal::addNewRef(): Plugin driver conflict");
+
+	return _refInstance;
+}
+
+void SegaAudioInterfaceInternal::releaseRef(SegaAudioInterface *owner) {
+	if (!_refCount)
+		return;
+
+	_refCount--;
+
+	if (_refCount) {
+		if (_refInstance)
+			_refInstance->removePluginDriver(owner);
+	} else {
+		delete _refInstance;
+		_refInstance = 0;
+	}
+}
+
+bool SegaAudioInterfaceInternal::init() {
+	if (_ready)
+		return true;
+
+	if (!TownsPC98_FmSynth::init())
+		return false;
+
+	_pcmBanks = new int8[0x10000];
+	memset(_pcmBanks, 0, 0x10000);
+	_pcmChan = new SegaPCMChannel*[8];
+	_psgDev = new SegaPSG(7670454 / 72, 16);
+	_pcmDev = new PCMDevice_Base(33300, 16, 8);
+	for (int i = 0; i < 8; ++i) {
+		_pcmChan[i] = new SegaPCMChannel();
+		_pcmDev->assignChannel(i, _pcmChan[i]);
+	}
+
+	reset();
+	
+	writeReg(0, 0x26, 0xC6);
+	writeReg(0, 0x25, 0x62);
+	writeReg(0, 0x24, 0x00);	
+	writeReg(0, 0x27, 0x30);
+
+	// Declare FM channels as music channels and PCM channels as sound effect channels. 
+	setSoundEffectChanMask(~0x1FF);
+
+	_ready = true;
+
+	return true;
+}
+
+void SegaAudioInterfaceInternal::loadPCMData(uint16 address, const uint8 *data, uint16 dataSize) {
+	if (!_ready)
+		return;
+	Common::StackLock lock(_mutex);
+	while (dataSize--)
+		_pcmBanks[address++] = (*data & 0x80) ? (*data++ & 0x7F) : -*data++;
+}
+
+void SegaAudioInterfaceInternal::playPCMChannel(uint8 channel, uint8 dataStart, uint16 loopStart, uint16 rate, uint8 pan, uint8 vol) {
+	if (!_ready)
+		return;
+	Common::StackLock lock(_mutex);
+	assert(channel < 8);
+	_pcmChan[channel]->play(_pcmBanks, pcmCountSamples(dataStart << 8), dataStart << 8, loopStart, pcmCountSamples(loopStart), rate, pan, vol);
+}
+
+void SegaAudioInterfaceInternal::stopPCMChannel(uint8 channel) {
+	if (!_ready)
+		return;
+	Common::StackLock lock(_mutex);
+	assert(channel < 8);
+	_pcmChan[channel]->stop();
+}
+
+void SegaAudioInterfaceInternal::psgWrite(uint8 data) {
+	if (!_ready)
+		return;
+	Common::StackLock lock(_mutex);
+	_psgDev->write(data);
+}
+
+void SegaAudioInterfaceInternal::setMusicVolume(int volume) {
+	if (!_ready)
+		return;
+	Common::StackLock lock(_mutex);
+	_musicVolume = CLIP<uint16>(volume, 0, Audio::Mixer::kMaxMixerVolume);
+	_pcmDev->setMusicVolume(_musicVolume);
+	setVolumeIntern(_musicVolume, _sfxVolume);
+}
+
+void SegaAudioInterfaceInternal::setSoundEffectVolume(int volume) {
+	if (!_ready)
+		return;
+	Common::StackLock lock(_mutex);
+	_sfxVolume = CLIP<uint16>(volume, 0, Audio::Mixer::kMaxMixerVolume);
+	_pcmDev->setSfxVolume(_sfxVolume);
+	setVolumeIntern(_musicVolume, _sfxVolume);
+}
+
+void SegaAudioInterfaceInternal::setSoundEffectChanMask(int mask) {
+	if (!_ready)
+		return;
+	Common::StackLock lock(_mutex);
+	_psgDev->setSfxChanMask((mask >> 6) & 7);
+	_pcmDev->setSfxChanMask(mask >> 9);
+	mask &= 0x3f;
+	setVolumeChannelMasks(~mask, mask);
+}
+
+Common::Mutex &SegaAudioInterfaceInternal::mutex() {
+	return _mutex;
+}
+
+int SegaAudioInterfaceInternal::mixerThreadLockCounter() const {
+	return _mixerThreadLockCounter;
+}
+
+bool SegaAudioInterfaceInternal::assignPluginDriver(SegaAudioInterface *owner, SegaAudioPluginDriver *driver, bool externalMutexHandling) {
+	Common::StackLock lock(_mutex);
+	if (_refCount <= 1)
+		return true;
+
+	if (_drv) {
+		if (driver && driver != _drv)
+			return false;
+	} else {
+		_drv = driver;
+		_drvOwner = owner;
+	}
+
+	return true;
+}
+
+void SegaAudioInterfaceInternal::removePluginDriver(SegaAudioInterface *owner) {
+	Common::StackLock lock(_mutex);
+	if (_drvOwner == owner)
+		_drv = 0;
+}
+
+void SegaAudioInterfaceInternal::nextTickEx(int32 *buffer, uint32 bufferSize) {
+	Common::StackLock lock(_mutex);
+	if (!_ready)
+		return;
+
+	_pcmDev->readBuffer(buffer, bufferSize);
+	_psgDev->readBuffer(buffer, bufferSize);
+}
+
+void SegaAudioInterfaceInternal::timerCallbackA() {
+	if (_drv && _ready)
+		_drv->timerCallbackA();
+}
+
+void SegaAudioInterfaceInternal::timerCallbackB() {
+	if (_drv && _ready)
+		_drv->timerCallbackB();
+}
+
+uint16 SegaAudioInterfaceInternal::pcmCountSamples(uint16 address) const {
+	const int8 *start = &_pcmBanks[address];
+	const int8 *end = &_pcmBanks[0xFFFF];
+	const int8 *pos = start;
+	for (; pos <= end; ++pos) {
+		if (*pos == 0x7F)
+			break;
+	}
+	return pos - start;
+}
+
+void SegaPCMChannel::play(const int8 *data, uint16 dataSize, uint16 startAddress, uint16 loopStart, uint16 loopLen, uint16 rate, uint8 pan, uint8 vol) {
+	setData(data, (startAddress + dataSize) << 11, startAddress << 11);
+	setupLoop(loopLen ? loopStart : (startAddress + dataSize), loopLen);
+	setRate(rate);
+	setPanPos(pan);
+	setVolume(vol);
+	activate();
+	_playing = true;
+}
+
+void SegaPCMChannel::stop() {
+	stopInternal();
+}
+
+bool SegaPCMChannel::isPlaying() const {
+	return _playing;
+}
+
+void SegaPCMChannel::stopInternal() {
+	_playing = false;
+}
+
+SegaPSG::SegaPSG(int samplingRate, int deviceVolume) : _intRate(3579545), _extRate(samplingRate), _deviceVolume(deviceVolume), _numChannels(3), _cr(-1),
+	_musicVolume(Audio::Mixer::kMaxMixerVolume), _sfxVolume(Audio::Mixer::kMaxMixerVolume), _sfxChanMask(0), _nfb(0), _nfs(0), _timer(0) {
+	memset(_attnTable, 0, sizeof(_attnTable));
+	for (int i = 0; i < 15; ++i)
+		_attnTable[i] =  (32767.0 / (double)(_numChannels + 1)) / pow(2.0, (double)(i << 1) / 6.0);
+}
+
+void SegaPSG::write(uint8 val) {
+	if (val & 0x80) {
+		uint8 reg = (val >> 4) & 7;
+		val &= 0x0F;
+		_cr = -1;
+		// The noise generator is not implemented, since we don't have a single test case for it.
+		if (reg == 7) {
+			_nat = val;
+		} else if (reg & 1) {
+			_channels[reg >> 1].curSample = _attnTable[val];
+		} else if (reg == 6) {
+			_nfb = val >> 2;
+			_nfs = val & 3;
+		} else {
+			_channels[reg >> 1].freq = (_channels[reg >> 1].freq & 0x3F0) | val;
+			_cr = reg >> 1;
+		}
+	} else if (_cr != -1) {
+		_channels[_cr].freq = (_channels[_cr].freq & 0x0F) | (val << 4);
+	}
+}
+
+void SegaPSG::setMusicVolume(uint16 vol) {
+	_musicVolume = vol;
+}
+
+void SegaPSG::setSfxVolume(uint16 vol) {
+	_sfxVolume = vol;
+}
+
+void SegaPSG::setSfxChanMask(int mask) {
+	_sfxChanMask = mask;
+}
+
+void SegaPSG::readBuffer(int32 *buffer, uint32 bufferSize) {
+	while (bufferSize--) {
+		_timer += _intRate;
+		while (_timer >= _extRate) {
+			_timer -= _extRate;
+			// The noise generator is not implemented, since we don't have a single test case for it.
+			for (int i = 0; i < _numChannels; ++i) {
+				Channel *c = &_channels[i];
+				if (c->counter)
+					c->counter--;
+				if (!c->counter) {
+					c->counter = c->freq << 4;
+					c->out = c->curSample;
+					c->curSample = ~c->curSample;
+					if (c->curSample < 0)
+						c->curSample++;
+				}
+			}
+		}
+
+		int32 smp = 0;
+		for (int i = 0; i < _numChannels; ++i)
+			smp += ((_channels[i].out * (((1 << i) & _sfxChanMask) ? _sfxVolume : _musicVolume)) / Audio::Mixer::kMaxMixerVolume);
+
+		smp = (smp * _deviceVolume) >> 7;
+		*buffer++ += smp;
+		*buffer++ += smp;
+	}
+}
+
+SegaAudioInterface::SegaAudioInterface(Audio::Mixer *mixer, SegaAudioPluginDriver *driver) {
+	_internal = SegaAudioInterfaceInternal::addNewRef(mixer, this, driver);
+}
+
+SegaAudioInterface::~SegaAudioInterface() {
+	SegaAudioInterfaceInternal::releaseRef(this);
+	_internal = 0;
+}
+
+bool SegaAudioInterface::init() {
+	return _internal->init();
+}
+
+void SegaAudioInterface::reset() {
+	_internal->reset();
+}
+
+void SegaAudioInterface::loadPCMData(uint16 address, const uint8 *data, uint16 dataSize) {
+	_internal->loadPCMData(address, data, dataSize);
+}
+
+void SegaAudioInterface::playPCMChannel(uint8 channel, uint8 dataStart, uint16 loopStart, uint16 rate, uint8 pan, uint8 vol) {
+	_internal->playPCMChannel(channel, dataStart, loopStart, rate, pan, vol);
+}
+
+void SegaAudioInterface::stopPCMChannel(uint8 channel) {
+	_internal->stopPCMChannel(channel);
+}
+
+void SegaAudioInterface::writeReg(uint8 part, uint8 regAddress, uint8 value) {
+	_internal->writeReg(part, regAddress, value);
+}
+
+uint8 SegaAudioInterface::readReg(uint8 part, uint8 regAddress) {
+	return _internal->readReg(part, regAddress);
+}
+
+void SegaAudioInterface::psgWrite(uint8 data) {
+	_internal->psgWrite(data);
+}
+
+void SegaAudioInterface::setMusicVolume(int volume) {
+	_internal->setMusicVolume(volume);
+}
+
+void SegaAudioInterface::setSoundEffectVolume(int volume) {
+	_internal->setSoundEffectVolume(volume);
+}
+
+void SegaAudioInterface::setSoundEffectChanMask(int mask) {
+	_internal->setSoundEffectChanMask(mask);
+}
+
+SegaAudioInterface::MutexLock SegaAudioInterface::stackLockMutex() {
+	return MutexLock(_internal);
+}
+
+SegaAudioInterface::MutexLock SegaAudioInterface::stackUnlockMutex() {
+	return MutexLock(_internal, _internal->mixerThreadLockCounter());
+}
+
+SegaAudioInterface::MutexLock::MutexLock(SegaAudioInterfaceInternal *saii, int reverse) : _saii(saii), _count(reverse) {
+	if (!_saii)
+		return;
+
+	if (!reverse) {
+		_saii->mutex().lock();
+		return;
+	}
+
+	while (reverse--)
+		_saii->mutex().unlock();
+}
+
+SegaAudioInterface::MutexLock::~MutexLock() {
+	if (!_saii)
+		return;
+
+	if (!_count)
+		_saii->mutex().unlock();
+
+	while (_count--)
+		_saii->mutex().lock();
+}
diff --git a/audio/softsynth/fmtowns_pc98/sega_audio.h b/audio/softsynth/fmtowns_pc98/sega_audio.h
new file mode 100644
index 0000000000..102cda9524
--- /dev/null
+++ b/audio/softsynth/fmtowns_pc98/sega_audio.h
@@ -0,0 +1,82 @@
+/* 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 SEGA_AUDIO_H
+#define SEGA_AUDIO_H
+
+#include "common/scummsys.h"
+
+namespace Audio {
+class Mixer;
+}
+
+class SegaAudioInterfaceInternal;
+class SegaAudioPluginDriver {
+public:
+	virtual ~SegaAudioPluginDriver() {}
+	virtual void timerCallback60Hz() {}
+	virtual void timerCallbackA() {}
+	virtual void timerCallbackB() {}
+};
+
+class SegaAudioInterface {
+public:
+	SegaAudioInterface(Audio::Mixer *mixer, SegaAudioPluginDriver *driver);
+	~SegaAudioInterface();
+
+	bool init();
+	void reset();
+
+	void loadPCMData(uint16 address, const uint8 *data, uint16 dataSize);
+	void playPCMChannel(uint8 channel, uint8 dataStart, uint16 loopStart, uint16 rate, uint8 pan, uint8 env);
+	void stopPCMChannel(uint8 channel);
+
+	void writeReg(uint8 part, uint8 regAddress, uint8 value);
+	uint8 readReg(uint8 part, uint8 regAddress);
+
+	void psgWrite(uint8 data);
+
+	void setMusicVolume(int volume);
+	void setSoundEffectVolume(int volume);
+
+	// Defines the channels used as sound effect channels for the purpose of ScummVM GUI volume control.
+	// The first 6 bits are 6 fm channels. The next 3 bits are psg channels. The bits that follow represent pcm channels.
+	void setSoundEffectChanMask(int mask);
+
+	class MutexLock {
+		friend class SegaAudioInterface;
+	public:
+		~MutexLock();
+	private:
+		MutexLock(SegaAudioInterfaceInternal *saii, int reverse = 0);
+		SegaAudioInterfaceInternal *_saii;
+		int _count;
+	};
+
+	MutexLock stackLockMutex();
+	MutexLock stackUnlockMutex();
+
+private:
+	SegaAudioInterfaceInternal *_internal;
+};
+
+#endif
diff --git a/audio/softsynth/fmtowns_pc98/towns_audio.cpp b/audio/softsynth/fmtowns_pc98/towns_audio.cpp
index 17fec43c0a..a7dd4b340a 100644
--- a/audio/softsynth/fmtowns_pc98/towns_audio.cpp
+++ b/audio/softsynth/fmtowns_pc98/towns_audio.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "audio/softsynth/fmtowns_pc98/towns_audio.h"
+#include "audio/softsynth/fmtowns_pc98/pcm_common.h"
 #include "audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h"
 
 #include "common/debug.h"
@@ -31,7 +32,7 @@
 
 class TownsAudio_WaveTable {
 friend class TownsAudioInterfaceInternal;
-friend class TownsAudio_PcmChannel;
+friend class TownsAudio_PCMChannel;
 public:
 	TownsAudio_WaveTable();
 	~TownsAudio_WaveTable();
@@ -52,14 +53,13 @@ private:
 	int8 *data;
 };
 
-class TownsAudio_PcmChannel {
+class TownsAudio_PCMChannel final : public PCMChannel_Base {
 public:
-	TownsAudio_PcmChannel();
-	~TownsAudio_PcmChannel();
+	TownsAudio_PCMChannel();
+	~TownsAudio_PCMChannel();
 
-	void clear();
-
-	void loadData(TownsAudio_WaveTable *w);
+	void clear() override;
+	void loadDataFromWaveTable(TownsAudio_WaveTable *w);
 	void loadData(uint8 *buffer, uint32 size);
 
 	int initInstrument(uint8 &note, TownsAudio_WaveTable *&tables, int numTables);
@@ -73,18 +73,16 @@ public:
 	void setPitch(uint32 pt);
 	void setBalance(uint8 blc);
 
-	void updateOutput();
-	int32 currentSampleLeft();
-	int32 currentSampleRight();
+	bool isPlaying() const override;
 
 	bool _keyPressed;
 	bool _reserved;
 	bool _activeKey;
 	bool _activeEffect;
-	bool _activeOutput;
 
 private:
-	void setupLoop(uint32 loopStart, uint32 loopLen);
+	void stopInternal() override;
+
 	void setNote(uint8 note, TownsAudio_WaveTable *w, bool stepLimit = false);
 	void setVelo(uint8 velo);
 
@@ -94,27 +92,14 @@ private:
 	void envRelease();
 
 	uint8 *_curInstrument;
+	int8 *_extData;
 
 	uint8 _note;
-
 	uint8 _velo;
 	uint8 _level;
-	uint8 _tl;
-
-	uint8 _panLeft;
-	uint8 _panRight;
-
-	int8 *_data;
-
-	uint32 _loopStart;
-	uint32 _loopLen;
-	uint32 _dataEnd;
 
 	uint16 _stepNote;
 	uint16 _stepPitch;
-	uint16 _step;
-
-	uint32 _pos;
 
 	uint8 _envTotalLevel;
 	uint8 _envAttackRate;
@@ -127,13 +112,11 @@ private:
 
 	EnvelopeState _envState;
 
-	int8 *_extData;
-
 	static const uint16 _pcmPhase1[];
 	static const uint16 _pcmPhase2[];
 };
 
-class TownsAudioInterfaceInternal : public TownsPC98_FmSynth {
+class TownsAudioInterfaceInternal final : public TownsPC98_FmSynth {
 private:
 	TownsAudioInterfaceInternal(Audio::Mixer *mixer, TownsAudioInterface *owner, TownsAudioInterfacePluginDriver *driver, bool externalMutex);
 public:
@@ -237,7 +220,8 @@ private:
 	int pcmSetPitch(int chan, int pitch);
 	int pcmSetLevel(int chan, int lvl);
 
-	TownsAudio_PcmChannel *_pcmChan;
+	TownsAudio_PCMChannel **_pcmChan;
+	PCMDevice_Base *_pcmDev;
 
 	uint8 _numReservedChannels;
 	uint8 *_pcmInstruments;
@@ -253,13 +237,8 @@ private:
 	uint8 _outputMute[16];
 	bool _updateOutputVol;
 
-	const uint32 _intRate;
-	const uint32 _extRate;
-	uint32 _timer;
-
 	uint16 _musicVolume;
 	uint16 _sfxVolume;
-	int _pcmSfxChanMask;
 
 	TownsAudioInterfacePluginDriver *_drv;
 	void *_drvOwner;
@@ -277,7 +256,7 @@ private:
 
 TownsAudioInterfaceInternal::TownsAudioInterfaceInternal(Audio::Mixer *mixer, TownsAudioInterface *owner, TownsAudioInterfacePluginDriver *driver, bool externalMutex) :
 	TownsPC98_FmSynth(mixer, kTypeTowns), _fmInstruments(0), _pcmInstruments(0), _pcmChan(0), _waveTables(0), _waveTablesTotalDataSize(0),
-	_intRate(72), _extRate(384), _timer(0), _drv(driver), _drvOwner(owner), _externalMutex(externalMutex), _pcmSfxChanMask(0), _outputVolumeFlags(0),
+	_drv(driver), _drvOwner(owner), _externalMutex(externalMutex), _outputVolumeFlags(0), _pcmDev(0),
 	_fmChanPlaying(0), _musicVolume(Audio::Mixer::kMaxMixerVolume), _sfxVolume(Audio::Mixer::kMaxMixerVolume),
 	_numReservedChannels(0), _numWaveTables(0), _updateOutputVol(false), _ready(false) {
 #define INTCB(x) &TownsAudioInterfaceInternal::intf_##x
@@ -407,7 +386,13 @@ TownsAudioInterfaceInternal::~TownsAudioInterfaceInternal() {
 	delete[] _fmInstruments;
 	delete[] _pcmInstruments;
 	delete[] _waveTables;
-	delete[] _pcmChan;
+
+	if (_pcmChan) {
+		for (int i = 0; i < 8; ++i)
+			delete _pcmChan[i];
+		delete[] _pcmChan;
+	}
+	delete _pcmDev;
 }
 
 TownsAudioInterfaceInternal *TownsAudioInterfaceInternal::addNewRef(Audio::Mixer *mixer, TownsAudioInterface *owner, TownsAudioInterfacePluginDriver *driver, bool externalMutex) {
@@ -446,14 +431,16 @@ bool TownsAudioInterfaceInternal::init() {
 	_fmInstruments = new uint8[128 * 48];
 	_pcmInstruments = new uint8[32 * 128];
 	_waveTables = new TownsAudio_WaveTable[128];
-	_pcmChan = new TownsAudio_PcmChannel[8];
-
-	_timer = 0;
+	_pcmChan = new TownsAudio_PCMChannel*[8];
+	_pcmDev = new PCMDevice_Base(19300, 128, 8);
+	for (int i = 0; i < 8; ++i) {
+		_pcmChan[i] = new TownsAudio_PCMChannel();
+		_pcmDev->assignChannel(i, _pcmChan[i]);
+	}
 
 	if (!TownsPC98_FmSynth::init())
 		return false;
 
-
 	setVolumeChannelMasks(-1, 0);
 
 	_ready = true;
@@ -492,18 +479,26 @@ int TownsAudioInterfaceInternal::processCommand(int command, va_list &args) {
 void TownsAudioInterfaceInternal::setMusicVolume(int volume) {
 	Common::StackLock lock(_mutex);
 	_musicVolume = CLIP<uint16>(volume, 0, Audio::Mixer::kMaxMixerVolume);
+	if (!_ready)
+		return;
+	_pcmDev->setMusicVolume(_musicVolume);
 	setVolumeIntern(_musicVolume, _sfxVolume);
 }
 
 void TownsAudioInterfaceInternal::setSoundEffectVolume(int volume) {
 	Common::StackLock lock(_mutex);
 	_sfxVolume = CLIP<uint16>(volume, 0, Audio::Mixer::kMaxMixerVolume);
+	if (!_ready)
+		return;
+	_pcmDev->setSfxVolume(_sfxVolume);
 	setVolumeIntern(_musicVolume, _sfxVolume);
 }
 
 void TownsAudioInterfaceInternal::setSoundEffectChanMask(int mask) {
 	Common::StackLock lock(_mutex);
-	_pcmSfxChanMask = mask >> 6;
+	if (!_ready)
+		return;
+	_pcmDev->setSfxChanMask(mask >> 6);
 	mask &= 0x3f;
 	setVolumeChannelMasks(~mask, mask);
 }
@@ -538,41 +533,7 @@ void TownsAudioInterfaceInternal::nextTickEx(int32 *buffer, uint32 bufferSize) {
 	if (_updateOutputVol)
 		updateOutputVolumeInternal();
 
-	for (uint32 i = 0; i < bufferSize; i++) {
-		_timer += _intRate;
-		while (_timer >= _extRate) {
-			_timer -= _extRate;
-
-			for (int ii = 0; ii < 8; ii++)
-				_pcmChan[ii].updateOutput();
-		}
-
-		int32 finOutL = 0;
-		int32 finOutR = 0;
-
-		for (int ii = 0; ii < 8; ii++) {
-			if (_pcmChan[ii]._activeOutput) {
-				int32 oL = _pcmChan[ii].currentSampleLeft();
-				int32 oR = _pcmChan[ii].currentSampleRight();
-				if ((1 << ii) & (~_pcmSfxChanMask)) {
-					oL = (oR * _musicVolume) / Audio::Mixer::kMaxMixerVolume;
-					oR = (oR * _musicVolume) / Audio::Mixer::kMaxMixerVolume;
-				}
-				if ((1 << ii) & _pcmSfxChanMask) {
-					oL = (oL * _sfxVolume) / Audio::Mixer::kMaxMixerVolume;
-					oR = (oR * _sfxVolume) / Audio::Mixer::kMaxMixerVolume;
-				}
-				finOutL += oL;
-				finOutR += oR;
-
-				if (!(_pcmChan[ii]._activeKey || _pcmChan[ii]._activeEffect))
-					_pcmChan[ii]._activeOutput = false;
-			}
-		}
-
-		buffer[i << 1] += finOutL;
-		buffer[(i << 1) + 1] += finOutR;
-	}
+	_pcmDev->readBuffer(buffer, bufferSize);
 }
 
 void TownsAudioInterfaceInternal::timerCallbackA() {
@@ -774,18 +735,18 @@ int TownsAudioInterfaceInternal::intf_reserveEffectChannels(va_list &args) {
 	if (numChan < _numReservedChannels) {
 		int c = 8 - _numReservedChannels;
 		for (int i = numChan; i; i--)
-			_pcmChan[c--]._activeEffect = false;
+			_pcmChan[c--]->_activeEffect = false;
 	} else {
 		int c = 7 - _numReservedChannels;
 		for (int i = numChan - _numReservedChannels; i; i--) {
-			_pcmChan[c]._keyPressed = false;
-			_pcmChan[c--]._activeKey = false;
+			_pcmChan[c]->_keyPressed = false;
+			_pcmChan[c--]->_activeKey = false;
 		}
 	}
 
 	_numReservedChannels = numChan;
 	for (int i = 0; i < 8; i++)
-		_pcmChan[i]._reserved = i >= (8 - _numReservedChannels) ? true : false;
+		_pcmChan[i]->_reserved = i >= (8 - _numReservedChannels) ? true : false;
 
 	return 0;
 }
@@ -865,10 +826,10 @@ int TownsAudioInterfaceInternal::intf_pcmPlayEffect(va_list &args) {
 
 	chan -= 0x40;
 
-	if (!_pcmChan[chan]._reserved)
+	if (!_pcmChan[chan]->_reserved)
 		return 7;
 
-	if (_pcmChan[chan]._activeEffect)
+	if (_pcmChan[chan]->_activeEffect)
 		return 2;
 
 	TownsAudio_WaveTable w;
@@ -880,10 +841,8 @@ int TownsAudioInterfaceInternal::intf_pcmPlayEffect(va_list &args) {
 	if (!w.size)
 		return 6;
 
-	TownsAudio_PcmChannel *p = &_pcmChan[chan];
-
-	p->loadData(data + 32, w.size);
-	p->keyOn(note, velo, &w);
+	_pcmChan[chan]->loadData(data + 32, w.size);
+	_pcmChan[chan]->keyOn(note, velo, &w);
 
 	return 0;
 }
@@ -899,12 +858,12 @@ int TownsAudioInterfaceInternal::intf_pcmEffectPlaying(va_list &args) {
 	if (chan < 0x40 || chan > 0x47)
 		return 1;
 	chan -= 0x40;
-	return _pcmChan[chan]._activeEffect ? 1 : 0;
+	return _pcmChan[chan]->_activeEffect ? 1 : 0;
 }
 
 int TownsAudioInterfaceInternal::intf_pcmDisableAllChannels(va_list &args) {
 	for (int i = 0; i < 8; ++i)
-		_pcmChan[i]._activeOutput = false;
+		_pcmChan[i]->deactivate();
 	return 0;
 }
 
@@ -1064,7 +1023,7 @@ int TownsAudioInterfaceInternal::intf_getOutputMute (va_list &args) {
 
 int TownsAudioInterfaceInternal::intf_pcmUpdateEnvelopeGenerator(va_list &args) {
 	for (int i = 0; i < 8; i++)
-		_pcmChan[i].updateEnvelopeGenerator();
+		_pcmChan[i]->updateEnvelopeGenerator();
 	return 0;
 }
 
@@ -1398,7 +1357,7 @@ void TownsAudioInterfaceInternal::pcmReset() {
 	_numReservedChannels = 0;
 
 	for (int i = 0; i < 8; i++)
-		_pcmChan[i].clear();
+		_pcmChan[i]->clear();
 
 	memset(_pcmInstruments, 0, 128 * 32);
 	static uint8 name[] = { 0x4E, 0x6F, 0x20, 0x44, 0x61, 0x74, 0x61, 0x21 };
@@ -1425,7 +1384,7 @@ int TownsAudioInterfaceInternal::pcmKeyOn(int chan, int note, int velo) {
 
 	chan -= 0x40;
 	uint8 noteT = note;
-	TownsAudio_PcmChannel *p = &_pcmChan[chan];
+	TownsAudio_PCMChannel *p = _pcmChan[chan];
 
 	if (p->_reserved || p->_keyPressed)
 		return 2;
@@ -1435,7 +1394,7 @@ int TownsAudioInterfaceInternal::pcmKeyOn(int chan, int note, int velo) {
 	if (res)
 		return res;
 
-	p->loadData(w);
+	p->loadDataFromWaveTable(w);
 	p->keyOn(noteT, velo, w);
 
 	return 0;
@@ -1446,7 +1405,7 @@ int TownsAudioInterfaceInternal::pcmKeyOff(int chan) {
 		return 1;
 
 	chan -= 0x40;
-	_pcmChan[chan].keyOff();
+	_pcmChan[chan]->keyOff();
 	return 0;
 }
 
@@ -1455,7 +1414,8 @@ int TownsAudioInterfaceInternal::pcmChanOff(int chan) {
 		return 1;
 
 	chan -= 0x40;
-	_pcmChan[chan]._keyPressed = _pcmChan[chan]._activeEffect = _pcmChan[chan]._activeKey = _pcmChan[chan]._activeOutput = false;
+	_pcmChan[chan]->_keyPressed = _pcmChan[chan]->_activeEffect = _pcmChan[chan]->_activeKey = false;
+	_pcmChan[chan]->deactivate();
 
 	return 0;
 }
@@ -1477,7 +1437,7 @@ int TownsAudioInterfaceInternal::pcmSetPanPos(int chan, int mode) {
 		blc = ((119 + mode) ^ (mode << 4)) & 0xff;
 	}
 
-	_pcmChan[chan].setBalance(blc);
+	_pcmChan[chan]->setBalance(blc);
 
 	return 0;
 }
@@ -1488,7 +1448,7 @@ int TownsAudioInterfaceInternal::pcmSetInstrument(int chan, int instrId) {
 	if (instrId > 31)
 		return 3;
 	chan -= 0x40;
-	_pcmChan[chan].setInstrument(&_pcmInstruments[instrId * 128]);
+	_pcmChan[chan]->setInstrument(&_pcmInstruments[instrId * 128]);
 
 	return 0;
 }
@@ -1509,8 +1469,6 @@ int TownsAudioInterfaceInternal::pcmSetPitch(int chan, int pitch) {
 		return 3;
 
 	chan -= 0x40;
-	TownsAudio_PcmChannel *p = &_pcmChan[chan];
-
 	uint32 pts = 0x4000;
 
 	if (pitch < 0)
@@ -1518,7 +1476,7 @@ int TownsAudioInterfaceInternal::pcmSetPitch(int chan, int pitch) {
 	else if (pitch > 0)
 		pts = (((pitch + 0x2001) << 16) / 0x2000) >> 2;
 
-	p->setPitch(pts);
+	_pcmChan[chan]->setPitch(pts);
 
 	return 0;
 }
@@ -1531,7 +1489,7 @@ int TownsAudioInterfaceInternal::pcmSetLevel(int chan, int lvl) {
 		return 3;
 
 	chan -= 0x40;
-	_pcmChan[chan].setLevel(lvl);
+	_pcmChan[chan]->setLevel(lvl);
 
 	return 0;
 }
@@ -1585,63 +1543,49 @@ const uint8 TownsAudioInterfaceInternal::_fmDefaultInstrument[] = {
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 };
 
-TownsAudio_PcmChannel::TownsAudio_PcmChannel() {
-	_extData = 0;
+TownsAudio_PCMChannel::TownsAudio_PCMChannel() : PCMChannel_Base(), _extData(0) {
 	clear();
 }
 
-TownsAudio_PcmChannel::~TownsAudio_PcmChannel() {
+TownsAudio_PCMChannel::~TownsAudio_PCMChannel() {
 	clear();
 }
 
-void TownsAudio_PcmChannel::clear() {
-	_curInstrument = 0;
-	_note = _tl = _level = _velo = 0;
-
-	_data = 0;
-	_dataEnd = 0;
-	_loopLen = 0;
+void TownsAudio_PCMChannel::clear() {
+	PCMChannel_Base::clear();
 
-	_pos = 0;
-	_loopStart = 0;
+	_curInstrument = 0;
+	_note = _level = _velo = 0;
 
-	_step = 0;
 	_stepNote = 0x4000;
 	_stepPitch = 0x4000;
 
-	_panLeft = _panRight = 7;
-
 	_envTotalLevel = _envAttackRate = _envDecayRate = _envSustainLevel = _envSustainRate = _envReleaseRate = 0;
 	_envStep = _envCurrentLevel = 0;
 
 	_envState = kEnvReady;
 
-	_activeKey = _activeEffect = _activeOutput = _keyPressed = _reserved = false;
+	_activeKey = _activeEffect = _keyPressed = _reserved = false;
 
 	delete[] _extData;
 	_extData = 0;
 }
 
-void TownsAudio_PcmChannel::loadData(TownsAudio_WaveTable *w) {
-	_data = w->data;
-	_dataEnd = w->size << 11;
-	_pos = 0;
+void TownsAudio_PCMChannel::loadDataFromWaveTable(TownsAudio_WaveTable *w) {
+	setData(w->data, w->size << 11);
 }
 
-void TownsAudio_PcmChannel::loadData(uint8 *buffer, uint32 size) {
+void TownsAudio_PCMChannel::loadData(uint8 *buffer, uint32 size) {
 	delete[] _extData;
 	_extData = new int8[size];
-	int8 *src = (int8 *)buffer;
+	int8 *src = (int8*)buffer;
 	int8 *dst = _extData;
 	for (uint32 i = 0; i < size; i++)
 		*dst++ = *src & 0x80 ? (*src++ & 0x7f) : -*src++;
-
-	_data = _extData;
-	_dataEnd = size << 11;
-	_pos = 0;
+	setData(_extData, size << 11);
 }
 
-int TownsAudio_PcmChannel::initInstrument(uint8 &note, TownsAudio_WaveTable *&tables, int numTables) {
+int TownsAudio_PCMChannel::initInstrument(uint8 &note, TownsAudio_WaveTable *&tables, int numTables) {
 	int i = 0;
 	for (; i < 8; i++) {
 		if (note <= _curInstrument[16 + 2 * i])
@@ -1675,7 +1619,7 @@ int TownsAudio_PcmChannel::initInstrument(uint8 &note, TownsAudio_WaveTable *&ta
 	return 0;
 }
 
-void TownsAudio_PcmChannel::keyOn(uint8 note, uint8 velo, TownsAudio_WaveTable *w) {
+void TownsAudio_PCMChannel::keyOn(uint8 note, uint8 velo, TownsAudio_WaveTable *w) {
 	setupLoop(w->loopLen ? w->loopStart : w->size, w->loopLen);
 	setNote(note, w, _reserved);
 	setVelo(velo);
@@ -1685,15 +1629,15 @@ void TownsAudio_PcmChannel::keyOn(uint8 note, uint8 velo, TownsAudio_WaveTable *
 	else
 		_keyPressed = _activeKey = true;
 
-	_activeOutput = true;
+	activate();
 }
 
-void TownsAudio_PcmChannel::keyOff() {
+void TownsAudio_PCMChannel::keyOff() {
 	_keyPressed = false;
 	envRelease();
 }
 
-void TownsAudio_PcmChannel::updateEnvelopeGenerator() {
+void TownsAudio_PCMChannel::updateEnvelopeGenerator() {
 	if (!_envCurrentLevel) {
 		_activeKey = false;
 		_envState = kEnvReady;
@@ -1731,17 +1675,17 @@ void TownsAudio_PcmChannel::updateEnvelopeGenerator() {
 	default:
 		break;
 	}
-	_tl = (_envCurrentLevel >> 8) << 1;
+	setVolume((_envCurrentLevel >> 8) << 1);
 }
 
-void TownsAudio_PcmChannel::setInstrument(uint8 *instr) {
+void TownsAudio_PCMChannel::setInstrument(uint8 *instr) {
 	_curInstrument = instr;
 }
 
-void TownsAudio_PcmChannel::setLevel(uint8 lvl) {
+void TownsAudio_PCMChannel::setLevel(uint8 lvl) {
 	if (_reserved) {
 		_velo = lvl;
-		_tl = lvl << 1;
+		setVolume(lvl << 1);
 	} else {
 		int32 t = _envStep * lvl;
 		if (_level)
@@ -1752,57 +1696,33 @@ void TownsAudio_PcmChannel::setLevel(uint8 lvl) {
 			t /= _level;
 		_envCurrentLevel = t;
 		_level = lvl;
-		_tl = _envCurrentLevel >> 8;
+		setVolume(_envCurrentLevel >> 8);
 	}
 }
 
-void TownsAudio_PcmChannel::setPitch(uint32 pt) {
+void TownsAudio_PCMChannel::setPitch(uint32 pt) {
 	_stepPitch = pt & 0xffff;
-	_step = (_stepNote * _stepPitch) >> 14;
+	uint16 step = (_stepNote * _stepPitch) >> 14;
 
 //	if (_pcmChanUnkFlag & _chanFlags[chan])
 //		 unk[chan] = (((p->step * 1000) << 11) / 98) / 20833;
-
 	/*else*/
-	if (_activeEffect && (_step > 2048))
-		_step = 2048;
+	setRate(_activeEffect && (step > 2048) ? 2048 : step);
 }
 
-void TownsAudio_PcmChannel::setBalance(uint8 blc) {
-	_panLeft = blc & 0x0f;
-	_panRight = blc >> 4;
+void TownsAudio_PCMChannel::setBalance(uint8 blc) {
+	setPanPos(blc);
 }
 
-void TownsAudio_PcmChannel::updateOutput() {
-	if (_activeKey || _activeEffect) {
-		_pos += _step;
-
-		if (_pos >= _dataEnd) {
-			if (_loopStart != _dataEnd) {
-				_pos = _loopStart;
-				_dataEnd = _loopLen;
-			} else {
-				_pos = 0;
-				_activeKey = _activeEffect = false;
-			}
-		}
-	}
-}
-
-int32 TownsAudio_PcmChannel::currentSampleLeft() {
-	return (_activeOutput && _panLeft) ? (((_data[_pos >> 11] * _tl) * _panLeft) >> 3) : 0;
+bool TownsAudio_PCMChannel::isPlaying() const {
+	return _activeKey || _activeEffect;
 }
 
-int32 TownsAudio_PcmChannel::currentSampleRight() {
-	return (_activeOutput && _panRight) ? (((_data[_pos >> 11] * _tl) * _panRight) >> 3) : 0;
+void TownsAudio_PCMChannel::stopInternal() {
+	_activeKey = _activeEffect = false;
 }
 
-void TownsAudio_PcmChannel::setupLoop(uint32 loopStart, uint32 loopLen) {
-	_loopStart = loopStart << 11;
-	_loopLen = loopLen << 11;
-}
-
-void TownsAudio_PcmChannel::setNote(uint8 note, TownsAudio_WaveTable *w, bool stepLimit) {
+void TownsAudio_PCMChannel::setNote(uint8 note, TownsAudio_WaveTable *w, bool stepLimit) {
 	_note = note;
 	int8 diff = _note - w->baseNote;
 	uint16 r = w->rate + w->rateOffs;
@@ -1829,27 +1749,25 @@ void TownsAudio_PcmChannel::setNote(uint8 note, TownsAudio_WaveTable *w, bool st
 	}
 
 	_stepNote = s & 0xffff;
-	_step = (s * _stepPitch) >> 14;
-
-	if (stepLimit && _step > 2048)
-		_step = 2048;
+	uint16 step = (s * _stepPitch) >> 14;
+	setRate(stepLimit && (step > 2048) ? 2048 : step);
 }
 
-void TownsAudio_PcmChannel::setVelo(uint8 velo) {
+void TownsAudio_PCMChannel::setVelo(uint8 velo) {
 	if (_reserved) {
 		_velo = velo;
-		_tl = velo << 1;
+		setVolume(velo << 1);
 	} else {
 		_velo = velo;
 		uint32 lvl = _level * _velo;
 		_envTotalLevel = ((_envTotalLevel * lvl) >> 14) & 0xff;
 		_envSustainLevel = ((_envSustainLevel * lvl) >> 14) & 0xff;
 		envAttack();
-		_tl = (_envCurrentLevel >> 8) << 1;
+		setVolume((_envCurrentLevel >> 8) << 1);
 	}
 }
 
-void TownsAudio_PcmChannel::envAttack() {
+void TownsAudio_PCMChannel::envAttack() {
 	_envState = kEnvAttacking;
 	int16 t = _envTotalLevel << 8;
 	if (_envAttackRate == 127) {
@@ -1863,7 +1781,7 @@ void TownsAudio_PcmChannel::envAttack() {
 	}
 }
 
-void TownsAudio_PcmChannel::envDecay() {
+void TownsAudio_PCMChannel::envDecay() {
 	_envState = kEnvDecaying;
 	int16 t = _envTotalLevel - _envSustainLevel;
 	if (t < 0 || _envDecayRate == 127) {
@@ -1876,7 +1794,7 @@ void TownsAudio_PcmChannel::envDecay() {
 	}
 }
 
-void TownsAudio_PcmChannel::envSustain() {
+void TownsAudio_PCMChannel::envSustain() {
 	_envState = kEnvSustaining;
 	if (_envSustainLevel && _envSustainRate)
 		_envStep = (_envSustainRate == 127) ? 0 : (_envCurrentLevel / _envSustainRate) >> 1;
@@ -1884,7 +1802,7 @@ void TownsAudio_PcmChannel::envSustain() {
 		_envStep = _envCurrentLevel = 1;
 }
 
-void TownsAudio_PcmChannel::envRelease() {
+void TownsAudio_PCMChannel::envRelease() {
 	_envState = kEnvReleasing;
 	if (_envReleaseRate == 127)
 		_envStep = 0;
@@ -1894,11 +1812,11 @@ void TownsAudio_PcmChannel::envRelease() {
 		_envStep = _envCurrentLevel = 1;
 }
 
-const uint16 TownsAudio_PcmChannel::_pcmPhase1[] =  {
+const uint16 TownsAudio_PCMChannel::_pcmPhase1[] =  {
 	0x879B, 0x0F37, 0x1F58, 0x306E, 0x4288, 0x55B6, 0x6A08, 0x7F8F, 0x965E, 0xAE88, 0xC882, 0xE341
 };
 
-const uint16 TownsAudio_PcmChannel::_pcmPhase2[] =  {
+const uint16 TownsAudio_PCMChannel::_pcmPhase2[] =  {
 	0xFEFE, 0xF1A0, 0xE411, 0xD744, 0xCB2F, 0xBFC7, 0xB504, 0xAAE2, 0xA144, 0x9827, 0x8FAC
 };
 


Commit: 8e800d44b129a59f26d0032227ed79dda187f34a
    https://github.com/scummvm/scummvm/commit/8e800d44b129a59f26d0032227ed79dda187f34a
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:21+02:00

Commit Message:
KYRA: (EOB/SegaCD) - add sound driver

Changed paths:
  A engines/kyra/sound/drivers/segacd.cpp
  A engines/kyra/sound/drivers/segacd.h
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h
    engines/kyra/engine/kyra_rpg.cpp
    engines/kyra/module.mk
    engines/kyra/sound/sound.cpp
    engines/kyra/sound/sound.h
    engines/kyra/sound/sound_amiga_eob.cpp
    engines/kyra/sound/sound_amiga_lok.cpp
    engines/kyra/sound/sound_intern.h
    engines/kyra/sound/sound_pc98_eob.cpp
    engines/kyra/sound/sound_pc98_lok.cpp
    engines/kyra/sound/sound_pc98_v2.cpp
    engines/kyra/sound/sound_pc_midi.cpp
    engines/kyra/sound/sound_pc_v1.cpp
    engines/kyra/sound/sound_pc_v1.h
    engines/kyra/sound/sound_segacd_eob.cpp
    engines/kyra/sound/sound_towns_darkmoon.cpp
    engines/kyra/sound/sound_towns_lok.cpp


diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 664b4fe32b..698aca5947 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -447,8 +447,6 @@ Common::Error EoBCoreEngine::init() {
 
 	if (_flags.platform == Common::kPlatformPC98)
 		_sound->loadSfxFile("EFECT.OBJ");
-	else if (_flags.platform == Common::kPlatformSegaCD)
-		_sound->loadSfxFile("FMSE");
 
 	// Setup volume settings (and read in all ConfigManager settings)
 	_configNullSound = (MidiDriver::getMusicType(dev) == MT_NULL);
@@ -2282,7 +2280,7 @@ void EoBCoreEngine::inflictCharacterDamage(int charIndex, int damage) {
 		c->flags &= 1;
 		c->food = 0;
 		removeAllCharacterEffects(charIndex);
-		snd_playSoundEffect(22);
+		snd_playSoundEffect(_flags.platform == Common::kPlatformSegaCD ? 0x8001 + (c->raceSex & 1) : 22);
 	}
 
 	if (c->effectsRemainder[0]) {
@@ -2712,6 +2710,9 @@ void EoBCoreEngine::snd_playSoundEffect(int track, int volume) {
 	if ((track < 1) || (_flags.gameID == GI_EOB2 && track > 119) || shouldQuit())
 		return;
 
+	if (_flags.platform == Common::kPlatformSegaCD && volume == 0xFF)
+		volume = 0x0E;
+
 	_sound->playSoundEffect(track, volume);
 }
 
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index e5ab2a82aa..0e6407f4ca 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -1261,7 +1261,7 @@ protected:
 	// sound
 	void snd_playSong(int id, bool loop = true);
 	void snd_playLevelScore();
-	void snd_playSoundEffect(int id, int volume=0xFF) override;
+	void snd_playSoundEffect(int id, int volume = 0xFF) override;
 	void snd_stopSound();
 	void snd_fadeOut(int del = 160);
 	virtual void snd_loadAmigaSounds(int level, int sub) = 0;
diff --git a/engines/kyra/engine/kyra_rpg.cpp b/engines/kyra/engine/kyra_rpg.cpp
index 4341c9c7a5..03307c4bcd 100644
--- a/engines/kyra/engine/kyra_rpg.cpp
+++ b/engines/kyra/engine/kyra_rpg.cpp
@@ -402,6 +402,8 @@ bool KyraRpgEngine::snd_processEnvironmentalSoundEffect(int soundId, int block)
 		_environmentSfxVol = dist ? (16 - dist) * 8 - 1 : 127;
 	else if (_flags.platform == Common::kPlatformAmiga)
 		_environmentSfxVol = dist ? (soundId != 13 ? dist : (dist >= 4) ? 4 : dist) : 1;
+	else if (_flags.platform == Common::kPlatformSegaCD)
+		_environmentSfxVol = dist < 3 ? 15 - dist : 11;
 	else
 		_environmentSfxVol = (15 - ((block || (_flags.gameID == GI_LOL && dist < 2)) ? dist : 0)) << 4;
 
diff --git a/engines/kyra/module.mk b/engines/kyra/module.mk
index e07e7500dd..b5557aaf5c 100644
--- a/engines/kyra/module.mk
+++ b/engines/kyra/module.mk
@@ -141,6 +141,7 @@ MODULE_OBJS += \
 	sound/drivers/audiomaster2.o \
 	sound/drivers/mlalf98.o \
 	sound/drivers/pcspeaker_v1.o \
+	sound/drivers/segacd.o \
 	text/text_eob_segacd.o
 endif
 
diff --git a/engines/kyra/sound/drivers/segacd.cpp b/engines/kyra/sound/drivers/segacd.cpp
new file mode 100644
index 0000000000..1876766992
--- /dev/null
+++ b/engines/kyra/sound/drivers/segacd.cpp
@@ -0,0 +1,1077 @@
+/* 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.
+ *
+ */
+
+#ifdef ENABLE_EOB
+
+#include "kyra/kyra_v1.h"
+#include "kyra/sound/drivers/segacd.h"
+
+#include "audio/softsynth/fmtowns_pc98/sega_audio.h"
+
+#include "common/endian.h"
+#include "common/ptr.h"
+
+#define SEGA_SND_DEBUG_EXT	1
+
+namespace Kyra {
+
+class SegaAudioDriverInternal;
+class SegaAudioChannel {
+public:
+	SegaAudioChannel(uint8 id, SegaAudioInterface *sai);
+	virtual ~SegaAudioChannel() {}
+
+	void initTrack();
+	bool update();
+
+	void lock() { _lock = true;  }
+	void unlock() { _lock = false; }
+
+protected:
+	uint8 setCountDown();
+	void startVbr();
+	uint16 getFrequency(uint8 note);
+
+	void cmd_setTempo();
+	void cmd_programChange();
+	void cmd_setVolume();
+	void cmd_incVolume();
+	void cmd_decVolume();
+	void cmd_modVolume();
+	void cmd_setOctave();
+	void cmd_incOctave();
+	void cmd_decOctave();
+	void cmd_modOctave();
+	void cmd_setNoteLen();
+	void cmd_setReleaseTimer();
+	void cmd_writeReg();
+	void cmd_setRepeatMarker();
+	void cmd_repeatFromMarker();
+	void cmd_removeRepeatMarker();
+	void cmd_beginRepeatSection();
+	void cmd_jump();
+	void cmd_jumpToSubroutine();
+	void cmd_returnFromSubroutine();
+	void cmd_initVbr();
+	void cmd_pitchBend();
+	void cmd_initCstVbr();
+	void cmd_enableTwoChanMode();
+	void cmd_disableTwoChanMode();
+	void cmd_panCenter();
+	void cmd_panLeft();
+	void cmd_panRight();
+	void cmd_UNK29();
+	void cmd_void() {}
+	void cmd_transpose();
+
+#if SEGA_SND_DEBUG_EXT
+	typedef Common::Functor0Mem<void, SegaAudioChannel> SegaSndFunc;
+	struct SegaSndOpcode {
+		SegaSndOpcode(SegaSndFunc *func, const char *desc, int dataLen) : _func(func), _desc(desc), _dataLen(dataLen) {}
+		~SegaSndOpcode() { delete _func; }
+		bool isValid() { return _func->isValid(); }
+		void operator()(int dbgInfoChan, const uint8 *dbgInfoData) const {
+			Common::String dstr = "";
+			for (int i = 0; i < _dataLen; ++i)
+				dstr += Common::String::format("0x%02x ", dbgInfoData[i]);
+			debugC(3, kDebugLevelSound, "Channel %d: %s() [ %s]", dbgInfoChan, _desc, dstr.c_str());
+			(*_func)();
+		}
+		SegaSndFunc *_func;
+		const char *_desc;
+		const int _dataLen;
+	};
+#else
+	typedef Common::Functor0Mem<void, SegaAudioChannel> SegaSndOpcode;
+#endif
+	typedef Common::SharedPtr<SegaSndOpcode> PSegaSndOpcode;
+	Common::Array<PSegaSndOpcode> _opcodes;
+
+	uint8 _id;
+	uint8 _para1;
+	uint8 _countDown;
+	uint8 _releaseTimer;
+	uint8 _noteLen;
+	uint8 _volume;
+	uint8 _octave;
+
+	uint16 _frequency;
+	int16 _pitchBend;
+	int8 _transpose;
+
+	uint8 _vbrTempo;
+	int16 _vbrState;
+	int16 _vbrIncStart;
+	uint8 _vbrSteps;
+	uint8 _vbrDelay;
+	uint8 _vbrTempoCurState;
+	int16 _vbrInc;
+	uint8 _vbrStepsCounter;
+	uint8 _vbrDelayCountDown;
+
+	uint8 _cstVbrEnable;
+	uint8 _cstVbrDelay;
+	uint8 _cstVbrDelayCountDown;
+	const uint8 *_cstVbrData;
+	const uint8 *_cstVbrDataCur;
+	const uint8 *_cstVbrDataTmp;
+
+	bool _lock;
+
+	const uint8 *_dataPtr;
+
+	SegaAudioInterface *_sga;
+
+private:
+	virtual void keyOff() = 0;
+	virtual void restoreTone() {}
+	virtual void fadeUpdate() {}
+	virtual bool setupTone() = 0;
+	virtual void setVolume(uint8 vol) = 0;
+	virtual void sendVolume(uint8 vol) = 0;
+	virtual void programChange() = 0;
+	virtual void setPanPos(uint8 pan) {}
+	virtual void sendFrequency(uint16 freq) = 0;
+	virtual void toggleSpecialMode(bool enable) {}
+	virtual void updateEnvelope() {}
+	virtual const uint16 *freqTable() = 0;
+
+	struct Marker {
+		Marker() : counter(0), pos(0) {}
+		Marker(uint8 c, const uint8 *p) : counter(c), pos(p) {}
+		bool operator==(const uint8 *p) const { return (pos == p); }
+		uint8 counter;
+		const uint8 *pos;
+	};
+
+	Common::Array<Marker> _repeatMarkers;
+	Common::Array<Marker> _sectionMarkers;
+	Common::Array<const uint8*> _returnMarkers;
+};
+
+class SegaAudioChannel_FM : public SegaAudioChannel {
+public:
+	SegaAudioChannel_FM(uint8 id, SegaAudioInterface *sai, uint8 part, uint8 regOffs);
+	~SegaAudioChannel_FM() override {}
+
+private:
+	void keyOff() override;
+	void fadeUpdate() override;
+	bool setupTone() override;
+	void setVolume(uint8 vol) override;
+	void sendVolume(uint8 vol) override;
+	void programChange() override;
+	void setPanPos(uint8 pan) override;
+	void sendFrequency(uint16 freq) override;
+	void toggleSpecialMode(bool enable) override;
+
+	const uint16 *freqTable() override { return _freqTable; }
+
+	uint8 _algorithm;
+	uint8 _program;
+	uint8 _cfreqReg;
+
+	const uint8 _part;
+	const uint8 _regOffs;
+	const uint8 _regKeyOn;
+	const uint8 _regKeyOff;
+
+	static const uint16 _freqTable[97];
+};
+
+class SegaAudioChannel_SG : public SegaAudioChannel {
+public:
+	SegaAudioChannel_SG(uint8 id, SegaAudioInterface *sai, uint8 regOffs);
+	~SegaAudioChannel_SG() override {}
+
+private:
+	void keyOff() override;
+	void restoreTone() override;
+	bool setupTone() override;
+	void setVolume(uint8 vol) override {}
+	void sendVolume(uint8 vol) override;
+	void programChange() override;
+	void sendFrequency(uint16 freq) override;
+	void updateEnvelope() override;
+
+	const uint16 *freqTable() override { return _freqTable; }
+
+	uint8 _envDelay;
+	uint8 _volume2;
+	const uint8 *_envDataAtt;
+	const uint8 *_envDataRel;
+	const uint8 *_envDataCur;
+	const uint8 _regOffs;
+
+	enum EnvState {
+		kDisabled = 0,
+		kAttack,
+		kSustain,
+		kRelease,
+		kReady,
+	};
+
+	int _envState;
+
+	static const uint16 _freqTable[97];
+};
+
+class SegaAudioChannel_NG : public SegaAudioChannel_FM {
+public:
+	SegaAudioChannel_NG(uint8 id, SegaAudioInterface *sai);
+	~SegaAudioChannel_NG() override {}
+
+private:
+};
+
+class SegaAudioDriverInternal : public SegaAudioPluginDriver {
+public:
+	SegaAudioDriverInternal(Audio::Mixer *mixer);
+	~SegaAudioDriverInternal();
+
+	static SegaAudioDriverInternal *open(Audio::Mixer *mixer);
+	static void close();
+
+	void startFMSound(const uint8 *trackData, uint8 volume, uint8 prioFlags);
+
+	void loadPCMData(uint16 address, const uint8 *data, uint16 dataSize);
+	void startPCMSound(uint8 channel, uint8 dataStart, uint16 loopStart, uint16 step, uint8 pan, uint8 vol);
+
+	void setMusicVolume(int volume);
+	void setSoundEffectVolume(int volume);
+
+	void timerCallbackA() override;
+	void timerCallbackB() override;
+
+	static uint8 calcVolume(int vol);
+	static int getFadeState();
+	static const uint8 *getTrack();
+	static const uint8 *getTrack(int channel);
+	static const uint8 *getProgram(int instrument);
+
+private:
+	void start();
+	void stop();
+	void fade();
+
+	void update();
+
+	SegaAudioChannel **_channels;
+
+	bool _priority;
+	uint8 _isPlaying;
+	uint8 _sfxInternal_a;
+
+	static const uint8 *_trackData;
+	static uint8 _fadeTicker;
+	static uint8 _attenuation;
+	static uint8 _fadeAttenuation;
+
+	SegaAudioInterface *_sga;
+
+	static SegaAudioDriverInternal *_refInstance;
+	static int _refCount;
+
+	bool _ready;
+};
+
+SegaAudioChannel::SegaAudioChannel(uint8 id, SegaAudioInterface *sai) : _id(id), _sga(sai), _para1(0), _countDown(0), _dataPtr(0),
+	_volume(0), _octave(0), _releaseTimer(0), _noteLen(0), _vbrTempo(0), _vbrState(0), _vbrIncStart(0), _vbrSteps(0),
+	_vbrDelay(0), _vbrTempoCurState(0), _vbrInc(0), _vbrStepsCounter(0), _vbrDelayCountDown(0), _frequency(0), _pitchBend(0),
+	_transpose(0), _cstVbrEnable(0), _cstVbrDelay(0), _cstVbrDelayCountDown(0), _cstVbrData(0), _cstVbrDataCur(0), _lock(false),
+	_cstVbrDataTmp(0) {
+#if SEGA_SND_DEBUG_EXT
+#define SGC(x, y)	_opcodes.push_back(PSegaSndOpcode(new SegaSndOpcode(new SegaSndFunc(this, &SegaAudioChannel::x), #x, y)))
+#else
+#define SGC(x, y)	_opcodes.push_back(PSegaSndOpcode(new SegaSndOpcode(this, &SegaAudioChannel::x)))
+#endif
+#define SGCR(x)	_opcodes.push_back(_opcodes[x])
+	SGC(cmd_setTempo, 1);
+	SGC(cmd_programChange, 1);
+	SGCR(1);
+	SGC(cmd_setVolume, 1);
+	SGC(cmd_incVolume, 0);
+	SGC(cmd_decVolume, 0);
+	SGC(cmd_modVolume, 1);
+	SGC(cmd_setOctave, 1);
+	SGC(cmd_incOctave, 0);
+	SGC(cmd_decOctave, 0);
+	SGC(cmd_modOctave, 1);
+	SGC(cmd_setNoteLen, 1);
+	SGC(cmd_setReleaseTimer, 1);
+	SGC(cmd_writeReg, 2);
+	SGC(cmd_setRepeatMarker, 1);
+	SGC(cmd_repeatFromMarker, 0);
+	SGC(cmd_removeRepeatMarker, 0);
+	SGC(cmd_beginRepeatSection, 2);
+	SGC(cmd_jump, 2);
+	SGC(cmd_jumpToSubroutine, 2);
+	SGC(cmd_returnFromSubroutine, 0);
+	SGC(cmd_initVbr, 5);
+	SGC(cmd_pitchBend, 1);
+	SGC(cmd_initCstVbr, 2);
+	SGC(cmd_enableTwoChanMode, 0);
+	SGC(cmd_disableTwoChanMode, 0);
+	SGC(cmd_panCenter, 0);
+	SGC(cmd_panLeft, 0);
+	SGC(cmd_panRight, 0);
+	SGC(cmd_UNK29, 1);
+	SGC(cmd_void, 0);
+	SGC(cmd_transpose, 1);
+#undef SGC
+#undef SGCR
+}
+
+void SegaAudioChannel::initTrack() {
+	_dataPtr = SegaAudioDriverInternal::getTrack(_id);
+	_para1 = 0;
+	_countDown = 1;
+	_repeatMarkers.clear();
+	_returnMarkers.clear();
+	_sectionMarkers.clear();
+	_cstVbrData = _cstVbrDataCur = _cstVbrDataTmp = 0;
+	_cstVbrEnable = _vbrTempo = 0;
+	_pitchBend = _vbrState = 0;
+	_transpose = 0;
+	_volume = 0;
+	_lock = false;
+}
+
+bool SegaAudioChannel::update() {
+	/*
+	not implemented/required: sfx channel lock handling
+	restoreTone();
+	*/
+	
+	fadeUpdate();
+
+	if (!_dataPtr)
+		return true;
+
+	if (!--_countDown) {
+		do {
+			uint8 cmd = *_dataPtr;
+			while (!(cmd & 0x80)) {
+				++_dataPtr;
+				if (_opcodes[cmd]->isValid())
+#if SEGA_SND_DEBUG_EXT
+					(*_opcodes[cmd])(_id, _dataPtr);
+#else
+					(*_opcodes[cmd])();
+#endif
+				cmd = *_dataPtr;
+			}
+
+			if (cmd == 0xFF)
+				return false;
+
+		} while (setupTone());
+	}
+
+	//if (_type != 2) {
+	if (!(_para1 & 0x10) && _countDown == _releaseTimer) {
+		keyOff();
+	}
+	//}
+
+	uint8 vbrSet = 0;
+	if (_vbrTempo) {
+		if (_vbrDelayCountDown) {
+			--_vbrDelayCountDown;
+		} else if (!--_vbrTempoCurState) {
+			vbrSet = _vbrTempoCurState = _vbrTempo;
+			_vbrState += _vbrInc;
+			if (!--_vbrStepsCounter) {
+				_vbrStepsCounter += (_vbrSteps + _vbrSteps);
+				_vbrInc = -_vbrInc;
+			}
+		}
+	}
+
+	if (_cstVbrEnable & 1) {
+		if (_cstVbrDelayCountDown) {
+			--_cstVbrDelayCountDown;
+		} else {
+			assert(_cstVbrDataCur);
+			const uint8 *in = _cstVbrDataCur;
+			for (bool cont = true; cont; ) {
+				uint8 lo = *in++;
+				uint8 hi = *in++;
+
+				if ((hi & 0xF8) != 0x80) {
+					_vbrState += (int16)((hi << 8) | lo);
+					cont = false;
+					vbrSet = 1;
+				} else {
+					switch (hi & 0x0F) {
+					case 0:
+						_cstVbrDataTmp = in;
+						break;
+					case 1:
+						assert(_cstVbrDataTmp);
+						in = _cstVbrDataTmp;
+						break;
+					case 2:
+						_cstVbrDelayCountDown = lo;
+						cont = false;
+						break;
+					case 3:
+						_cstVbrEnable += _para1;
+						cont = false;
+						break;
+					default:
+						error("SegaAudioChannel::update(): Unknown error");
+						break;
+					}
+				}
+			}
+			_cstVbrDataCur = in;
+		}
+	}
+
+	if (vbrSet)
+		sendFrequency(_frequency + _vbrState);
+
+	updateEnvelope();
+
+	_lock = false;
+
+	return true;
+}
+
+uint8 SegaAudioChannel::setCountDown() {
+	_para1 = *_dataPtr++;
+	_countDown = (_para1 & 0x20) ? *_dataPtr++ : _noteLen;
+	debugC(3, kDebugLevelSound, "Channel %d: Note %d, Duration %d", _id, _octave * 12 + (_para1 & 0x0F), _countDown);
+	return _para1;
+}
+
+void SegaAudioChannel::startVbr() {
+	if (_cstVbrEnable) {
+		_cstVbrEnable = 1;
+		_cstVbrDataCur = _cstVbrData;
+		_cstVbrDelayCountDown = _cstVbrDelay;
+	}
+
+	if (_vbrTempo) {
+		_vbrTempoCurState = _vbrTempo;
+		_vbrInc = _vbrIncStart;
+		_vbrStepsCounter = _vbrSteps;
+		_vbrDelayCountDown = _vbrDelay;
+	}
+	_vbrState = 0;
+}
+
+uint16 SegaAudioChannel::getFrequency(uint8 note) {
+	_frequency = freqTable()[_octave * 12 + note + _transpose] + _pitchBend;
+	return _frequency;
+}
+
+void SegaAudioChannel::cmd_setTempo() {
+	_sga->writeReg(0, 0x26, *_dataPtr++);
+}
+
+void SegaAudioChannel::cmd_programChange() {
+	programChange();
+}
+
+void SegaAudioChannel::cmd_setVolume() {
+	_volume = *_dataPtr++;
+	setVolume(_volume);
+}
+
+void SegaAudioChannel::cmd_incVolume() {
+	setVolume(++_volume);
+}
+
+void SegaAudioChannel::cmd_decVolume() {
+	setVolume(--_volume);
+}
+
+void SegaAudioChannel::cmd_modVolume() {
+	_volume += *_dataPtr++;
+	setVolume(_volume);
+}
+
+void SegaAudioChannel::cmd_setOctave() {
+	_octave = *_dataPtr++ - 1;
+	_transpose = 0;
+}
+
+void SegaAudioChannel::cmd_incOctave() {
+	_octave++;
+}
+
+void SegaAudioChannel::cmd_decOctave() {
+	_octave--;
+}
+
+void SegaAudioChannel::cmd_modOctave() {
+	_octave += *_dataPtr++;
+}
+
+void SegaAudioChannel::cmd_setNoteLen() {
+	_noteLen = *_dataPtr++;
+}
+
+void SegaAudioChannel::cmd_setReleaseTimer() {
+	_releaseTimer = *_dataPtr++;
+}
+
+void SegaAudioChannel::cmd_writeReg() {
+	uint8 reg = *_dataPtr++;
+	uint8 val = *_dataPtr++;
+	if (!_lock)
+		_sga->writeReg(_id >= 7 ? 1 : 0, reg, val);
+}
+
+void SegaAudioChannel::cmd_setRepeatMarker() {
+	_repeatMarkers.push_back(Marker(*_dataPtr, _dataPtr + 1));
+	_dataPtr++;
+}
+
+void SegaAudioChannel::cmd_repeatFromMarker() {
+	assert(!_repeatMarkers.empty());
+	if (--_repeatMarkers.back().counter)
+		_dataPtr = _repeatMarkers.back().pos;
+	else
+		_repeatMarkers.pop_back();
+}
+
+void SegaAudioChannel::cmd_removeRepeatMarker() {
+	assert(!_repeatMarkers.empty());
+	_repeatMarkers.pop_back();
+}
+
+void SegaAudioChannel::cmd_beginRepeatSection() {
+	uint8 totalReps = *_dataPtr++;
+
+	Common::Array<Marker>::iterator i = Common::find(_sectionMarkers.begin(), _sectionMarkers.end(), _dataPtr);
+	if (i == _sectionMarkers.end()) {
+		_sectionMarkers.push_back(Marker(*_dataPtr, _dataPtr));
+		i = _sectionMarkers.end() - 1;
+	}
+	_dataPtr++;
+
+	if (++i->counter == totalReps) {
+		_sectionMarkers.erase(i);
+		cmd_jump();
+	} else {
+		_dataPtr += 2;
+	}
+}
+
+void SegaAudioChannel::cmd_jump() {
+	uint16 offset = READ_LE_UINT16(_dataPtr);
+	_dataPtr = SegaAudioDriverInternal::getTrack() + offset;
+}
+
+void SegaAudioChannel::cmd_jumpToSubroutine() {
+	_returnMarkers.push_back(_dataPtr + 2);
+	cmd_jump();
+}
+
+void SegaAudioChannel::cmd_returnFromSubroutine() {
+	_dataPtr = _returnMarkers.back();
+	assert(_dataPtr);
+	_returnMarkers.pop_back();
+}
+
+void SegaAudioChannel::cmd_initVbr() {
+	_vbrTempo = *_dataPtr++;
+	if (!_vbrTempo)
+		return;
+	_vbrIncStart = READ_LE_INT16(_dataPtr);
+	_dataPtr += 2;
+	_vbrInc = 0;
+	_vbrSteps = *_dataPtr++;
+	_vbrDelay = *_dataPtr++;
+}
+
+void SegaAudioChannel::cmd_pitchBend() {
+	_pitchBend = (int8)*_dataPtr++;
+	if (_id == 10)
+		_pitchBend <<= 4;
+}
+
+void SegaAudioChannel::cmd_initCstVbr() {
+	_cstVbrEnable = *_dataPtr++;
+	if (!_cstVbrEnable)
+		return;
+	_cstVbrData = SegaAudioDriverInternal::getProgram(_cstVbrEnable);
+	_cstVbrEnable = 2;
+	_cstVbrDelay = *_dataPtr++;
+}
+
+void SegaAudioChannel::cmd_enableTwoChanMode() {
+	toggleSpecialMode(true);
+}
+
+void SegaAudioChannel::cmd_disableTwoChanMode() {
+	toggleSpecialMode(false);
+}
+
+void SegaAudioChannel::cmd_panCenter() {
+	setPanPos(0xC0);
+}
+
+void SegaAudioChannel::cmd_panLeft() {
+	setPanPos(0x80);
+}
+
+void SegaAudioChannel::cmd_panRight() {
+	setPanPos(0x40);
+}
+
+void SegaAudioChannel::cmd_UNK29() {
+	_dataPtr++;
+}
+
+void SegaAudioChannel::cmd_transpose() {
+	_transpose += (int8)*_dataPtr++;
+}
+
+SegaAudioChannel_FM::SegaAudioChannel_FM(uint8 id, SegaAudioInterface *sai, uint8 part, uint8 regOffs) : SegaAudioChannel(id, sai), _part(part), _regOffs(regOffs),
+	_regKeyOn(0xF0 | regOffs | (part ? 4 : 0)), _regKeyOff(regOffs | (part ? 4 : 0)), _algorithm(0), _program(0), _cfreqReg(0) {
+}
+
+void SegaAudioChannel_FM::keyOff() {
+	debugC(5, kDebugLevelSound, "Channel %d: Key Off", _id);
+	if (!_lock)
+		_sga->writeReg(0, 0x28, _regKeyOff);
+}
+
+void SegaAudioChannel_FM::fadeUpdate() {
+	if (SegaAudioDriverInternal::getFadeState() == _id)
+		sendVolume(_volume);
+}
+
+bool SegaAudioChannel_FM::setupTone() {
+	if (_cfreqReg > 0 && _cfreqReg < 4) {
+		uint8 note = *_dataPtr++ & 0x0F;
+
+		if (note) {
+			static uint8 frqreg[] = { 0xAD, 0xA9, 0xAE, 0xAA, 0xAC, 0xA8 };
+			uint16 f = _freqTable[_octave * 12 + note + _transpose];
+			if (!_lock) {
+				_sga->writeReg(_part, frqreg[(_cfreqReg - 1) << 1], f >> 8);
+				_sga->writeReg(_part, frqreg[((_cfreqReg - 1) << 1) + 1], f & 0xFF);
+			}
+		}
+
+		_cfreqReg++;
+		return true;
+
+	} else if (_cfreqReg == 4) {
+		_cfreqReg = 1;
+	}
+
+	uint8 para = _para1;
+	if (!(_para1 & 0x10))
+		keyOff();
+
+	uint8 note = setCountDown() & 0x0F;
+	if (!note)
+		return false;
+
+	sendFrequency(getFrequency(note));
+
+	if (!(para & 0x10)) {
+		if (!_lock)
+			_sga->writeReg(0, 0x28, _regKeyOn);
+		startVbr();
+	}
+
+	return false;
+}
+
+void SegaAudioChannel_FM::setVolume(uint8 vol) {
+	sendVolume(vol);
+}
+
+void SegaAudioChannel_FM::sendVolume(uint8 vol) {
+	if (_lock)
+		return;
+
+	static const uint8 carrier[8] = { 1,  1,  1,  1,  2,  3,  3,  4 };
+	vol = SegaAudioDriverInternal::calcVolume(vol);
+
+	const int8 *in = (const int8*)SegaAudioDriverInternal::getProgram(_program) + 7;
+	for (uint8 c = 0; c < carrier[_algorithm]; ++c)
+		_sga->writeReg(_part, 0x4C + _regOffs - (c << 2), vol + *in--);
+}
+
+void SegaAudioChannel_FM::programChange() {
+	_program = *_dataPtr++;
+	const uint8 *in = SegaAudioDriverInternal::getProgram(_program);
+	if (!_lock) {
+		for (uint8 reg = 0x30 + _regOffs; reg < 0x8F; reg += 4)
+			_sga->writeReg(_part, reg, *in++);
+	} else {
+		in += 24;
+	}
+
+	_algorithm = *in & 7;
+	if (!_lock)
+		_sga->writeReg(_part, 0xB0 + _regOffs, *in++);
+	sendVolume(_volume);
+}
+
+void SegaAudioChannel_FM::setPanPos(uint8 pan) {
+	if (!_lock)
+		_sga->writeReg(_part, 0xB4 + _regOffs, pan);
+}
+
+void SegaAudioChannel_FM::sendFrequency(uint16 freq) {
+	if (_lock)
+		return;
+	_sga->writeReg(_part, 0xA4 + _regOffs, freq >> 8);
+	_sga->writeReg(_part, 0xA0 + _regOffs, freq & 0xFF);
+}
+
+void SegaAudioChannel_FM::toggleSpecialMode(bool enable) {
+	_cfreqReg = enable ? 1 : 0;
+	_sga->writeReg(0, 0x27, (_cfreqReg << 6) | 0x0F);
+}
+
+const uint16 SegaAudioChannel_FM::_freqTable[97] = {
+	0x0000,
+	0x0284, 0x02ab, 0x02d3, 0x02fe, 0x032d, 0x035c, 0x038f, 0x03c5,	0x03ff, 0x043c, 0x047c, 0x04c0,
+	0x0a84, 0x0aab, 0x0ad3, 0x0afe,	0x0b2d, 0x0b5c, 0x0b8f, 0x0bc5, 0x0bff, 0x0c3c, 0x0c7c, 0x0cc0,
+	0x1284, 0x12ab, 0x12d3, 0x12fe, 0x132d, 0x135c, 0x138f, 0x13c5,	0x13ff, 0x143c, 0x147c, 0x14c0,
+	0x1a84, 0x1aab, 0x1ad3, 0x1afe, 0x1b2d, 0x1b5c, 0x1b8f, 0x1bc5, 0x1bff, 0x1c3c, 0x1c7c, 0x1cc0,
+	0x2284, 0x22ab, 0x22d3, 0x22fe, 0x232d, 0x235c, 0x238f, 0x23c5, 0x23ff, 0x243c, 0x247c, 0x24c0,
+	0x2a84, 0x2aab, 0x2ad3, 0x2afe, 0x2b2d, 0x2b5c, 0x2b8f, 0x2bc5, 0x2bff, 0x2c3c, 0x2c7c, 0x2cc0,
+	0x3284, 0x32ab, 0x32d3, 0x32fe, 0x332d, 0x335c, 0x338f, 0x33c5, 0x33ff, 0x343c, 0x347c, 0x34c0,
+	0x3a84, 0x3aab, 0x3ad3, 0x3afe, 0x3b2d, 0x3b5c, 0x3b8f, 0x3bc5, 0x3bff, 0x3c3c, 0x3c7c, 0x3cc0
+};
+
+SegaAudioChannel_SG::SegaAudioChannel_SG(uint8 id, SegaAudioInterface *sai, uint8 regOffs) : SegaAudioChannel(id, sai), _regOffs(regOffs), _envDataAtt(0), _envDataRel(0),
+	_envDataCur(0), _envDelay(0), _envState(0), _volume2(0) {
+}
+
+void SegaAudioChannel_SG::keyOff() {
+	if (_envState == kDisabled) {
+		sendVolume(0);
+	} else if (_envState < kRelease) {
+		_envState = kRelease;
+		_envDelay = 1;
+		_envDataCur = _envDataRel;
+	}
+}
+
+void SegaAudioChannel_SG::restoreTone() {
+	_sga->psgWrite(0x80 | _regOffs | (_frequency & 0x0F));
+	_sga->psgWrite((_frequency >> 4) & 0x3F);
+	_sga->psgWrite((0x90 | _regOffs | (_volume2 & 0x0F)) ^ 0x0F);
+}
+
+bool SegaAudioChannel_SG::setupTone() {
+	uint8 para = _para1;
+	uint8 note = setCountDown() & 0x0F;
+	if (!note) {
+		keyOff();
+		return false;
+	}
+
+	sendFrequency(getFrequency(note));
+
+	if (!(para & 0x10)) {
+		startVbr();
+		if (_envState == kDisabled) {
+			sendVolume(SegaAudioDriverInternal::calcVolume(_volume));
+		} else {
+			_envState = kAttack;
+			_envDelay = 1;
+			_envDataCur = _envDataAtt;
+		}
+	}
+
+	return false;
+}
+
+void SegaAudioChannel_SG::sendVolume(uint8 vol) {
+	_volume2 = vol;
+	if (!_lock)
+		_sga->psgWrite((0x90 | _regOffs | (vol & 0x0F)) ^ 0x0F);
+}
+
+void SegaAudioChannel_SG::programChange() {
+	uint8 prg = *_dataPtr++;
+	if (!prg) {
+		_envState = kDisabled;
+		return;
+	}
+
+	_envState = kReady;
+	_envDataAtt = SegaAudioDriverInternal::getProgram(prg);
+	_envDataRel = SegaAudioDriverInternal::getProgram(*_dataPtr++);
+	sendVolume(0);
+}
+
+void SegaAudioChannel_SG::sendFrequency(uint16 freq) {
+	if (_lock)
+		return;
+	_sga->psgWrite(0x80 | _regOffs | (freq & 0x0F));
+	_sga->psgWrite((freq >> 4) & 0x3F);
+}
+
+void SegaAudioChannel_SG::updateEnvelope() {
+	if (_envState != kAttack && _envState != kRelease)
+		return;
+
+	if (--_envDelay)
+		return;
+
+	if (*_envDataCur == 0xFF) {
+		_envState++;
+		return;
+	}
+
+	sendVolume(SegaAudioDriverInternal::calcVolume(_volume + (int8)*_envDataCur++));
+	_envDelay = *_envDataCur++;
+}
+
+const uint16 SegaAudioChannel_SG::_freqTable[97] = {
+	0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x03f7, 0x03be, 0x0388,
+	0x0356, 0x0326, 0x02f9, 0x02ce, 0x02a5, 0x0280, 0x025c, 0x023a, 0x021a, 0x01fb, 0x01df, 0x01c4,
+	0x01ab, 0x0193, 0x017d, 0x0167, 0x0153, 0x0140, 0x012e, 0x011d, 0x010d, 0x00fe, 0x00ef, 0x00e2,
+	0x00d6, 0x00c9, 0x00be, 0x00b4, 0x00a9, 0x00a0, 0x0097, 0x008f, 0x0087, 0x007f, 0x0078, 0x0071,
+	0x006b, 0x0065, 0x005f, 0x005a, 0x0055, 0x0050, 0x004b, 0x0047, 0x0043, 0x0040, 0x003c, 0x0039,
+	0x0036, 0x0033, 0x0030, 0x002d, 0x002b, 0x0028, 0x0026, 0x0024, 0x0022, 0x0020, 0x001f, 0x001d,
+	0x001b, 0x001a, 0x0018, 0x0017, 0x0016, 0x0015, 0x0013, 0x0012, 0x0011, 0x0010, 0x000f, 0x000e
+};
+
+SegaAudioChannel_NG::SegaAudioChannel_NG(uint8 id, SegaAudioInterface *sai) : SegaAudioChannel_FM(id, sai, 0, 0) {
+}
+
+SegaAudioDriverInternal *SegaAudioDriverInternal::_refInstance = 0;
+int SegaAudioDriverInternal::_refCount = 0;
+uint8 SegaAudioDriverInternal::_attenuation = 0;
+uint8 SegaAudioDriverInternal::_fadeTicker = 0;
+uint8 SegaAudioDriverInternal::_fadeAttenuation = 0;
+const uint8 *SegaAudioDriverInternal::_trackData = 0;
+
+SegaAudioDriverInternal::SegaAudioDriverInternal(Audio::Mixer *mixer) : SegaAudioPluginDriver(), _sga(0), _priority(false), _channels(0), _isPlaying(0), _sfxInternal_a(0), _ready(false) {
+	_sga = new SegaAudioInterface(mixer, this);
+	_sga->init();
+	// Setup all channels as sound effect channels. Some FM tunes (like the gameover tune) are actual music pieces and not just sfx.
+	// Unfortunately there isn't any good way to distinguish the tune types. Most tracks are classical sfx. So for the volume control
+	// we treat the CD Audio as music and all FM and PCM output as sound effects.
+	_sga->setSoundEffectChanMask(-1);
+
+	_channels = new SegaAudioChannel*[10];
+	for (int i = 0; i < 3; ++i)
+		_channels[i] = new SegaAudioChannel_FM(i, _sga, 0, i);
+	for (int i = 3; i < 6; ++i)
+		_channels[i] = new SegaAudioChannel_SG(i, _sga, (i - 3) << 5);
+	_channels[6] = new SegaAudioChannel_NG(6, _sga);
+	for (int i = 7; i < 10; ++i)
+		_channels[i] = new SegaAudioChannel_FM(i, _sga, 1, i - 7);
+
+	_sga->writeReg(0, 0x27, 0x3F);
+	_ready = true;
+}
+
+SegaAudioDriverInternal::~SegaAudioDriverInternal() {
+	_ready = false;
+	delete _sga;
+
+	if (_channels) {
+		for (int i = 0; i < 10; ++i)
+			delete _channels[i];
+		delete[] _channels;
+	}
+}
+
+SegaAudioDriverInternal *SegaAudioDriverInternal::open(Audio::Mixer *mixer) {
+	_refCount++;
+
+	if (_refCount == 1 && _refInstance == 0)
+		_refInstance = new SegaAudioDriverInternal(mixer);
+	else if (_refCount < 2 || _refInstance == 0)
+		error("SegaAudioDriverInternal::open(): Internal instance management failure");
+
+	return _refInstance;
+}
+
+void SegaAudioDriverInternal::close() {
+	if (!_refCount)
+		return;
+
+	_refCount--;
+
+	if (!_refCount) {
+		delete _refInstance;
+		_refInstance = 0;
+	}
+}
+
+void SegaAudioDriverInternal::startFMSound(const uint8 *trackData, uint8 volume, uint8 prioFlags) {
+	if (!_isPlaying)
+		_priority = false;
+
+	if (prioFlags & SegaAudioDriver::kPrioHigh)
+		_priority = true;
+	else if (_isPlaying && (_priority || (prioFlags & SegaAudioDriver::kPrioLow)))
+		return;
+
+	SegaAudioInterface::MutexLock lock = _sga->stackLockMutex();
+	stop();
+
+	_trackData = trackData;
+	_attenuation = (uint8)-((volume & 0x0F) - 0x0F);
+	_sfxInternal_a = 0;
+
+	start();
+}
+
+void SegaAudioDriverInternal::loadPCMData(uint16 address, const uint8 *data, uint16 dataSize) {
+	_sga->loadPCMData(address, data, dataSize);
+}
+
+void SegaAudioDriverInternal::startPCMSound(uint8 channel, uint8 dataStart, uint16 loopStart, uint16 rate, uint8 pan, uint8 vol) {
+	_sga->playPCMChannel(channel, dataStart, loopStart, rate, pan, vol);
+}
+
+void SegaAudioDriverInternal::setMusicVolume(int volume) {
+	_sga->setMusicVolume(volume);
+}
+
+void SegaAudioDriverInternal::setSoundEffectVolume(int volume) {
+	_sga->setSoundEffectVolume(volume);
+}
+
+void SegaAudioDriverInternal::timerCallbackA() {
+	if (_ready && _isPlaying != 0xFF)
+		update();
+}
+
+void SegaAudioDriverInternal::timerCallbackB() {
+
+}
+
+uint8 SegaAudioDriverInternal::calcVolume(int vol) {
+	static const uint8 volTable[18] = { 0x5F, 0x2A, 0x28, 0x25, 0x22, 0x20, 0x1D, 0x1A, 0x18, 0x15, 0x12, 0x10, 0x0D, 0x0A, 0x08, 0x05, 0x02, 0x00 };
+	vol = MAX<int>((int8)(vol & 0xFF) - _attenuation - _fadeAttenuation, 0) & 0xFF;
+	assert(vol < ARRAYSIZE(volTable));
+	return volTable[vol];
+}
+
+int SegaAudioDriverInternal::getFadeState() {
+	return _fadeAttenuation ? _fadeTicker - 1 : -1;
+}
+
+const uint8 *SegaAudioDriverInternal::getTrack() {
+	return _trackData;
+}
+
+const uint8 *SegaAudioDriverInternal::getTrack(int channel) {
+	return _trackData ? _trackData + READ_LE_UINT16(_trackData + 1 + (channel << 1)) : 0;
+}
+
+const uint8 *SegaAudioDriverInternal::getProgram(int instrument) {
+	return getTrack(12 + instrument);
+}
+
+void SegaAudioDriverInternal::start() {
+	if (_isPlaying)
+		stop();
+
+	debugC(3, kDebugLevelSound, "%s", "\nStarting sound...");
+
+	_isPlaying = 1;
+	for (int i = 0; i < 10; ++i)
+		_channels[i]->initTrack();
+
+	_sga->writeReg(0, 0x26, 0xE9);
+}
+
+void SegaAudioDriverInternal::stop() {
+	_fadeAttenuation = 15;
+	_channels[0]->unlock();
+	/*
+	*/
+	update();
+	_fadeAttenuation = 0;
+	_fadeTicker = 0;
+	_isPlaying = 0;
+	//_unkByte = 0;
+	//_sai->writeReg(0, 0x27, 0);
+}
+
+void SegaAudioDriverInternal::fade() {
+	if (!_isPlaying || _fadeAttenuation)
+		return;
+	_fadeAttenuation = 1;
+	_fadeTicker = 12;
+}
+
+void SegaAudioDriverInternal::update() {
+	if (_fadeAttenuation) {
+		if (!--_fadeTicker) {
+			_fadeTicker = 12;
+			if (++_fadeAttenuation == 14)
+				stop();
+		}
+	}
+
+	for (int i = 0; i < 10; ++i) {
+		if (!_channels[i]->update())
+			stop();
+	}
+}
+
+SegaAudioDriver::SegaAudioDriver(Audio::Mixer *mixer) {
+	_drv = SegaAudioDriverInternal::open(mixer);
+}
+
+SegaAudioDriver::~SegaAudioDriver() {
+	SegaAudioDriverInternal::close();
+	_drv = 0;
+}
+
+void SegaAudioDriver::startFMSound(const uint8 *trackData, uint8 volume, PrioFlags prioFlags) {
+	_drv->startFMSound(trackData, volume, (uint8)prioFlags);
+}
+
+void SegaAudioDriver::loadPCMData(uint16 address, const uint8 *data, uint16 dataLen) {
+	_drv->loadPCMData(address, data, dataLen);
+}
+
+void SegaAudioDriver::startPCMSound(uint8 channel, uint8 dataStart, uint16 loopStart, uint16 rate, uint8 pan, uint8 vol) {
+	_drv->startPCMSound(channel, dataStart, loopStart, rate, pan, vol);
+}
+
+void SegaAudioDriver::setMusicVolume(int volume) {
+	_drv->setMusicVolume(volume);
+}
+
+void SegaAudioDriver::setSoundEffectVolume(int volume) {
+	_drv->setSoundEffectVolume(volume);
+}
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/sound/drivers/segacd.h b/engines/kyra/sound/drivers/segacd.h
new file mode 100644
index 0000000000..8e7b47c571
--- /dev/null
+++ b/engines/kyra/sound/drivers/segacd.h
@@ -0,0 +1,69 @@
+/* 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.
+ *
+ */
+
+#ifdef ENABLE_EOB
+
+#ifndef KYRA_SOUND_SEGACD_H
+#define KYRA_SOUND_SEGACD_H
+
+#include "common/scummsys.h"
+#include "common/array.h"
+
+namespace Common {
+	class SeekableReadStream;
+}
+
+namespace Audio {
+	class Mixer;
+}
+
+namespace Kyra {
+
+class SegaAudioDriverInternal;
+
+class SegaAudioDriver {
+public:
+	SegaAudioDriver(Audio::Mixer *mixer);
+	~SegaAudioDriver();
+
+	enum PrioFlags {
+		kPrioHigh = 0x10,
+		kPrioLow = 0x20,
+	};
+
+	void startFMSound(const uint8 *trackData, uint8 volume, PrioFlags prioFlags);
+
+	void loadPCMData(uint16 address, const uint8 *data, uint16 dataLen);
+	void startPCMSound(uint8 channel, uint8 dataStart, uint16 loopStart = 0xFF00, uint16 rate = 0x300, uint8 pan = 0xFF, uint8 vol = 0xFF);
+
+	void setMusicVolume(int volume);
+	void setSoundEffectVolume(int volume);
+
+private:
+	SegaAudioDriverInternal *_drv;
+};
+
+} // End of namespace Kyra
+
+#endif
+
+#endif
diff --git a/engines/kyra/sound/sound.cpp b/engines/kyra/sound/sound.cpp
index 39784f4682..f89f7f550d 100644
--- a/engines/kyra/sound/sound.cpp
+++ b/engines/kyra/sound/sound.cpp
@@ -235,7 +235,7 @@ bool MixedSoundDriver::isPlaying() const {
 	return _music->isPlaying() | _sfx->isPlaying();
 }
 
-void MixedSoundDriver::playSoundEffect(uint8 track, uint8 volume) {
+void MixedSoundDriver::playSoundEffect(uint16 track, uint8 volume) {
 	_sfx->playSoundEffect(track, volume);
 }
 
diff --git a/engines/kyra/sound/sound.h b/engines/kyra/sound/sound.h
index b57d649faf..716e86e78d 100644
--- a/engines/kyra/sound/sound.h
+++ b/engines/kyra/sound/sound.h
@@ -195,7 +195,7 @@ public:
 	 *
 	 * @param track sound effect id
 	 */
-	virtual void playSoundEffect(uint8 track, uint8 volume = 0xFF) = 0;
+	virtual void playSoundEffect(uint16 track, uint8 volume = 0xFF) = 0;
 
 	/**
 	 * Stop playback of all sfx tracks.
@@ -350,7 +350,7 @@ public:
 	void haltTrack() override;
 	bool isPlaying() const override;
 
-	void playSoundEffect(uint8 track, uint8 volume = 0xFF) override;
+	void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
 
 	void stopAllSoundEffects() override;
 
diff --git a/engines/kyra/sound/sound_amiga_eob.cpp b/engines/kyra/sound/sound_amiga_eob.cpp
index 25ccb8cd56..0fdf57d7d5 100644
--- a/engines/kyra/sound/sound_amiga_eob.cpp
+++ b/engines/kyra/sound/sound_amiga_eob.cpp
@@ -168,7 +168,7 @@ void SoundAmiga_EoB::haltTrack() {
 	_lastSound.clear();
 }
 
-void SoundAmiga_EoB::playSoundEffect(uint8 track, uint8 volume) {
+void SoundAmiga_EoB::playSoundEffect(uint16 track, uint8 volume) {
 	if (_currentResourceSet == -1 || !_sfxEnabled || !_ready)
 		return;
 
diff --git a/engines/kyra/sound/sound_amiga_lok.cpp b/engines/kyra/sound/sound_amiga_lok.cpp
index 5edea54f3a..eca49f58a5 100644
--- a/engines/kyra/sound/sound_amiga_lok.cpp
+++ b/engines/kyra/sound/sound_amiga_lok.cpp
@@ -189,7 +189,7 @@ void SoundAmiga_LoK::beginFadeOut() {
 	_driver->setVolume(0x40);
 }
 
-void SoundAmiga_LoK::playSoundEffect(uint8 track, uint8) {
+void SoundAmiga_LoK::playSoundEffect(uint16 track, uint8) {
 	debugC(5, kDebugLevelSound, "SoundAmiga_LoK::playSoundEffect(%d)", track);
 	const AmigaSfxTable *sfx = 0;
 	bool pan = false;
diff --git a/engines/kyra/sound/sound_intern.h b/engines/kyra/sound/sound_intern.h
index 4c36c44029..362f2b8eed 100644
--- a/engines/kyra/sound/sound_intern.h
+++ b/engines/kyra/sound/sound_intern.h
@@ -74,7 +74,7 @@ public:
 	void haltTrack() override;
 	bool isPlaying() const override;
 
-	void playSoundEffect(uint8 track, uint8 volume = 0xFF) override;
+	void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
 	void stopAllSoundEffects() override;
 
 	void beginFadeOut() override;
@@ -130,7 +130,7 @@ public:
 	void playTrack(uint8 track) override;
 	void haltTrack() override;
 
-	void playSoundEffect(uint8 track, uint8 volume = 0xFF) override;
+	void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
 	void stopAllSoundEffects() override;
 
 	void beginFadeOut() override;
@@ -185,7 +185,7 @@ public:
 	void beginFadeOut() override;
 
 	int32 voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, uint8 priority, bool isSfx) override { return -1; }
-	void playSoundEffect(uint8 track, uint8 volume = 0xFF) override;
+	void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
 
 	void updateVolumeSettings() override;
 
@@ -221,7 +221,7 @@ public:
 	void beginFadeOut() override;
 
 	int32 voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume = 255, uint8 priority = 255, bool isSfx = true) override;
-	void playSoundEffect(uint8 track, uint8 volume = 0xFF) override;
+	void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
 
 	void updateVolumeSettings() override;
 
@@ -336,7 +336,7 @@ public:
 	void beginFadeOut() override;
 
 	int32 voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, uint8 priority, bool isSfx) override { return -1; }
-	void playSoundEffect(uint8 track, uint8 volume = 0xFF) override;
+	void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
 
 protected:
 	Audio::MaxTrax *_driver;
@@ -373,7 +373,7 @@ public:
 	void haltTrack() override;
 	bool isPlaying() const override;
 
-	void playSoundEffect(uint8 track, uint8 volume = 0xFF) override;
+	void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
 	void stopAllSoundEffects() override;
 
 	void beginFadeOut() override;
@@ -425,7 +425,7 @@ public:
 	void unloadSoundFile(Common::String file) override;
 	void playTrack(uint8 track) override;
 	void haltTrack() override;
-	void playSoundEffect(uint8 track, uint8 volume = 0xFF) override;
+	void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
 	void beginFadeOut() override { beginFadeOut(160); }
 	void beginFadeOut(int delay) override;
 	void updateVolumeSettings() override;
@@ -461,7 +461,7 @@ public:
 	void loadSfxFile(Common::String file) override;
 	void playTrack(uint8 track) override;
 	void haltTrack() override;
-	void playSoundEffect(uint8 track, uint8) override;
+	void playSoundEffect(uint16 track, uint8) override;
 	void beginFadeOut() override {}
 	void updateVolumeSettings() override;
 
@@ -477,37 +477,41 @@ private:
 	bool _ready;
 };
 
+class SegaAudioDriver;
 class SoundSegaCD_EoB : public Sound {
 public:
 	SoundSegaCD_EoB(KyraEngine_v1 *vm, Audio::Mixer *mixer);
-	virtual ~SoundSegaCD_EoB();
-
-	kType getMusicType() const;
-
-	bool init();
-	void initAudioResourceInfo(int set, void *info);
-	void selectAudioResourceSet(int set);
-	bool hasSoundFile(uint file) const { return false; }
-	void loadSoundFile(uint file) {}
-	void loadSoundFile(Common::String file) {}
-	void loadSfxFile(Common::String file);
-	void playTrack(uint8 track);
-	void haltTrack();
-	void playSoundEffect(uint8 track, uint8);
-	bool isPlaying() const;
-	void beginFadeOut() {}
-	void updateVolumeSettings();
+	~SoundSegaCD_EoB() override;
 
-private:
-	KyraEngine_v1 *_vm;
-	//MLALF98 *_driver;
+	kType getMusicType() const override;
+
+	bool init() override;
+	void initAudioResourceInfo(int, void*) override {}
+	void selectAudioResourceSet(int) override {}
+	bool hasSoundFile(uint file) const override { return false; }
+	void loadSoundFile(uint file) override {}
+	void loadSoundFile(Common::String file) override {}
+	void playTrack(uint8 track) override;
+	void haltTrack() override;
+	void playSoundEffect(uint16 track, uint8 volume) override;
+	bool isPlaying() const override;
+	void beginFadeOut() override {}
+	void updateVolumeSettings() override;
 
-	//SoundResourceInfo_PC *_resInfo[3];
-	//int _currentResourceSet;
+private:
+	void loadPCMData();
+	void loadFMData();
 
-	//uint32 _sfxDelay;
+	KyraEngine_v1 *_vm;
+	SegaAudioDriver *_driver;
 
+	uint8 _pcmOffsets[8];
+	uint16 _fmOffsets[140];
+	const uint8 *_fmData;
+	int _lastSoundEffect;
 	bool _ready;
+
+	static const uint8 _fmTrackMap[140];
 };
 
 #endif
diff --git a/engines/kyra/sound/sound_pc98_eob.cpp b/engines/kyra/sound/sound_pc98_eob.cpp
index 8f0dc548f0..a9e7c9868b 100644
--- a/engines/kyra/sound/sound_pc98_eob.cpp
+++ b/engines/kyra/sound/sound_pc98_eob.cpp
@@ -103,7 +103,7 @@ void SoundPC98_EoB::haltTrack() {
 	playTrack(0);
 }
 
-void SoundPC98_EoB::playSoundEffect(uint8 track, uint8) {
+void SoundPC98_EoB::playSoundEffect(uint16 track, uint8) {
 	if (_currentResourceSet != kMusicIngame || !_sfxEnabled || !_ready || track >= 120 || (track != 28 && _sfxDelay > _vm->_system->getMillis()))
 		return;
 	_driver->startSoundEffect(track);
diff --git a/engines/kyra/sound/sound_pc98_lok.cpp b/engines/kyra/sound/sound_pc98_lok.cpp
index 8695fc5e35..478f65aec2 100644
--- a/engines/kyra/sound/sound_pc98_lok.cpp
+++ b/engines/kyra/sound/sound_pc98_lok.cpp
@@ -124,7 +124,7 @@ void SoundPC98_LoK::beginFadeOut() {
 	haltTrack();
 }
 
-void SoundPC98_LoK::playSoundEffect(uint8 track, uint8) {
+void SoundPC98_LoK::playSoundEffect(uint16 track, uint8) {
 	if (!_sfxTrackData)
 		return;
 
diff --git a/engines/kyra/sound/sound_pc98_v2.cpp b/engines/kyra/sound/sound_pc98_v2.cpp
index 64c805a0b3..969702d9c4 100644
--- a/engines/kyra/sound/sound_pc98_v2.cpp
+++ b/engines/kyra/sound/sound_pc98_v2.cpp
@@ -246,7 +246,7 @@ int32 SoundTownsPC98_v2::voicePlay(const char *file, Audio::SoundHandle *handle,
 	return 1;
 }
 
-void SoundTownsPC98_v2::playSoundEffect(uint8 track, uint8) {
+void SoundTownsPC98_v2::playSoundEffect(uint16 track, uint8) {
 	if (!_useFmSfx || !_sfxTrackData)
 		return;
 
diff --git a/engines/kyra/sound/sound_pc_midi.cpp b/engines/kyra/sound/sound_pc_midi.cpp
index 0aed316dbd..f687e9a157 100644
--- a/engines/kyra/sound/sound_pc_midi.cpp
+++ b/engines/kyra/sound/sound_pc_midi.cpp
@@ -318,7 +318,7 @@ bool SoundMidiPC::isPlaying() const {
 	return _music->isPlaying();
 }
 
-void SoundMidiPC::playSoundEffect(uint8 track, uint8) {
+void SoundMidiPC::playSoundEffect(uint16 track, uint8) {
 	if (!_sfxEnabled)
 		return;
 
diff --git a/engines/kyra/sound/sound_pc_v1.cpp b/engines/kyra/sound/sound_pc_v1.cpp
index a370e2ee41..dc37978a31 100644
--- a/engines/kyra/sound/sound_pc_v1.cpp
+++ b/engines/kyra/sound/sound_pc_v1.cpp
@@ -153,7 +153,7 @@ bool SoundPC_v1::isPlaying() const {
 	return _driver->isChannelPlaying(0);
 }
 
-void SoundPC_v1::playSoundEffect(uint8 track, uint8 volume) {
+void SoundPC_v1::playSoundEffect(uint16 track, uint8 volume) {
 	if (_sfxEnabled)
 		play(track, volume);
 }
diff --git a/engines/kyra/sound/sound_pc_v1.h b/engines/kyra/sound/sound_pc_v1.h
index f9d6009b0a..b565b3ed82 100644
--- a/engines/kyra/sound/sound_pc_v1.h
+++ b/engines/kyra/sound/sound_pc_v1.h
@@ -69,7 +69,7 @@ public:
 	void haltTrack() override;
 	bool isPlaying() const override;
 
-	void playSoundEffect(uint8 track, uint8 volume = 0xFF) override;
+	void playSoundEffect(uint16 track, uint8 volume = 0xFF) override;
 
 	void beginFadeOut() override;
 
diff --git a/engines/kyra/sound/sound_segacd_eob.cpp b/engines/kyra/sound/sound_segacd_eob.cpp
index cb41e2fcb8..19d4084097 100644
--- a/engines/kyra/sound/sound_segacd_eob.cpp
+++ b/engines/kyra/sound/sound_segacd_eob.cpp
@@ -22,6 +22,7 @@
 
 #ifdef ENABLE_EOB
 
+#include "kyra/sound/drivers/segacd.h"
 #include "kyra/sound/sound_intern.h"
 #include "kyra/resource/resource.h"
 #include "common/config-manager.h"
@@ -30,15 +31,14 @@
 namespace Kyra {
 
 SoundSegaCD_EoB::SoundSegaCD_EoB(KyraEngine_v1 *vm, Audio::Mixer *mixer) : Sound(vm, mixer),
-	_vm(vm), /*_driver(0), _currentResourceSet(-1), _sfxDelay(0),*/ _ready(false) {
-	//memset(_resInfo, 0, sizeof(_resInfo));
+	_vm(vm), _driver(0), _fmData(0), _lastSoundEffect(-1), _ready(false) {
+	memset(_pcmOffsets, 0, sizeof(_pcmOffsets));
+	memset(_fmOffsets, 0, sizeof(_fmOffsets));
 }
 
 SoundSegaCD_EoB::~SoundSegaCD_EoB() {
-	//delete _driver;
-
-	for (int i = 0; i < 3; i++)
-		initAudioResourceInfo(i, 0);
+	delete _driver;
+	delete[] _fmData;
 }
 
 Sound::kType SoundSegaCD_EoB::getMusicType() const {
@@ -46,29 +46,15 @@ Sound::kType SoundSegaCD_EoB::getMusicType() const {
 }
 
 bool SoundSegaCD_EoB::init() {
-	//_driver = new MLALF98(_mixer, MLALF98::kType9801_86);
+	_driver = new SegaAudioDriver(_mixer);
 	g_system->getAudioCDManager()->open();
-	_ready = true;
-	return true;
-}
 
-void SoundSegaCD_EoB::initAudioResourceInfo(int set, void *info) {
-	//delete _resInfo[set];
-	//_resInfo[set] = info ? new SoundResourceInfo_PC(*(SoundResourceInfo_PC*)info) : 0;
-}
-
-void SoundSegaCD_EoB::selectAudioResourceSet(int set) {
-	/*if (set == _currentResourceSet || !_ready)
-		return;
+	loadPCMData();
+	loadFMData();
 
-	if (!_resInfo[set])
-		return;
-
-	_currentResourceSet = set;*/
-}
-
-void SoundSegaCD_EoB::loadSfxFile(Common::String file) {
+	_ready = true;
 
+	return true;
 }
 
 void SoundSegaCD_EoB::playTrack(uint8 track) {
@@ -93,10 +79,27 @@ void SoundSegaCD_EoB::haltTrack() {
 	g_system->getAudioCDManager()->stop();
 }
 
-void SoundSegaCD_EoB::playSoundEffect(uint8 track, uint8) {
+void SoundSegaCD_EoB::playSoundEffect(uint16 track, uint8 volume) {
 	if (!_sfxEnabled || !_ready)
 		return;
-//	_driver->startSoundEffect(track);
+
+	uint8 flags = track >> 8;
+	track &= 0xFF;
+
+	if (flags & 0x80) {
+		track--;
+		assert(track < ARRAYSIZE(_pcmOffsets));
+		for (uint8 i = 0; i < 8; ++i)
+			_driver->startPCMSound(i, _pcmOffsets[track]);
+
+	} else {
+		uint8 snd = (flags & 0x40) ? track : _fmTrackMap[track];
+		if (snd == 0 || snd > 135)
+			return;
+
+		_driver->startFMSound(&_fmData[_fmOffsets[snd - 1]], volume, (SegaAudioDriver::PrioFlags)flags);
+		_lastSoundEffect = track;
+	}
 }
 
 bool SoundSegaCD_EoB::isPlaying() const {
@@ -104,17 +107,71 @@ bool SoundSegaCD_EoB::isPlaying() const {
 }
 
 void SoundSegaCD_EoB::updateVolumeSettings() {
-	if (/*!_driver ||*/ !_ready)
+	if (!_driver || !_ready)
 		return;
 
-	//bool mute = false;
-	//if (ConfMan.hasKey("mute"))
-	//	mute = ConfMan.getBool("mute");
+	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")));
+	_driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
+	_driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
 }
 
+void SoundSegaCD_EoB::loadPCMData() {
+	uint32 dataSize;
+	uint8 *data1 = _vm->resource()->fileData("PCM", &dataSize);
+	if (!data1)
+		error("SoundSegaCD_EoB::loadPCMData: File not found: '%s'", "PCM");
+
+	uint8 *data2 = new uint8[0x100];
+	memset(data2, 0xFF, 0x100);
+	data2[0] = 0x80;
+
+	for (int i = 0; i < ARRAYSIZE(_pcmOffsets); ++i)
+		_pcmOffsets[i] = data1[(i << 2) + 2];
+
+	_driver->loadPCMData(0, data1 + 64, dataSize - 64);
+	_driver->loadPCMData(0xFF00, data2, 0x100);
+
+	delete[] data1;
+	delete[] data2;
+}
+
+void SoundSegaCD_EoB::loadFMData() {
+	Common::SeekableReadStreamEndian *in = _vm->resource()->createEndianAwareReadStream("FMSE");
+	if (!in)
+		error("SoundSegaCD_EoB::loadFMData: File not found: '%s'", "FMSE");
+
+	for (int i = 0; i < ARRAYSIZE(_fmOffsets); ++i)
+		_fmOffsets[i] = (in->readUint32() - ARRAYSIZE(_fmOffsets) * 4) & 0xFFFF;
+
+	uint32 dataSize = in->size() - in->pos();
+	uint8 *data = new uint8[dataSize];
+	in->read(data, dataSize);
+	delete[] _fmData;
+	_fmData = data;
+
+	delete in;
+}
+
+const uint8 SoundSegaCD_EoB::_fmTrackMap[140] = {
+	0x00, 0x05, 0x40, 0x01, 0x02, 0x02, 0x06, 0x07, 0x12, 0x0a,
+	0x11, 0x1f, 0x1e, 0x12, 0x09, 0x0c, 0x0b, 0x17, 0x21, 0x0d,
+	0x00, 0x14, 0x00, 0x16, 0x00, 0x00, 0x26, 0x0f, 0x13, 0x10,
+	0x00, 0x00, 0x27, 0x00, 0x1a, 0x28, 0x39, 0x46, 0x33, 0x4a,
+	0x3b, 0x48, 0x33, 0x47, 0x38, 0x4d, 0x3e, 0x45, 0x36, 0x41,
+	0x3a, 0x49, 0x46, 0x38, 0x44, 0x37, 0x42, 0x34, 0x4b, 0x3c,
+	0x41, 0x3b, 0x40, 0x38, 0x47, 0x39, 0x4d, 0x35, 0x4c, 0x3d,
+	0x4e, 0x3d, 0x42, 0x43, 0x36, 0x32, 0x00, 0x60, 0x1f, 0x82,
+	0x1c, 0x29, 0x00, 0x00, 0x00, 0x65, 0x24, 0x60, 0x62, 0x6d,
+	0x00, 0x78, 0x70, 0x74, 0x7e, 0x7d, 0x66, 0x81, 0x6a, 0x67,
+	0x80, 0x68, 0x64, 0x6c, 0x77, 0x77, 0x77, 0x61, 0x61, 0x61,
+	0x71, 0x79, 0x7f, 0x73, 0x7a, 0x7b, 0x71, 0x7c, 0x6e, 0x0e,
+	0x75, 0x76, 0x78, 0x6b, 0x30, 0x2f, 0x03, 0x04, 0x23, 0x2b,
+	0x5a, 0x1a, 0x1c, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
 } // End of namespace Kyra
 
 #endif // ENABLE_EOB
diff --git a/engines/kyra/sound/sound_towns_darkmoon.cpp b/engines/kyra/sound/sound_towns_darkmoon.cpp
index 17ced42209..33d227aa3c 100644
--- a/engines/kyra/sound/sound_towns_darkmoon.cpp
+++ b/engines/kyra/sound/sound_towns_darkmoon.cpp
@@ -214,7 +214,7 @@ bool SoundTowns_Darkmoon::isPlaying() const {
 	return g_system->getAudioCDManager()->isPlaying();
 }
 
-void SoundTowns_Darkmoon::playSoundEffect(uint8 track, uint8 volume) {
+void SoundTowns_Darkmoon::playSoundEffect(uint16 track, uint8 volume) {
 	if (!_sfxEnabled)
 		return;
 
diff --git a/engines/kyra/sound/sound_towns_lok.cpp b/engines/kyra/sound/sound_towns_lok.cpp
index d66b649907..16de13cd96 100644
--- a/engines/kyra/sound/sound_towns_lok.cpp
+++ b/engines/kyra/sound/sound_towns_lok.cpp
@@ -149,7 +149,7 @@ void SoundTowns_LoK::loadSoundFile(uint file) {
 	_sfxFileData = _vm->resource()->fileData(res()->fileList[file], 0);
 }
 
-void SoundTowns_LoK::playSoundEffect(uint8 track, uint8) {
+void SoundTowns_LoK::playSoundEffect(uint16 track, uint8) {
 	if (!_sfxEnabled || !_sfxFileData)
 		return;
 


Commit: dda1559f0387668f252c5da1180107105a7fb1db
    https://github.com/scummvm/scummvm/commit/dda1559f0387668f252c5da1180107105a7fb1db
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:21+02:00

Commit Message:
KYRA: (EOB/SegaCD) - SegaCD sound effect adjustments

Changed paths:
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/kyra_rpg.h
    engines/kyra/engine/scene_rpg.cpp
    engines/kyra/engine/sprites_eob.cpp
    engines/kyra/engine/timer_rpg.cpp
    engines/kyra/script/script_eob.cpp
    engines/kyra/sound/drivers/segacd.cpp


diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 698aca5947..a5d3f7ed3b 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -31,6 +31,7 @@
 #include "kyra/gui/debugger.h"
 
 #include "common/config-manager.h"
+#include "common/debug-channels.h"
 #include "common/translation.h"
 
 #include "gui/error.h"
@@ -450,7 +451,7 @@ Common::Error EoBCoreEngine::init() {
 
 	// Setup volume settings (and read in all ConfigManager settings)
 	_configNullSound = (MidiDriver::getMusicType(dev) == MT_NULL);
-	syncSoundSettings();	
+	syncSoundSettings();
 
 	if (!_screen->init())
 		error("screen()->init() failed");
@@ -1527,7 +1528,7 @@ void EoBCoreEngine::increaseCharacterLevel(int charIndex, int levelIndex) {
 
 	gui_drawCharPortraitWithStats(charIndex);
 	_txt->printMessage(_levelGainStrings[0], -1, _characters[charIndex].name);
-	snd_playSoundEffect(23);
+	snd_playSoundEffect(_flags.platform == Common::kPlatformSegaCD ? 0x1017 : 0x17);
 }
 
 void EoBCoreEngine::setWeaponSlotStatus(int charIndex, int mode, int slot) {
@@ -2213,10 +2214,13 @@ void EoBCoreEngine::inflictMonsterDamage(EoBMonsterInPlay *m, int damage, bool g
 		}
 	}
 
-	if (m->hitPointsCur <= 0)
+	if (m->hitPointsCur <= 0) {
+		if (_flags.platform == Common::kPlatformSegaCD)
+			snd_playSoundEffect(0x1082);
 		killMonster(m, giveExperience);
-	else if (getBlockDistance(m->block, _currentBlock) < 4)
+	} else if (getBlockDistance(m->block, _currentBlock) < 4) {
 		m->dest = _currentBlock;
+	}
 }
 
 void EoBCoreEngine::calcAndInflictMonsterDamage(EoBMonsterInPlay *m, int times, int pips, int offs, int flags, int savingThrowType, int savingThrowEffect) {
@@ -2710,8 +2714,12 @@ void EoBCoreEngine::snd_playSoundEffect(int track, int volume) {
 	if ((track < 1) || (_flags.gameID == GI_EOB2 && track > 119) || shouldQuit())
 		return;
 
-	if (_flags.platform == Common::kPlatformSegaCD && volume == 0xFF)
-		volume = 0x0E;
+	if (_flags.platform == Common::kPlatformSegaCD) {
+		if (volume == 0xFF)
+			volume = 0x0E;
+		if (track == 23 || track == 28)
+			track |= 0x1000;
+	}
 
 	_sound->playSoundEffect(track, volume);
 }
diff --git a/engines/kyra/engine/kyra_rpg.h b/engines/kyra/engine/kyra_rpg.h
index 73bcb72824..5f95a6f8dc 100644
--- a/engines/kyra/engine/kyra_rpg.h
+++ b/engines/kyra/engine/kyra_rpg.h
@@ -276,6 +276,7 @@ protected:
 	void processDoorSwitch(uint16 block, int openClose);
 	void openCloseDoor(int block, int openClose);
 	void completeDoorOperations();
+	bool isSpecialDoor(int block);
 
 	uint8 *_wllVmpMap;
 	int8 *_wllShapeMap;
diff --git a/engines/kyra/engine/scene_rpg.cpp b/engines/kyra/engine/scene_rpg.cpp
index a4a4d351f4..3de2ec0b17 100644
--- a/engines/kyra/engine/scene_rpg.cpp
+++ b/engines/kyra/engine/scene_rpg.cpp
@@ -677,7 +677,7 @@ void KyraRpgEngine::openCloseDoor(int block, int openClose) {
 			_levelBlockProperties[block].walls[c] += openClose;
 			_levelBlockProperties[block].walls[c ^ 2] += openClose;
 
-			int snd = (openClose == -1) ? 4 : 3;
+			int snd = (openClose == -1) ? 4 : (isSpecialDoor(block) ? 126 : 3);
 			if (_flags.gameID == GI_LOL) {
 				snd_processEnvironmentalSoundEffect(snd + 28, _currentBlock);
 				if (!checkSceneUpdateNeed(block))
@@ -714,6 +714,20 @@ void KyraRpgEngine::completeDoorOperations() {
 	}
 }
 
+bool KyraRpgEngine::isSpecialDoor(int block) {
+	if (_flags.platform != Common::kPlatformSegaCD || _currentLevel != 2)
+		return false;
+
+	static const uint16 specialBlocks[4] = { 0x122, 0x275, 0x3C8, 0x1E7 };
+
+	for (int i = 0; i < 4; ++i) {
+		if (block == specialBlocks[i])
+			return true;
+	}
+
+	return false;
+}
+
 } // End of namespace Kyra
 
 #endif // ENABLE_EOB || ENABLE_LOL
diff --git a/engines/kyra/engine/sprites_eob.cpp b/engines/kyra/engine/sprites_eob.cpp
index 05a643e54f..33952e47ab 100644
--- a/engines/kyra/engine/sprites_eob.cpp
+++ b/engines/kyra/engine/sprites_eob.cpp
@@ -1214,8 +1214,9 @@ bool EoBCoreEngine::walkMonsterNextStep(EoBMonsterInPlay *m, int destBlock, int
 		m->dir = direction;
 
 	checkSceneUpdateNeed(obl);
+	uint16 prioFlag = (_flags.platform == Common::kPlatformSegaCD) ? 0x2000 : 0;
 	if (!_partyResting && p->sound2 > 0)
-		snd_processEnvironmentalSoundEffect(p->sound2, m->block);
+		snd_processEnvironmentalSoundEffect(p->sound2 | prioFlag, m->block);
 
 	return true;
 }
diff --git a/engines/kyra/engine/timer_rpg.cpp b/engines/kyra/engine/timer_rpg.cpp
index 3ad3947d80..d9a60bc21f 100644
--- a/engines/kyra/engine/timer_rpg.cpp
+++ b/engines/kyra/engine/timer_rpg.cpp
@@ -77,7 +77,8 @@ void KyraRpgEngine::timerProcessDoors(int timerNum) {
 			}
 		} else {
 			checkSceneUpdateNeed(b);
-			snd_updateEnvironmentalSfx(snd);
+			if (!isSpecialDoor(b))
+				snd_updateEnvironmentalSfx(snd);
 		}
 
 		if (flg & 0x30)
diff --git a/engines/kyra/script/script_eob.cpp b/engines/kyra/script/script_eob.cpp
index 99791de14d..21a70ff1b5 100644
--- a/engines/kyra/script/script_eob.cpp
+++ b/engines/kyra/script/script_eob.cpp
@@ -634,15 +634,19 @@ int EoBInfProcessor::oeob_setFlags(int8 *data) {
 
 int EoBInfProcessor::oeob_playSoundEffect(int8 *data) {
 	int8 *pos = data;
-	uint16 block = READ_LE_UINT16(pos + 1);
+	uint16 snd = (uint8)*pos++;
+	uint16 block = READ_LE_UINT16(pos);
+	pos += 2;
+
+	if (_vm->gameFlags().platform == Common::kPlatformSegaCD && (snd == 28 || snd == 133))
+		snd |= 0x1000;
 
 	if (block) {
-		_vm->snd_processEnvironmentalSoundEffect(pos[0], block);
+		_vm->snd_processEnvironmentalSoundEffect(snd, block);
 	} else {
-		_vm->snd_playSoundEffect(pos[0]);
+		_vm->snd_playSoundEffect(snd);
 	}
 
-	pos += 3;
 	return pos - data;
 }
 
diff --git a/engines/kyra/sound/drivers/segacd.cpp b/engines/kyra/sound/drivers/segacd.cpp
index 1876766992..1b2eb04c6a 100644
--- a/engines/kyra/sound/drivers/segacd.cpp
+++ b/engines/kyra/sound/drivers/segacd.cpp
@@ -30,7 +30,7 @@
 #include "common/endian.h"
 #include "common/ptr.h"
 
-#define SEGA_SND_DEBUG_EXT	1
+#define SEGA_SND_DEBUG_EXT	0
 
 namespace Kyra {
 


Commit: ed5f695c04a7600b70d6458ae47a4d50b515785d
    https://github.com/scummvm/scummvm/commit/ed5f695c04a7600b70d6458ae47a4d50b515785d
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:21+02:00

Commit Message:
KYRA: (EOB/SegaCD) - fix warnings

Changed paths:
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/sound/drivers/segacd.cpp
    engines/kyra/sound/drivers/segacd.h


diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 15dce0b813..909f8afa09 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -1510,7 +1510,8 @@ GUI_EoB::GUI_EoB(EoBCoreEngine *vm) : GUI(vm), _vm(vm), _screen(vm->_screen), _n
 		_saveSlotStringsTemp[i] = new char[26];
 		memset(_saveSlotStringsTemp[i], 0, 26);
 	}
-	_saveSlotIdTemp = new int16[6];
+	_saveSlotIdTemp = new int16[7];
+	memset(_saveSlotIdTemp, 0xFF, sizeof(int16) * 7);
 	_savegameOffset = 0;
 	_saveSlotX = _saveSlotY = 0;
 
@@ -3215,7 +3216,7 @@ int GUI_EoB::selectSaveSlotDialog(int x, int y, int id) {
 		bool clickedButton = false;
 
 		if (inputFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inputFlag == _vm->_keyMap[Common::KEYCODE_RETURN]) {
-			runLoop = (_vm->gameFlags().platform == Common::kPlatformSegaCD && _saveSlotIdTemp[newHighlight] == -1 && id == 1);
+			runLoop = (_vm->gameFlags().platform == Common::kPlatformSegaCD && _saveSlotIdTemp[newHighlight] == -1 && newHighlight < _numSlotsVisible && id == 1);
 			clickedButton = true;
 		} else if (inputFlag == _vm->_keyMap[Common::KEYCODE_ESCAPE]) {
 			newHighlight = _numSlotsVisible;
@@ -3264,7 +3265,7 @@ int GUI_EoB::selectSaveSlotDialog(int x, int y, int id) {
 			if (slot != -1) {
 				newHighlight = slot;
 				if (inputFlag == 199) {
-					runLoop = (_vm->gameFlags().platform == Common::kPlatformSegaCD && _saveSlotIdTemp[newHighlight] == -1 && id == 1);
+					runLoop = (_vm->gameFlags().platform == Common::kPlatformSegaCD && _saveSlotIdTemp[newHighlight] == -1 && slot < _numSlotsVisible && id == 1);
 					clickedButton = true;
 				}
 			}
diff --git a/engines/kyra/sound/drivers/segacd.cpp b/engines/kyra/sound/drivers/segacd.cpp
index 1b2eb04c6a..f77f643eb7 100644
--- a/engines/kyra/sound/drivers/segacd.cpp
+++ b/engines/kyra/sound/drivers/segacd.cpp
@@ -227,7 +227,7 @@ private:
 		kAttack,
 		kSustain,
 		kRelease,
-		kReady,
+		kReady
 	};
 
 	int _envState;
diff --git a/engines/kyra/sound/drivers/segacd.h b/engines/kyra/sound/drivers/segacd.h
index 8e7b47c571..3f1466f9e8 100644
--- a/engines/kyra/sound/drivers/segacd.h
+++ b/engines/kyra/sound/drivers/segacd.h
@@ -47,7 +47,7 @@ public:
 
 	enum PrioFlags {
 		kPrioHigh = 0x10,
-		kPrioLow = 0x20,
+		kPrioLow = 0x20
 	};
 
 	void startFMSound(const uint8 *trackData, uint8 volume, PrioFlags prioFlags);


Commit: c8b042f0bc2073f5b4bd81f7abababbcf0ca20b2
    https://github.com/scummvm/scummvm/commit/c8b042f0bc2073f5b4bd81f7abababbcf0ca20b2
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:21+02:00

Commit Message:
KYRA: (EOB) - fix level graphics glitch

The init script of the final level in EOB I makes some modifications to global data that can mess up certain other levels if you return there (especially by loading a savegame). So we reset that data.

Changed paths:
    engines/kyra/engine/eobcommon.h
    engines/kyra/engine/scene_eob.cpp


diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index 0e6407f4ca..ba6a966600 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -636,6 +636,7 @@ protected:
 	void assignWallsAndDecorations(int wallIndex, int vmpIndex, int decDataIndex, int specialType, int flags);
 	void releaseDecorations();
 	void releaseDoorShapes();
+	void resetWallData();
 	void toggleWallState(int wall, int flags);
 	virtual void loadDoorShapes(int doorType1, int shapeId1, int doorType2, int shapeId2) = 0;
 	virtual const uint8 *loadDoorShapes(const char *filename, int doorIndex, const uint8 *shapeDefs) = 0;
diff --git a/engines/kyra/engine/scene_eob.cpp b/engines/kyra/engine/scene_eob.cpp
index 5e7e4ee98b..1777c41d06 100644
--- a/engines/kyra/engine/scene_eob.cpp
+++ b/engines/kyra/engine/scene_eob.cpp
@@ -42,6 +42,8 @@ void EoBCoreEngine::loadLevel(int level, int sub) {
 	disableSysTimer(2);
 	uint32 end = _system->getMillis() + 500;
 
+	resetWallData();
+
 	readLevelFileData(level);
 
 	Common::String gfxFile;
@@ -526,6 +528,24 @@ void EoBCoreEngine::releaseDoorShapes() {
 	}
 }
 
+void EoBCoreEngine::resetWallData() {
+	memset(_wllVmpMap, 0, 256);
+	_wllVmpMap[1] = 1;
+	_wllVmpMap[2] = 2;
+	memset(&_wllVmpMap[3], 3, 20);
+	_wllVmpMap[23] = 4;
+	_wllVmpMap[24] = 5;
+	memset(_wllShapeMap, 0, 256);
+	memset(&_wllShapeMap[3], -1, 5);
+	memset(&_wllShapeMap[13], -1, 5);
+	memset(_wllWallFlags, 0, 256);
+	memcpy(_wllWallFlags, _wllFlagPreset, _wllFlagPresetSize);
+	memset(_specialWallTypes, 0, 256);
+	memset(&_specialWallTypes[3], 1, 5);
+	memset(&_specialWallTypes[13], 1, 5);
+	_specialWallTypes[8] = _specialWallTypes[18] = 6;
+}
+
 void EoBCoreEngine::toggleWallState(int wall, int toggle) {
 	wall = wall * 10 + 3;
 
@@ -536,7 +556,7 @@ void EoBCoreEngine::toggleWallState(int wall, int toggle) {
 		if (toggle)
 			_wllWallFlags[wall + i] |= 2;
 		else
-			_wllWallFlags[wall + i] &= 0xFD;
+			_wllWallFlags[wall + i] &= ~2;
 	}
 }
 


Commit: 2b2e90c8c61bd450c0861465d7ee46862a5f5010
    https://github.com/scummvm/scummvm/commit/2b2e90c8c61bd450c0861465d7ee46862a5f5010
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:23+02:00

Commit Message:
KYRA: (EOB/SegaCD) - improve/cleanup sequence player

Changed paths:
    engines/kyra/engine/eob.h
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/eobcommon.h
    engines/kyra/sequence/seqplayer_eob_segacd.cpp
    engines/kyra/sequence/seqplayer_eob_segacd.h
    engines/kyra/sequence/sequences_eob.cpp


diff --git a/engines/kyra/engine/eob.h b/engines/kyra/engine/eob.h
index a099829ff3..71bc358a9f 100644
--- a/engines/kyra/engine/eob.h
+++ b/engines/kyra/engine/eob.h
@@ -102,6 +102,7 @@ private:
 	void seq_segaSetupSequence(int sequenceId);
 	void seq_segaRestoreAfterSequence();
 	bool seq_segaPlaySequence(int sequenceId, bool setupScreen = false);
+	void seq_segaPausePlayer(bool pause) override;
 
 	const char *const *_finBonusStrings;
 	SegaSequencePlayer *_seqPlayer;
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index a5d3f7ed3b..357935cd9e 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -1820,6 +1820,11 @@ void EoBCoreEngine::delay(uint32 millis, bool, bool) {
 	}
 }
 
+void EoBCoreEngine::pauseEngineIntern(bool pause) {
+	KyraEngine_v1::pauseEngineIntern(pause);
+	seq_segaPausePlayer(pause);
+}
+
 void EoBCoreEngine::displayParchment(int id) {
 	_txt->setWaitButtonMode(1);
 	_txt->resetPageBreakString();
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index ba6a966600..7b86b1aba9 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -903,13 +903,15 @@ protected:
 
 	// misc
 	void delay(uint32 millis, bool doUpdate = false, bool isMainLoop = false) override;
+	void pauseEngineIntern(bool pause) override;
 
 	virtual void displayParchment(int id);
 	int countResurrectionCandidates();
 
 	void seq_portal();
 	virtual const uint8 **makePortalShapes();
-	bool seq_playSegaSequence(int id) { return true; }
+	//bool seq_playSegaSequence(int id) { return true; }
+	virtual void seq_segaPausePlayer(bool pause) {}
 	bool checkPassword();
 
 	Common::String convertAsciiToSjis(Common::String str);
diff --git a/engines/kyra/sequence/seqplayer_eob_segacd.cpp b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
index 9302c9c023..aeaf4bbdd4 100644
--- a/engines/kyra/sequence/seqplayer_eob_segacd.cpp
+++ b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
@@ -33,8 +33,8 @@
 namespace Kyra {
 
 SegaSequencePlayer::SegaSequencePlayer(EoBEngine *vm, Screen_EoB *screen, SegaCDResource *res) : _vm(vm), _screen(screen), _res(res), _tileSets(0), _debugResyncCnt(0), _speechAnimType(0),
-	_playingID(1), _waterdeepScene(0), _playSpeechAnimation(0), _waitFlag(false), _waterdeepSceneTimer(0), _speechAnimTimer(0), _speechAnimNo(0), _speechAnimFrame(0),
-	_newTrack(-1), _renderer(_screen->sega_getRenderer()), _animator(_screen->sega_getAnimator()) {
+	_playingID(1), _waterdeepScene(0), _playSpeechAnimation(0), _frameTimer(0), _waterdeepSceneTimer(0), _speechAnimTimer(0), _speechAnimNo(0), _speechAnimFrame(0),
+	_newTrack(-1), _pauseStart(0), _renderer(_screen->sega_getRenderer()), _animator(_screen->sega_getAnimator()) {
 #define SQOPC(x) _opcodes.push_back(new SQOpcode(this, &SegaSequencePlayer::x, #x))
 	SQOPC(s_initDrawObject);
 	SQOPC(s_drawTileSet);
@@ -233,14 +233,16 @@ bool SegaSequencePlayer::play(int id) {
 	return true;
 }
 
-void SegaSequencePlayer::setWaitFlag(bool enable) {
-	_waitFlag = enable;
+void SegaSequencePlayer::pause(bool pause) {
+	if (pause)
+		_pauseStart = _vm->_system->getMillis();
+	else
+		_frameTimer += (_vm->_system->getMillis() - _pauseStart);
 }
 
 void SegaSequencePlayer::run(const uint8 *data) {
 	_waterdeepScene = _playSpeechAnimation = false;
-	uint32 frameCounter = 0;
-	uint32 nextFrame = 0;
+	_frameTimer = _vm->_system->getMillis();
 
 	for (bool runLoop = true; runLoop && !(_vm->shouldQuit() || _vm->skipFlag()); ) {
 		uint16 frameSize = READ_BE_UINT16(data);
@@ -249,12 +251,10 @@ void SegaSequencePlayer::run(const uint8 *data) {
 
 		uint32 frameStart = _vm->_system->getMillis();
 		uint16 timeStamp = READ_BE_UINT16(data + 2);
-		uint32 lastFrame = nextFrame;
-		nextFrame = timeStamp * 16;
-		if (nextFrame < lastFrame)
-			frameCounter = 0;
+		uint32 nextFrame = _frameTimer + (timeStamp * 16667) / 1000;
+		bool insertDelay = false;
 
-		if (frameCounter >= nextFrame) {
+		if (_vm->_system->getMillis() >= nextFrame) {
 			debugC(5, kDebugLevelSequence, "SeqPlayer: Timestamp %08d", timeStamp);
 			for (uint16 timeStamp2 = timeStamp; timeStamp2 == timeStamp; ) {
 				uint16 op = READ_BE_UINT16(data + 4);
@@ -266,6 +266,8 @@ void SegaSequencePlayer::run(const uint8 *data) {
 
 				timeStamp2 = READ_BE_UINT16(data + 2);
 			}
+		} else {
+			insertDelay = true;
 		}
 
 		if (_waterdeepScene)
@@ -280,27 +282,11 @@ void SegaSequencePlayer::run(const uint8 *data) {
 		_screen->sega_updatePaletteFaders(-1);
 		_screen->updateScreen();
 
-		uint32 now = _vm->_system->getMillis();
-		int diff = now - (frameStart + 16);
-		if (diff < 0)
-			_vm->delay((uint32)-diff);
-		else if (diff) {
-			frameCounter += diff;
-			// This will be triggered with higher values whenever there is a palette fading and the code waits for it
-			// to finish (the code will wait for s_fadeToBlack() and s_fadeToNeutral() but not for s_paletteOps(), unless
-			// followed by s_waitForPaletteFade()). This doesn't cause issues, since it is the intended original behavior,
-			// but it needs to be compensated in the timing. The original does that automatically, since the frameCounter
-			// comes from the vblank interrupt vector. I don't use _system->getMillis directly for the frame timer, since
-			// it will be messed up when activating the GMM during sequence playback.
-			debugC(4, kDebugLevelSequence, "    Out of Sync. Catching up millis: %d", diff);
-			_debugResyncCnt += diff;
+		if (insertDelay) {
+			int diff = _vm->_system->getMillis() - (frameStart + 16);
+			if (diff < 0)
+				_vm->delay((uint32)-diff);
 		}
-		frameCounter += 16;
-
-		/*if (_finFlag & 0x80) {
-			if (_waitFlag || _curVis == 55 || _curVis == 56)
-				return true;
-		}*/
 	}
 }
 
@@ -592,11 +578,6 @@ void SegaSequencePlayer::s_playCD(const uint8 *pos) {
 	if (track)
 		_newTrack = track;
 	_vm->snd_stopSound();
-
-	if (_waitFlag) {
-		while (!(_vm->shouldQuit() || _vm->skipFlag()))
-			_vm->delay(20);
-	}
 }
 
 void SegaSequencePlayer::s_displayTextEn(const uint8 *pos) {
diff --git a/engines/kyra/sequence/seqplayer_eob_segacd.h b/engines/kyra/sequence/seqplayer_eob_segacd.h
index b2651c02e7..42ea5c10b7 100644
--- a/engines/kyra/sequence/seqplayer_eob_segacd.h
+++ b/engines/kyra/sequence/seqplayer_eob_segacd.h
@@ -41,7 +41,7 @@ public:
 	~SegaSequencePlayer();
 
 	bool play(int id);
-	void setWaitFlag(bool enable);
+	void pause(bool pause);
 
 private:
 	void run(const uint8 *data);
@@ -73,8 +73,9 @@ private:
 	uint16 _playSpeechAnimation;
 	uint16 _speechAnimType;
 	uint16 _speechAnimDrawOps[14];
+	uint32 _frameTimer;
+	uint32 _pauseStart;
 
-	bool _waitFlag;
 	int _waterdeepSceneTimer, _speechAnimTimer;
 	uint16 _speechAnimNo, _speechAnimFrame;
 	int _playingID;
diff --git a/engines/kyra/sequence/sequences_eob.cpp b/engines/kyra/sequence/sequences_eob.cpp
index f47a1f4df3..154f29b395 100644
--- a/engines/kyra/sequence/sequences_eob.cpp
+++ b/engines/kyra/sequence/sequences_eob.cpp
@@ -2800,6 +2800,13 @@ bool EoBEngine::seq_segaPlaySequence(int sequenceId, bool setupScreen) {
 	return true;
 }
 
+void EoBEngine::seq_segaPausePlayer(bool pause) {
+	if (_flags.platform != Common::kPlatformSegaCD)
+		return;
+
+	_seqPlayer->pause(pause);
+}
+
 #undef updateScrollState
 #undef displaySubtitle
 #undef printSub


Commit: 3f753e80a7b7382d68c533bfb1c4276b1de106d4
    https://github.com/scummvm/scummvm/commit/3f753e80a7b7382d68c533bfb1c4276b1de106d4
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:23+02:00

Commit Message:
KYRA: (EOB/SegaCD) - fix playing timer and time stamps

Changed paths:
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/sequence/sequences_eob.cpp


diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 357935cd9e..05a428414e 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -797,7 +797,7 @@ bool EoBCoreEngine::checkPartyStatus(bool handleDeath) {
 
 void EoBCoreEngine::updateAnimTimers() {
 	uint32 curTime = _system->getMillis();
-	if (_lastVIntTick + 1000 <= curTime) {
+	if (_lastSecTick + 1000 <= curTime) {
 		_lastSecTick = curTime;
 		_totalPlaySecs++;
 	}
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 909f8afa09..85eac8031f 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -3148,7 +3148,7 @@ bool GUI_EoB::runSaveMenu(int x, int y) {
 					of = _vm->screen()->setFont(Screen::FID_6_FNT);
 					y++;
 				} else if (_vm->gameFlags().platform == Common::kPlatformSegaCD) {
-					Common::strlcpy(_saveSlotStringsTemp[slot], Common::String::format("%s\r FLOOR %-2u %u:%02u", _vm->_characters[0].name, _vm->_currentLevel, _vm->_totalPlaySecs / 3600, _vm->_totalPlaySecs / 60).c_str(), 25);
+					Common::strlcpy(_saveSlotStringsTemp[slot], Common::String::format("%s\r FLOOR %-2u %u:%02u", _vm->_characters[0].name, _vm->_currentLevel, _vm->_totalPlaySecs / 3600, (_vm->_totalPlaySecs % 3600) / 60).c_str(), 25);
 					in = strlen(_saveSlotStringsTemp[slot]);
 				} else {
 					in = getTextInput(_saveSlotStringsTemp[slot], x + 1, fy, 19, _vm->guiSettings()->colors.guiColorBlue, 0, _vm->guiSettings()->colors.guiColorDarkRed);
diff --git a/engines/kyra/sequence/sequences_eob.cpp b/engines/kyra/sequence/sequences_eob.cpp
index 154f29b395..b9ac3765ba 100644
--- a/engines/kyra/sequence/sequences_eob.cpp
+++ b/engines/kyra/sequence/sequences_eob.cpp
@@ -2690,7 +2690,7 @@ void EoBEngine::seq_segaShowStats() {
 			++specialSearches;
 	}
 
-	_txt->printShadedText(Common::String::format("%u:%02u:%02u", _totalPlaySecs / 3600, _totalPlaySecs / 60, _totalPlaySecs).c_str(), 148, 28, 0xFF, 0x00, -1, -1, 0, false);
+	_txt->printShadedText(Common::String::format("%u:%02u:%02u", _totalPlaySecs / 3600, (_totalPlaySecs % 3600) / 60, (_totalPlaySecs % 3600) % 60).c_str(), 148, 28, 0xFF, 0x00, -1, -1, 0, false);
 	_txt->printShadedText(Common::String::format("%u", _totalEnemiesKilled).c_str(), 148, 40, 0xFF, 0x00, -1, -1, 0, false);
 	_txt->printShadedText(Common::String::format("%u", _totalSteps).c_str(), 148, 52, 0xFF, 0x00, -1, -1, 0, false);
 	_txt->printShadedText(Common::String::format("%u(%u%%)", partyArrows, partyArrows * 100 / 26).c_str(), 148, 64, 0xFF, 0x00, -1, -1, 0, false);


Commit: fc0e1cfc34ed5e0d8239fcd59ab5a9cf983a834c
    https://github.com/scummvm/scummvm/commit/fc0e1cfc34ed5e0d8239fcd59ab5a9cf983a834c
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:24+02:00

Commit Message:
KYRA: (EOB/SegaCD) - fix Wshadow warning

Changed paths:
    engines/kyra/engine/magic_eob.cpp


diff --git a/engines/kyra/engine/magic_eob.cpp b/engines/kyra/engine/magic_eob.cpp
index f92097fd98..fc2dcdb90c 100644
--- a/engines/kyra/engine/magic_eob.cpp
+++ b/engines/kyra/engine/magic_eob.cpp
@@ -445,8 +445,8 @@ void EoBCoreEngine::sparkEffectOffensive() {
 		if (sceneShake) {
 			_screen->copyRegion(0, 0, 0, 0, 176, 120, 0, 2, Screen::CR_NO_P_CHECK);
 			if (!_sceneShakeCountdown) {
-				for (int i = 0; i < 16; i++)
-					_screen->copyRegionToBuffer(0, _sparkEffectOfX[i], _sparkEffectOfY[i], 16, 16, &_spellAnimBuffer[i << sh]);
+				for (int ii = 0; ii < 16; ii++)
+					_screen->copyRegionToBuffer(0, _sparkEffectOfX[ii], _sparkEffectOfY[ii], 16, 16, &_spellAnimBuffer[ii << sh]);
 			}
 		}
 


Commit: 3773572ea019e993c2694aa39064c61eab310e3d
    https://github.com/scummvm/scummvm/commit/3773572ea019e993c2694aa39064c61eab310e3d
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:24+02:00

Commit Message:
KYRA: (EOB/SegaCD) - fix drow sequence gfx glitch

Changed paths:
    engines/kyra/sequence/seqplayer_eob_segacd.cpp
    engines/kyra/sequence/sequences_eob.cpp


diff --git a/engines/kyra/sequence/seqplayer_eob_segacd.cpp b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
index aeaf4bbdd4..0633b6a8ea 100644
--- a/engines/kyra/sequence/seqplayer_eob_segacd.cpp
+++ b/engines/kyra/sequence/seqplayer_eob_segacd.cpp
@@ -222,10 +222,6 @@ bool SegaSequencePlayer::play(int id) {
 		_screen->sega_paletteOps(4, 0, 0);
 	}
 
-	_scrollManager->setVScrollTimers(0, 1, 0, 0, 1, 0);
-	_scrollManager->setHScrollTimers(0, 1, 0, 0, 1, 0);
-	_scrollManager->updateScrollTimers();
-
 	_vm->_allowSkip = false;
 	_vm->resetSkipFlag();
 
diff --git a/engines/kyra/sequence/sequences_eob.cpp b/engines/kyra/sequence/sequences_eob.cpp
index b9ac3765ba..47333313be 100644
--- a/engines/kyra/sequence/sequences_eob.cpp
+++ b/engines/kyra/sequence/sequences_eob.cpp
@@ -2772,6 +2772,10 @@ void EoBEngine::seq_segaRestoreAfterSequence() {
 	r->setupWindowPlane(0, 0, SegaRenderer::kWinToLeft, SegaRenderer::kWinToTop);
 	r->fillRectWithTiles(0, 0, 0, 40, 28, 0x2000);
 	r->fillRectWithTiles(1, 0, 0, 40, 28, 0x2000);
+	r->writeUint16VSRAM(0, 0);
+	r->writeUint16VSRAM(2, 0);
+	r->writeUint16VRAM(0xD800, 0);
+	r->writeUint16VRAM(0xD802, 0);
 	_screen->clearPage(0);
 }
 


Commit: 314eec3c45ef0e5ed00329f86a5b4a30c961cb6b
    https://github.com/scummvm/scummvm/commit/314eec3c45ef0e5ed00329f86a5b4a30c961cb6b
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:24+02:00

Commit Message:
KYRA: (EOB/SegaCD) - fix compass glitch

Changed paths:
    engines/kyra/engine/eob.cpp
    engines/kyra/gui/gui_eob_segacd.cpp


diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index 566c420fcf..d7901c9a22 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -1029,6 +1029,9 @@ void EoBEngine::displayParchment(int id) {
 
 	_screen->sega_fadeToBlack(2);
 
+	Button b;
+	clickedSpellbookAbort(&b);
+
 	int temp = 0;
 	const char *const *strings = _staticres->loadStrings(kEoB1ParchmentStrings, temp);
 
diff --git a/engines/kyra/gui/gui_eob_segacd.cpp b/engines/kyra/gui/gui_eob_segacd.cpp
index 37d8f8a48e..0daac33913 100644
--- a/engines/kyra/gui/gui_eob_segacd.cpp
+++ b/engines/kyra/gui/gui_eob_segacd.cpp
@@ -236,7 +236,8 @@ void EoBEngine::gui_displayMap() {
 	disableSysTimer(2);
 
 	_screen->sega_fadeToBlack(2);
-
+	Button b;
+	clickedSpellbookAbort(&b);
 	gui_resetAnimations();
 	for (int i = 0; i < 6; i++) {
 		if (!testCharacter(i, 1))


Commit: 4e9a86c03e629fb197beb0a3b739ead5f8324de4
    https://github.com/scummvm/scummvm/commit/4e9a86c03e629fb197beb0a3b739ead5f8324de4
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:24+02:00

Commit Message:
KYRA: (EOB/SegaCD) - fix king healing scene

Changed paths:
    engines/kyra/engine/eob.cpp
    engines/kyra/graphics/screen_eob_segacd.cpp


diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index d7901c9a22..3df68adee6 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -475,7 +475,8 @@ void EoBEngine::runNpcDialogue(int npcIndex) {
 		if (!checkScriptFlags(0x100000)) {
 			if (deletePartyItems(6, -1)) {
 				_npcSequenceSub = 0;
-				drawNpcScene(npcIndex);
+				if (_flags.platform != Common::kPlatformSegaCD)
+					drawNpcScene(npcIndex);
 				TXT(28);
 				createItemOnCurrentBlock(32);
 				setScriptFlags(0x100000);
diff --git a/engines/kyra/graphics/screen_eob_segacd.cpp b/engines/kyra/graphics/screen_eob_segacd.cpp
index 601279599f..c6313dc123 100644
--- a/engines/kyra/graphics/screen_eob_segacd.cpp
+++ b/engines/kyra/graphics/screen_eob_segacd.cpp
@@ -36,7 +36,7 @@ void Screen_EoB::sega_initGraphics() {
 	_segaRenderer->setPlaneTableLocation(SegaRenderer::kPlaneA, 0xC000);
 	_segaRenderer->setPlaneTableLocation(SegaRenderer::kPlaneB, 0xE000);
 	_segaRenderer->setPlaneTableLocation(SegaRenderer::kWindowPlane, 0xF000);
-	_segaRenderer->setupPlaneAB(1024, 256);
+	_segaRenderer->setupPlaneAB(512, 512);
 	_segaRenderer->setupWindowPlane(0, 0, SegaRenderer::kWinToLeft, SegaRenderer::kWinToTop);
 	_segaRenderer->setHScrollTableLocation(0xD800);
 	_segaRenderer->setSpriteTableLocation(0xDC00);
@@ -1030,7 +1030,7 @@ void SegaAnimator::update() {
 		*dst++ = (uint16)(s->x + 128);
 	}
 
-	for ( ; dst < &_tempBuffer[320]; dst += 4)
+	for (; dst < &_tempBuffer[320]; dst += 4)
 		*dst = 0;
 
 	_renderer->loadToVRAM(_tempBuffer, 640, 0xDC00);
@@ -1204,7 +1204,7 @@ const uint8 *SegaCDFont::getGlyphData(uint16 c, uint8 &charWidth, uint8 &charHei
 	return res;
 }
 
-ScrollManager::ScrollManager(SegaRenderer *renderer) :_renderer(renderer) {
+ScrollManager::ScrollManager(SegaRenderer *renderer) : _renderer(renderer) {
 	_vScrollTimers = new ScrollTimer[2];
 	assert(_vScrollTimers);
 	_hScrollTimers = new ScrollTimer[2];


Commit: 287902994ca5d38669225c8b80158a8c2b221674
    https://github.com/scummvm/scummvm/commit/287902994ca5d38669225c8b80158a8c2b221674
Author: athrxx (athrxx at scummvm.org)
Date: 2020-07-30T22:19:24+02:00

Commit Message:
NEWS: update with EOB/SegaCD

Changed paths:
    NEWS.md


diff --git a/NEWS.md b/NEWS.md
index fd8e59a6c6..6b5d190a10 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -26,6 +26,8 @@ For a more comprehensive changelog of the latest experimental code, see:
    - Fixed animation speed.
 
  Kyra:
+   - Added support for the SegaCD version of Eye of the Beholder I (with CD-Audio, animated
+     cutscenes and map function).
    - Added support for the PC-98 version of Eye of the Beholder I.
    - Added support for the Spanish versions of Eye of the Beholder I and II, Legend of
      Kyrandia 1 (CD-ROM fan translation) and Legend of Kyrandia 2 (floppy version and




More information about the Scummvm-git-logs mailing list