[Scummvm-git-logs] scummvm master -> 6e9f7abc7becd04131126b36ae5639e2c2b585ac

athrxx noreply at scummvm.org
Tue Jul 11 12:35:55 UTC 2023


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

Summary:
3c64b08d7a KYRA: (EOB II/PC98) - minor sound code adjustments
a15ce9a202 KYRA: (LOL/ZH) - text displayer fixes
5e8333f638 KYRA: (EOB II/ZH) - fix/improve text display
1831f02c36 KYRA: (EOB/LOL) - reduce text displayer hacks
8510798d44 KYRA: (EOB II/ZH) - add missing kyradat resources
6e9f7abc7b KYRA: (EOB II/ZH) - fix dialogue buttons


Commit: 3c64b08d7aa7dbdeeae79762f6b187d8ceea3251
    https://github.com/scummvm/scummvm/commit/3c64b08d7aa7dbdeeae79762f6b187d8ceea3251
Author: athrxx (athrxx at scummvm.org)
Date: 2023-07-11T14:34:31+02:00

Commit Message:
KYRA: (EOB II/PC98) - minor sound code adjustments

Changed paths:
    engines/kyra/sound/drivers/capcom98.cpp


diff --git a/engines/kyra/sound/drivers/capcom98.cpp b/engines/kyra/sound/drivers/capcom98.cpp
index edbb0be0eda..8a130bf426d 100644
--- a/engines/kyra/sound/drivers/capcom98.cpp
+++ b/engines/kyra/sound/drivers/capcom98.cpp
@@ -21,9 +21,11 @@
 
 #ifdef ENABLE_EOB
 
+#include "kyra/kyra_v1.h"
 #include "kyra/sound/drivers/capcom98.h"
 #include "audio/mixer.h"
 #include "audio/softsynth/fmtowns_pc98/pc98_audio.h"
+#include "common/debug.h"
 #include "common/func.h"
 #include "common/system.h"
 
@@ -103,12 +105,12 @@ private:
 	const uint16 _playFlags;
 };
 
-class CapcomPC98_MIDI final : public CapcomPC98Player {
+class CapcomPC98Player_MIDI final : public CapcomPC98Player {
 public:
 	typedef Common::Functor0Mem<PC98AudioCore::MutexLock, CapcomPC98AudioDriverInternal> MutexProc;
 
-	CapcomPC98_MIDI(MidiDriver::DeviceHandle dev, bool isMT32, MutexProc &mutexProc);
-	~CapcomPC98_MIDI();
+	CapcomPC98Player_MIDI(MidiDriver::DeviceHandle dev, bool isMT32, MutexProc &mutexProc);
+	~CapcomPC98Player_MIDI();
 
 	bool init() override;
 	void deinit() override {}
@@ -132,10 +134,10 @@ private:
 	MutexProc &_mproc;
 };
 
-class CapcomPC98_FM_Channel {
+class CapcomPC98_FMChannel {
 public:
-	CapcomPC98_FM_Channel(uint8 id, PC98AudioCore *&ac, const Common::Array<const uint8*>&instruments, const uint8 &fadeState);
-	~CapcomPC98_FM_Channel();
+	CapcomPC98_FMChannel(uint8 id, PC98AudioCore *&ac, const Common::Array<const uint8*>&instruments, const uint8 &fadeState);
+	~CapcomPC98_FMChannel();
 
 	void reset();
 
@@ -157,7 +159,7 @@ public:
 	void processSounds();
 
 private:
-	typedef Common::Functor0Mem<void, CapcomPC98_FM_Channel> VbrHandlerProc;
+	typedef Common::Functor0Mem<void, CapcomPC98_FMChannel> VbrHandlerProc;
 
 	const uint8 _regOffset;
 	uint8 _program;
@@ -230,12 +232,12 @@ private:
 	static const uint8 _volTablePara[128];
 };
 
-class CapcomPC98_FM final : public CapcomPC98Player, PC98AudioPluginDriver {
+class CapcomPC98Player_FM final : public CapcomPC98Player, PC98AudioPluginDriver {
 public:
 	typedef Common::Functor0Mem<void, CapcomPC98AudioDriverInternal> CBProc;
 
-	CapcomPC98_FM(Audio::Mixer *mixer, CBProc &cbproc, bool playerPrio, uint16 playFlags, uint8 chanReserveFlags, uint8 chanDisableFlags, uint16 volControlMask, bool needsTimer);
-	~CapcomPC98_FM() override;
+	CapcomPC98Player_FM(Audio::Mixer *mixer, CBProc &cbproc, bool playerPrio, uint16 playFlags, uint8 chanReserveFlags, uint8 chanDisableFlags, uint16 volControlMask, bool needsTimer);
+	~CapcomPC98Player_FM() override;
 
 	bool init() override;
 	void deinit() override;
@@ -256,7 +258,7 @@ private:
 	void restoreStateIntern() override;
 
 	PC98AudioCore *_ac;
-	CapcomPC98_FM_Channel **_chan;
+	CapcomPC98_FMChannel **_chan;
 	Common::Array<const uint8*>_instruments;
 
 	CBProc &_cbProc;
@@ -301,9 +303,9 @@ private:
 	void updateMasterVolume();
 
 	CapcomPC98Player *_players[2];
-	CapcomPC98_FM *_fmDevice;
-	CapcomPC98_FM::CBProc *_timerProc;
-	CapcomPC98_MIDI::MutexProc *_mutexProc;
+	CapcomPC98Player_FM *_fmDevice;
+	CapcomPC98Player_FM::CBProc *_timerProc;
+	CapcomPC98Player_MIDI::MutexProc *_mutexProc;
 
 	static CapcomPC98AudioDriverInternal *_refInstance;
 	static int _refCount;
@@ -355,6 +357,7 @@ void CapcomPC98Player::fadeOut(uint16 speed) {
 	if (speed) {
 		_fadeTicker =_fadeSpeed = speed;
 		_fadeState = 0;
+		PC98AudioCore::MutexLock lock = lockMutex();
 		_flags |= kFadeOut;
 	} else {
 		stopSound();
@@ -362,6 +365,7 @@ void CapcomPC98Player::fadeOut(uint16 speed) {
 }
 
 void CapcomPC98Player::allNotesOff(uint16 chanFlags) {
+	PC98AudioCore::MutexLock lock = lockMutex();
 	for (int i = 0; i < 16; ++i) {
 		if ((1 << i) & chanFlags)
 			send(0x007BB0 | i);
@@ -369,11 +373,13 @@ void CapcomPC98Player::allNotesOff(uint16 chanFlags) {
 }
 
 void CapcomPC98Player::setMusicVolume (int vol) {
+	PC98AudioCore::MutexLock lock = lockMutex();
 	_musicVolume = vol;
 	updateMasterVolume();
 }
 
 void CapcomPC98Player::setSoundEffectVolume (int vol) {
+	PC98AudioCore::MutexLock lock = lockMutex();
 	_sfxVolume = vol;
 	updateMasterVolume();
 }
@@ -482,7 +488,7 @@ void CapcomPC98Player::restorePlayer() {
 	_storedEvents.clear();
 }
 
-CapcomPC98_MIDI::CapcomPC98_MIDI(MidiDriver::DeviceHandle dev, bool isMT32, MutexProc &mutexProc) : CapcomPC98Player(true, kStdPlay, 0xFFFF, 0), _isMT32(isMT32), _mproc(mutexProc), _midi(nullptr), _programMapping(nullptr) {
+CapcomPC98Player_MIDI::CapcomPC98Player_MIDI(MidiDriver::DeviceHandle dev, bool isMT32, MutexProc &mutexProc) : CapcomPC98Player(true, kStdPlay, 0xFFFF, 0), _isMT32(isMT32), _mproc(mutexProc), _midi(nullptr), _programMapping(nullptr) {
 	_midi = MidiDriver::createMidi(dev);
 	uint8 *map = new uint8[128];
 	assert(map);
@@ -498,13 +504,13 @@ CapcomPC98_MIDI::CapcomPC98_MIDI(MidiDriver::DeviceHandle dev, bool isMT32, Mute
 	memset(_chanVolume, 0, sizeof(_chanVolume));
 }
 
-CapcomPC98_MIDI::~CapcomPC98_MIDI() {
+CapcomPC98Player_MIDI::~CapcomPC98Player_MIDI() {
 	_midi->close();
 	delete _midi;
 	delete[] _programMapping;
 }
 
-bool CapcomPC98_MIDI::init() {
+bool CapcomPC98Player_MIDI::init() {
 	if (!_midi || !_programMapping)
 		return false;
 
@@ -527,16 +533,18 @@ bool CapcomPC98_MIDI::init() {
 	return true;
 }
 
-void CapcomPC98_MIDI::reset() {
+void CapcomPC98Player_MIDI::reset() {
 	memset(_chanVolume, 0x7F, sizeof(_chanVolume));
 }
 
-void CapcomPC98_MIDI::send(uint32 evt) {
+void CapcomPC98Player_MIDI::send(uint32 evt) {
 	uint8 cmd = evt & 0xF0;
 	uint8 ch = evt & 0x0F;
 	uint8 p1 = (evt >> 8) & 0xFF;
 	uint8 p2 = (evt >> 16) & 0xFF;
 
+	debugC(5, kDebugLevelSound, "CapcomPC98Player_MIDI::send(): [0x%02x] [0x%02x] [0x%02x]", evt & 0xff, p1, p2);
+
 	if (cmd == 0xC0) {
 		evt = (evt & 0xFF) | (_programMapping[p1] << 8);
 	} else if (cmd == 0xB0) {
@@ -546,25 +554,29 @@ void CapcomPC98_MIDI::send(uint32 evt) {
 		} else if (((evt >> 8) & 0xFF) == 7) {
 			_chanVolume[ch] = p2;
 		}
+	} else if (evt == 0xF0) {
+		// This doesn't seem to be used at all. So I haven't implemented it (yet).
+		warning("CapcomPC98Player_MIDI::send(): Unhandled sysex message encountered");
 	}
+
 	_midi->send(evt);
 }
 
-void CapcomPC98_MIDI::processSounds() {
+void CapcomPC98Player_MIDI::processSounds() {
 	if (_fadeState) {
 		for (int i = 0; i < 16; ++i)
 			_midi->send(0x0007B0 | i | (CLIP<int>(_chanVolume[i] - _fadeState, 0, 127) << 16));
 	}
 }
 
-PC98AudioCore::MutexLock CapcomPC98_MIDI::lockMutex() {
+PC98AudioCore::MutexLock CapcomPC98Player_MIDI::lockMutex() {
 	if (!_mproc.isValid())
 		error("CapcomPC98_MIDI::lockMutex(): Invalid call");
 	return _mproc();
 }
 
-void CapcomPC98_MIDI::updateMasterVolume()  {
-	// This driver is not used for sound effects. So I don't add support for sound effects volume control.
+void CapcomPC98Player_MIDI::updateMasterVolume()  {
+	// This player is not used for sound effects. So I don't add support for sound effects volume control here.
 	if (_isMT32) {
 		byte vol = _musicVolume * 100 / Audio::Mixer::kMaxMixerVolume;
 		byte mt32VolSysEx[9] = { 0x41, 0x10, 0x16, 0x12, 0x10, 0x00, 0x16, vol, 0x00 };
@@ -584,7 +596,7 @@ void CapcomPC98_MIDI::updateMasterVolume()  {
 }
 
 // This is not identical to the one we have in our common code (not even similar).
-const uint8 CapcomPC98_MIDI::_programMapping_mt32ToGM[128] = {
+const uint8 CapcomPC98Player_MIDI::_programMapping_mt32ToGM[128] = {
 	0x00, 0x02, 0x01, 0x03, 0x04, 0x05, 0x10, 0x13, 0x16, 0x65, 0x0a, 0x00, 0x68, 0x67, 0x2e, 0x25,
 	0x08, 0x09, 0x0a, 0x0c, 0x0d, 0x0e, 0x57, 0x38, 0x3b, 0x3c, 0x3d, 0x3e, 0x3b, 0x3b, 0x3b, 0x1f,
 	0x3d, 0x1c, 0x1c, 0x1c, 0x1c, 0x1e, 0x1f, 0x1f, 0x35, 0x38, 0x37, 0x38, 0x36, 0x33, 0x39, 0x70,
@@ -595,17 +607,17 @@ const uint8 CapcomPC98_MIDI::_programMapping_mt32ToGM[128] = {
 	0x70, 0x71, 0x72, 0x76, 0x75, 0x74, 0x73, 0x77, 0x78, 0x79, 0x7a, 0x7c, 0x7b, 0x7d, 0x7e, 0x7f
 };
 
-CapcomPC98_FM_Channel::CapcomPC98_FM_Channel(uint8 id, PC98AudioCore *&ac, const Common::Array<const uint8*>&instruments, const uint8 &fadeState) : _regOffset(id), _ac(ac), _instruments(instruments),
+CapcomPC98_FMChannel::CapcomPC98_FMChannel(uint8 id, PC98AudioCore *&ac, const Common::Array<const uint8*>&instruments, const uint8 &fadeState) : _regOffset(id), _ac(ac), _instruments(instruments),
 	_isPlaying(false), _note(0), _carrier(0), _volume(0), _velocity(0), _noteEffCur(0), _program(0), _noteEffPrev(0), _breathControl(0), _frequency(0), _modWheel(0), _pitchBendSensitivity(0),
 	_pitchBendPara(0), _pitchBendEff(0), _vbrState(0), _vbrStep(0), _vbrCycleTicker(0), _vbrDelayTicker(0), _vbrHandler(nullptr), _prtEnable(false),
 	_prtTime(0), _prtState(0), _prtStep(0), _prtCycleLength(0), _fadeState(fadeState) {
-	typedef void (CapcomPC98_FM_Channel::*Proc)();
+	typedef void (CapcomPC98_FMChannel::*Proc)();
 	static const Proc procs[] = {
-		&CapcomPC98_FM_Channel::vbrHandler0,
-		&CapcomPC98_FM_Channel::vbrHandler1,
-		&CapcomPC98_FM_Channel::vbrHandler2,
-		&CapcomPC98_FM_Channel::vbrHandler3,
-		&CapcomPC98_FM_Channel::dummyProc
+		&CapcomPC98_FMChannel::vbrHandler0,
+		&CapcomPC98_FMChannel::vbrHandler1,
+		&CapcomPC98_FMChannel::vbrHandler2,
+		&CapcomPC98_FMChannel::vbrHandler3,
+		&CapcomPC98_FMChannel::dummyProc
 	};
 
 	assert(ARRAYSIZE(_vbrHandlers) == ARRAYSIZE(procs));
@@ -621,14 +633,14 @@ CapcomPC98_FM_Channel::CapcomPC98_FM_Channel(uint8 id, PC98AudioCore *&ac, const
 	reset();
 }
 
-CapcomPC98_FM_Channel::~CapcomPC98_FM_Channel() {
+CapcomPC98_FMChannel::~CapcomPC98_FMChannel() {
 	for (int i = 0; i < ARRAYSIZE(_vbrHandlers); ++i)
 		delete _vbrHandlers[i];
 
 	delete[] _instrument.regData;
 }
 
-void CapcomPC98_FM_Channel::reset() {
+void CapcomPC98_FMChannel::reset() {
 	_vbrHandler = _vbrHandlers[4];
 	_frequency = 0xFFFF;
 	_vbrState = 0;
@@ -646,18 +658,18 @@ void CapcomPC98_FM_Channel::reset() {
 	_modWheel = 0;
 }
 
-void CapcomPC98_FM_Channel::keyOff() {
+void CapcomPC98_FMChannel::keyOff() {
 	_ac->writeReg(0, 0x28, _regOffset);
 }
 
-void CapcomPC98_FM_Channel::noteOff(uint8 note) {
+void CapcomPC98_FMChannel::noteOff(uint8 note) {
 	if (!_isPlaying || _note != note)
 		return;
 	keyOff();
 	_isPlaying = false;
 }
 
-void CapcomPC98_FM_Channel::noteOn(uint8 note, uint8 velo) {
+void CapcomPC98_FMChannel::noteOn(uint8 note, uint8 velo) {
 	_noteEffPrev = _noteEffCur;
 	_note = note;
 	_velocity = velo;
@@ -683,7 +695,7 @@ void CapcomPC98_FM_Channel::noteOn(uint8 note, uint8 velo) {
 	_isPlaying = true;
 }
 
-void CapcomPC98_FM_Channel::programChange(uint8 prg) {
+void CapcomPC98_FMChannel::programChange(uint8 prg) {
 	if (prg >= _instruments.size())
 		return;
 
@@ -709,45 +721,45 @@ void CapcomPC98_FM_Channel::programChange(uint8 prg) {
 	setupVibrato();
 }
 
-void CapcomPC98_FM_Channel::pitchBend(uint16 pb) {
+void CapcomPC98_FMChannel::pitchBend(uint16 pb) {
 	_pitchBendPara = (pb - 0x2000) << 2;
 	updatePitchBend();
 }
 
-void CapcomPC98_FM_Channel::restore() {
+void CapcomPC98_FMChannel::restore() {
 	programChange(_program);
 	_frequency = 0xFFFF;
 }
 
-void CapcomPC98_FM_Channel::modWheel(uint8 mw) {
+void CapcomPC98_FMChannel::modWheel(uint8 mw) {
 	_modWheel = mw;
 }
 
-void CapcomPC98_FM_Channel::breathControl(uint8 bc) {
+void CapcomPC98_FMChannel::breathControl(uint8 bc) {
 	_breathControl = bc;
 	updateVolume();
 }
 
-void CapcomPC98_FM_Channel::pitchBendSensitivity(uint8 pbs) {
+void CapcomPC98_FMChannel::pitchBendSensitivity(uint8 pbs) {
 	_pitchBendSensitivity = pbs;
 	pitchBend(_pitchBendPara);
 }
 
-void CapcomPC98_FM_Channel::portamentoTime(uint8 pt) {
+void CapcomPC98_FMChannel::portamentoTime(uint8 pt) {
 	_prtTime = pt;
 }
 
-void CapcomPC98_FM_Channel::volume(uint8 vol) {
+void CapcomPC98_FMChannel::volume(uint8 vol) {
 	_volume = vol;
 	if (_isPlaying)
 		updateVolume();
 }
 
-void CapcomPC98_FM_Channel::togglePortamento(uint8 enable) {
+void CapcomPC98_FMChannel::togglePortamento(uint8 enable) {
 	_prtEnable = (enable >= 0x40);
 }
 
-void CapcomPC98_FM_Channel::allNotesOff() {
+void CapcomPC98_FMChannel::allNotesOff() {
 	_isPlaying = false;
 	for (int i = _regOffset; i < 16 + _regOffset; i += 4) {
 		_ac->writeReg(0, 0x40 + i, 0x7F);
@@ -756,7 +768,7 @@ void CapcomPC98_FM_Channel::allNotesOff() {
 	_ac->writeReg(0, 0x28, _regOffset);
 }
 
-void CapcomPC98_FM_Channel::processSounds() {
+void CapcomPC98_FMChannel::processSounds() {
 	if (!_isPlaying)
 		return;
 
@@ -769,7 +781,7 @@ void CapcomPC98_FM_Channel::processSounds() {
 		updateVolume();
 }
 
-void CapcomPC98_FM_Channel::loadInstrument(const uint8 *in) {
+void CapcomPC98_FMChannel::loadInstrument(const uint8 *in) {
 	memcpy(_instrument.name, in, 8);
 	in += 8;
 	_instrument.fbl_alg = *in++;
@@ -788,12 +800,12 @@ void CapcomPC98_FM_Channel::loadInstrument(const uint8 *in) {
 	memcpy(_instrument.regData, in, 52);
 }
 
-void CapcomPC98_FM_Channel::updatePitchBend() {
+void CapcomPC98_FMChannel::updatePitchBend() {
 	_pitchBendEff = (_pitchBendPara * (_pitchBendSensitivity << 1)) >> 16;
 	updateFrequency();
 }
 
-void CapcomPC98_FM_Channel::updateVolume() {
+void CapcomPC98_FMChannel::updateVolume() {
 	uint8 cr = _carrier;
 	const uint8 *s = _instrument.regData;
 	for (int i = _regOffset; i < 16 + _regOffset; i += 4) {
@@ -839,7 +851,7 @@ void CapcomPC98_FM_Channel::updateVolume() {
 	}
 }
 
-void CapcomPC98_FM_Channel::updatePortamento() {
+void CapcomPC98_FMChannel::updatePortamento() {
 	if (_prtCycleLength) {
 		_prtCycleLength--;
 		_prtState += _prtStep;
@@ -848,7 +860,7 @@ void CapcomPC98_FM_Channel::updatePortamento() {
 	}
 }
 
-void CapcomPC98_FM_Channel::updateFrequency() {
+void CapcomPC98_FMChannel::updateFrequency() {
 	int16 tone = (MIN<int16>(_modWheel + _instrument.vbrSensitivity, 255) * (_vbrState >> 16)) >> 8;
 	tone = CLIP<int16>(tone + (_noteEffCur << 8), 0, 0x5FFF);
 	tone = CLIP<int16>(tone + _pitchBendEff, 0, 0x5FFF);
@@ -867,7 +879,7 @@ void CapcomPC98_FM_Channel::updateFrequency() {
 	_ac->writeReg(0, 0xA0 + _regOffset, freq & 0xFF);
 }
 
-void CapcomPC98_FM_Channel::setupPortamento() {
+void CapcomPC98_FMChannel::setupPortamento() {
 	if (!_prtTime || !_prtEnable) {
 		_prtCycleLength = 0;
 		_prtState = 0;
@@ -880,7 +892,7 @@ void CapcomPC98_FM_Channel::setupPortamento() {
 	_prtState = (-diff << 16);
 }
 
-void CapcomPC98_FM_Channel::setupVibrato() {
+void CapcomPC98_FMChannel::setupVibrato() {
 	_vbrHandler = _vbrHandlers[4];
 
 	if (_instrument.vbrCycleLength == 0 || _instrument.vbrType > 4)
@@ -898,7 +910,7 @@ void CapcomPC98_FM_Channel::setupVibrato() {
 		break;
 
 	case 1:
-		_vbrState = _vbrDelayTicker ? 0 : 3072 << 16;
+		_vbrState = _vbrDelayTicker ? 0 : (3072 << 16);
 		_vbrCycleTicker = 0;
 		_vbrHandler = _vbrHandlers[1];
 		break;
@@ -906,7 +918,7 @@ void CapcomPC98_FM_Channel::setupVibrato() {
 	case 2:
 	case 3:
 		_vbrStep = (6144 << 16) / _instrument.vbrCycleLength;
-		_vbrState = _vbrDelayTicker ? 0 : (_instrument.vbrType == 2 ? -3072 : 3072) << 16;
+		_vbrState = _vbrDelayTicker ? 0 : ((_instrument.vbrType == 2 ? -3072 : 3072) << 16);
 		_vbrCycleTicker = _instrument.vbrCycleLength - 1;
 		_vbrHandler = _vbrHandlers[_instrument.vbrType];
 		
@@ -917,7 +929,7 @@ void CapcomPC98_FM_Channel::setupVibrato() {
 	}
 }
 
-void CapcomPC98_FM_Channel::vbrHandler0() {
+void CapcomPC98_FMChannel::vbrHandler0() {
 	if (_vbrDelayTicker) {
 		_vbrDelayTicker--;
 		return;
@@ -934,7 +946,7 @@ void CapcomPC98_FM_Channel::vbrHandler0() {
 	}
 }
 
-void CapcomPC98_FM_Channel::vbrHandler1() {
+void CapcomPC98_FMChannel::vbrHandler1() {
 	if (_vbrDelayTicker) {
 		_vbrDelayTicker--;
 		return;
@@ -946,7 +958,7 @@ void CapcomPC98_FM_Channel::vbrHandler1() {
 		_vbrCycleTicker = 0;
 }
 
-void CapcomPC98_FM_Channel::vbrHandler2() {
+void CapcomPC98_FMChannel::vbrHandler2() {
 	if (_vbrDelayTicker) {
 		_vbrDelayTicker--;
 		return;
@@ -960,7 +972,7 @@ void CapcomPC98_FM_Channel::vbrHandler2() {
 	}
 }
 
-void CapcomPC98_FM_Channel::vbrHandler3() {
+void CapcomPC98_FMChannel::vbrHandler3() {
 	if (_vbrDelayTicker) {
 		_vbrDelayTicker--;
 		return;
@@ -974,11 +986,11 @@ void CapcomPC98_FM_Channel::vbrHandler3() {
 	}
 }
 
-const uint16 CapcomPC98_FM_Channel::_freqMSBTable[] = {
+const uint16 CapcomPC98_FMChannel::_freqMSBTable[] = {
 	0x026a, 0x028f, 0x02b6, 0x02df, 0x030b, 0x0339, 0x036a, 0x039e, 0x03d5, 0x0410, 0x044e, 0x048f
 };
 
-const uint8 CapcomPC98_FM_Channel::_freqLSBTables[12][64] = {
+const uint8 CapcomPC98_FMChannel::_freqLSBTables[12][64] = {
 	{
 		0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09,
 		0x09, 0x0a, 0x0a, 0x0b, 0x0c, 0x0c, 0x0d, 0x0d, 0x0e, 0x0e, 0x0f, 0x0f, 0x10, 0x11, 0x11, 0x12,
@@ -1053,7 +1065,7 @@ const uint8 CapcomPC98_FM_Channel::_freqLSBTables[12][64] = {
 	}
 };
 
-const uint8 CapcomPC98_FM_Channel::_volTablesInst[4][128] = {
+const uint8 CapcomPC98_FMChannel::_volTablesInst[4][128] = {
 	{
 		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, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
@@ -1095,7 +1107,7 @@ const uint8 CapcomPC98_FM_Channel::_volTablesInst[4][128] = {
 	}
 };
 
-const uint8 CapcomPC98_FM_Channel::_volTableCarrier[] = {
+const uint8 CapcomPC98_FMChannel::_volTableCarrier[] = {
 	0x2a, 0x2a, 0x29, 0x29, 0x29, 0x28, 0x28, 0x28, 0x27, 0x27, 0x27, 0x26, 0x26, 0x26, 0x25, 0x25,
 	0x25, 0x24, 0x24, 0x24, 0x23, 0x23, 0x23, 0x22, 0x22, 0x22, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20,
 	0x1f, 0x1f, 0x1f, 0x1e, 0x1e, 0x1e, 0x1d, 0x1d, 0x1d, 0x1c, 0x1c, 0x1c, 0x1b, 0x1b, 0x1b, 0x1a,
@@ -1106,7 +1118,7 @@ const uint8 CapcomPC98_FM_Channel::_volTableCarrier[] = {
 	0x05, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00
 };
 
-const uint8 CapcomPC98_FM_Channel::_volTablePara[] = {
+const uint8 CapcomPC98_FMChannel::_volTablePara[] = {
 	0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78, 0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70,
 	0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x68, 0x67, 0x66, 0x65, 0x64, 0x63, 0x62, 0x61, 0x60,
 	0x5f, 0x5e, 0x5d, 0x5c, 0x5b, 0x5a, 0x59, 0x58, 0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50,
@@ -1117,16 +1129,16 @@ const uint8 CapcomPC98_FM_Channel::_volTablePara[] = {
 	0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00
 };
 
-CapcomPC98_FM::CapcomPC98_FM(Audio::Mixer *mixer, CBProc &proc, bool playerPrio, uint16 playFlags, uint8 chanReserveFlags, uint8 chanDisableFlags, uint16 volControlMask, bool needsTimer) : CapcomPC98Player(playerPrio, playFlags, chanReserveFlags, chanDisableFlags), PC98AudioPluginDriver(), _cbProc(proc), _volControlMask(volControlMask), _ac(nullptr), _chan(nullptr), _ready(false) {
+CapcomPC98Player_FM::CapcomPC98Player_FM(Audio::Mixer *mixer, CBProc &proc, bool playerPrio, uint16 playFlags, uint8 chanReserveFlags, uint8 chanDisableFlags, uint16 volControlMask, bool needsTimer) : CapcomPC98Player(playerPrio, playFlags, chanReserveFlags, chanDisableFlags), PC98AudioPluginDriver(), _cbProc(proc), _volControlMask(volControlMask), _ac(nullptr), _chan(nullptr), _ready(false) {
 	_ac = new PC98AudioCore(mixer, needsTimer ? this : nullptr, kType26);
 	assert(_ac);
-	_chan = new CapcomPC98_FM_Channel*[3];
+	_chan = new CapcomPC98_FMChannel*[3];
 	assert(_chan);
 	for (int i = 0; i < 3; ++i)
-		_chan[i] = new CapcomPC98_FM_Channel(i, _ac, _instruments, _fadeState);
+		_chan[i] = new CapcomPC98_FMChannel(i, _ac, _instruments, _fadeState);
 }
 
-CapcomPC98_FM::~CapcomPC98_FM() {
+CapcomPC98Player_FM::~CapcomPC98Player_FM() {
 	delete _ac;
 	if (_chan) {
 		for (int i = 0; i < 3; ++i)
@@ -1135,7 +1147,7 @@ CapcomPC98_FM::~CapcomPC98_FM() {
 	}
 }
 
-bool CapcomPC98_FM::init() {
+bool CapcomPC98Player_FM::init() {
 	if (!(_chan && _ac && _ac->init()))
 		return false;
 
@@ -1172,12 +1184,12 @@ bool CapcomPC98_FM::init() {
 	return true;
 }
 
-void CapcomPC98_FM::deinit() {
+void CapcomPC98Player_FM::deinit() {
 	PC98AudioCore::MutexLock lock = _ac->stackLockMutex();
 	_ready = false;
 }
 
-void CapcomPC98_FM::reset() {
+void CapcomPC98Player_FM::reset() {
 	for (int i = 0; i < 3; ++i)
 		_chan[i]->reset();
 
@@ -1188,7 +1200,7 @@ void CapcomPC98_FM::reset() {
 		_chan[i]->programChange(0);
 }
 
-void CapcomPC98_FM::loadInstruments(const uint8 *data, uint16 number) {
+void CapcomPC98Player_FM::loadInstruments(const uint8 *data, uint16 number) {
 	_instruments.clear();
 	while (number--) {
 		_instruments.push_back(data);
@@ -1196,17 +1208,19 @@ void CapcomPC98_FM::loadInstruments(const uint8 *data, uint16 number) {
 	}
 }
 
-PC98AudioCore::MutexLock CapcomPC98_FM::lockMutex() {
+PC98AudioCore::MutexLock CapcomPC98Player_FM::lockMutex() {
 	if (!_ready)
 		error("CapcomPC98_FM::lockMutex(): Invalid call");
 	return _ac->stackLockMutex();
 }
 
-void CapcomPC98_FM::send(uint32 evt) {
+void CapcomPC98Player_FM::send(uint32 evt) {
 	uint8 ch = evt & 0x0F;
 	uint8 p1 = (evt >> 8) & 0xFF;
 	uint8 p2 = (evt >> 16) & 0xFF;
 
+	debugC(5, kDebugLevelSound, "CapcomPC98Player_FM::send(): [0x%02x] [0x%02x] [0x%02x]", evt & 0xff, p1, p2);
+
 	if (ch > 2)
 		return;
 
@@ -1234,24 +1248,24 @@ void CapcomPC98_FM::send(uint32 evt) {
 	}
 }
 
-void CapcomPC98_FM::timerCallbackA() {
+void CapcomPC98Player_FM::timerCallbackA() {
 	if (_ready && _cbProc.isValid()) {
 		PC98AudioCore::MutexLock lock = _ac->stackLockMutex();
 		_cbProc();
 	}
 }
 
-void CapcomPC98_FM::processSounds() {
+void CapcomPC98Player_FM::processSounds() {
 	for (int i = 0; i < 3; ++i)
 		_chan[i]->processSounds();
 }
 
-void CapcomPC98_FM::updateMasterVolume()  {
+void CapcomPC98Player_FM::updateMasterVolume()  {
 	_ac->setMusicVolume(_musicVolume);
 	_ac->setSoundEffectVolume(_sfxVolume);
 }
 
-void CapcomPC98_FM::controlChange(uint8 ch, uint8 control, uint8 val) {
+void CapcomPC98Player_FM::controlChange(uint8 ch, uint8 control, uint8 val) {
 	if (ch > 2)
 		return;
 
@@ -1285,7 +1299,7 @@ void CapcomPC98_FM::controlChange(uint8 ch, uint8 control, uint8 val) {
 	}
 }
 
-void CapcomPC98_FM::restoreStateIntern() {
+void CapcomPC98Player_FM::restoreStateIntern() {
 	for (int i = 0; i < 3; ++i) {
 		if ((1 << i) & _chanReserveFlags)
 			continue;
@@ -1293,11 +1307,11 @@ void CapcomPC98_FM::restoreStateIntern() {
 	}
 }
 
-void CapcomPC98_FM::setVolControlMask() {
+void CapcomPC98Player_FM::setVolControlMask() {
 	_ac->setSoundEffectChanMask(_volControlMask);
 }
 
-const uint8 CapcomPC98_FM::_initData[72] = {
+const uint8 CapcomPC98Player_FM::_initData[72] = {
 	0x49, 0x4e, 0x49, 0x54, 0x5f, 0x56, 0x4f, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	0x00, 0x00,
 	0x00, 0x00, 0x01, 0x7f, 0x1f, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7f, 0x1f,
 	0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7f, 0x1f, 0x00, 0x00, 0x0f, 0x00, 0x00,
@@ -1309,18 +1323,18 @@ int CapcomPC98AudioDriverInternal::_refCount = 0;
 
 CapcomPC98AudioDriverInternal::CapcomPC98AudioDriverInternal(Audio::Mixer *mixer, MidiDriver::DeviceHandle dev) : _ready(false), _fmDevice(nullptr), _timerProc(nullptr), _mutexProc(nullptr), _marker(0), _musicVolume(0), _sfxVolume(0) {
 	MusicType type = MidiDriver::getMusicType(dev);
-	_timerProc = new CapcomPC98_FM::CBProc(this, &CapcomPC98AudioDriverInternal::timerCallback);
+	_timerProc = new CapcomPC98Player_FM::CBProc(this, &CapcomPC98AudioDriverInternal::timerCallback);
 	assert(_timerProc);
-	_mutexProc = new CapcomPC98_MIDI::MutexProc(this, &CapcomPC98AudioDriverInternal::lockMutex);
+	_mutexProc = new CapcomPC98Player_MIDI::MutexProc(this, &CapcomPC98AudioDriverInternal::lockMutex);
 	assert(_mutexProc);
 
 	if (type == MT_MT32 || type == MT_GM) {
-		_players[0] = new CapcomPC98_MIDI(dev, type == MT_MT32, *_mutexProc);
-		_players[1] = _fmDevice = new CapcomPC98_FM(mixer, *_timerProc, true, CapcomPC98Player::kPrioPlay, 4, (uint8)~4, 0xFFFF, true);
+		_players[0] = new CapcomPC98Player_MIDI(dev, type == MT_MT32, *_mutexProc);
+		_players[1] = _fmDevice = new CapcomPC98Player_FM(mixer, *_timerProc, true, CapcomPC98Player::kPrioPlay, 4, (uint8)~4, 0xFFFF, true);
 		_marker = 1;
 	} else {
-		_players[0] = new CapcomPC98_FM(mixer, *_timerProc, false, CapcomPC98Player::kStdPlay, 3, 0, 0, false);
-		_players[1] = _fmDevice = new CapcomPC98_FM(mixer, *_timerProc, true, CapcomPC98Player::kPrioPlay | CapcomPC98Player::kPrioClaim, 4, (uint8)~4, 4, true);
+		_players[0] = new CapcomPC98Player_FM(mixer, *_timerProc, false, CapcomPC98Player::kStdPlay, 3, 0, 0, false);
+		_players[1] = _fmDevice = new CapcomPC98Player_FM(mixer, *_timerProc, true, CapcomPC98Player::kPrioPlay | CapcomPC98Player::kPrioClaim, 4, (uint8)~4, 4, true);
 	}
 
 	bool ready = true;


Commit: a15ce9a202ac0ead49753624333e3592368334af
    https://github.com/scummvm/scummvm/commit/a15ce9a202ac0ead49753624333e3592368334af
Author: athrxx (athrxx at scummvm.org)
Date: 2023-07-11T14:34:38+02:00

Commit Message:
KYRA: (LOL/ZH) - text displayer fixes

The original does not use the text displayer code. I can find
it in the executable, but apparently only as dead code. The
text wrapping is done with a much simpler method. I have
adapted the code to fix some glitches with the "More"
button (which the Chinese version apparently does not use
for the single-line console).
It seems fine, now, but there are also glitches in EOB II, so
I'll have a look at that separately...

Changed paths:
    engines/kyra/resource/staticres_lol.cpp
    engines/kyra/text/text_rpg.cpp


diff --git a/engines/kyra/resource/staticres_lol.cpp b/engines/kyra/resource/staticres_lol.cpp
index 7323c00c50b..cb9244512d1 100644
--- a/engines/kyra/resource/staticres_lol.cpp
+++ b/engines/kyra/resource/staticres_lol.cpp
@@ -605,7 +605,7 @@ const ScreenDim Screen_LoL::_screenDimTable256C[] = {
 };
 
 const ScreenDim Screen_LoL::_screenDimTableZH[] = {
-	{ 0x00, 0x00, 0x28, 0xC8, 0xC7, 0xCF, 0x00, 0x00 }, // Taken from Intro // Fuul screen // Looks good
+	{ 0x00, 0x00, 0x28, 0xC8, 0xC7, 0xCF, 0x00, 0x00 }, // Taken from Intro // Full screen // Looks good
 	{ 0x08, 0x48, 0x18, 0x38, 0xFE, 0x01, 0x00, 0x00 }, // Not checked
 	{ 0x0E, 0x00, 0x16, 0x78, 0xFE, 0x01, 0x00, 0x00 }, // Not checked
 	{ 0x0B, 0x7B, 0x1C, 0x12, 0xFE, 0xFC, 0x00, 0x00 }, // Not checked
@@ -816,7 +816,7 @@ const KyraRpgGUISettings LoLEngine::_guiSettings = {
 };
 
 const KyraRpgGUISettings LoLEngine::_guiSettingsZH = {
-	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 144, 254, 60, 18, 2, 66, { 0, 0 }, { 0, 0 }, { 0, 0 } },
+	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 144, 254, 68, 18, 2, 66, { 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/text/text_rpg.cpp b/engines/kyra/text/text_rpg.cpp
index 6a98d6926fd..e02dcf21f53 100644
--- a/engines/kyra/text/text_rpg.cpp
+++ b/engines/kyra/text/text_rpg.cpp
@@ -343,8 +343,8 @@ void TextDisplayer_rpg::printLine(char *str) {
 	int sjisOffs = (sjisTextMode || _vm->game() != GI_LOL) ? 8 : 9;
 
 	int fh;
-	if (_screen->_currentFont == Screen::FID_CHINESE_FNT && _vm->_flags.lang == Common::Language::ZH_TWN && _vm->_flags.gameID == GI_EOB2)
-		fh = 14;
+	if (_screen->_currentFont == Screen::FID_CHINESE_FNT && _vm->_flags.lang == Common::Language::ZH_TWN)
+		fh = _vm->_flags.gameID == GI_EOB2 ? 14 : 15;
 	else if (_screen->_currentFont == Screen::FID_SJIS_TEXTMODE_FNT)
 		fh = 9;
 	else
@@ -381,7 +381,6 @@ void TextDisplayer_rpg::printLine(char *str) {
 	char c = 0;
 	uint8 twoByteCharOffs = 0;
 
-
 	if (sjisTextMode) {
 		bool ct = true;
 
@@ -412,6 +411,13 @@ void TextDisplayer_rpg::printLine(char *str) {
 			}
 			s = n2;
 		}
+	} else if (_isChinese) {
+		s = strlen(str);
+		twoByteCharOffs = 16;
+		if ((lw + _textDimData[sdx].column) >= w) {
+			s -= ((lw + _textDimData[sdx].column) - w) >> 3;
+			w -= _textDimData[sdx].column;	
+		}
 	} else {
 		if (_vm->gameFlags().lang == Common::JA_JPN) {
 			for (int i = 0; i < s; ++i) {
@@ -421,12 +427,12 @@ void TextDisplayer_rpg::printLine(char *str) {
 			}
 		}
 
-		if (_isChinese) {
+		/*if (_isChinese) {
 			for (int i = 0; i < s; ++i) {
 				if (str[i] & 0x80)
 					twoByteCharOffs = 16;
 			}
-		}
+		}*/
 
 		if ((lw + _textDimData[sdx].column) >= w) {
 			if ((lines - 1) <= _lineCount && _allowPageBreak)
@@ -445,10 +451,10 @@ void TextDisplayer_rpg::printLine(char *str) {
 
 				for (strPos = 0; strPos < s; ++strPos) {
 					uint8 cu = (uint8) str[strPos];
-					if (_isChinese && (cu & 0x80)) {
+					/*if (_isChinese && (cu & 0x80)) {
 						lw += twoByteCharOffs;
 						strPos++;
-					} else if (cu >= 0xE0 || (cu > 0x80 && cu < 0xA0)) {
+					} else */if (cu >= 0xE0 || (cu > 0x80 && cu < 0xA0)) {
 						lw += sjisOffs;
 						strPos++;
 					} else {
@@ -504,7 +510,6 @@ void TextDisplayer_rpg::printLine(char *str) {
 					s = lineLastCharPos;
 				}
 			}
-
 		}
 	}
 
@@ -567,7 +572,8 @@ void TextDisplayer_rpg::printLine(char *str) {
 	_textDimData[sdx].line++;
 	_lineCount++;
 
-	printLine(str);
+	if (_numCharsLeft || !_isChinese)
+		printLine(str);
 }
 
 void TextDisplayer_rpg::printDialogueText(int stringId, const char *pageBreakString, const char*) {


Commit: 5e8333f638b58c249c8da04a995a122a680a893d
    https://github.com/scummvm/scummvm/commit/5e8333f638b58c249c8da04a995a122a680a893d
Author: athrxx (athrxx at scummvm.org)
Date: 2023-07-11T14:34:52+02:00

Commit Message:
KYRA: (EOB II/ZH) - fix/improve text display

The text display glitches in many different ways, since the
original devs did some pretty ugly hacks that we have to
emulate somehow. This commit fixes a couple of things
(spacing, pixel/line overdrawing, shadow, original
linebreak style). There are still several things that have to
be addressed separately, like various text colors, the
whole character portrait/stats pages, etc.

Changed paths:
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/lol.cpp
    engines/kyra/engine/magic_eob.cpp
    engines/kyra/engine/timer_eob.cpp
    engines/kyra/graphics/screen_eob.cpp
    engines/kyra/gui/debugger.cpp
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/resource/staticres_eob.cpp
    engines/kyra/script/script_eob.cpp
    engines/kyra/text/text_rpg.cpp
    engines/kyra/text/text_rpg.h


diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 96eb9e67bda..103caef5c89 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -730,6 +730,10 @@ void EoBCoreEngine::runLoop() {
 	_drawSceneTimer = _system->getMillis();
 	_screen->setFont(_conFont);
 	_screen->setScreenDim(7);
+	if (_flags.lang == Common::ZH_TWN) {
+		_txt->setShadowColor(guiSettings()->colors.fill);
+		_txt->setLineSpacing(-1);
+	}
 
 	_runFlag = true;
 
@@ -1612,6 +1616,11 @@ void EoBCoreEngine::initDialogueSequence() {
 	delete s;
 
 	_txt->setupField(9, 0);
+
+	if (_flags.lang == Common::ZH_TWN) {
+		_txt->setShadowColor(guiSettings()->colors.guiColorBlack);
+		_txt->setLineSpacing(0);
+	}
 }
 
 void EoBCoreEngine::restoreAfterDialogueSequence() {
@@ -1624,6 +1633,11 @@ void EoBCoreEngine::restoreAfterDialogueSequence() {
 	//_allowSkip = false;
 	_screen->setScreenDim(7);
 
+	if (_flags.lang == Common::ZH_TWN) {
+		_txt->setShadowColor(guiSettings()->colors.fill);
+		_txt->setLineSpacing(-1);
+	}
+
 	if (_flags.gameID == GI_EOB2)
 		snd_playSoundEffect(2);
 
@@ -1712,6 +1726,9 @@ int EoBCoreEngine::runDialogue(int dialogueTextId, int numStr, int loopButtonId,
 void EoBCoreEngine::restParty_displayWarning(const char *str) {
 	int od = _screen->curDimIndex();
 	_screen->setScreenDim(7);
+	int osh = (_flags.lang == Common::ZH_TWN) ? _txt->setShadowColor(guiSettings()->colors.fill) : 0xFFFF;
+	int ols = (_flags.lang == Common::ZH_TWN) ? _txt->setLineSpacing(-1) : 0xFFFF;
+
 	Screen::FontId of = _screen->setFont(_conFont);
 	_screen->setCurPage(0);
 
@@ -1719,6 +1736,11 @@ void EoBCoreEngine::restParty_displayWarning(const char *str) {
 
 	_screen->setFont(of);
 	_screen->setScreenDim(od);
+
+	if (osh != 0xFFFF)
+		_txt->setShadowColor(osh);
+	if (ols != 0xFFFF)
+		_txt->setLineSpacing(ols);
 }
 
 bool EoBCoreEngine::restParty_updateMonsters() {
@@ -1737,10 +1759,18 @@ bool EoBCoreEngine::restParty_updateMonsters() {
 		Screen::FontId of = _screen->setFont(_conFont);
 		int od = _screen->curDimIndex();
 		_screen->setScreenDim(7);
+		int osh = (_flags.lang == Common::ZH_TWN) ? _txt->setShadowColor(guiSettings()->colors.fill) : 0xFFFF;
+		int ols = (_flags.lang == Common::ZH_TWN) ? _txt->setLineSpacing(-1) : 0xFFFF;
+
 		updateMonsters(0);
 		updateMonsters(1);
 		timerProcessFlyingObjects(0);
+
 		_screen->setScreenDim(od);
+		if (osh != 0xFFFF)
+			_txt->setShadowColor(osh);
+		if (ols != 0xFFFF)
+			_txt->setLineSpacing(ols);
 		_screen->setFont(of);
 
 		_partyResting = false;
diff --git a/engines/kyra/engine/lol.cpp b/engines/kyra/engine/lol.cpp
index 8623dfb1e1a..1b4b720ca69 100644
--- a/engines/kyra/engine/lol.cpp
+++ b/engines/kyra/engine/lol.cpp
@@ -393,6 +393,7 @@ Common::Error LoLEngine::init() {
 	}
 
 	_txt = new TextDisplayer_LoL(this, _screen);
+	_txt->setLineSpacing(_flags.lang == Common::JA_JPN && _flags.use16ColorMode ? 1 : (_flags.lang == Common::ZH_TWN ? -1 : 0));
 
 	_screen->setAnimBlockPtr(10000);
 	_screen->setScreenDim(0);
diff --git a/engines/kyra/engine/magic_eob.cpp b/engines/kyra/engine/magic_eob.cpp
index 2502f063011..6fc51eef9bc 100644
--- a/engines/kyra/engine/magic_eob.cpp
+++ b/engines/kyra/engine/magic_eob.cpp
@@ -219,11 +219,13 @@ void EoBCoreEngine::castSpell(int spell, int weaponSlot) {
 		}
 	}
 
-
 	int cs = (_flags.platform == Common::kPlatformSegaCD && _flags.lang == Common::JA_JPN) ? _screen->setFontStyles(_screen->_currentFont, Font::kStyleNarrow1) : -1;
 
 	_txt->printMessage(_magicStrings1[4], -1, c->name, s->name);
 
+	if (_flags.lang == Common::ZH_TWN)
+		_txt->printMessage("\r", -1);
+
 	if (cs != -1)
 		_screen->setFontStyles(_screen->_currentFont, cs);
 
@@ -245,8 +247,16 @@ void EoBCoreEngine::removeCharacterEffect(int spell, int charIndex, int showWarn
 		int od = _screen->curDimIndex();
 		Screen::FontId of = _screen->setFont(_conFont);
 		_screen->setScreenDim(7);
+		int osh = (_flags.lang == Common::ZH_TWN) ? _txt->setShadowColor(guiSettings()->colors.fill) : 0xFFFF;
+		int ols = (_flags.lang == Common::ZH_TWN) ? _txt->setLineSpacing(-1) : 0xFFFF;
+
 		printWarning(Common::String::format(_magicStrings3[_flags.gameID == GI_EOB1 ? 3 : 2], c->name, s->name).c_str());
+
 		_screen->setScreenDim(od);
+		if (osh != 0xFFFF)
+			_txt->setShadowColor(osh);
+		if (ols != 0xFFFF)
+			_txt->setLineSpacing(ols);
 		_screen->setFont(of);
 	}
 
diff --git a/engines/kyra/engine/timer_eob.cpp b/engines/kyra/engine/timer_eob.cpp
index ffe8278a48c..9d88733ac42 100644
--- a/engines/kyra/engine/timer_eob.cpp
+++ b/engines/kyra/engine/timer_eob.cpp
@@ -306,6 +306,8 @@ void EoBCoreEngine::timerSpecialCharacterUpdate(int timerNum) {
 		int od = _screen->curDimIndex();
 		Screen::FontId of = _screen->setFont(_conFont);
 		_screen->setScreenDim(7);
+		int osh = (_flags.lang == Common::ZH_TWN) ? _txt->setShadowColor(guiSettings()->colors.fill) : 0xFFFF;
+		int ols = (_flags.lang == Common::ZH_TWN) ? _txt->setLineSpacing(-1) : 0xFFFF;
 
 		switch (evt) {
 		case 2:
@@ -369,6 +371,10 @@ void EoBCoreEngine::timerSpecialCharacterUpdate(int timerNum) {
 		}
 
 		_screen->setScreenDim(od);
+		if (osh != 0xFFFF)
+			_txt->setShadowColor(osh);
+		if (ols != 0xFFFF)
+			_txt->setLineSpacing(ols);
 		_screen->setFont(of);
 	}
 
diff --git a/engines/kyra/graphics/screen_eob.cpp b/engines/kyra/graphics/screen_eob.cpp
index 25b8fdaf4bd..fdbee787ed6 100644
--- a/engines/kyra/graphics/screen_eob.cpp
+++ b/engines/kyra/graphics/screen_eob.cpp
@@ -2272,7 +2272,10 @@ void OldDOSFont::unload() {
 }
 
 uint16 ChineseTwoByteFontEoB::translateBig5(uint16 in) const {
-	if (in < 0x80)
+	// The original explicitly turns the braces '<' and '>' into '"'.
+	if (in == '<' || in == '>')
+		return '\"';
+	else if (in < 0x80)
 		return in;
 	in = ((in & 0xff00) >> 8) | ((in & 0xff) << 8);
 	if (_big5->hasGlyphForBig5Char(in))
@@ -2287,7 +2290,7 @@ int ChineseTwoByteFontEoB::getCharWidth(uint16 c) const {
 
 int ChineseTwoByteFontEoB::getCharHeight(uint16 c) const {
 	uint16 t = translateBig5(c);
-	return (t < 0x80) ? _singleByte->getCharHeight(t) : _big5->getFontHeight() + 1;
+	return (t < 0x80) ? _singleByte->getCharHeight(t) : _big5->getFontHeight();
 }
 
 void ChineseTwoByteFontEoB::drawChar(uint16 c, byte *dst, int pitch, int bpp) const {
diff --git a/engines/kyra/gui/debugger.cpp b/engines/kyra/gui/debugger.cpp
index 1e86329f951..6bb28cab858 100644
--- a/engines/kyra/gui/debugger.cpp
+++ b/engines/kyra/gui/debugger.cpp
@@ -605,6 +605,10 @@ bool Debugger_EoB::cmdSetPosition(int argc, const char **argv) {
 			_vm->generateTempData();
 			_vm->txt()->removePageBreakFlag();
 			_vm->screen()->setScreenDim(7);
+			if (_vm->gameFlags().lang == Common::ZH_TWN) {
+				_vm->txt()->setShadowColor(_vm->guiSettings()->colors.fill);
+				_vm->txt()->setLineSpacing(0);
+			}
 
 			_vm->loadLevel(level, sub);
 
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 962c4597805..1598221eb32 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -51,12 +51,11 @@ void EoBCoreEngine::gui_drawPlayField(bool refresh) {
 	int cp = _screen->setCurPage(2);
 	if (_flags.lang == Common::Language::ZH_TWN) {
 		Screen::FontId of = _screen->setFont(Screen::FID_CHINESE_FNT);
-		_screen->fillRect(290, 180, 318, 194, guiSettings()->colors.fill);
+		_screen->fillRect(291, 180, 317, 195, guiSettings()->colors.fill);
 		_screen->printShadedText("\xbf\xef?" /* 選? */, 292, 181, guiSettings()->colors.guiColorYellow, guiSettings()->colors.fill, guiSettings()->colors.guiColorBlack, -1);
 		_screen->setFont(of);
-
-		_screen->fillRect(3, 170, 290, 198, guiSettings()->colors.fill);
-		_screen->fillRect(4, 171, 289, 197, guiSettings()->colors.guiColorBlack);
+		_screen->fillRect(4, 171, 289, 198, guiSettings()->colors.guiColorBlack);
+		_screen->drawBox(3, 170, 290, 199, guiSettings()->colors.fill);
 	}
 	gui_drawCompass(true);
 
@@ -3967,6 +3966,11 @@ bool GUI_EoB::restParty() {
 	else
 		_screen->sega_clearTextBuffer(0);
 
+	if (_vm->gameFlags().lang == Common::ZH_TWN)
+		_screen->drawBox(3, 170, 290, 199, 0xb7);
+
+	int osh = (_vm->gameFlags().lang == Common::ZH_TWN) ? _vm->txt()->setShadowColor(_vm->guiSettings()->colors.fill) : 0xFFFF;
+	int ols = (_vm->gameFlags().lang == Common::ZH_TWN) ? _vm->txt()->setLineSpacing(-1) : 0xFFFF;
 	Screen::FontId of = _screen->setFont(_vm->_conFont);
 
 	restParty_updateRestTime(hours, true);
@@ -4187,6 +4191,10 @@ bool GUI_EoB::restParty() {
 	_vm->removeInputTop();
 	_screen->setScreenDim(4);
 	_screen->setFont(of);
+	if (osh != 0xFFFF)
+		_vm->txt()->setShadowColor(osh);
+	if (ols != 0xFFFF)
+		_vm->txt()->setLineSpacing(ols);
 
 	if (!res) {
 		if (!injured)
@@ -4208,6 +4216,8 @@ void GUI_EoB::printScribeScrollSpellString(const int16 *menuItems, int id, bool
 
 bool GUI_EoB::confirmDialogue(int id) {
 	int od = _screen->curDimIndex();
+	int osh = (_vm->gameFlags().lang == Common::ZH_TWN) ? _vm->txt()->setShadowColor(_vm->guiSettings()->colors.guiColorBlack) : 0xFFFF;
+	int ols = (_vm->gameFlags().lang == Common::ZH_TWN) ? _vm->txt()->setLineSpacing(0) : 0xFFFF;
 	Screen::FontId of = _screen->setFont(_menuFont);
 
 	Button *buttonList = initMenu(5);
@@ -4264,6 +4274,10 @@ bool GUI_EoB::confirmDialogue(int id) {
 
 	_screen->setFont(of);
 	_screen->setScreenDim(od);
+	if (osh != 0xFFFF)
+		_vm->txt()->setShadowColor(osh);
+	if (ols != 0xFFFF)
+		_vm->txt()->setLineSpacing(ols);
 
 	return result;
 }
@@ -4579,6 +4593,8 @@ void GUI_EoB::drawMenuButtonBox(int x, int y, int w, int h, bool clicked, bool n
 void GUI_EoB::drawTextBox(int dim, int id) {
 	int od = _screen->curDimIndex();
 	_screen->setScreenDim(dim);
+	int osh = (_vm->gameFlags().lang == Common::ZH_TWN) ? _vm->txt()->setShadowColor(_vm->guiSettings()->colors.guiColorBlack) : 0xFFFF;
+	int ols = (_vm->gameFlags().lang == Common::ZH_TWN) ? _vm->txt()->setLineSpacing(0) : 0xFFFF;
 
 	Screen::FontId of = _screen->setFont(_menuFont);
 	int cs = (_vm->gameFlags().platform == Common::kPlatformPC98 && !_vm->gameFlags().use16ColorMode) ? _screen->setFontStyles(_menuFont, Font::kStyleFat) : -1;
@@ -4595,6 +4611,11 @@ void GUI_EoB::drawTextBox(int dim, int id) {
 	_screen->setCurPage(0);
 	_screen->copyRegion(0, 0, dm->sx << 3, dm->sy, dm->w << 3, dm->h, 2, 0, Screen::CR_NO_P_CHECK);
 	_screen->updateScreen();
+
+	if (osh != 0xFFFF)
+		_vm->txt()->setShadowColor(osh);
+	if (ols != 0xFFFF)
+		_vm->txt()->setLineSpacing(ols);
 	_screen->setScreenDim(od);
 
 	if (cs != -1)
@@ -4827,6 +4848,8 @@ void GUI_EoB::restParty_updateRestTime(int hours, bool init) {
 	Screen::FontId of = _screen->setFont(_menuFont);
 	int od = _screen->curDimIndex();
 	_screen->setScreenDim(10);
+	int osh = (_vm->gameFlags().lang == Common::ZH_TWN) ? _vm->txt()->setShadowColor(_vm->guiSettings()->colors.guiColorBlack) : 0xFFFF;
+	int ols = (_vm->gameFlags().lang == Common::ZH_TWN) ? _vm->txt()->setLineSpacing(0) : 0xFFFF;
 
 	if (init) {
 		_screen->setCurPage(0);
@@ -4843,6 +4866,10 @@ void GUI_EoB::restParty_updateRestTime(int hours, bool init) {
 	_screen->set16bitShadingLevel(0);
 	_screen->updateScreen();
 	_vm->delay(160);
+	if (osh != 0xFFFF)
+		_vm->txt()->setShadowColor(osh);
+	if (ols != 0xFFFF)
+		_vm->txt()->setLineSpacing(ols);
 	_screen->setScreenDim(od);
 	_screen->setFont(of);
 }
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index 585e082dd6e..dcb60c5a222 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -186,7 +186,7 @@ const ScreenDim Screen_EoB::_screenDimTableZH[] = {
 	{ 0x02, 0x18, 0x14, 0x78, 0x0F, 0x02, 0x03, 0x00 },
 	{ 0x00, 0x00, 0x16, 0x78, 0x0F, 0x0D, 0x00, 0x00 },
 	{ 0x0A, 0x6C, 0x15, 0x28, 0x0F, 0x00, 0x00, 0x00 },
-	{ 0x01, 0xAB, 0x23, 0x1C, 0x0F, 0x0C, 0x00, 0x00 },
+	{ 0x01, 0xAB, 0x23, 0x1C, 0x0F, 0x00, 0x00, 0x00 },
 	{ 0x02, 0x18, 0x14, 0x00, 0x0F, 0x02, 0x03, 0x00 },
 	{ 0x01, 0x7D, 0x26, 0x40, 0x0F, 0x00, 0x03, 0x00 },
 	{ 0x00, 0x00, 0x16, 0x90, 0x0F, 0x02, 0x00, 0x00 },
diff --git a/engines/kyra/script/script_eob.cpp b/engines/kyra/script/script_eob.cpp
index 62e03818d61..7749354b346 100644
--- a/engines/kyra/script/script_eob.cpp
+++ b/engines/kyra/script/script_eob.cpp
@@ -1326,6 +1326,10 @@ int EoBInfProcessor::oeob_loadNewLevelOrMonsters(int8 *data) {
 		_vm->generateTempData();
 		_vm->txt()->removePageBreakFlag();
 		_screen->setScreenDim(7);
+		if (_vm->gameFlags().lang == Common::ZH_TWN) {
+			_vm->txt()->setShadowColor(_vm->guiSettings()->colors.fill);
+			_vm->txt()->setLineSpacing(-1);
+		}
 
 		_vm->loadLevel(index, cmd);
 		debugC(5, kDebugLevelScript, "         - entering level '%d', sub level '%d', start block '0x%.04X', start direction '%d'", index, cmd, _vm->_currentBlock, _vm->_currentDirection);
@@ -1542,7 +1546,13 @@ int EoBInfProcessor::oeob_sequence(int8 *data) {
 		_vm->npcSequence(cmd);
 		break;
 	}
+
 	_vm->screen()->setScreenDim(7);
+	if (_vm->gameFlags().lang == Common::ZH_TWN) {
+		_vm->txt()->setShadowColor(_vm->guiSettings()->colors.fill);
+		_vm->txt()->setLineSpacing(-1);
+	}
+
 	return pos - data;
 }
 
diff --git a/engines/kyra/text/text_rpg.cpp b/engines/kyra/text/text_rpg.cpp
index e02dcf21f53..65211178de6 100644
--- a/engines/kyra/text/text_rpg.cpp
+++ b/engines/kyra/text/text_rpg.cpp
@@ -33,9 +33,9 @@ enum {
 };
 
 TextDisplayer_rpg::TextDisplayer_rpg(KyraRpgEngine *engine, Screen *scr) : _vm(engine), _screen(scr),
-	_lineCount(0), _printFlag(false), _lineWidth(0), _numCharsTotal(0), _allowPageBreak(true),
+_lineCount(0), _printFlag(false), _lineWidth(0), _numCharsTotal(0), _allowPageBreak(true),
 	_numCharsLeft(0), _numCharsPrinted(0), _sjisTextModeLineBreak(false), _waitButtonMode(1),
-	_pc98TextMode(engine->gameFlags().use16ColorMode && engine->game() == GI_LOL),
+	_pc98TextMode(engine->gameFlags().use16ColorMode && engine->game() == GI_LOL), _shadowColor(0), _lineSpacing(0), _preventHalfWidthLineEnd(false),
 	_waitButtonFont(Screen::FID_6_FNT), _isChinese(_vm->gameFlags().lang == Common::Language::ZH_TWN || _vm->gameFlags().lang == Common::Language::ZH_CHN) {
 
 	static const uint8 amigaColorMap[16] = {
@@ -55,6 +55,8 @@ TextDisplayer_rpg::TextDisplayer_rpg(KyraRpgEngine *engine, Screen *scr) : _vm(e
 	else if ((_vm->game() == GI_LOL && _vm->gameFlags().lang == Common::Language::ZH_TWN))
 		_waitButtonFont = Screen::FID_CHINESE_FNT;
 
+	_preventHalfWidthLineEnd = ((_vm->gameFlags().lang == Common::JA_JPN && _vm->game() == GI_EOB1) || (_vm->gameFlags().lang == Common::Language::ZH_TWN && _vm->game() == GI_EOB2));
+
 	_textDimData = new TextDimData[_screen->screenDimTableCount()];
 
 	for (int i = 0; i < 256; ++i)
@@ -77,7 +79,7 @@ TextDisplayer_rpg::TextDisplayer_rpg(KyraRpgEngine *engine, Screen *scr) : _vm(e
 	_table2 = new char[16]();
 
 	_tempString1 = _tempString2 = 0;
-
+	_ctrl[0] = _ctrl[1] = _ctrl[2] = '\0';
 	_waitButtonSpace = 0;
 }
 
@@ -183,7 +185,7 @@ void TextDisplayer_rpg::displayText(char *str, ...) {
 				_currentLine[_numCharsLeft] = '\0';
 
 				_lineWidth += sjisOffs;
-				if (_vm->game() == GI_EOB1 && ((sd->w << 3) - sjisOffs) <= (_textDimData[sdx].column + _lineWidth))
+				if (_preventHalfWidthLineEnd && ((sd->w << 3) - sjisOffs) <= (_textDimData[sdx].column + _lineWidth))
 					printLine(_currentLine);
 				c = parseCommand();
 				continue;
@@ -201,6 +203,8 @@ void TextDisplayer_rpg::displayText(char *str, ...) {
 				_currentLine[_numCharsLeft] = '\0';
 
 				_lineWidth += Graphics::Big5Font::kChineseTraditionalWidth;
+				if (_preventHalfWidthLineEnd && (_textDimData[sdx].column + _lineWidth + Graphics::Big5Font::kChineseTraditionalWidth) > (sd->w << 3))
+					printLine(_currentLine);
 				c = parseCommand();
 				continue;
 			}
@@ -263,6 +267,7 @@ void TextDisplayer_rpg::displayText(char *str, ...) {
 		default:
 			if (_vm->game() == GI_EOB1 || _vm->game() == GI_LOL || (unsigned char)c > 30) {
 				_lineWidth += (sjisTextMode ? 4 : (_screen->_currentFont == Screen::FID_SJIS_TEXTMODE_FNT ? 9 : _screen->getCharWidth((uint8)c)));
+
 				_currentLine[_numCharsLeft++] = c;
 				_currentLine[_numCharsLeft] = 0;
 
@@ -342,13 +347,7 @@ 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;
-	if (_screen->_currentFont == Screen::FID_CHINESE_FNT && _vm->_flags.lang == Common::Language::ZH_TWN)
-		fh = _vm->_flags.gameID == GI_EOB2 ? 14 : 15;
-	else if (_screen->_currentFont == Screen::FID_SJIS_TEXTMODE_FNT)
-		fh = 9;
-	else
-		fh = _screen->getFontHeight() + _screen->_lineSpacing;
+	int fh = _screen->getFontHeight() + _screen->_lineSpacing + _lineSpacing;
 	int lines = (sd->h - _screen->_lineSpacing) / fh;
 
 	while (_textDimData[sdx].line >= lines) {
@@ -365,6 +364,11 @@ void TextDisplayer_rpg::printLine(char *str) {
 		if (h2)
 			_screen->copyRegion(sd->sx << 3, sd->sy + fh, sd->sx << 3, sd->sy, sd->w << 3, h2, _screen->_curPage, _screen->_curPage, Screen::CR_NO_P_CHECK);
 
+		// In Chinese EOBII some characters overdraw the valid boundaries by one pixel
+		// (at least the ',' does). So, the original redraws the border here. We do the same...
+		if (_isChinese && _vm->_flags.gameID == GI_EOB2 && sdx == 7)
+			_screen->drawBox(3, 170, 290, 199, 0xb7);
+
 		_screen->set16bitShadingLevel(4);
 		_screen->fillRect(sd->sx << 3, sd->sy + h1, ((sd->sx + sd->w) << 3) - 1, sd->sy + sd->h - 1, _textDimData[sdx].color2);
 		_screen->set16bitShadingLevel(0);
@@ -427,13 +431,6 @@ void TextDisplayer_rpg::printLine(char *str) {
 			}
 		}
 
-		/*if (_isChinese) {
-			for (int i = 0; i < s; ++i) {
-				if (str[i] & 0x80)
-					twoByteCharOffs = 16;
-			}
-		}*/
-
 		if ((lw + _textDimData[sdx].column) >= w) {
 			if ((lines - 1) <= _lineCount && _allowPageBreak)
 				// cut off line to leave space for "MORE" button
@@ -451,10 +448,7 @@ void TextDisplayer_rpg::printLine(char *str) {
 
 				for (strPos = 0; strPos < s; ++strPos) {
 					uint8 cu = (uint8) str[strPos];
-					/*if (_isChinese && (cu & 0x80)) {
-						lw += twoByteCharOffs;
-						strPos++;
-					} else */if (cu >= 0xE0 || (cu > 0x80 && cu < 0xA0)) {
+					if (cu >= 0xE0 || (cu > 0x80 && cu < 0xA0)) {
 						lw += sjisOffs;
 						strPos++;
 					} else {
@@ -543,7 +537,18 @@ void TextDisplayer_rpg::printLine(char *str) {
 		}
 		_screen->printText(str, x1 & ~3, (y + 8) & ~7, col, 0);
 	} else {
-		_screen->printText(str, x1, y, col, _textDimData[sdx].color2);
+		uint8 col2 = _textDimData[sdx].color2;
+		if (_shadowColor) {
+			_screen->printText(str, x1 - 1, y, _shadowColor, col2);
+			_screen->printText(str, x1, y + 1, _shadowColor, 0);
+			_screen->printText(str, x1 - 1, y + 1, _shadowColor, 0);
+			// Another hack for Chinese EOBII. Due to the reduced line spacing - while still drawing a shadow for the font - the
+			// lines will overdraw by one pixel if we don't clear the bottom line. This will otherwise cause glitches when doing line feeds.
+			for (int i = 0; i < -_lineSpacing && y + fh + i < sd->sy + sd->h; ++i)
+				_screen->drawClippedLine(x1 - 1, y + fh + i, x1 + lw, y + fh + i, _textDimData[sdx].color2);
+			col2 = 0;
+		}
+		_screen->printText(str, x1, y, col, col2);
 	}
 
 	_textDimData[sdx].column += lw;
@@ -643,10 +648,24 @@ int TextDisplayer_rpg::clearDim(int dim) {
 void TextDisplayer_rpg::clearCurDim() {
 	int d = _screen->curDimIndex();
 	const ScreenDim *tmp = _screen->getScreenDim(d);
+
+	int xOffs = 0;
+	int wOffs = 0;
+	int hOffs = 0;
+
+	if (_shadowColor) {
+		if (tmp->sx > 0)
+			xOffs = wOffs = 1;
+		if (tmp->sy + tmp->h < Screen::SCREEN_H)
+			hOffs = 1;
+	}
+
 	if (_pc98TextMode) {
-		_screen->fillRect(tmp->sx << 3, tmp->sy, ((tmp->sx + tmp->w) << 3) - 2, (tmp->sy + tmp->h) - 2, _textDimData[d].color2);
-	} else
-		_screen->fillRect(tmp->sx << 3, tmp->sy, ((tmp->sx + tmp->w) << 3) - 1, (tmp->sy + tmp->h) - 1, _textDimData[d].color2);
+		--wOffs;
+		--hOffs;
+	}
+
+	_screen->fillRect((tmp->sx << 3) - xOffs, tmp->sy, ((tmp->sx + tmp->w) << 3) - 1 + wOffs, (tmp->sy + tmp->h) - 1 + hOffs, _textDimData[d].color2);
 
 	_lineCount = 0;
 	_textDimData[d].column = _textDimData[d].line = 0;
diff --git a/engines/kyra/text/text_rpg.h b/engines/kyra/text/text_rpg.h
index 42ca83c4a13..9a2f0c264d8 100644
--- a/engines/kyra/text/text_rpg.h
+++ b/engines/kyra/text/text_rpg.h
@@ -53,6 +53,8 @@ public:
 
 	void allowPageBreak(bool mode) { _allowPageBreak = mode; }
 	void setWaitButtonMode(int mode) { _waitButtonMode = mode; }
+	int setShadowColor(int col) { SWAP(col, _shadowColor); return col; }
+	int setLineSpacing(int spacing) { SWAP(spacing, _lineSpacing); return spacing; }
 	int lineCount() const { return _lineCount; }
 	const uint8 *colorMap() const { return _colorMap; }
 
@@ -107,6 +109,9 @@ protected:
 
 private:
 	Screen *_screen;
+	int _lineSpacing;
+	int _shadowColor;
+	bool _preventHalfWidthLineEnd;
 
 	char *_table1;
 	char *_table2;


Commit: 1831f02c364d231ae0ef3704e5fff4d85fff9748
    https://github.com/scummvm/scummvm/commit/1831f02c364d231ae0ef3704e5fff4d85fff9748
Author: athrxx (athrxx at scummvm.org)
Date: 2023-07-11T14:34:56+02:00

Commit Message:
KYRA: (EOB/LOL) - reduce text displayer hacks

Trying to make this code a bit less ugly. It has been
hacked up by basically every Japanese and Chinese
version in different ways. This is a start to revert some
of the hacks.

Changed paths:
    engines/kyra/engine/darkmoon.cpp
    engines/kyra/engine/eob.cpp
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/lol.cpp
    engines/kyra/engine/magic_eob.cpp
    engines/kyra/engine/timer_eob.cpp
    engines/kyra/gui/debugger.cpp
    engines/kyra/gui/gui_eob.cpp
    engines/kyra/resource/staticres_eob.cpp
    engines/kyra/script/script_eob.cpp
    engines/kyra/script/script_eob.h
    engines/kyra/text/text_lol.cpp
    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 4aea8b53b01..b772ccc2387 100644
--- a/engines/kyra/engine/darkmoon.cpp
+++ b/engines/kyra/engine/darkmoon.cpp
@@ -76,6 +76,10 @@ Common::Error DarkMoonEngine::init() {
 		_screen->modifyScreenDim(27, 0, 0, 21, 5);
 	} else if (_flags.lang == Common::Language::ZH_TWN) {
 		_screen->modifyScreenDim(6, 10, 72, 21, 40);
+		_txt->setShadowColor(-1, guiSettings()->colors.guiColorBlack);
+		_txt->setLineSpacing(7, -1);
+		_txt->setColorMapping(7, 12, 0);
+		_txt->setShadowColor(7, guiSettings()->colors.fill);
 	}
 
 	return Common::kNoError;
diff --git a/engines/kyra/engine/eob.cpp b/engines/kyra/engine/eob.cpp
index 7dc3b51a06b..e6d3d3dfe97 100644
--- a/engines/kyra/engine/eob.cpp
+++ b/engines/kyra/engine/eob.cpp
@@ -127,6 +127,7 @@ Common::Error EoBEngine::init() {
 	if (_flags.platform == Common::kPlatformPC98) {
 		_screen->modifyScreenDim(28, 0x0A, 0xA4, 0x15, 0x18);
 		_screen->modifyScreenDim(12, 0x01, 0x04, 0x14, 0x9A);
+		_txt->setColorMapping(-1, 12, 0);
 	} else if (_flags.platform == Common::kPlatformSegaCD) {
 		_screen->modifyScreenDim(27, 0x00, 0x02, 0x11, 0x03);
 		_screen->modifyScreenDim(28, 0x07, 0xA0, 0x17, 0x24);
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 103caef5c89..7f741d196a9 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -472,6 +472,12 @@ Common::Error EoBCoreEngine::init() {
 		assert(_gui);
 		_txt = new TextDisplayer_rpg(this, _screen);
 		assert(_txt);
+
+		if (_flags.platform == Common::kPlatformAmiga) {
+			static const uint8 cmap[16] = { 0x00, 0x06, 0x1d, 0x1b, 0x1a, 0x17, 0x18, 0x0e, 0x19, 0x1c, 0x1c, 0x1e, 0x13, 0x0a, 0x11, 0x1f };
+			for (int i = 0; i < ARRAYSIZE(cmap); ++i)
+				_txt->setColorMapping(-1, i, cmap[i]);
+		}
 	}
 
 	_inf = new EoBInfProcessor(this, _screen);
@@ -730,11 +736,6 @@ void EoBCoreEngine::runLoop() {
 	_drawSceneTimer = _system->getMillis();
 	_screen->setFont(_conFont);
 	_screen->setScreenDim(7);
-	if (_flags.lang == Common::ZH_TWN) {
-		_txt->setShadowColor(guiSettings()->colors.fill);
-		_txt->setLineSpacing(-1);
-	}
-
 	_runFlag = true;
 
 	while (!shouldQuit() && _runFlag) {
@@ -1616,11 +1617,6 @@ void EoBCoreEngine::initDialogueSequence() {
 	delete s;
 
 	_txt->setupField(9, 0);
-
-	if (_flags.lang == Common::ZH_TWN) {
-		_txt->setShadowColor(guiSettings()->colors.guiColorBlack);
-		_txt->setLineSpacing(0);
-	}
 }
 
 void EoBCoreEngine::restoreAfterDialogueSequence() {
@@ -1633,11 +1629,6 @@ void EoBCoreEngine::restoreAfterDialogueSequence() {
 	//_allowSkip = false;
 	_screen->setScreenDim(7);
 
-	if (_flags.lang == Common::ZH_TWN) {
-		_txt->setShadowColor(guiSettings()->colors.fill);
-		_txt->setLineSpacing(-1);
-	}
-
 	if (_flags.gameID == GI_EOB2)
 		snd_playSoundEffect(2);
 
@@ -1726,8 +1717,6 @@ int EoBCoreEngine::runDialogue(int dialogueTextId, int numStr, int loopButtonId,
 void EoBCoreEngine::restParty_displayWarning(const char *str) {
 	int od = _screen->curDimIndex();
 	_screen->setScreenDim(7);
-	int osh = (_flags.lang == Common::ZH_TWN) ? _txt->setShadowColor(guiSettings()->colors.fill) : 0xFFFF;
-	int ols = (_flags.lang == Common::ZH_TWN) ? _txt->setLineSpacing(-1) : 0xFFFF;
 
 	Screen::FontId of = _screen->setFont(_conFont);
 	_screen->setCurPage(0);
@@ -1736,11 +1725,6 @@ void EoBCoreEngine::restParty_displayWarning(const char *str) {
 
 	_screen->setFont(of);
 	_screen->setScreenDim(od);
-
-	if (osh != 0xFFFF)
-		_txt->setShadowColor(osh);
-	if (ols != 0xFFFF)
-		_txt->setLineSpacing(ols);
 }
 
 bool EoBCoreEngine::restParty_updateMonsters() {
@@ -1759,18 +1743,12 @@ bool EoBCoreEngine::restParty_updateMonsters() {
 		Screen::FontId of = _screen->setFont(_conFont);
 		int od = _screen->curDimIndex();
 		_screen->setScreenDim(7);
-		int osh = (_flags.lang == Common::ZH_TWN) ? _txt->setShadowColor(guiSettings()->colors.fill) : 0xFFFF;
-		int ols = (_flags.lang == Common::ZH_TWN) ? _txt->setLineSpacing(-1) : 0xFFFF;
 
 		updateMonsters(0);
 		updateMonsters(1);
 		timerProcessFlyingObjects(0);
 
 		_screen->setScreenDim(od);
-		if (osh != 0xFFFF)
-			_txt->setShadowColor(osh);
-		if (ols != 0xFFFF)
-			_txt->setLineSpacing(ols);
 		_screen->setFont(of);
 
 		_partyResting = false;
diff --git a/engines/kyra/engine/lol.cpp b/engines/kyra/engine/lol.cpp
index 1b4b720ca69..1506fa65339 100644
--- a/engines/kyra/engine/lol.cpp
+++ b/engines/kyra/engine/lol.cpp
@@ -48,6 +48,7 @@ LoLEngine::LoLEngine(OSystem *system, const GameFlags &flags) : KyraRpgEngine(sy
 	_screen = 0;
 	_gui = 0;
 	_tim = 0;
+	_txt = 0;
 
 	_lang = 0;
 	_langIntern = 0;
@@ -393,7 +394,29 @@ Common::Error LoLEngine::init() {
 	}
 
 	_txt = new TextDisplayer_LoL(this, _screen);
-	_txt->setLineSpacing(_flags.lang == Common::JA_JPN && _flags.use16ColorMode ? 1 : (_flags.lang == Common::ZH_TWN ? -1 : 0));
+	if (_flags.lang == Common::JA_JPN && _flags.use16ColorMode) {
+		for (int i = 0; i < _screen->screenDimTableCount(); ++i) {
+			_txt->setLineSpacing(i, 1);
+			if ((i >= 3 && i <= 5) || i == 15) {
+				for (int ii = 0; ii < 256; ++ii)
+					_txt->setColorMapping(i, ii, 1);
+				_txt->setColorMapping(i, 0, 0);
+				_txt->setColorMapping(i, 0x11, 0x11);
+				_txt->setColorMapping(i, 0x18, 0x61);
+				_txt->setColorMapping(i, 0x33, 0xE1);
+				_txt->setColorMapping(i, 0x44, 0x44);
+				_txt->setColorMapping(i, 0x55, 0x81);
+				_txt->setColorMapping(i, 0x88, 0x41);
+				_txt->setColorMapping(i, 0x99, 0xA1);
+				_txt->setColorMapping(i, 0xAA, 0x21);
+				_txt->setVisualLineSpacingAdjust(i, -1);
+			} else {
+				_txt->setCharSpacing(i, 1);
+			}
+		}
+	} else if (_flags.lang == Common::ZH_TWN) {
+		_txt->setLineSpacing(-1, -1);
+	}
 
 	_screen->setAnimBlockPtr(10000);
 	_screen->setScreenDim(0);
@@ -625,7 +648,7 @@ void LoLEngine::checkFloatingPointerRegions() {
 
 	Common::Point p = getMousePos();
 
-	if (!(_updateFlags & 4) & !_floatingCursorControl) {
+	if (!(_updateFlags & 4) && !_floatingCursorControl) {
 		if (posWithinRect(p.x, p.y, 96, 0, 303, 136)) {
 			if (!posWithinRect(p.x, p.y, 128, 16, 271, 119)) {
 				if (posWithinRect(p.x, p.y, 112, 0, 287, 15))
@@ -2267,6 +2290,8 @@ int LoLEngine::processMagicHeal(int charNum, int spellLevel) {
 	uint16 pY = 138;
 	uint16 diff[4];
 	uint16 pts[4];
+	memset(pX, 0, sizeof(pX));
+	memset(diff, 0, sizeof(diff));
 	memset(pts, 0, sizeof(pts));
 
 	while (charNum < n) {
diff --git a/engines/kyra/engine/magic_eob.cpp b/engines/kyra/engine/magic_eob.cpp
index 6fc51eef9bc..5349ca82234 100644
--- a/engines/kyra/engine/magic_eob.cpp
+++ b/engines/kyra/engine/magic_eob.cpp
@@ -247,16 +247,10 @@ void EoBCoreEngine::removeCharacterEffect(int spell, int charIndex, int showWarn
 		int od = _screen->curDimIndex();
 		Screen::FontId of = _screen->setFont(_conFont);
 		_screen->setScreenDim(7);
-		int osh = (_flags.lang == Common::ZH_TWN) ? _txt->setShadowColor(guiSettings()->colors.fill) : 0xFFFF;
-		int ols = (_flags.lang == Common::ZH_TWN) ? _txt->setLineSpacing(-1) : 0xFFFF;
 
 		printWarning(Common::String::format(_magicStrings3[_flags.gameID == GI_EOB1 ? 3 : 2], c->name, s->name).c_str());
 
 		_screen->setScreenDim(od);
-		if (osh != 0xFFFF)
-			_txt->setShadowColor(osh);
-		if (ols != 0xFFFF)
-			_txt->setLineSpacing(ols);
 		_screen->setFont(of);
 	}
 
diff --git a/engines/kyra/engine/timer_eob.cpp b/engines/kyra/engine/timer_eob.cpp
index 9d88733ac42..ffe8278a48c 100644
--- a/engines/kyra/engine/timer_eob.cpp
+++ b/engines/kyra/engine/timer_eob.cpp
@@ -306,8 +306,6 @@ void EoBCoreEngine::timerSpecialCharacterUpdate(int timerNum) {
 		int od = _screen->curDimIndex();
 		Screen::FontId of = _screen->setFont(_conFont);
 		_screen->setScreenDim(7);
-		int osh = (_flags.lang == Common::ZH_TWN) ? _txt->setShadowColor(guiSettings()->colors.fill) : 0xFFFF;
-		int ols = (_flags.lang == Common::ZH_TWN) ? _txt->setLineSpacing(-1) : 0xFFFF;
 
 		switch (evt) {
 		case 2:
@@ -371,10 +369,6 @@ void EoBCoreEngine::timerSpecialCharacterUpdate(int timerNum) {
 		}
 
 		_screen->setScreenDim(od);
-		if (osh != 0xFFFF)
-			_txt->setShadowColor(osh);
-		if (ols != 0xFFFF)
-			_txt->setLineSpacing(ols);
 		_screen->setFont(of);
 	}
 
diff --git a/engines/kyra/gui/debugger.cpp b/engines/kyra/gui/debugger.cpp
index 6bb28cab858..1e86329f951 100644
--- a/engines/kyra/gui/debugger.cpp
+++ b/engines/kyra/gui/debugger.cpp
@@ -605,10 +605,6 @@ bool Debugger_EoB::cmdSetPosition(int argc, const char **argv) {
 			_vm->generateTempData();
 			_vm->txt()->removePageBreakFlag();
 			_vm->screen()->setScreenDim(7);
-			if (_vm->gameFlags().lang == Common::ZH_TWN) {
-				_vm->txt()->setShadowColor(_vm->guiSettings()->colors.fill);
-				_vm->txt()->setLineSpacing(0);
-			}
 
 			_vm->loadLevel(level, sub);
 
diff --git a/engines/kyra/gui/gui_eob.cpp b/engines/kyra/gui/gui_eob.cpp
index 1598221eb32..0ff4aac72c3 100644
--- a/engines/kyra/gui/gui_eob.cpp
+++ b/engines/kyra/gui/gui_eob.cpp
@@ -3967,10 +3967,8 @@ bool GUI_EoB::restParty() {
 		_screen->sega_clearTextBuffer(0);
 
 	if (_vm->gameFlags().lang == Common::ZH_TWN)
-		_screen->drawBox(3, 170, 290, 199, 0xb7);
+		_screen->drawBox(3, 170, 290, 199, _vm->guiSettings()->colors.fill);
 
-	int osh = (_vm->gameFlags().lang == Common::ZH_TWN) ? _vm->txt()->setShadowColor(_vm->guiSettings()->colors.fill) : 0xFFFF;
-	int ols = (_vm->gameFlags().lang == Common::ZH_TWN) ? _vm->txt()->setLineSpacing(-1) : 0xFFFF;
 	Screen::FontId of = _screen->setFont(_vm->_conFont);
 
 	restParty_updateRestTime(hours, true);
@@ -4191,10 +4189,6 @@ bool GUI_EoB::restParty() {
 	_vm->removeInputTop();
 	_screen->setScreenDim(4);
 	_screen->setFont(of);
-	if (osh != 0xFFFF)
-		_vm->txt()->setShadowColor(osh);
-	if (ols != 0xFFFF)
-		_vm->txt()->setLineSpacing(ols);
 
 	if (!res) {
 		if (!injured)
@@ -4216,8 +4210,6 @@ void GUI_EoB::printScribeScrollSpellString(const int16 *menuItems, int id, bool
 
 bool GUI_EoB::confirmDialogue(int id) {
 	int od = _screen->curDimIndex();
-	int osh = (_vm->gameFlags().lang == Common::ZH_TWN) ? _vm->txt()->setShadowColor(_vm->guiSettings()->colors.guiColorBlack) : 0xFFFF;
-	int ols = (_vm->gameFlags().lang == Common::ZH_TWN) ? _vm->txt()->setLineSpacing(0) : 0xFFFF;
 	Screen::FontId of = _screen->setFont(_menuFont);
 
 	Button *buttonList = initMenu(5);
@@ -4274,10 +4266,6 @@ bool GUI_EoB::confirmDialogue(int id) {
 
 	_screen->setFont(of);
 	_screen->setScreenDim(od);
-	if (osh != 0xFFFF)
-		_vm->txt()->setShadowColor(osh);
-	if (ols != 0xFFFF)
-		_vm->txt()->setLineSpacing(ols);
 
 	return result;
 }
@@ -4593,9 +4581,6 @@ void GUI_EoB::drawMenuButtonBox(int x, int y, int w, int h, bool clicked, bool n
 void GUI_EoB::drawTextBox(int dim, int id) {
 	int od = _screen->curDimIndex();
 	_screen->setScreenDim(dim);
-	int osh = (_vm->gameFlags().lang == Common::ZH_TWN) ? _vm->txt()->setShadowColor(_vm->guiSettings()->colors.guiColorBlack) : 0xFFFF;
-	int ols = (_vm->gameFlags().lang == Common::ZH_TWN) ? _vm->txt()->setLineSpacing(0) : 0xFFFF;
-
 	Screen::FontId of = _screen->setFont(_menuFont);
 	int cs = (_vm->gameFlags().platform == Common::kPlatformPC98 && !_vm->gameFlags().use16ColorMode) ? _screen->setFontStyles(_menuFont, Font::kStyleFat) : -1;
 
@@ -4612,10 +4597,6 @@ void GUI_EoB::drawTextBox(int dim, int id) {
 	_screen->copyRegion(0, 0, dm->sx << 3, dm->sy, dm->w << 3, dm->h, 2, 0, Screen::CR_NO_P_CHECK);
 	_screen->updateScreen();
 
-	if (osh != 0xFFFF)
-		_vm->txt()->setShadowColor(osh);
-	if (ols != 0xFFFF)
-		_vm->txt()->setLineSpacing(ols);
 	_screen->setScreenDim(od);
 
 	if (cs != -1)
@@ -4848,8 +4829,6 @@ void GUI_EoB::restParty_updateRestTime(int hours, bool init) {
 	Screen::FontId of = _screen->setFont(_menuFont);
 	int od = _screen->curDimIndex();
 	_screen->setScreenDim(10);
-	int osh = (_vm->gameFlags().lang == Common::ZH_TWN) ? _vm->txt()->setShadowColor(_vm->guiSettings()->colors.guiColorBlack) : 0xFFFF;
-	int ols = (_vm->gameFlags().lang == Common::ZH_TWN) ? _vm->txt()->setLineSpacing(0) : 0xFFFF;
 
 	if (init) {
 		_screen->setCurPage(0);
@@ -4866,10 +4845,6 @@ void GUI_EoB::restParty_updateRestTime(int hours, bool init) {
 	_screen->set16bitShadingLevel(0);
 	_screen->updateScreen();
 	_vm->delay(160);
-	if (osh != 0xFFFF)
-		_vm->txt()->setShadowColor(osh);
-	if (ols != 0xFFFF)
-		_vm->txt()->setLineSpacing(ols);
 	_screen->setScreenDim(od);
 	_screen->setFont(of);
 }
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index dcb60c5a222..585e082dd6e 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -186,7 +186,7 @@ const ScreenDim Screen_EoB::_screenDimTableZH[] = {
 	{ 0x02, 0x18, 0x14, 0x78, 0x0F, 0x02, 0x03, 0x00 },
 	{ 0x00, 0x00, 0x16, 0x78, 0x0F, 0x0D, 0x00, 0x00 },
 	{ 0x0A, 0x6C, 0x15, 0x28, 0x0F, 0x00, 0x00, 0x00 },
-	{ 0x01, 0xAB, 0x23, 0x1C, 0x0F, 0x00, 0x00, 0x00 },
+	{ 0x01, 0xAB, 0x23, 0x1C, 0x0F, 0x0C, 0x00, 0x00 },
 	{ 0x02, 0x18, 0x14, 0x00, 0x0F, 0x02, 0x03, 0x00 },
 	{ 0x01, 0x7D, 0x26, 0x40, 0x0F, 0x00, 0x03, 0x00 },
 	{ 0x00, 0x00, 0x16, 0x90, 0x0F, 0x02, 0x00, 0x00 },
diff --git a/engines/kyra/script/script_eob.cpp b/engines/kyra/script/script_eob.cpp
index 7749354b346..377daa3ae91 100644
--- a/engines/kyra/script/script_eob.cpp
+++ b/engines/kyra/script/script_eob.cpp
@@ -540,18 +540,11 @@ int EoBInfProcessor::oeob_printMessage_v1(int8 *data) {
 		col[3] = *pos++;
 	}
 
-	if (_vm->gameFlags().platform == Common::kPlatformAmiga) {
-		assert((uint8)col[1] < 16);
-		assert((uint8)col[3] < 16);
-		col[1] = _amigaColorMap[(uint8)col[1]];
-		col[3] = _amigaColorMap[(uint8)col[3]];
-	}
-
 	_vm->txt()->printMessage(col);
 	_vm->txt()->printMessage(str);
 
-	col[1] = _vm->gameFlags().platform == Common::kPlatformSegaCD ? 0xFF : _vm->txt()->colorMap()[_screen->_curDim->col1];
-	col[3] = _vm->txt()->colorMap()[_screen->_curDim->col2];
+	col[1] = _vm->gameFlags().platform == Common::kPlatformSegaCD ? 0xFF : _screen->_curDim->col1;
+	col[3] = _screen->_curDim->col2;
 	_vm->txt()->printMessage(col);
 
 	if (lineBreak)
@@ -570,10 +563,6 @@ int EoBInfProcessor::oeob_printMessage_v2(int8 *data) {
 
 	int c = 0;
 	_vm->_dialogueFieldAmiga = true;
-	if (_vm->gameFlags().platform == Common::kPlatformAmiga) {
-		assert(col < 16);
-		col = _amigaColorMap[col];
-	}
 
 	if (_activeCharacter == -1) {
 		c = _vm->rollDice(1, 6, -1);
@@ -1326,10 +1315,6 @@ int EoBInfProcessor::oeob_loadNewLevelOrMonsters(int8 *data) {
 		_vm->generateTempData();
 		_vm->txt()->removePageBreakFlag();
 		_screen->setScreenDim(7);
-		if (_vm->gameFlags().lang == Common::ZH_TWN) {
-			_vm->txt()->setShadowColor(_vm->guiSettings()->colors.fill);
-			_vm->txt()->setLineSpacing(-1);
-		}
 
 		_vm->loadLevel(index, cmd);
 		debugC(5, kDebugLevelScript, "         - entering level '%d', sub level '%d', start block '0x%.04X', start direction '%d'", index, cmd, _vm->_currentBlock, _vm->_currentDirection);
@@ -1548,10 +1533,6 @@ int EoBInfProcessor::oeob_sequence(int8 *data) {
 	}
 
 	_vm->screen()->setScreenDim(7);
-	if (_vm->gameFlags().lang == Common::ZH_TWN) {
-		_vm->txt()->setShadowColor(_vm->guiSettings()->colors.fill);
-		_vm->txt()->setLineSpacing(-1);
-	}
 
 	return pos - data;
 }
@@ -1668,10 +1649,6 @@ int EoBInfProcessor::oeob_specialEvent(int8 *data) {
 	return pos - data;
 }
 
-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
 };
diff --git a/engines/kyra/script/script_eob.h b/engines/kyra/script/script_eob.h
index 0e9683293b8..041f3d19b34 100644
--- a/engines/kyra/script/script_eob.h
+++ b/engines/kyra/script/script_eob.h
@@ -120,7 +120,6 @@ private:
 
 	int8 _activeCharacter;
 
-	static const uint8 _amigaColorMap[16];
 	static const uint8 _segaCDColorMap[16];
 
 	const int _commandMin;
diff --git a/engines/kyra/text/text_lol.cpp b/engines/kyra/text/text_lol.cpp
index 98cb883a747..22882f41560 100644
--- a/engines/kyra/text/text_lol.cpp
+++ b/engines/kyra/text/text_lol.cpp
@@ -139,11 +139,11 @@ void TextDisplayer_LoL::printDialogueText2(int dim, const char *str, EMCState *s
 	if (dim == 3) {
 		if (_vm->_updateFlags & 2) {
 			oldDim = clearDim(4);
-			_textDimData[4].color1 = _vm->gameFlags().use16ColorMode ? 0x33 : 254;
+			_textDimData[4].color1= _vm->gameFlags().use16ColorMode ? 0x33 : 254;
 			_textDimData[4].color2 = _screen->_curDim->col2;
 		} else {
 			oldDim = clearDim(3);
-			_textDimData[3].color1 = _vm->gameFlags().use16ColorMode ? 0x33 : 192;
+			_textDimData[3].color1= _vm->gameFlags().use16ColorMode ? 0x33 : 192;
 			_textDimData[3].color2 = _screen->_curDim->col2;
 			if (!_vm->gameFlags().use16ColorMode)
 				_screen->copyColor(192, 254);
@@ -155,7 +155,7 @@ void TextDisplayer_LoL::printDialogueText2(int dim, const char *str, EMCState *s
 		oldDim = _screen->curDimIndex();
 		_screen->setScreenDim(dim);
 		_lineCount = 0;
-		_textDimData[dim].color1 = _vm->gameFlags().use16ColorMode ? 0x33 : 254;
+		_textDimData[dim].color1= _vm->gameFlags().use16ColorMode ? 0x33 : 254;
 		_textDimData[dim].color2 = _screen->_curDim->col2;
 	}
 
@@ -194,14 +194,14 @@ void TextDisplayer_LoL::printMessage(uint16 type, const char *str, ...) {
 
 	if (_vm->_updateFlags & 2) {
 		clearDim(4);
-		_textDimData[4].color1 = col;
+		_textDimData[4].color1= col;
 	} else {
 		clearDim(3);
 		if (_vm->gameFlags().use16ColorMode) {
-			_textDimData[3].color1 = col;
+			_textDimData[3].color1= col;
 		} else {
 			_screen->copyColor(192, col);
-			_textDimData[3].color1 = 192;
+			_textDimData[3].color1= 192;
 		}
 		_vm->enableTimer(11);
 	}
diff --git a/engines/kyra/text/text_rpg.cpp b/engines/kyra/text/text_rpg.cpp
index 65211178de6..f4e056a33a8 100644
--- a/engines/kyra/text/text_rpg.cpp
+++ b/engines/kyra/text/text_rpg.cpp
@@ -33,17 +33,12 @@ enum {
 };
 
 TextDisplayer_rpg::TextDisplayer_rpg(KyraRpgEngine *engine, Screen *scr) : _vm(engine), _screen(scr),
-_lineCount(0), _printFlag(false), _lineWidth(0), _numCharsTotal(0), _allowPageBreak(true),
-	_numCharsLeft(0), _numCharsPrinted(0), _sjisTextModeLineBreak(false), _waitButtonMode(1),
-	_pc98TextMode(engine->gameFlags().use16ColorMode && engine->game() == GI_LOL), _shadowColor(0), _lineSpacing(0), _preventHalfWidthLineEnd(false),
+	_lineCount(0), _printFlag(false), _lineWidth(0), _numCharsTotal(0), _allowPageBreak(true), _dimCount(scr ? scr->screenDimTableCount() : 0),
+	_numCharsLeft(0), _numCharsPrinted(0), _twoByteLineBreakFlag(false), _waitButtonMode(1),
+	_pc98TextMode(engine->gameFlags().use16ColorMode && engine->game() == GI_LOL),
 	_waitButtonFont(Screen::FID_6_FNT), _isChinese(_vm->gameFlags().lang == Common::Language::ZH_TWN || _vm->gameFlags().lang == Common::Language::ZH_CHN) {
 
-	static const uint8 amigaColorMap[16] = {
-		0x00, 0x06, 0x1d, 0x1b, 0x1a, 0x17, 0x18, 0x0e, 0x19, 0x1c, 0x1c, 0x1e, 0x13, 0x0a, 0x11, 0x1f
-	};
-
 	_dialogueBuffer = new char[kEoBTextBufferSize]();
-
 	_currentLine = new char[85]();
 
 	if (_pc98TextMode)
@@ -55,22 +50,18 @@ _lineCount(0), _printFlag(false), _lineWidth(0), _numCharsTotal(0), _allowPageBr
 	else if ((_vm->game() == GI_LOL && _vm->gameFlags().lang == Common::Language::ZH_TWN))
 		_waitButtonFont = Screen::FID_CHINESE_FNT;
 
-	_preventHalfWidthLineEnd = ((_vm->gameFlags().lang == Common::JA_JPN && _vm->game() == GI_EOB1) || (_vm->gameFlags().lang == Common::Language::ZH_TWN && _vm->game() == GI_EOB2));
+	_textDimData = new TextDimData[_dimCount];
+	memset(_textDimData, 0, sizeof(TextDimData) * _dimCount);
 
-	_textDimData = new TextDimData[_screen->screenDimTableCount()];
+	applySetting(-1, kNoHalfWidthLineEnd, ((_vm->gameFlags().lang == Common::JA_JPN && _vm->game() == GI_EOB1) || (_vm->gameFlags().lang == Common::Language::ZH_TWN && _vm->game() == GI_EOB2)) ? 1 : 0);
 
 	for (int i = 0; i < 256; ++i)
 		_colorMap[i] = i;
 
-	if (_vm->gameFlags().platform == Common::kPlatformAmiga)
-		memcpy(_colorMap, amigaColorMap, 16);
-	else if (_vm->game() == GI_EOB1 && _vm->gameFlags().platform == Common::kPlatformPC98)
-		_colorMap[12] = 0;
-
-	for (int i = 0; i < _screen->screenDimTableCount(); i++) {
+	for (int i = 0; i < _dimCount; ++i) {
 		const ScreenDim *d = _screen->getScreenDim(i);
-		_textDimData[i].color1 = _colorMap[d->col1];
-		_textDimData[i].color2 = _colorMap[d->col2];
+		_textDimData[i].color1= d->col1;
+		_textDimData[i].color2 = d->col2;
 		_textDimData[i].line = d->line;
 		_textDimData[i].column = d->column;
 	}
@@ -84,6 +75,7 @@ _lineCount(0), _printFlag(false), _lineWidth(0), _numCharsTotal(0), _allowPageBr
 }
 
 TextDisplayer_rpg::~TextDisplayer_rpg() {
+	setColorMapping(-1, 0 ,0);
 	delete[] _dialogueBuffer;
 	delete[] _currentLine;
 	delete[] _textDimData;
@@ -127,6 +119,23 @@ void TextDisplayer_rpg::removePageBreakFlag() {
 	_allowPageBreak = false;
 }
 
+void TextDisplayer_rpg::setColorMapping(int sd, uint8 from, uint8 to) {
+	if (sd < -1 || sd >= _dimCount)
+		error("TextDisplayer_rpg::mapColor(): arg out of range");
+
+	if (sd == -1) {
+		for (int i = 0; i < _dimCount; ++i) {
+			delete[] _textDimData[i].colorMap;
+			_textDimData[i].colorMap = 0;
+		}
+		_colorMap[from] = to;
+	} else {
+		if (_textDimData[sd].colorMap == nullptr)
+			_textDimData[sd].colorMap = new uint8[256];
+		_textDimData[sd].colorMap[from] = to;
+	}
+}
+
 void TextDisplayer_rpg::displayText(char *str, ...) {
 	convertString(str);
 	_printFlag = false;
@@ -151,7 +160,6 @@ void TextDisplayer_rpg::displayText(char *str, ...) {
 	int sdx = _screen->curDimIndex();
 
 	bool sjisTextMode = (_pc98TextMode && (sdx == 3 || sdx == 4 || sdx == 5 || sdx == 15)) ? true : false;
-	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->_charSpacing);
@@ -174,40 +182,22 @@ void TextDisplayer_rpg::displayText(char *str, ...) {
 			c = parseCommand();
 		}
 
-		if (_vm->gameFlags().lang == Common::JA_JPN) {
-			uint8 cu = (uint8) c;
-			if (cu >= 0xE0 || (cu > 0x80 && cu < 0xA0)) {
-				if ((_textDimData[sdx].column + _lineWidth + sjisOffs) > (sd->w << 3))
-					printLine(_currentLine);
-
-				_currentLine[_numCharsLeft++] = c;
-				_currentLine[_numCharsLeft++] = parseCommand();
-				_currentLine[_numCharsLeft] = '\0';
-
-				_lineWidth += sjisOffs;
-				if (_preventHalfWidthLineEnd && ((sd->w << 3) - sjisOffs) <= (_textDimData[sdx].column + _lineWidth))
-					printLine(_currentLine);
-				c = parseCommand();
-				continue;
-			}
-		}
+		if (isTwoByteChar(c)) {
+			char next = parseCommand();
+			int cw = _screen->getCharWidth((uint8)c | (uint8)next << 8) + _textDimData[sdx].charSpacing;
+			if (_textDimData[sdx].column + _lineWidth + cw > (sd->w << 3))
+				printLine(_currentLine);
 
-		if (_isChinese) {
-			uint8 cu = (uint8) c;
-			if (cu & 0x80) {
-				if ((_textDimData[sdx].column + _lineWidth + Graphics::Big5Font::kChineseTraditionalWidth) > (sd->w << 3))
-					printLine(_currentLine);
+			_currentLine[_numCharsLeft++] = c;
+			_currentLine[_numCharsLeft++] = next;
+			_currentLine[_numCharsLeft] = '\0';
 
-				_currentLine[_numCharsLeft++] = c;
-				_currentLine[_numCharsLeft++] = parseCommand();
-				_currentLine[_numCharsLeft] = '\0';
+			_lineWidth += cw;
+			if (_textDimData[sdx].noHalfWidthLineEnd && (_textDimData[sdx].column + _lineWidth + cw >= (sd->w << 3)))
+				printLine(_currentLine);
 
-				_lineWidth += Graphics::Big5Font::kChineseTraditionalWidth;
-				if (_preventHalfWidthLineEnd && (_textDimData[sdx].column + _lineWidth + Graphics::Big5Font::kChineseTraditionalWidth) > (sd->w << 3))
-					printLine(_currentLine);
-				c = parseCommand();
-				continue;
-			}
+			c = parseCommand();
+			continue;
 		}
 
 		uint16 dv = _textDimData[sdx].column / (_screen->getFontWidth() + _screen->_charSpacing);
@@ -223,17 +213,11 @@ void TextDisplayer_rpg::displayText(char *str, ...) {
 		case 1:
 			printLine(_currentLine);
 			_textDimData[sdx].color2 = parseCommand();
-			// EOB II Amiga does not use a color table here. EOB I doesn't do any color mapping here.
-			/*if (_vm->gameFlags().platform == Common::kPlatformAmiga)
-				_textDimData[sdx].color2 = _colorMap[_textDimData[sdx].color2];*/
 			break;
 
 		case 5:
 			printLine(_currentLine);
-			_textDimData[sdx].color1 = parseCommand();
-			// EOB I doesn't do any color mapping here.
-			if (_vm->gameFlags().platform == Common::kPlatformAmiga && _vm->game() == GI_EOB2)
-				_textDimData[sdx].color1 = _colorMap[_textDimData[sdx].color1];
+			_textDimData[sdx].color1= parseCommand();
 			break;
 
 		case 8:
@@ -247,9 +231,9 @@ void TextDisplayer_rpg::displayText(char *str, ...) {
 
 		case 12:
 			if (sjisTextMode)
-				_sjisTextModeLineBreak = true;
+				_twoByteLineBreakFlag = true;
 			printLine(_currentLine);
-			_sjisTextModeLineBreak = false;
+			_twoByteLineBreakFlag = false;
 			//_lineWidth = 0;
 			_lineCount++;
 			_textDimData[sdx].column = 0;
@@ -266,7 +250,8 @@ void TextDisplayer_rpg::displayText(char *str, ...) {
 
 		default:
 			if (_vm->game() == GI_EOB1 || _vm->game() == GI_LOL || (unsigned char)c > 30) {
-				_lineWidth += (sjisTextMode ? 4 : (_screen->_currentFont == Screen::FID_SJIS_TEXTMODE_FNT ? 9 : _screen->getCharWidth((uint8)c)));
+				int cw = _screen->getCharWidth((uint8)c) + _textDimData[sdx].charSpacing;
+				_lineWidth += cw;
 
 				_currentLine[_numCharsLeft++] = c;
 				_currentLine[_numCharsLeft] = 0;
@@ -345,9 +330,8 @@ void TextDisplayer_rpg::printLine(char *str) {
 	const ScreenDim *sd = _screen->_curDim;
 	int sdx = _screen->curDimIndex();
 	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->getFontHeight() + _screen->_lineSpacing + _lineSpacing;
+	int fh = _screen->getFontHeight() + _screen->_lineSpacing + _textDimData[sdx].lineSpacing;
 	int lines = (sd->h - _screen->_lineSpacing) / fh;
 
 	while (_textDimData[sdx].line >= lines) {
@@ -364,13 +348,14 @@ void TextDisplayer_rpg::printLine(char *str) {
 		if (h2)
 			_screen->copyRegion(sd->sx << 3, sd->sy + fh, sd->sx << 3, sd->sy, sd->w << 3, h2, _screen->_curPage, _screen->_curPage, Screen::CR_NO_P_CHECK);
 
-		// In Chinese EOBII some characters overdraw the valid boundaries by one pixel
-		// (at least the ',' does). So, the original redraws the border here. We do the same...
+		// HACK: In Chinese EOBII some characters overdraw the valid boundaries by one pixel
+		// (at least the ',' does). So, the original redraws the border here. We do the same
+		// since for now I don't have any good idea how to do this less ugly... 
 		if (_isChinese && _vm->_flags.gameID == GI_EOB2 && sdx == 7)
-			_screen->drawBox(3, 170, 290, 199, 0xb7);
+			_screen->drawBox(3, 170, 290, 199, _vm->guiSettings()->colors.fill);
 
 		_screen->set16bitShadingLevel(4);
-		_screen->fillRect(sd->sx << 3, sd->sy + h1, ((sd->sx + sd->w) << 3) - 1, sd->sy + sd->h - 1, _textDimData[sdx].color2);
+		_screen->fillRect(sd->sx << 3, sd->sy + h1, ((sd->sx + sd->w) << 3) - 1, sd->sy + sd->h - 1, remapColor(sdx, _textDimData[sdx].color2));
 		_screen->set16bitShadingLevel(0);
 
 		if (_textDimData[sdx].line)
@@ -378,11 +363,10 @@ void TextDisplayer_rpg::printLine(char *str) {
 	}
 
 	int x1 = (sd->sx << 3) + _textDimData[sdx].column;
-	int y = sd->sy + (sjisTextMode ? (_textDimData[sdx].line << 3) : (fh * _textDimData[sdx].line));
+	int y = sd->sy + (fh + _textDimData[sdx].visualLineSpacingAdjust) * _textDimData[sdx].line;
 	int w = sd->w << 3;
 	int lw = _lineWidth;
 	int s = _numCharsLeft;
-	char c = 0;
 	uint8 twoByteCharOffs = 0;
 
 	if (sjisTextMode) {
@@ -393,7 +377,7 @@ void TextDisplayer_rpg::printLine(char *str) {
 				// cut off line to leave space for "MORE" button
 				w -= _vm->guiSettings()->buttons.waitReserve;
 		} else {
-			if (!_sjisTextModeLineBreak || (_lineCount + 1 < lines - 1))
+			if (!_twoByteLineBreakFlag || (_lineCount + 1 < lines - 1))
 				ct = false;
 			else
 				// cut off line to leave space for "MORE" button
@@ -407,31 +391,26 @@ void TextDisplayer_rpg::printLine(char *str) {
 			int n1 = (w / 4) - 1;
 
 			while (n2 < n1 && n2 < s) {
-				c = str[n2];
-				uint8 cu = (uint8) c;
-				if (cu >= 0xE0 || (cu > 0x80 && cu < 0xA0))
+				if (isTwoByteChar(str[n2]))
 					n2++;
 				n2++;
 			}
 			s = n2;
 		}
-	} else if (_isChinese) {
-		s = strlen(str);
-		twoByteCharOffs = 16;
-		if ((lw + _textDimData[sdx].column) >= w) {
-			s -= ((lw + _textDimData[sdx].column) - w) >> 3;
-			w -= _textDimData[sdx].column;	
-		}
 	} else {
-		if (_vm->gameFlags().lang == Common::JA_JPN) {
-			for (int i = 0; i < s; ++i) {
-				uint8 cu = (uint8) str[i];
-				if (cu >= 0xE0 || (cu > 0x80 && cu < 0xA0))
-					twoByteCharOffs = (_vm->game() == GI_EOB1) ? 16 : 8;
-			}
+		for (int i = 0; i < s; ++i) {
+			if (isTwoByteChar(str[i]))
+				twoByteCharOffs = (_vm->game() == GI_EOB1 || _isChinese) ? 16 : 8;
 		}
 
-		if ((lw + _textDimData[sdx].column) >= w) {
+		if (_isChinese) {
+			s = strlen(str);
+			if ((lw + _textDimData[sdx].column) >= w) {
+				s -= ((lw + _textDimData[sdx].column - w) >> 3);
+				w -= _textDimData[sdx].column;	
+			}
+
+		} else if ((lw + _textDimData[sdx].column) >= w) {
 			if ((lines - 1) <= _lineCount && _allowPageBreak)
 				// cut off line to leave space for "MORE" button
 				w -= _vm->guiSettings()->buttons.waitReserve;
@@ -444,16 +423,13 @@ void TextDisplayer_rpg::printLine(char *str) {
 			if (twoByteCharOffs) {
 				lw = 0;
 				int prevStrPos = 0;
-				c = str[0];
 
 				for (strPos = 0; strPos < s; ++strPos) {
-					uint8 cu = (uint8) str[strPos];
-					if (cu >= 0xE0 || (cu > 0x80 && cu < 0xA0)) {
-						lw += sjisOffs;
-						strPos++;
-					} else {
-						lw += _screen->getCharWidth((uint8)c);
-					}
+					uint8 c = str[strPos];
+					if (isTwoByteChar(c))
+						lw += (_screen->getCharWidth(c | (uint8)str[++strPos] << 8) + _textDimData[sdx].charSpacing);						
+					else
+						lw += _screen->getCharWidth(c);
 
 					if (!lineLastCharPos && w < lw + twoByteCharOffs)
 						lineLastCharPos = prevStrPos;
@@ -464,7 +440,6 @@ void TextDisplayer_rpg::printLine(char *str) {
 						break;
 					}
 					prevStrPos = strPos;
-					c = (char) cu;
 				}
 
 				if (!lineLastCharPos) {
@@ -480,9 +455,8 @@ void TextDisplayer_rpg::printLine(char *str) {
 			} else {
 				while (strPos > 0) {
 					//cut off line after last space
-					c = str[strPos];
-
-					lw -= _screen->getCharWidth((uint8)c);
+					uint8 c = str[strPos];
+					lw -= _screen->getCharWidth(c);
 
 					if (!lineLastCharPos && lw <= w)
 						lineLastCharPos = strPos;
@@ -507,58 +481,35 @@ void TextDisplayer_rpg::printLine(char *str) {
 		}
 	}
 
-	c = str[s];
+	char lastChr = str[s];
 	str[s] = 0;
 
-	uint8 col = _textDimData[sdx].color1;
+	uint8 col1 = remapColor(sdx, _textDimData[sdx].color1);
+	uint8 col2 = remapColor(sdx, _textDimData[sdx].color2);
 	if (sjisTextMode && (sdx == 2 || sdx == 3 || sdx == 4 || sdx == 5 || sdx == 15)) {
-		switch (_textDimData[sdx].color1) {
-		case 0x88:
-			col = 0x41;
-			break;
-		case 0x55:
-			col = 0x81;
-			break;
-		case 0xAA:
-			col = 0x21;
-			break;
-		case 0x99:
-			col = 0xA1;
-			break;
-		case 0x33:
-			col = 0xE1;
-			break;
-		case 0x18:
-			col = 0x61;
-			break;
-		default:
-			col = 1;
-			break;
-		}
-		_screen->printText(str, x1 & ~3, (y + 8) & ~7, col, 0);
-	} else {
-		uint8 col2 = _textDimData[sdx].color2;
-		if (_shadowColor) {
-			_screen->printText(str, x1 - 1, y, _shadowColor, col2);
-			_screen->printText(str, x1, y + 1, _shadowColor, 0);
-			_screen->printText(str, x1 - 1, y + 1, _shadowColor, 0);
-			// Another hack for Chinese EOBII. Due to the reduced line spacing - while still drawing a shadow for the font - the
-			// lines will overdraw by one pixel if we don't clear the bottom line. This will otherwise cause glitches when doing line feeds.
-			for (int i = 0; i < -_lineSpacing && y + fh + i < sd->sy + sd->h; ++i)
-				_screen->drawClippedLine(x1 - 1, y + fh + i, x1 + lw, y + fh + i, _textDimData[sdx].color2);
-			col2 = 0;
-		}
-		_screen->printText(str, x1, y, col, col2);
+		x1 &= ~3;
+		y = (y + 8) & ~7;
+		col2 = 0;
 	}
 
+	if (_textDimData[sdx].shadowColor) {
+		_screen->printText(str, x1 - 1, y, _textDimData[sdx].shadowColor, col2);
+		_screen->printText(str, x1, y + 1, _textDimData[sdx].shadowColor, 0);
+		_screen->printText(str, x1 - 1, y + 1, _textDimData[sdx].shadowColor, 0);
+		// Another hack for Chinese EOBII. Due to the reduced line spacing - while still drawing a shadow for the font - the
+		// lines will overdraw by one pixel if we don't clear the bottom line. This will otherwise cause glitches when doing line feeds.
+		for (int i = 0; i < -_textDimData[sdx].lineSpacing && y + fh + i < sd->sy + sd->h; ++i)
+			_screen->drawClippedLine(x1 - 1, y + fh + i, x1 + lw, y + fh + i, col2);
+		col2 = 0;
+	}
+	_screen->printText(str, x1, y, col1, col2);
+
 	_textDimData[sdx].column += lw;
 	_numCharsPrinted += strlen(str);
 
-	str[s] = c;
-
-	if (c == ' ')
+	str[s] = lastChr;
+	if (lastChr == ' ')
 		s++;
-
 	if (str[s] == ' ')
 		s++;
 
@@ -568,7 +519,7 @@ void TextDisplayer_rpg::printLine(char *str) {
 	str[len] = 0;
 
 	_numCharsLeft = strlen(str);
-	_lineWidth = sjisTextMode ? (_numCharsLeft << 2) : (_screen->_currentFont == Screen::FID_SJIS_TEXTMODE_FNT ? _numCharsLeft * 9 : _screen->getTextWidth(str));
+	_lineWidth = _screen->getTextWidth(str) + _textDimData[sdx].charSpacing * _numCharsLeft;
 
 	if (!_numCharsLeft && (_textDimData[sdx].column + twoByteCharOffs) <= (sd->w << 3))
 		return;
@@ -620,7 +571,7 @@ void TextDisplayer_rpg::printMessage(const char *str, int textColor, ...) {
 	int tc = _textDimData[_screen->curDimIndex()].color1;
 
 	if (textColor != -1)
-		_textDimData[_screen->curDimIndex()].color1 = textColor;
+		_textDimData[_screen->curDimIndex()].color1= textColor;
 
 	va_list args;
 	va_start(args, textColor);
@@ -630,7 +581,7 @@ void TextDisplayer_rpg::printMessage(const char *str, int textColor, ...) {
 	displayText(_dialogueBuffer, textColor);
 
 	if (_vm->game() != GI_EOB1)
-		_textDimData[_screen->curDimIndex()].color1 = tc;
+		_textDimData[_screen->curDimIndex()].color1= tc;
 
 	if (!_screen->_curPage)
 		_screen->updateScreen();
@@ -639,8 +590,9 @@ void TextDisplayer_rpg::printMessage(const char *str, int textColor, ...) {
 int TextDisplayer_rpg::clearDim(int dim) {
 	int res = _screen->curDimIndex();
 	_screen->setScreenDim(dim);
-	_textDimData[dim].color1 = _colorMap[_screen->_curDim->col1];
-	_textDimData[dim].color2 = (_vm->game() == GI_LOL || _vm->gameFlags().platform == Common::kPlatformAmiga) ? _colorMap[_screen->_curDim->col2] : _vm->guiSettings()->colors.fill;
+	_textDimData[dim].color1= _screen->_curDim->col1;
+	_textDimData[dim].color2 = (_vm->game() == GI_LOL || _vm->gameFlags().platform == Common::kPlatformAmiga) ? _screen->_curDim->col2 : _vm->guiSettings()->colors.fill;
+
 	clearCurDim();
 	return res;
 }
@@ -653,11 +605,9 @@ void TextDisplayer_rpg::clearCurDim() {
 	int wOffs = 0;
 	int hOffs = 0;
 
-	if (_shadowColor) {
+	if (_textDimData[d].shadowColor) {
 		if (tmp->sx > 0)
 			xOffs = wOffs = 1;
-		if (tmp->sy + tmp->h < Screen::SCREEN_H)
-			hOffs = 1;
 	}
 
 	if (_pc98TextMode) {
@@ -665,7 +615,7 @@ void TextDisplayer_rpg::clearCurDim() {
 		--hOffs;
 	}
 
-	_screen->fillRect((tmp->sx << 3) - xOffs, tmp->sy, ((tmp->sx + tmp->w) << 3) - 1 + wOffs, (tmp->sy + tmp->h) - 1 + hOffs, _textDimData[d].color2);
+	_screen->fillRect((tmp->sx << 3) - xOffs, tmp->sy, ((tmp->sx + tmp->w) << 3) - 1 + wOffs, (tmp->sy + tmp->h) - 1 + hOffs, remapColor(d, _textDimData[d].color2));
 
 	_lineCount = 0;
 	_textDimData[d].column = _textDimData[d].line = 0;
@@ -698,7 +648,8 @@ void TextDisplayer_rpg::textPageBreak() {
 	if (_vm->speechEnabled() && _vm->_activeVoiceFileTotalTime && _numCharsTotal)
 		speechPartTime = _vm->_system->getMillis() + ((_numCharsPrinted * _vm->_activeVoiceFileTotalTime) / _numCharsTotal);
 
-	const ScreenDim *dim = _screen->getScreenDim(_screen->curDimIndex());
+	int sdx = _screen->curDimIndex();
+	const ScreenDim *dim = _screen->getScreenDim(sdx);
 
 	int x = ((dim->sx + dim->w) << 3) - (_vm->_dialogueButtonWidth + 3);
 	int y = 0;
@@ -776,9 +727,9 @@ void TextDisplayer_rpg::textPageBreak() {
 
 	_screen->set16bitShadingLevel(4);
 	if (_vm->game() == GI_LOL && _vm->gameFlags().use16ColorMode)
-		_screen->fillRect(x + 8, y, x + 57, y + _vm->guiSettings()->buttons.height, _textDimData[_screen->curDimIndex()].color2);
+		_screen->fillRect(x + 8, y, x + 57, y + _vm->guiSettings()->buttons.height, remapColor(sdx, _textDimData[sdx].color2));
 	else
-		_screen->fillRect(x, y, x + w - 1, y + _vm->guiSettings()->buttons.height - 1, _textDimData[_screen->curDimIndex()].color2);
+		_screen->fillRect(x, y, x + w - 1, y + _vm->guiSettings()->buttons.height - 1, remapColor(sdx, _textDimData[sdx].color2));
 
 	clearCurDim();
 	_screen->set16bitShadingLevel(0);
@@ -858,6 +809,46 @@ void TextDisplayer_rpg::convertString(char *str) {
 	}
 }
 
+bool TextDisplayer_rpg::isTwoByteChar(uint8 c) const {
+	if (_vm->gameFlags().lang == Common::JA_JPN)
+		return (c >= 0xE0 || (c > 0x80 && c < 0xA0));
+	else if (_vm->gameFlags().lang == Common::ZH_TWN)
+		return (c & 0x80);
+	return false;
+}
+
+void TextDisplayer_rpg::applySetting(int sd, int ix, int val) {
+	if (sd < -1 || sd >= _dimCount || ix >= kOutOfRange)
+		error("TextDisplayer_rpg::applySetting(): arg out of range");
+
+	const int *memberAddr[] = {
+		&_textDimData[0].lineSpacing,
+		&_textDimData[0].visualLineSpacingAdjust,
+		&_textDimData[0].charSpacing,
+		&_textDimData[0].shadowColor,
+		&_textDimData[0].noHalfWidthLineEnd
+	};
+
+	int offset = (const byte*)memberAddr[ix] - (const byte*)&_textDimData[0];
+
+	if (sd == -1) {
+		for (int i = 0; i < _dimCount; ++i)
+			*(int*)((byte*)&_textDimData[i] + offset) = val;
+	} else {
+		*(int*)((byte*)&_textDimData[sd] + offset) = val;
+	}
+}
+
+uint8 TextDisplayer_rpg::remapColor(int sd, uint8 color) const {
+	if (sd < -1 || sd >= _dimCount)
+		error("TextDisplayer_rpg::applySetting(): arg out of range");
+
+	if (sd != -1 && _textDimData[sd].colorMap != nullptr)
+		return _textDimData[sd].colorMap[color];
+
+	return _colorMap[color];
+}
+
 } // End of namespace Kyra
 
 #endif // (ENABLE_EOB || ENABLE_LOL)
diff --git a/engines/kyra/text/text_rpg.h b/engines/kyra/text/text_rpg.h
index 9a2f0c264d8..6fa673141e4 100644
--- a/engines/kyra/text/text_rpg.h
+++ b/engines/kyra/text/text_rpg.h
@@ -53,10 +53,17 @@ public:
 
 	void allowPageBreak(bool mode) { _allowPageBreak = mode; }
 	void setWaitButtonMode(int mode) { _waitButtonMode = mode; }
-	int setShadowColor(int col) { SWAP(col, _shadowColor); return col; }
-	int setLineSpacing(int spacing) { SWAP(spacing, _lineSpacing); return spacing; }
 	int lineCount() const { return _lineCount; }
-	const uint8 *colorMap() const { return _colorMap; }
+	//const uint8 *colorMap() const { return _colorMap; }
+
+	// These methods are ScummVM specific. They are supposed to make necessary modifications
+	// to the text displayer for the various Japanese and Chinese versions without too much
+	// hackery...
+	void setColorMapping(int sd, uint8 from, uint8 to);
+	void setShadowColor(int sd, int col) { applySetting(sd, kShadowColor, col); }
+	void setLineSpacing(int sd, int spacing) { applySetting(sd, kLineSpacing, spacing); }
+	void setVisualLineSpacingAdjust(int sd, int adj) { applySetting(sd, kVisualLineSpacingAdjust, adj); }
+	void setCharSpacing(int sd, int spacing) { applySetting(sd, kCharSpacing, spacing); }
 
 protected:
 	virtual KyraRpgEngine *vm() { return _vm; }
@@ -84,7 +91,7 @@ protected:
 	uint32 _numCharsPrinted;
 
 	bool _printFlag;
-	bool _sjisTextModeLineBreak;
+	bool _twoByteLineBreakFlag;
 	const bool _pc98TextMode;
 
 	Common::String _pageBreakString;
@@ -102,17 +109,36 @@ protected:
 		uint8 color2;
 		uint16 column;
 		uint8 line;
+		// These properties don't appear in the original code. The various Japanese and Chinese versions
+		// just had their modifications hacked in in whatever way the devs felt like. These properties
+		// help making the necessary adjustments without too much hackery...
+		int lineSpacing;
+		int visualLineSpacingAdjust; // LOL PC-98 has the worst hack here. The visual line spacing is different than the one that is used to measure the text field space.
+		int charSpacing;
+		int shadowColor;
+		int noHalfWidthLineEnd;
+		uint8 *colorMap;
 	};
 
 	TextDimData *_textDimData;
+	const int _dimCount;
 	KyraRpgEngine *_vm;
 
 private:
-	Screen *_screen;
-	int _lineSpacing;
-	int _shadowColor;
-	bool _preventHalfWidthLineEnd;
+	bool isTwoByteChar(uint8 c) const;
+	void applySetting(int sd, int ix, int val);
+	uint8 remapColor(int sd, uint8 color) const;
+
+	enum TextFieldVar {
+		kLineSpacing = 0,
+		kVisualLineSpacingAdjust,
+		kCharSpacing,
+		kShadowColor,
+		kNoHalfWidthLineEnd,
+		kOutOfRange
+	};
 
+	Screen *_screen;
 	char *_table1;
 	char *_table2;
 


Commit: 8510798d44c9b23d89512fe3b36bd7372cc22d11
    https://github.com/scummvm/scummvm/commit/8510798d44c9b23d89512fe3b36bd7372cc22d11
Author: athrxx (athrxx at scummvm.org)
Date: 2023-07-11T14:35:03+02:00

Commit Message:
KYRA: (EOB II/ZH) - add missing kyradat resources

The kyradat resources should now be complete. I also
added some specific Chinese grammar handling  logic
for magic items.

Changed paths:
    devtools/create_kyradat/resources.cpp
    devtools/create_kyradat/resources/eob2_dos_chinese.h
    dists/engine-data/kyra.dat
    engines/kyra/engine/items_eob.cpp


diff --git a/devtools/create_kyradat/resources.cpp b/devtools/create_kyradat/resources.cpp
index 4eb076c58e0..3967b2fde5b 100644
--- a/devtools/create_kyradat/resources.cpp
+++ b/devtools/create_kyradat/resources.cpp
@@ -3561,7 +3561,7 @@ static const ResourceProvider resourceProviders[] = {
 	{ kEoBBaseSaveThrwLvlIndex, kEoB2, kPlatformDOS, kTalkieVersion, UNK_LANG, &kEoB2SaveThrwLvlIndexDOSProvider },
 	{ kEoBBaseSaveThrwModDiv, kEoB2, kPlatformDOS, kTalkieVersion, UNK_LANG, &kEoB2SaveThrwModDivDOSProvider },
 	{ kEoBBaseSaveThrwModExt, kEoB2, kPlatformDOS, kTalkieVersion, UNK_LANG, &kEoB2SaveThrwModExtDOSProvider },
-	{ kEoBBaseEnchantedString, kEoB2, kPlatformDOS, kTalkieVersion, UNK_LANG, &kEoB2EnchantedStringDOSProvider },
+	{ kEoBBaseEnchantedString, kEoB2, kPlatformDOS, kTalkieVersion, UNK_LANG, &kEoB2EnchantedStringDOSChineseProvider },
 	{ kEoBBaseMenuStringsSpellNo, kEoB2, kPlatformDOS, kTalkieVersion, UNK_LANG, &kEoB2MenuStringsSpellNoDOSProvider },
 	{ kEoBBaseSpellLevelsMage, kEoB2, kPlatformDOS, kTalkieVersion, UNK_LANG, &kEoB2SpellLevelsMageDOSProvider },
 	{ kEoBBaseSpellLevelsCleric, kEoB2, kPlatformDOS, kTalkieVersion, UNK_LANG, &kEoB2SpellLevelsClericDOSProvider },
diff --git a/devtools/create_kyradat/resources/eob2_dos_chinese.h b/devtools/create_kyradat/resources/eob2_dos_chinese.h
index ab21e37060b..944d21c1337 100644
--- a/devtools/create_kyradat/resources/eob2_dos_chinese.h
+++ b/devtools/create_kyradat/resources/eob2_dos_chinese.h
@@ -181,6 +181,12 @@ static const char *const kEoB2CursedStringDOSChinese[1] = {
 
 static const StringListProvider kEoB2CursedStringDOSChineseProvider = { ARRAYSIZE(kEoB2CursedStringDOSChinese), kEoB2CursedStringDOSChinese };
 
+static const char *const kEoB2EnchantedStringDOSChinese[1] = {
+	"+%d%s"
+};
+
+static const StringListProvider kEoB2EnchantedStringDOSChineseProvider = { ARRAYSIZE(kEoB2EnchantedStringDOSChinese), kEoB2EnchantedStringDOSChinese };
+
 static const char *const kEoB2MagicObjectStringsDOSChinese[5] = {
 	"\xa7\xc5\xae\x76", /* "巫師"; */
 	"\xaa\xaa\xae\x76", /* "牧師"; */
@@ -192,37 +198,38 @@ static const char *const kEoB2MagicObjectStringsDOSChinese[5] = {
 static const StringListProvider kEoB2MagicObjectStringsDOSChineseProvider = { ARRAYSIZE(kEoB2MagicObjectStringsDOSChinese), kEoB2MagicObjectStringsDOSChinese };
 
 static const char *const kEoB2MagicObjectString5DOSChinese[1] = {
-	"Stick"
+	"\xac\x50\xa4\xf5\xc4\x5f\xa7\xfa", /* "星火寶杖"; */
 };
 
 static const StringListProvider kEoB2MagicObjectString5DOSChineseProvider = { ARRAYSIZE(kEoB2MagicObjectString5DOSChinese), kEoB2MagicObjectString5DOSChinese };
 
-static const char *const kEoB2PatternSuffixDOSChinese[1] = {
-	"%s of %s"
+static const char *const kEoB2PatternSuffixDOSChinese[2] = {
+	"%s""\xaa\xba""%s""\xaa""k""\xb3""N""\xb1\xb2\xb6""b", /* %s的%s法術捲軸 */
+	"%s""\xaa\xba""%s" /* %sçš„%s */
 };
 
 static const StringListProvider kEoB2PatternSuffixDOSChineseProvider = { ARRAYSIZE(kEoB2PatternSuffixDOSChinese), kEoB2PatternSuffixDOSChinese }; 
 
 static const char *const kEoB2PatternGrFix1DOSChinese[1] = {
-	"%s of %s"
+	"\xa7\xc5\xae""v" /* 巫師 */
 };
 
 static const StringListProvider kEoB2PatternGrFix1DOSChineseProvider = { ARRAYSIZE(kEoB2PatternGrFix1DOSChinese), kEoB2PatternGrFix1DOSChinese };
 
 static const char *const kEoB2PatternGrFix2DOSChinese[1] = {
-	"%s of %s"
+	"\xaa\xaa\xae""v" /* 牧師 */
 };
 
 static const StringListProvider kEoB2PatternGrFix2DOSChineseProvider = { ARRAYSIZE(kEoB2PatternGrFix2DOSChinese), kEoB2PatternGrFix2DOSChinese };
 
 static const char *const kEoB2ValidateArmorStringDOSChinese[1] = {
-	"%s can't wear that type of armor.\r"
+	"%s""\xa4\xa3\xbe""A""\xa6""X""\xac\xef\xc0\xb9""%s.\r", /* %s不適合穿戴%s.\r */
 };
 
 static const StringListProvider kEoB2ValidateArmorStringDOSChineseProvider = { ARRAYSIZE(kEoB2ValidateArmorStringDOSChinese), kEoB2ValidateArmorStringDOSChinese };
 
 static const char *const kEoB2ValidateCursedStringDOSChinese[1] = {
-	"%s cannot release the weapon!  It is cursed!\r"
+	"%s""\xb5""L""\xaa""k""\xa9\xf1\xb6""}""\xb3""o%s,""\xa6""]""\xac\xb0\xa5\xa6\xa4""w""\xb3""Q""\xa4""U""\xb6""A""\xa9""G""\xa4""F!\r" /* %s無法放開這%s,因為它已被下詛咒了!\r */
 };
 
 static const StringListProvider kEoB2ValidateCursedStringDOSChineseProvider = { ARRAYSIZE(kEoB2ValidateCursedStringDOSChinese), kEoB2ValidateCursedStringDOSChinese };
@@ -247,9 +254,9 @@ static const char *const kEoB2WandStringsDOSChinese[1] = {
 static const StringListProvider kEoB2WandStringsDOSChineseProvider = { ARRAYSIZE(kEoB2WandStringsDOSChinese), kEoB2WandStringsDOSChinese };
 
 static const char *const kEoB2ItemMisuseStringsDOSChinese[3] = {
-	"%s can not use this item.\r",
-	"This item automatically used when worn.\r",
-	"This item is not used in this way.\r"
+	"%s\xa4\xa3\xb7""|""\xa8\xcf\xa5\xce\xb3""o""\xb6\xb5\xaa\xab\xab""~.\r", /* %s不會使用這項物品.\r */
+	"\xb3""o""\xaa\xab\xab""~""\xa4""w""\xb8""g""\xb3""Q""\xb8\xcb\xb0""t""\xa6""b""\xa8\xad\xa4""W""\xa8\xcf\xa5\xce\xa4""F.\r", /* 這物品已經被裝配在身上使用了.\r */
+	"\xb3""o""\xb6\xb5\xaa\xab\xab""~""\xa4\xa3\xaf\xe0\xa6""b""\xb3""o""\xb8\xcc\xa8\xcf\xa5\xce"".\r" /* 這項物品不能在這裡使用.\r */
 };
 
 static const StringListProvider kEoB2ItemMisuseStringsDOSChineseProvider = { ARRAYSIZE(kEoB2ItemMisuseStringsDOSChinese), kEoB2ItemMisuseStringsDOSChinese };
@@ -274,14 +281,14 @@ static const char *const kEoB2PotionEffectStringsDOSChinese[8] = {
 static const StringListProvider kEoB2PotionEffectStringsDOSChineseProvider = { ARRAYSIZE(kEoB2PotionEffectStringsDOSChinese), kEoB2PotionEffectStringsDOSChinese };
 
 static const char *const kEoB2YesNoStringsDOSChinese[2] = {
-	"yes",
-	"no"
+	"\xac""O ""\xaa\xba", /* 是 的 */
+	"\xa4\xa3"" ""\xad""n" /* 不 要 */
 };
 
 static const StringListProvider kEoB2YesNoStringsDOSChineseProvider = { ARRAYSIZE(kEoB2YesNoStringsDOSChinese), kEoB2YesNoStringsDOSChinese };
 
 static const char *const kEoB2MoreStringsDOSChinese[1] = {
-	"MORE"
+	"\xc4""~ ""\xc4\xf2" /* 繼 續 */
 };
 
 static const StringListProvider kEoB2MoreStringsDOSChineseProvider = { ARRAYSIZE(kEoB2MoreStringsDOSChinese), kEoB2MoreStringsDOSChinese };
@@ -293,7 +300,7 @@ static const char *const kEoB2NpcMaxStringsDOSChinese[1] = {
 static const StringListProvider kEoB2NpcMaxStringsDOSChineseProvider = { ARRAYSIZE(kEoB2NpcMaxStringsDOSChinese), kEoB2NpcMaxStringsDOSChinese };
 
 static const char *const kEoB2OkStringsDOSChinese[1] = {
-	"OK"
+	"\xa7\xb9\xb2\xa6" /* 完畢 */
 };
 
 static const StringListProvider kEoB2OkStringsDOSChineseProvider = { ARRAYSIZE(kEoB2OkStringsDOSChinese), kEoB2OkStringsDOSChinese };
@@ -305,13 +312,13 @@ static const char *const kEoB2NpcJoinStringsDOSChinese[1] = {
 static const StringListProvider kEoB2NpcJoinStringsDOSChineseProvider = { ARRAYSIZE(kEoB2NpcJoinStringsDOSChinese), kEoB2NpcJoinStringsDOSChinese };
 
 static const char *const kEoB2CancelStringsDOSChinese[1] = {
-	"CANCEL"
+	"\xa7\xb9\xa6\xa8" /* 完成 */
 };
 
 static const StringListProvider kEoB2CancelStringsDOSChineseProvider = { ARRAYSIZE(kEoB2CancelStringsDOSChinese), kEoB2CancelStringsDOSChinese };
 
 static const char *const kEoB2AbortStringsDOSChinese[1] = {
-	"ABORT"
+	"\xa9\xf1"" ""\xb1\xf3" /* 放 棄 */
 };
 
 static const StringListProvider kEoB2AbortStringsDOSChineseProvider = { ARRAYSIZE(kEoB2AbortStringsDOSChinese), kEoB2AbortStringsDOSChinese };
diff --git a/dists/engine-data/kyra.dat b/dists/engine-data/kyra.dat
index f14d95d1967..7a2a15fbebb 100644
Binary files a/dists/engine-data/kyra.dat and b/dists/engine-data/kyra.dat differ
diff --git a/engines/kyra/engine/items_eob.cpp b/engines/kyra/engine/items_eob.cpp
index 008476006a9..a2a9949a68a 100644
--- a/engines/kyra/engine/items_eob.cpp
+++ b/engines/kyra/engine/items_eob.cpp
@@ -245,7 +245,7 @@ int EoBCoreEngine::validateInventorySlotForItem(Item item, int charIndex, int sl
 		return 1;
 
 	if (slot == 17 && item && !itemUsableByCharacter(charIndex, item)) {
-		_txt->printMessage(_validateArmorString[0], -1, _characters[charIndex].name);
+		_txt->printMessage(_validateArmorString[0], -1, _characters[charIndex].name, _itemNames[_items[item].nameUnid]);
 		return 0;
 	}
 
@@ -254,7 +254,7 @@ int EoBCoreEngine::validateInventorySlotForItem(Item item, int charIndex, int sl
 
 	if (_items[itm].flags & 0x20 && (_flags.gameID == GI_EOB1 || slot < 2)) {
 		if (_flags.gameID == GI_EOB2 && ex > 0 && ex < 4)
-			_txt->printMessage(_validateCursedString[0], -1, _characters[charIndex].name);
+			_txt->printMessage(_validateCursedString[0], -1, _characters[charIndex].name, _itemNames[_items[item].nameUnid]);
 		return 0;
 	}
 
@@ -477,10 +477,14 @@ void EoBCoreEngine::printFullItemName(Item item) {
 			break;
 		}
 
-
 		if (tstr3) {
 			if (!tstr2) {
 				tmpString = tstr3;
+			} else if (_flags.lang == Common::ZH_TWN) {
+				if (!correctSuffixCase)
+					SWAP(tstr2, tstr3);
+				Common::String t2(tstr2);
+				tmpString = Common::String::format(_patternSuffix[t2.contains(_patternGrFix1[0]) || t2.contains(_patternGrFix2[0]) ? 0 : 1], tstr2, tstr3);
 			} else {
 				if (_flags.lang == Common::JA_JPN)
 					SWAP(tstr2, tstr3);


Commit: 6e9f7abc7becd04131126b36ae5639e2c2b585ac
    https://github.com/scummvm/scummvm/commit/6e9f7abc7becd04131126b36ae5639e2c2b585ac
Author: athrxx (athrxx at scummvm.org)
Date: 2023-07-11T14:35:10+02:00

Commit Message:
KYRA: (EOB II/ZH) - fix dialogue buttons

I have tested only the dialgue sequences in the forest
and in the temple entrance hall, but these now look
accurate when comparing to DOSBox

Changed paths:
    engines/kyra/engine/darkmoon.cpp
    engines/kyra/engine/darkmoon.h
    engines/kyra/engine/eobcommon.cpp
    engines/kyra/engine/kyra_rpg.cpp
    engines/kyra/engine/kyra_rpg.h
    engines/kyra/gui/gui_rpg.cpp
    engines/kyra/resource/staticres_eob.cpp
    engines/kyra/resource/staticres_lol.cpp
    engines/kyra/text/text_rpg.cpp


diff --git a/engines/kyra/engine/darkmoon.cpp b/engines/kyra/engine/darkmoon.cpp
index b772ccc2387..524c2c08e64 100644
--- a/engines/kyra/engine/darkmoon.cpp
+++ b/engines/kyra/engine/darkmoon.cpp
@@ -794,6 +794,8 @@ const KyraRpgGUISettings *DarkMoonEngine::guiSettings() const {
 		return &_guiSettingsFMTowns;
 	else if (_flags.platform == Common::kPlatformPC98)
 		return &_guiSettingsPC98;
+	else if (_flags.lang == Common::ZH_TWN)
+		return &_guiSettingsDOS_ZH;
 	else
 		return &_guiSettingsDOS;
 }
diff --git a/engines/kyra/engine/darkmoon.h b/engines/kyra/engine/darkmoon.h
index 372af40ee50..d6f22666c85 100644
--- a/engines/kyra/engine/darkmoon.h
+++ b/engines/kyra/engine/darkmoon.h
@@ -149,10 +149,14 @@ private:
 	const char *const *_utilMenuStrings;
 
 	static const KyraRpgGUISettings _guiSettingsDOS;
+	static const KyraRpgGUISettings _guiSettingsDOS_ZH;
 	static const KyraRpgGUISettings _guiSettingsFMTowns;
 	static const KyraRpgGUISettings _guiSettingsPC98;
 	static const KyraRpgGUISettings _guiSettingsAmiga;
 	static const uint8 _egaDefaultPalette[];
+
+	static const uint16 _dlgButtonPosX_ZH[17];
+	static const uint8 _dlgButtonPosY_ZH[17];
 };
 
 } // End of namespace Kyra
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 7f741d196a9..8c15d1b19a4 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -1574,8 +1574,16 @@ void EoBCoreEngine::setupDialogueButtons(int presetfirst, int numStr, va_list &a
 
 	_dialogueButtonPosX = &guiSettings()->buttons.posX[presetfirst];
 	_dialogueButtonPosY = &guiSettings()->buttons.posY[presetfirst];
-	_dialogueButtonXoffs = (_flags.platform == Common::kPlatformSegaCD) ? 8 : 0;
-	_dialogueButtonYoffs = (_flags.platform == Common::kPlatformSegaCD) ? 160 : yOffs;
+	_dialogueButtonXoffs = 0;
+
+	if (_flags.lang == Common::ZH_TWN) {
+		_dialogueButtonYoffs = numStr > 3 ? 166 : 184;
+	} else if (_flags.platform == Common::kPlatformSegaCD) {
+		_dialogueButtonXoffs = 8;
+		_dialogueButtonYoffs =  160;
+	} else {
+		_dialogueButtonYoffs = yOffs;
+	}
 
 	drawDialogueButtons();
 
diff --git a/engines/kyra/engine/kyra_rpg.cpp b/engines/kyra/engine/kyra_rpg.cpp
index 1e40a8fdc3d..54897612050 100644
--- a/engines/kyra/engine/kyra_rpg.cpp
+++ b/engines/kyra/engine/kyra_rpg.cpp
@@ -267,8 +267,13 @@ void KyraRpgEngine::drawDialogueButtons() {
 			screen()->set16bitShadingLevel(4);
 			gui_drawBox(x, (_dialogueButtonYoffs + _dialogueButtonPosY[i]), _dialogueButtonWidth, guiSettings()->buttons.height, guiSettings()->colors.frame1, guiSettings()->colors.frame2, guiSettings()->colors.fill);
 			screen()->set16bitShadingLevel(0);
-			screen()->printText(_dialogueButtonString[i], x + (_dialogueButtonWidth >> 1) - (screen()->getTextWidth(_dialogueButtonString[i])) / 2,
-			                    (_dialogueButtonYoffs + _dialogueButtonPosY[i]) + yOffset, _dialogueHighlightedButton == i ? _dialogueButtonLabelColor1 : _dialogueButtonLabelColor2, 0);
+			if (guiSettings()->buttons.labelShadow && _flags.gameID != GI_LOL) {
+				((Screen_EoB*)screen())->printShadedText(_dialogueButtonString[i], x + (_dialogueButtonWidth >> 1) - (screen()->getTextWidth(_dialogueButtonString[i])) / 2,
+					(_dialogueButtonYoffs + _dialogueButtonPosY[i]) + yOffset, _dialogueHighlightedButton == i ? _dialogueButtonLabelColor1 : _dialogueButtonLabelColor2, 0, guiSettings()->colors.guiColorBlack);
+			} else {
+				screen()->printText(_dialogueButtonString[i], x + (_dialogueButtonWidth >> 1) - (screen()->getTextWidth(_dialogueButtonString[i])) / 2,
+					(_dialogueButtonYoffs + _dialogueButtonPosY[i]) + yOffset, _dialogueHighlightedButton == i ? _dialogueButtonLabelColor1 : _dialogueButtonLabelColor2, 0);
+			}
 		}
 	}
 	if (cs != -1)
diff --git a/engines/kyra/engine/kyra_rpg.h b/engines/kyra/engine/kyra_rpg.h
index e5482b05b49..e759ab34143 100644
--- a/engines/kyra/engine/kyra_rpg.h
+++ b/engines/kyra/engine/kyra_rpg.h
@@ -92,6 +92,7 @@ struct KyraRpgGUISettings {
 		const uint8 *posY;
 		uint8 labelColor1;
 		uint8 labelColor2;
+		bool labelShadow;
 		uint16 width;
 		uint16 height;
 		int16 txtOffsY;
diff --git a/engines/kyra/gui/gui_rpg.cpp b/engines/kyra/gui/gui_rpg.cpp
index 96a3cba6755..74351f80040 100644
--- a/engines/kyra/gui/gui_rpg.cpp
+++ b/engines/kyra/gui/gui_rpg.cpp
@@ -41,6 +41,7 @@ void KyraRpgEngine::removeInputTop() {
 void KyraRpgEngine::gui_drawBox(int x, int y, int w, int h, int frameColor1, int frameColor2, int fillColor) {
 	w--;
 	h--;
+
 	if (fillColor != -1)
 		screen()->fillRect(x + 1, y + 1, x + w - 1, y + h - 1, fillColor);
 
diff --git a/engines/kyra/resource/staticres_eob.cpp b/engines/kyra/resource/staticres_eob.cpp
index 585e082dd6e..1c17fc6163e 100644
--- a/engines/kyra/resource/staticres_eob.cpp
+++ b/engines/kyra/resource/staticres_eob.cpp
@@ -1663,7 +1663,7 @@ void EoBEngine::initSpells() {
 }
 
 const KyraRpgGUISettings EoBEngine::_guiSettingsVGA = {
-	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 9, 15, 95, 9, 2, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
+	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 9, 15, false, 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 },
@@ -1674,7 +1674,7 @@ const KyraRpgGUISettings EoBEngine::_guiSettingsVGA = {
 };
 
 const KyraRpgGUISettings EoBEngine::_guiSettingsEGA = {
-	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 9, 15, 95, 9, 2, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
+	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 9, 15, false, 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 },
@@ -1685,7 +1685,7 @@ const KyraRpgGUISettings EoBEngine::_guiSettingsEGA = {
 };
 
 const KyraRpgGUISettings EoBEngine::_guiSettingsPC98 = {
-	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 9, 15, 95, 11, 1, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
+	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 9, 15, false, 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 },
@@ -1696,7 +1696,7 @@ const KyraRpgGUISettings EoBEngine::_guiSettingsPC98 = {
 };
 
 const KyraRpgGUISettings EoBEngine::_guiSettingsAmiga = {
-	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 28, 31, 95, 9, 2, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
+	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 28, 31, false, 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 },
@@ -1707,7 +1707,7 @@ const KyraRpgGUISettings EoBEngine::_guiSettingsAmiga = {
 };
 
 const KyraRpgGUISettings EoBEngine::_guiSettingsAmigaMainMenu = {
-	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 28, 31, 95, 9, 2, 7, { 285, 139 }, { 189, 162 }, { 31, 31 } },
+	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 28, 31, false, 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 },
@@ -1718,7 +1718,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 } },
+	{ _dlgButtonPosX_Sega, _dlgButtonPosY_Sega, 0x66, 0xFF, false, 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, 0x3C },
 	{	{ 184, 256, -1}, { 1, 57, 113 }, 64, 55,
 		{ 8, 80, -1 }, { 16, 72, 128 }, { 184, -1, -1 }, { 8, -1, -1 },
@@ -1939,7 +1939,7 @@ void DarkMoonEngine::initSpells() {
 }
 
 const KyraRpgGUISettings DarkMoonEngine::_guiSettingsFMTowns = {
-	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 9, 15, 95, 11, 1, 7, { 221, 76 }, { 187, 162 }, { 95, 95 } },
+	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 9, 15, false, 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 },
@@ -1950,7 +1950,7 @@ const KyraRpgGUISettings DarkMoonEngine::_guiSettingsFMTowns = {
 };
 
 const KyraRpgGUISettings DarkMoonEngine::_guiSettingsPC98 = {
-	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 9, 15, 95, 11, 2, 7, { 221, 76 }, { 189, 162 }, { 95, 95 } },
+	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 9, 15, false, 95, 11, 2, 7, { 221, 76 }, { 189, 162 }, { 95, 95 } },
 	{ 186, 181, 183, 183, 184, 17, 23, 20, 186, 181, 183, 182, 177, 180, 15, 6, 8, 9, 2, 5, 4, 3, 12 },
 	{	{ 184, 256, -1}, { 2, 54, 106 }, 64, 50,
 		{ 8, 80, -1 }, { 11, 63, 115 }, { 181, -1, -1 }, { 3, -1, -1 },
@@ -1961,7 +1961,18 @@ const KyraRpgGUISettings DarkMoonEngine::_guiSettingsPC98 = {
 };
 
 const KyraRpgGUISettings DarkMoonEngine::_guiSettingsDOS = {
-	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 9, 15, 95, 9, 2, 7, { 221, 76 }, { 189, 162 }, { 95, 95 } },
+	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 9, 15, false, 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 },
+		{ 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_ZH = {
+	{ _dlgButtonPosX_ZH, _dlgButtonPosY_ZH, 9, 15, true, 69, 16, 1, 7, { 246, 86 }, { 184, 184 }, { 69, 69 } },
 	{ 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 },
@@ -1972,7 +1983,7 @@ const KyraRpgGUISettings DarkMoonEngine::_guiSettingsDOS = {
 };
 
 const KyraRpgGUISettings DarkMoonEngine::_guiSettingsAmiga = {
-	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 28, 31, 95, 9, 2, 7, { 221, 76 }, { 189, 162 }, { 95, 95 } },
+	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 28, 31, false, 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 },
@@ -1982,6 +1993,10 @@ const KyraRpgGUISettings DarkMoonEngine::_guiSettingsAmiga = {
 	}
 };
 
+const uint16 DarkMoonEngine::_dlgButtonPosX_ZH[17] = { 62, 192, 6, 86, 166, 246, 6, 86, 166, 246, 168, 250, 32, 174, 248, 0 };
+
+const uint8 DarkMoonEngine::_dlgButtonPosY_ZH[17] = { 0, 0, 0, 0, 0, 0, 18, 18, 18, 18, 36, 36, 36, 36, 54, 54, 54 };
+
 const uint8 DarkMoonEngine::_egaDefaultPalette[] = {
 	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
 };
diff --git a/engines/kyra/resource/staticres_lol.cpp b/engines/kyra/resource/staticres_lol.cpp
index cb9244512d1..80e49f4bae0 100644
--- a/engines/kyra/resource/staticres_lol.cpp
+++ b/engines/kyra/resource/staticres_lol.cpp
@@ -805,7 +805,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 = {
-	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 144, 254, 74, 9, 2, 80, { 0, 0 }, { 0, 0 }, { 0, 0 } },
+	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 144, 254, false, 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 },
@@ -816,7 +816,7 @@ const KyraRpgGUISettings LoLEngine::_guiSettings = {
 };
 
 const KyraRpgGUISettings LoLEngine::_guiSettingsZH = {
-	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 144, 254, 68, 18, 2, 66, { 0, 0 }, { 0, 0 }, { 0, 0 } },
+	{ _dlgButtonPosX_Def, _dlgButtonPosY_Def, 144, 254, false, 68, 18, 2, 66, { 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/text/text_rpg.cpp b/engines/kyra/text/text_rpg.cpp
index f4e056a33a8..c46ca96ee94 100644
--- a/engines/kyra/text/text_rpg.cpp
+++ b/engines/kyra/text/text_rpg.cpp
@@ -126,12 +126,15 @@ void TextDisplayer_rpg::setColorMapping(int sd, uint8 from, uint8 to) {
 	if (sd == -1) {
 		for (int i = 0; i < _dimCount; ++i) {
 			delete[] _textDimData[i].colorMap;
-			_textDimData[i].colorMap = 0;
+			_textDimData[i].colorMap = nullptr;
 		}
 		_colorMap[from] = to;
 	} else {
-		if (_textDimData[sd].colorMap == nullptr)
+		if (_textDimData[sd].colorMap == nullptr) {
 			_textDimData[sd].colorMap = new uint8[256];
+			for (int i = 0; i < 256; ++i)
+				_textDimData[sd].colorMap[i] = i;
+		}
 		_textDimData[sd].colorMap[from] = to;
 	}
 }
@@ -334,6 +337,11 @@ void TextDisplayer_rpg::printLine(char *str) {
 	int fh = _screen->getFontHeight() + _screen->_lineSpacing + _textDimData[sdx].lineSpacing;
 	int lines = (sd->h - _screen->_lineSpacing) / fh;
 
+	// Another hack for Chinese EOB II...The original prints text at the very bottom of the text field,
+	// even if there is a good risk of printing text over the dialogue buttons.
+	if (_isChinese && _allowPageBreak)
+		++lines;
+
 	while (_textDimData[sdx].line >= lines) {
 		if ((lines - _waitButtonSpace) <= _lineCount && _allowPageBreak) {
 			_lineCount = 0;
@@ -656,7 +664,7 @@ void TextDisplayer_rpg::textPageBreak() {
 	int w = _vm->_dialogueButtonWidth;
 
 	if (_vm->game() == GI_LOL) {
-		if (_vm->gameFlags().lang == Common::Language::ZH_TWN) {
+		if (_isChinese) {
 			y = dim->sy + dim->h - 15;
 		} else if (_vm->_needSceneRestore && (_vm->_updateFlags & 2)) {
 			if (_vm->_currentControlMode || !(_vm->_updateFlags & 2)) {
@@ -681,7 +689,10 @@ void TextDisplayer_rpg::textPageBreak() {
 		_screen->set16bitShadingLevel(4);
 		_vm->gui_drawBox(x, y, w, _vm->guiSettings()->buttons.height, _vm->guiSettings()->colors.frame1, _vm->guiSettings()->colors.frame2, _vm->guiSettings()->colors.fill);
 		_screen->set16bitShadingLevel(0);
-		_screen->printText(_pageBreakString.c_str(), x + (w >> 1) - (_vm->screen()->getTextWidth(_pageBreakString.c_str()) >> 1), y + _vm->guiSettings()->buttons.txtOffsY, _vm->_dialogueButtonLabelColor1, 0);
+		if (_vm->guiSettings()->buttons.labelShadow && _vm->game() != GI_LOL)
+			((Screen_EoB*)screen())->printShadedText(_pageBreakString.c_str(), x + (w >> 1) - (_vm->screen()->getTextWidth(_pageBreakString.c_str()) >> 1), y + _vm->guiSettings()->buttons.txtOffsY, _vm->_dialogueButtonLabelColor1, 0, _vm->guiSettings()->colors.guiColorBlack);
+		else
+			_screen->printText(_pageBreakString.c_str(), x + (w >> 1) - (_vm->screen()->getTextWidth(_pageBreakString.c_str()) >> 1), y + _vm->guiSettings()->buttons.txtOffsY, _vm->_dialogueButtonLabelColor1, 0);
 	}
 
 	_vm->removeInputTop();
@@ -731,6 +742,10 @@ void TextDisplayer_rpg::textPageBreak() {
 	else
 		_screen->fillRect(x, y, x + w - 1, y + _vm->guiSettings()->buttons.height - 1, remapColor(sdx, _textDimData[sdx].color2));
 
+	// Fix border overdraw glitch
+	if (_vm->game() == GI_EOB2 && _isChinese && y + _vm->guiSettings()->buttons.height == 200)
+		_screen->drawClippedLine(x, 199, x + w - 1, 199, _vm->guiSettings()->colors.frame1);
+
 	clearCurDim();
 	_screen->set16bitShadingLevel(0);
 	_screen->updateScreen();
@@ -784,7 +799,7 @@ void TextDisplayer_rpg::displayWaitButton() {
 	clearCurDim();
 	_screen->set16bitShadingLevel(0);
 	_screen->updateScreen();
-	_vm->_dialogueButtonWidth = 95;
+	_vm->_dialogueButtonWidth = _vm->guiSettings()->buttons.width;
 	SWAP(_vm->_dialogueButtonLabelColor1, _vm->_dialogueButtonLabelColor2);
 }
 
@@ -812,7 +827,7 @@ void TextDisplayer_rpg::convertString(char *str) {
 bool TextDisplayer_rpg::isTwoByteChar(uint8 c) const {
 	if (_vm->gameFlags().lang == Common::JA_JPN)
 		return (c >= 0xE0 || (c > 0x80 && c < 0xA0));
-	else if (_vm->gameFlags().lang == Common::ZH_TWN)
+	else if (_isChinese)
 		return (c & 0x80);
 	return false;
 }




More information about the Scummvm-git-logs mailing list