2007-05-30 21:56:52 +00:00
|
|
|
/* ScummVM - Graphic Adventure Engine
|
|
|
|
*
|
|
|
|
* ScummVM is the legal property of its developers, whose names
|
|
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
|
|
* file distributed with this source distribution.
|
2001-10-09 14:30:12 +00:00
|
|
|
*
|
2021-12-26 18:47:58 +01:00
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
2014-02-18 02:34:24 +01:00
|
|
|
*
|
2001-10-09 14:30:12 +00:00
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
2014-02-18 02:34:24 +01:00
|
|
|
*
|
2001-10-09 14:30:12 +00:00
|
|
|
* You should have received a copy of the GNU General Public License
|
2021-12-26 18:47:58 +01:00
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2001-10-09 14:30:12 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2007-09-19 08:40:12 +00:00
|
|
|
|
2003-12-27 15:22:59 +00:00
|
|
|
|
|
|
|
#include "common/config-manager.h"
|
2016-05-21 13:55:38 +03:00
|
|
|
#include "audio/mixer.h"
|
2003-12-27 15:22:59 +00:00
|
|
|
|
2003-10-03 18:33:57 +00:00
|
|
|
#include "scumm/actor.h"
|
|
|
|
#include "scumm/charset.h"
|
|
|
|
#include "scumm/dialogs.h"
|
2006-03-03 15:16:02 +00:00
|
|
|
#include "scumm/file.h"
|
2021-11-15 15:05:18 -08:00
|
|
|
#include "scumm/imuse_digi/dimuse_engine.h"
|
2008-05-06 03:00:26 +00:00
|
|
|
#ifdef ENABLE_HE
|
2006-02-15 00:57:50 +00:00
|
|
|
#include "scumm/he/intern_he.h"
|
2020-12-07 18:27:18 +01:00
|
|
|
#include "scumm/he/localizer.h"
|
2006-02-04 12:11:10 +00:00
|
|
|
#endif
|
2011-05-11 16:03:50 +02:00
|
|
|
#include "scumm/resource.h"
|
2009-03-20 16:33:58 +00:00
|
|
|
#include "scumm/scumm.h"
|
2009-03-20 22:33:45 +00:00
|
|
|
#include "scumm/scumm_v6.h"
|
2021-08-25 20:20:58 +02:00
|
|
|
#include "scumm/scumm_v7.h"
|
2003-10-03 18:33:57 +00:00
|
|
|
#include "scumm/verbs.h"
|
2006-04-23 18:52:39 +00:00
|
|
|
#include "scumm/he/sound_he.h"
|
2001-10-09 14:30:12 +00:00
|
|
|
|
2020-10-28 23:03:23 +09:00
|
|
|
#include "scumm/ks_check.h"
|
|
|
|
|
2003-10-03 18:33:57 +00:00
|
|
|
namespace Scumm {
|
|
|
|
|
2006-01-10 00:34:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
#pragma mark --- "High level" message code ---
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
2004-09-18 20:50:15 +00:00
|
|
|
void ScummEngine::printString(int m, const byte *msg) {
|
|
|
|
switch (m) {
|
|
|
|
case 0:
|
2021-07-25 13:54:14 +02:00
|
|
|
// WORKAROUND bug #12734: The script tries to clear the currently
|
|
|
|
// displayed message after Rapp gives you the map, but that means
|
|
|
|
// you'll never see Guybrush's reaction to finding a map piece.
|
|
|
|
//
|
|
|
|
// It's a bit hard to pin down the exact case, since it happens
|
|
|
|
// at a few different points during the script. We limit it to
|
|
|
|
// when the player has the map piece.
|
|
|
|
//
|
|
|
|
// We have to do it here, because we don't want to delay the
|
|
|
|
// animation of Rapp turning back to Ashes.
|
|
|
|
if (_game.id == GID_MONKEY2 && _roomResource == 19 &&
|
|
|
|
vm.slot[_currentScript].number == 203 &&
|
|
|
|
_actorToPrintStrFor == 255 && strcmp((const char *)msg, " ") == 0 &&
|
2022-03-17 16:52:31 +01:00
|
|
|
getOwner(200) == VAR(VAR_EGO) && VAR(VAR_HAVE_MSG) &&
|
2022-03-20 10:50:47 +01:00
|
|
|
_enableEnhancements) {
|
2021-07-25 13:54:14 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-04-03 09:44:34 +02:00
|
|
|
// WORKAROUND bug #13378: In the German CD version, Sam's
|
|
|
|
// reactions to Max beating up the scientist run much too quick
|
|
|
|
// for the animation to match. We get around this by slowing
|
|
|
|
// down that animation.
|
2022-04-01 17:36:51 +02:00
|
|
|
//
|
2022-04-03 12:27:39 +02:00
|
|
|
// In the italian CD version, the whole scene is sped up to
|
|
|
|
// keep up with Sam's speech. We compensate for this by slowing
|
|
|
|
// down the other animations.
|
2022-03-31 16:01:25 +02:00
|
|
|
if (_game.id == GID_SAMNMAX && vm.slot[_currentScript].number == 65 && _enableEnhancements) {
|
2022-04-01 17:36:51 +02:00
|
|
|
Actor *a;
|
|
|
|
|
2022-04-03 09:44:34 +02:00
|
|
|
if (_language == Common::DE_DEU && strcmp(_game.variant, "Floppy") != 0) {
|
2022-03-31 16:01:25 +02:00
|
|
|
if (memcmp(msg + 16, "Ohh!", 4) == 0) {
|
2022-04-01 17:36:51 +02:00
|
|
|
a = derefActorSafe(2, "printString");
|
2022-03-31 16:01:25 +02:00
|
|
|
if (a)
|
|
|
|
a->setAnimSpeed(3);
|
|
|
|
}
|
2022-04-03 12:27:39 +02:00
|
|
|
} else if (_language == Common::IT_ITA && strcmp(_game.variant, "Floppy") != 0) {
|
2022-04-01 17:36:51 +02:00
|
|
|
if (memcmp(msg + 16, "Ooh.", 4) == 0) {
|
|
|
|
a = derefActorSafe(3, "printString");
|
|
|
|
if (a)
|
|
|
|
a->setAnimSpeed(2);
|
|
|
|
a = derefActorSafe(10, "printString");
|
2022-07-03 18:30:47 +02:00
|
|
|
if (a)
|
2022-04-01 17:36:51 +02:00
|
|
|
a->setAnimSpeed(2);
|
|
|
|
}
|
2022-03-31 16:01:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-09-18 20:50:15 +00:00
|
|
|
actorTalk(msg);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
drawString(1, msg);
|
|
|
|
break;
|
|
|
|
case 2:
|
2005-06-13 07:09:12 +00:00
|
|
|
debugMessage(msg);
|
2004-09-18 20:50:15 +00:00
|
|
|
break;
|
|
|
|
case 3:
|
2004-11-22 22:32:46 +00:00
|
|
|
showMessageDialog(msg);
|
2004-09-18 20:50:15 +00:00
|
|
|
break;
|
2019-12-23 11:56:16 +00:00
|
|
|
default:
|
|
|
|
break;
|
2004-09-18 20:50:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-06-13 07:09:12 +00:00
|
|
|
void ScummEngine::debugMessage(const byte *msg) {
|
2005-05-07 09:53:06 +00:00
|
|
|
byte buffer[500];
|
2005-05-26 14:14:32 +00:00
|
|
|
convertMessageToString(msg, buffer, sizeof(buffer));
|
2001-10-26 17:34:50 +00:00
|
|
|
|
2003-04-22 16:46:33 +00:00
|
|
|
if ((buffer[0] != 0xFF) && _debugMode) {
|
|
|
|
debug(0, "DEBUG: %s", buffer);
|
2002-12-28 12:22:39 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
if (buffer[0] == 0xFF && buffer[1] == 10) {
|
|
|
|
uint32 a, b;
|
2004-08-12 04:19:47 +00:00
|
|
|
int channel = 0;
|
2001-11-05 19:21:49 +00:00
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
a = buffer[2] | (buffer[3] << 8) | (buffer[6] << 16) | (buffer[7] << 24);
|
2002-07-07 20:25:23 +00:00
|
|
|
b = buffer[10] | (buffer[11] << 8) | (buffer[14] << 16) | (buffer[15] << 24);
|
2002-09-16 08:22:05 +00:00
|
|
|
|
|
|
|
// Sam and Max uses a caching system, printing empty messages
|
2021-06-07 17:44:05 -06:00
|
|
|
// and setting VAR_V6_SOUNDMODE beforehand. See patch #8051.
|
2006-02-20 16:51:30 +00:00
|
|
|
if (_game.id == GID_SAMNMAX)
|
2004-08-12 04:19:47 +00:00
|
|
|
channel = VAR(VAR_V6_SOUNDMODE);
|
|
|
|
|
2018-06-04 06:41:51 +02:00
|
|
|
if (channel != 2)
|
2004-08-13 01:02:24 +00:00
|
|
|
_sound->talkSound(a, b, 1, channel);
|
2003-06-09 00:35:32 +00:00
|
|
|
}
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
|
|
|
|
2004-11-22 22:32:46 +00:00
|
|
|
void ScummEngine::showMessageDialog(const byte *msg) {
|
2004-02-22 14:48:13 +00:00
|
|
|
// Original COMI used different code at this point.
|
|
|
|
// Seemed to use blastText for the messages
|
2005-05-07 09:53:06 +00:00
|
|
|
byte buf[500];
|
2001-10-09 14:30:12 +00:00
|
|
|
|
2005-05-26 14:14:32 +00:00
|
|
|
convertMessageToString(msg, buf, sizeof(buf));
|
2001-10-09 14:30:12 +00:00
|
|
|
|
2002-09-22 01:17:53 +00:00
|
|
|
if (_string[3].color == 0)
|
|
|
|
_string[3].color = 4;
|
2001-10-09 14:30:12 +00:00
|
|
|
|
2020-07-04 03:13:05 +05:30
|
|
|
InfoDialog dialog(this, Common::U32String((char *)buf));
|
2003-09-11 10:32:15 +00:00
|
|
|
VAR(VAR_KEYPRESS) = runDialog(dialog);
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
|
|
|
|
2006-01-10 00:34:13 +00:00
|
|
|
#pragma mark -
|
|
|
|
#pragma mark --- Core message/subtitle code ---
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
2005-11-02 21:47:44 +00:00
|
|
|
bool ScummEngine::handleNextCharsetCode(Actor *a, int *code) {
|
2002-09-11 13:28:34 +00:00
|
|
|
uint32 talk_sound_a = 0;
|
|
|
|
uint32 talk_sound_b = 0;
|
2005-11-19 10:45:41 +00:00
|
|
|
int color, frme, c = 0, oldy;
|
2005-11-02 21:47:44 +00:00
|
|
|
bool endLoop = false;
|
|
|
|
byte *buffer = _charsetBuffer + _charsetBufPos;
|
|
|
|
while (!endLoop) {
|
|
|
|
c = *buffer++;
|
2006-02-20 16:51:30 +00:00
|
|
|
if (!(c == 0xFF || (_game.version <= 6 && c == 0xFE))) {
|
2005-11-02 21:47:44 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
c = *buffer++;
|
2008-04-28 15:21:36 +00:00
|
|
|
|
2008-08-02 21:36:08 +00:00
|
|
|
if (_newLineCharacter != 0 && c == _newLineCharacter) {
|
2008-04-28 15:21:36 +00:00
|
|
|
c = 13;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2005-11-02 21:47:44 +00:00
|
|
|
switch (c) {
|
|
|
|
case 1:
|
|
|
|
c = 13; // new line
|
2015-06-09 11:58:59 +10:00
|
|
|
_msgCount = _screenWidth;
|
2005-11-02 21:47:44 +00:00
|
|
|
endLoop = true;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
_haveMsg = 0;
|
|
|
|
_keepText = true;
|
|
|
|
endLoop = true;
|
|
|
|
break;
|
|
|
|
case 3:
|
2022-03-22 14:15:49 +01:00
|
|
|
_haveMsg = _game.version == 7 && !(_game.id == GID_FT && _game.features & GF_DEMO) ? 1 : 0xFF;
|
2005-11-02 21:47:44 +00:00
|
|
|
_keepText = false;
|
2015-06-09 11:58:59 +10:00
|
|
|
_msgCount = 0;
|
2005-11-02 21:47:44 +00:00
|
|
|
endLoop = true;
|
2022-03-31 16:01:25 +02:00
|
|
|
|
|
|
|
// WORKAROUND bug #13378: Some of the speech is badly
|
|
|
|
// synced to the subtitles, particularly in the
|
|
|
|
// localized versions. This happens because a single
|
|
|
|
// speech line is used for a text that's broken up by
|
|
|
|
// one or more embedded "wait" codes. Rather than
|
|
|
|
// relying on the calculated talk delay, hard-code
|
|
|
|
// better ones.
|
|
|
|
if (_game.id == GID_SAMNMAX && _enableEnhancements && isScriptRunning(65)) {
|
|
|
|
typedef struct {
|
|
|
|
const char *str;
|
|
|
|
const int16 talkDelay;
|
|
|
|
const byte action;
|
|
|
|
} TimingAdjustment;
|
|
|
|
|
|
|
|
TimingAdjustment *adjustments;
|
|
|
|
int numAdjustments;
|
|
|
|
|
|
|
|
// We identify the broken up strings that need
|
|
|
|
// adjustment by the upcoming text.
|
|
|
|
|
2022-04-01 19:58:19 +02:00
|
|
|
TimingAdjustment timingAdjustmentsEN[] = {
|
|
|
|
{ "It's just that", 100, 0 },
|
|
|
|
{ "you're TOO nice", 90, 0 },
|
|
|
|
{ "^unpredictable.", 170, 0 },
|
|
|
|
{ "Yikes!", 120, 0 },
|
|
|
|
{ "Huh?", 90, 0 },
|
|
|
|
{ "Why do you", 110, 0 },
|
|
|
|
{ "Maybe we can", 75, 0 },
|
|
|
|
{ "Mind if I drive?", 160, 0 }
|
|
|
|
};
|
|
|
|
|
2022-04-03 09:44:34 +02:00
|
|
|
TimingAdjustment timingAdjustmentsDEFloppy[] = {
|
|
|
|
{ "Und daf\x81r^", 110, 0 },
|
|
|
|
{ "Es ist blo\xe1^", 120, 0 },
|
|
|
|
{ "Hey.", 50, 0 },
|
|
|
|
{ "Klasse Schlag!", 30, 0 },
|
|
|
|
{ "Uiii!", 80, 0 },
|
|
|
|
{ "H\x84h?", 60, 0 },
|
|
|
|
{ "Kann ich seine", 110, 0 },
|
|
|
|
{ "Warum, glaubst", 110, 0 },
|
|
|
|
{ "La\xe1 uns von", 220, 0 },
|
|
|
|
{ "Vielleicht", 90, 0 },
|
|
|
|
{ "Kann ich fahren?", 220, 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
TimingAdjustment timingAdjustmentsDECD[] = {
|
2022-03-31 16:01:25 +02:00
|
|
|
{ "Und daf\x81r^", 110, 0 },
|
|
|
|
{ "Es ist blo\xe1^", 120, 0 },
|
|
|
|
{ "Hey.", 130, 0 },
|
|
|
|
{ "Klasse Schlag!", 150, 0 },
|
|
|
|
{ "Uiii!", 185, 1 },
|
|
|
|
{ "H\x84h?", 150, 0 },
|
|
|
|
{ "Kann ich seine", 110, 0 },
|
|
|
|
{ "Warum, glaubst", 110, 0 },
|
|
|
|
{ "Vielleicht", 90, 0 },
|
|
|
|
{ "Kann ich fahren?", 240, 0 }
|
|
|
|
};
|
|
|
|
|
2022-04-03 12:27:39 +02:00
|
|
|
TimingAdjustment timingAdjustmentsITFloppy[] = {
|
|
|
|
{ "E per questo^", 140, 0 },
|
|
|
|
{ "E' che^ecco^", 100, 0 },
|
|
|
|
{ "^imprevedibile.", 170, 0 },
|
|
|
|
{ "Huh?", 110, 0 },
|
|
|
|
{ "Perch\x82 pensi", 90, 0 },
|
|
|
|
{ "Andiamocene da", 230, 0 },
|
|
|
|
{ "Forse possiamo", 75, 0 },
|
|
|
|
{ "Ti dispiace", 160, 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
TimingAdjustment timingAdjustmentsITCD[] = {
|
2022-04-01 17:36:51 +02:00
|
|
|
{ "E per questo^", 120, 0 },
|
|
|
|
{ "Forse sei", 75, 0 },
|
|
|
|
{ "^imprevedibile.", 170, 0 },
|
|
|
|
{ "Oh.", 20, 0 },
|
|
|
|
{ "Ehi, bel colpo.", 30, 0 },
|
|
|
|
{ "Yikes!", 90, 0 },
|
|
|
|
{ "Huh?", 50, 0 },
|
|
|
|
{ "Posso tenere", 100, 0 },
|
|
|
|
{ "Perch\x82 pensi", 120, 0 },
|
|
|
|
{ "Andiamocene", 250, 0 },
|
|
|
|
{ "Forse possiamo", 90, 0 },
|
|
|
|
{ "Ti dispiace", 200, 0 }
|
|
|
|
};
|
|
|
|
|
2022-04-03 09:52:56 +02:00
|
|
|
TimingAdjustment timingAdjustmentsFRFloppy[] = {
|
|
|
|
{ "Et pour me", 120, 0 },
|
|
|
|
{ "C'est que^euh^", 100, 0 },
|
|
|
|
{ "vous \x88tes", 65, 0 },
|
|
|
|
{ "^impr\x82visible", 170, 0 },
|
|
|
|
{ "Pourquoi est-ce", 100, 0 },
|
|
|
|
{ "Filons de cet", 190, 0 },
|
|
|
|
{ "Nous pourrons", 65, 0 },
|
|
|
|
{ "Je peux conduire", 170, 0 },
|
|
|
|
{ "Je n'oublierai", 90, 0 }
|
|
|
|
};
|
|
|
|
|
2022-04-01 18:36:55 +02:00
|
|
|
TimingAdjustment timingAdjustmentsFRCD[] = {
|
|
|
|
{ "Oh.", 85, 0 },
|
|
|
|
{ "H\x82, pas mal.", 80, 0 },
|
|
|
|
{ "Yiik!", 110, 0 },
|
|
|
|
{ "Je peux garder", 130, 0 },
|
|
|
|
{ "Pourquoi est-ce", 120, 0 },
|
|
|
|
{ "Nous pourrons", 80, 0 },
|
|
|
|
{ "Je peux conduire", 220, 0 }
|
|
|
|
};
|
|
|
|
|
2022-04-01 20:25:53 +02:00
|
|
|
TimingAdjustment timingAdjustmentsES[] = {
|
|
|
|
{ "Y por eso^", 130, 0 },
|
|
|
|
{ "es simplemente", 100, 0 },
|
|
|
|
{ "eres DEMASIADO", 90, 0 },
|
|
|
|
{ "\xa8Hug?", 110, 0 },
|
|
|
|
{ "\xa8Por qu\x82", 110, 0 },
|
|
|
|
{ "Tal vez podamos", 75, 0 },
|
|
|
|
{ "\xa8Te importa", 160, 0 }
|
|
|
|
};
|
|
|
|
|
2022-03-31 16:01:25 +02:00
|
|
|
switch (_language) {
|
2022-04-01 19:58:19 +02:00
|
|
|
case Common::EN_ANY:
|
|
|
|
adjustments = timingAdjustmentsEN;
|
|
|
|
numAdjustments = ARRAYSIZE(timingAdjustmentsEN);
|
|
|
|
break;
|
2022-03-31 16:01:25 +02:00
|
|
|
case Common::DE_DEU:
|
2022-04-03 09:44:34 +02:00
|
|
|
if (strcmp(_game.variant, "Floppy") == 0) {
|
|
|
|
adjustments = timingAdjustmentsDEFloppy;
|
|
|
|
numAdjustments = ARRAYSIZE(timingAdjustmentsDEFloppy);
|
|
|
|
} else {
|
|
|
|
adjustments = timingAdjustmentsDECD;
|
|
|
|
numAdjustments = ARRAYSIZE(timingAdjustmentsDECD);
|
|
|
|
}
|
2022-03-31 16:01:25 +02:00
|
|
|
break;
|
2022-04-01 17:36:51 +02:00
|
|
|
case Common::IT_ITA:
|
2022-04-03 12:27:39 +02:00
|
|
|
if (strcmp(_game.variant, "Floppy") == 0) {
|
|
|
|
adjustments = timingAdjustmentsITFloppy;
|
|
|
|
numAdjustments = ARRAYSIZE(timingAdjustmentsITFloppy);
|
|
|
|
} else {
|
|
|
|
adjustments = timingAdjustmentsITCD;
|
|
|
|
numAdjustments = ARRAYSIZE(timingAdjustmentsITCD);
|
|
|
|
}
|
2022-04-01 17:36:51 +02:00
|
|
|
break;
|
2022-04-01 18:36:55 +02:00
|
|
|
case Common::FR_FRA:
|
2022-04-03 09:52:56 +02:00
|
|
|
if (strcmp(_game.variant, "Floppy") == 0) {
|
|
|
|
adjustments = timingAdjustmentsFRFloppy;
|
|
|
|
numAdjustments = ARRAYSIZE(timingAdjustmentsFRFloppy);
|
|
|
|
} else {
|
2022-04-01 18:36:55 +02:00
|
|
|
adjustments = timingAdjustmentsFRCD;
|
|
|
|
numAdjustments = ARRAYSIZE(timingAdjustmentsFRCD);
|
|
|
|
}
|
|
|
|
break;
|
2022-04-01 20:25:53 +02:00
|
|
|
case Common::ES_ESP:
|
|
|
|
adjustments = timingAdjustmentsES;
|
|
|
|
numAdjustments = ARRAYSIZE(timingAdjustmentsES);
|
|
|
|
break;
|
2022-03-31 16:01:25 +02:00
|
|
|
default:
|
|
|
|
adjustments = nullptr;
|
|
|
|
numAdjustments = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
byte action = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < numAdjustments; i++) {
|
|
|
|
int len = strlen(adjustments[i].str);
|
|
|
|
if (memcmp(buffer, adjustments[i].str, len) == 0) {
|
|
|
|
_talkDelay = adjustments[i].talkDelay;
|
|
|
|
action = adjustments[i].action;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_language == Common::DE_DEU) {
|
|
|
|
Actor *act;
|
|
|
|
|
|
|
|
switch (action) {
|
|
|
|
case 1:
|
|
|
|
act = derefActorSafe(2, "handleNextCharsetCode");
|
|
|
|
if (act)
|
|
|
|
act->setAnimSpeed(2);
|
|
|
|
|
|
|
|
// The actor speaks so slowly that the background
|
|
|
|
// animations have run their course. Try to restart
|
|
|
|
// them, even though it won't be quite seamless.
|
|
|
|
|
|
|
|
int actors[] = { 3, 10 };
|
|
|
|
|
|
|
|
for (int i = 0; i < ARRAYSIZE(actors); i++) {
|
|
|
|
act = derefActorSafe(actors[i], "handleNextCharsetCode");
|
|
|
|
if (act) {
|
|
|
|
act->startAnimActor(act->_initFrame);
|
|
|
|
act->animateActor(249);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-11-02 21:47:44 +00:00
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
// Ignore this code here. Occurs e.g. in MI2 when you
|
|
|
|
// talk to the carpenter on scabb island. It works like
|
|
|
|
// code 1 (=newline) in verb texts, but is ignored in
|
|
|
|
// spoken text (i.e. here). Used for very long verb
|
|
|
|
// sentences.
|
|
|
|
break;
|
|
|
|
case 9:
|
|
|
|
frme = buffer[0] | (buffer[1] << 8);
|
|
|
|
buffer += 2;
|
|
|
|
if (a)
|
|
|
|
a->startAnimActor(frme);
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
// Note the similarity to the code in debugMessage()
|
|
|
|
talk_sound_a = buffer[0] | (buffer[1] << 8) | (buffer[4] << 16) | (buffer[5] << 24);
|
|
|
|
talk_sound_b = buffer[8] | (buffer[9] << 8) | (buffer[12] << 16) | (buffer[13] << 24);
|
|
|
|
buffer += 14;
|
2006-02-20 16:51:30 +00:00
|
|
|
if (_game.heversion >= 60) {
|
2020-12-07 18:27:18 +01:00
|
|
|
#ifdef ENABLE_HE
|
|
|
|
((SoundHE *)_sound)->startHETalkSound(_localizer ? _localizer->mapTalk(talk_sound_a) : talk_sound_a);
|
|
|
|
#else
|
2006-04-23 18:52:39 +00:00
|
|
|
((SoundHE *)_sound)->startHETalkSound(talk_sound_a);
|
2020-12-07 18:27:18 +01:00
|
|
|
#endif
|
2005-11-02 21:47:44 +00:00
|
|
|
} else {
|
|
|
|
_sound->talkSound(talk_sound_a, talk_sound_b, 2);
|
|
|
|
}
|
|
|
|
_haveActorSpeechMsg = false;
|
|
|
|
break;
|
|
|
|
case 12:
|
|
|
|
color = buffer[0] | (buffer[1] << 8);
|
|
|
|
buffer += 2;
|
|
|
|
if (color == 0xFF)
|
|
|
|
_charset->setColor(_charsetColor);
|
|
|
|
else
|
|
|
|
_charset->setColor(color);
|
|
|
|
break;
|
|
|
|
case 13:
|
|
|
|
debug(0, "handleNextCharsetCode: Unknown opcode 13 %d", READ_LE_UINT16(buffer));
|
|
|
|
buffer += 2;
|
|
|
|
break;
|
|
|
|
case 14:
|
|
|
|
oldy = _charset->getFontHeight();
|
|
|
|
_charset->setCurID(*buffer++);
|
|
|
|
buffer += 2;
|
2005-11-03 02:24:53 +00:00
|
|
|
memcpy(_charsetColorMap, _charsetData[_charset->getCurID()], 4);
|
2007-01-28 20:11:31 +00:00
|
|
|
_nextTop -= _charset->getFontHeight() - oldy;
|
2005-11-02 21:47:44 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
error("handleNextCharsetCode: invalid code %d", c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_charsetBufPos = buffer - _charsetBuffer;
|
|
|
|
*code = c;
|
|
|
|
return (c != 2 && c != 3);
|
|
|
|
}
|
|
|
|
|
2008-05-06 03:00:26 +00:00
|
|
|
#ifdef ENABLE_HE
|
2005-11-02 21:47:44 +00:00
|
|
|
bool ScummEngine_v72he::handleNextCharsetCode(Actor *a, int *code) {
|
2006-02-20 16:51:30 +00:00
|
|
|
const int charsetCode = (_game.heversion >= 80) ? 127 : 64;
|
2005-11-02 21:47:44 +00:00
|
|
|
uint32 talk_sound_a = 0;
|
2011-05-14 23:50:18 +02:00
|
|
|
//uint32 talk_sound_b = 0;
|
2005-11-19 10:45:41 +00:00
|
|
|
int i, c = 0;
|
2004-08-29 01:24:17 +00:00
|
|
|
char value[32];
|
2005-11-03 10:12:12 +00:00
|
|
|
bool endLoop = false;
|
2005-11-03 10:06:17 +00:00
|
|
|
bool endText = false;
|
2005-11-02 21:47:44 +00:00
|
|
|
byte *buffer = _charsetBuffer + _charsetBufPos;
|
2005-11-03 02:24:53 +00:00
|
|
|
while (!endLoop) {
|
2005-11-02 21:47:44 +00:00
|
|
|
c = *buffer++;
|
|
|
|
if (c != charsetCode) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
c = *buffer++;
|
|
|
|
switch (c) {
|
|
|
|
case 84:
|
|
|
|
i = 0;
|
|
|
|
c = *buffer++;
|
|
|
|
while (c != 44) {
|
|
|
|
value[i] = c;
|
|
|
|
c = *buffer++;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
value[i] = 0;
|
|
|
|
talk_sound_a = atoi(value);
|
|
|
|
i = 0;
|
|
|
|
c = *buffer++;
|
|
|
|
while (c != charsetCode) {
|
|
|
|
value[i] = c;
|
|
|
|
c = *buffer++;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
value[i] = 0;
|
2011-05-14 23:50:18 +02:00
|
|
|
//talk_sound_b = atoi(value);
|
2020-12-07 18:27:18 +01:00
|
|
|
((SoundHE *)_sound)->startHETalkSound(_localizer ? _localizer->mapTalk(talk_sound_a) : talk_sound_a);
|
2005-11-02 21:47:44 +00:00
|
|
|
break;
|
|
|
|
case 104:
|
|
|
|
_haveMsg = 0;
|
|
|
|
_keepText = true;
|
2005-11-03 10:06:17 +00:00
|
|
|
endLoop = endText = true;
|
2005-11-02 21:47:44 +00:00
|
|
|
break;
|
|
|
|
case 110:
|
|
|
|
c = 13; // new line
|
2005-11-03 02:24:53 +00:00
|
|
|
endLoop = true;
|
2005-11-02 21:47:44 +00:00
|
|
|
break;
|
|
|
|
case 116:
|
|
|
|
i = 0;
|
2005-11-03 02:24:53 +00:00
|
|
|
memset(value, 0, sizeof(value));
|
2005-11-02 21:47:44 +00:00
|
|
|
c = *buffer++;
|
|
|
|
while (c != charsetCode) {
|
|
|
|
value[i] = c;
|
|
|
|
c = *buffer++;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
value[i] = 0;
|
|
|
|
talk_sound_a = atoi(value);
|
2011-05-14 23:50:18 +02:00
|
|
|
//talk_sound_b = 0;
|
2020-12-07 18:27:18 +01:00
|
|
|
((SoundHE *)_sound)->startHETalkSound(_localizer ? _localizer->mapTalk(talk_sound_a) : talk_sound_a);
|
2005-11-02 21:47:44 +00:00
|
|
|
break;
|
|
|
|
case 119:
|
|
|
|
_haveMsg = 0xFF;
|
|
|
|
_keepText = false;
|
2005-11-03 10:06:17 +00:00
|
|
|
endLoop = endText = true;
|
2005-11-02 21:47:44 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
error("handleNextCharsetCode: invalid code %d", c);
|
|
|
|
}
|
2005-11-03 02:24:53 +00:00
|
|
|
}
|
2005-11-02 21:47:44 +00:00
|
|
|
_charsetBufPos = buffer - _charsetBuffer;
|
|
|
|
*code = c;
|
2005-11-03 10:06:17 +00:00
|
|
|
return (endText == 0);
|
2005-11-02 21:47:44 +00:00
|
|
|
}
|
|
|
|
#endif
|
2002-02-13 17:33:52 +00:00
|
|
|
|
2010-12-02 00:27:26 +00:00
|
|
|
bool ScummEngine::newLine() {
|
|
|
|
_nextLeft = _string[0].xpos;
|
|
|
|
if (_charset->_center) {
|
|
|
|
_nextLeft -= _charset->getStringWidth(0, _charsetBuffer + _charsetBufPos) / 2;
|
|
|
|
if (_nextLeft < 0)
|
2020-12-04 00:00:48 +01:00
|
|
|
// The commented out part of the next line was meant as a fix for Kanji text glitches in DIG.
|
|
|
|
// But these glitches couldn't be reproduced in recent tests. So the underlying issue might
|
|
|
|
// have been taken care of in a different manner. And the fix actually caused other text glitches
|
|
|
|
// (FT/German, if you look at the sign on the container at game start). After counterchecking
|
|
|
|
// the original code it seems that setting _nextLeft to 0 is the right thing to do here.
|
|
|
|
_nextLeft = /*_game.version >= 6 ? _string[0].xpos :*/ 0;
|
2022-05-05 16:35:02 +03:00
|
|
|
} else if (_isRTL) {
|
2022-07-25 22:24:17 +03:00
|
|
|
if (_game.id == GID_MANIAC || ((_game.id == GID_MONKEY || _game.id == GID_MONKEY2) && _charset->getCurID() == 4)) {
|
2018-10-04 00:27:28 +03:00
|
|
|
_nextLeft = _screenWidth - _charset->getStringWidth(0, _charsetBuffer + _charsetBufPos) - _nextLeft;
|
|
|
|
}
|
2010-12-02 00:27:26 +00:00
|
|
|
}
|
|
|
|
if (_game.version == 0) {
|
|
|
|
return false;
|
|
|
|
} else if (!(_game.platform == Common::kPlatformFMTowns) && _string[0].height) {
|
|
|
|
_nextTop += _string[0].height;
|
|
|
|
} else {
|
|
|
|
bool useCJK = _useCJKMode;
|
|
|
|
// SCUMM5 FM-Towns doesn't use the height of the ROM font here.
|
|
|
|
if (_game.platform == Common::kPlatformFMTowns && _game.version == 5)
|
|
|
|
_useCJKMode = false;
|
|
|
|
_nextTop += _charset->getFontHeight();
|
|
|
|
_useCJKMode = useCJK;
|
|
|
|
}
|
|
|
|
if (_game.version > 3) {
|
|
|
|
// FIXME: is this really needed?
|
|
|
|
_charset->_disableOffsX = true;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-07-16 15:23:00 +03:00
|
|
|
void ScummEngine::fakeBidiString(byte *ltext, bool ignoreVerb) const {
|
2019-12-31 21:43:03 +02:00
|
|
|
// Provides custom made BiDi mechanism.
|
|
|
|
// Reverses texts on each line marked by control characters (considering different control characters used in verbs panel)
|
|
|
|
// While preserving original order of numbers (also negative numbers and comma separated)
|
2020-01-01 19:51:26 +02:00
|
|
|
int32 ll = 0;
|
2022-07-26 10:09:20 +03:00
|
|
|
if ((_game.id == GID_INDY4 && ltext[ll] == 0x7F) || (_game.id == GID_MONKEY2 && ltext[ll] == 0x07)) {
|
2019-12-21 21:21:01 +02:00
|
|
|
ll++;
|
|
|
|
}
|
2020-01-01 19:51:26 +02:00
|
|
|
while (ltext[ll] == 0xFF) {
|
2019-12-21 21:21:01 +02:00
|
|
|
ll += 4;
|
|
|
|
}
|
2020-01-01 19:51:26 +02:00
|
|
|
int32 ipos = 0;
|
|
|
|
int32 start = 0;
|
|
|
|
byte *text = ltext + ll;
|
|
|
|
byte *current = text;
|
2019-12-31 22:12:45 +02:00
|
|
|
|
2020-01-01 19:51:26 +02:00
|
|
|
int32 bufferSize = 384;
|
|
|
|
byte * const buff = (byte *)calloc(sizeof(byte), bufferSize);
|
2019-12-31 22:12:45 +02:00
|
|
|
assert(buff);
|
2020-01-01 19:51:26 +02:00
|
|
|
byte * const stack = (byte *)calloc(sizeof(byte), bufferSize);
|
2019-12-31 22:12:45 +02:00
|
|
|
assert(stack);
|
|
|
|
|
2019-12-31 21:43:03 +02:00
|
|
|
while (1) {
|
2020-01-01 19:51:26 +02:00
|
|
|
if (*current == 0x0D || *current == 0 || *current == 0xFF || *current == 0xFE) {
|
2019-12-21 21:21:01 +02:00
|
|
|
|
|
|
|
// ignore the line break for verbs texts
|
|
|
|
if (ignoreVerb && (*(current + 1) == 8)) {
|
|
|
|
*(current + 1) = *current;
|
2020-01-01 19:51:26 +02:00
|
|
|
*current = 0x08;
|
2019-12-21 21:21:01 +02:00
|
|
|
ipos += 2;
|
|
|
|
current += 2;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-12-31 22:12:45 +02:00
|
|
|
memset(buff, 0, bufferSize);
|
|
|
|
memset(stack, 0, bufferSize);
|
2019-12-21 21:21:01 +02:00
|
|
|
|
2019-12-31 22:20:09 +02:00
|
|
|
// Reverse string on current line (between start and ipos).
|
2020-01-01 19:51:26 +02:00
|
|
|
int32 sthead = 0;
|
|
|
|
byte last = 0;
|
2020-01-01 19:57:00 +02:00
|
|
|
for (int32 j = 0; j < ipos; j++) {
|
2020-01-01 19:51:26 +02:00
|
|
|
byte *curr = text + start + ipos - j - 1;
|
2019-12-31 22:19:02 +02:00
|
|
|
// Special cases to preserve original ordering (numbers).
|
2019-12-22 21:22:40 +02:00
|
|
|
if (Common::isDigit(*curr) ||
|
2020-01-01 19:51:26 +02:00
|
|
|
(*curr == (byte)',' && j + 1 < ipos && Common::isDigit(*(curr - 1)) && Common::isDigit(last)) ||
|
|
|
|
(*curr == (byte)'-' && (j + 1 == ipos || Common::isSpace(*(curr - 1))) && Common::isDigit(last))) {
|
2019-12-21 21:21:01 +02:00
|
|
|
++sthead;
|
|
|
|
stack[sthead] = *curr;
|
|
|
|
} else {
|
|
|
|
while (sthead > 0) {
|
|
|
|
buff[j - sthead] = stack[sthead];
|
|
|
|
--sthead;
|
|
|
|
}
|
|
|
|
buff[j] = *curr;
|
|
|
|
}
|
|
|
|
last = *curr;
|
|
|
|
}
|
|
|
|
while (sthead > 0) {
|
|
|
|
buff[ipos - sthead] = stack[sthead];
|
|
|
|
--sthead;
|
|
|
|
}
|
|
|
|
memcpy(text + start, buff, ipos);
|
|
|
|
start += ipos + 1;
|
|
|
|
ipos = -1;
|
2020-01-01 19:51:26 +02:00
|
|
|
if (*current == 0xFF || *current == 0xFE) {
|
2019-12-21 21:21:01 +02:00
|
|
|
current++;
|
2020-01-01 19:51:26 +02:00
|
|
|
if (*current == 0x03 || *current == 0x02) {
|
2019-12-21 21:21:01 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (*current == 0x0A || *current == 0x0C) {
|
|
|
|
start += 2;
|
|
|
|
current += 2;
|
|
|
|
}
|
|
|
|
start++;
|
|
|
|
ipos++;
|
|
|
|
current++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (*current) {
|
|
|
|
ipos++;
|
|
|
|
current++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2022-07-26 10:09:20 +03:00
|
|
|
if (!ignoreVerb) {
|
|
|
|
if (_game.id == GID_INDY4 && ltext[0] == 0x7F) {
|
|
|
|
ltext[start + ipos + ll] = 0x80;
|
|
|
|
ltext[start + ipos + ll + 1] = 0;
|
|
|
|
} else if (_game.id == GID_MONKEY2 && ltext[0] == 0x07) {
|
|
|
|
ltext[0] = ' ';
|
|
|
|
ltext[start + ipos + ll] = 0x07;
|
|
|
|
ltext[start + ipos + ll + 1] = 0;
|
|
|
|
}
|
2019-12-21 21:21:01 +02:00
|
|
|
}
|
2019-12-31 22:12:45 +02:00
|
|
|
|
|
|
|
free(buff);
|
|
|
|
free(stack);
|
2019-12-21 21:21:01 +02:00
|
|
|
}
|
|
|
|
|
2005-11-02 21:47:44 +00:00
|
|
|
void ScummEngine::CHARSET_1() {
|
|
|
|
Actor *a;
|
2006-03-12 00:53:30 +00:00
|
|
|
if (_game.heversion >= 70 && _haveMsg == 3) {
|
|
|
|
stopTalk();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2002-06-02 12:07:12 +00:00
|
|
|
if (!_haveMsg)
|
|
|
|
return;
|
|
|
|
|
2006-03-14 09:33:37 +00:00
|
|
|
if (_game.version >= 4 && _game.version <= 6) {
|
2006-05-06 22:49:57 +00:00
|
|
|
// Do nothing while the camera is moving
|
2003-11-16 20:52:57 +00:00
|
|
|
if ((camera._dest.x / 8) != (camera._cur.x / 8) || camera._cur.x != camera._last.x)
|
2002-04-11 17:19:16 +00:00
|
|
|
return;
|
2002-03-06 00:18:22 +00:00
|
|
|
}
|
|
|
|
|
2021-11-13 23:40:38 +02:00
|
|
|
a = nullptr;
|
2004-03-15 03:33:08 +00:00
|
|
|
if (getTalkingActor() != 0xFF)
|
|
|
|
a = derefActorSafe(getTalkingActor(), "CHARSET_1");
|
2001-10-09 14:30:12 +00:00
|
|
|
|
2007-04-15 20:36:44 +00:00
|
|
|
if (a && _string[0].overhead) {
|
2004-09-29 22:06:24 +00:00
|
|
|
int s;
|
|
|
|
|
2007-09-08 11:15:27 +00:00
|
|
|
_string[0].xpos = a->getPos().x - _virtscr[kMainVirtScreen].xstart;
|
2006-11-19 00:39:48 +00:00
|
|
|
_string[0].ypos = a->getPos().y - a->getElevation() - _screenTop;
|
2004-09-29 22:06:24 +00:00
|
|
|
|
2006-02-20 16:51:30 +00:00
|
|
|
if (_game.version <= 5) {
|
2001-10-09 14:30:12 +00:00
|
|
|
|
2003-05-08 15:48:50 +00:00
|
|
|
if (VAR(VAR_V5_TALK_STRING_Y) < 0) {
|
2005-03-11 01:10:06 +00:00
|
|
|
s = (a->_scaley * (int)VAR(VAR_V5_TALK_STRING_Y)) / 0xFF;
|
2004-09-29 22:06:24 +00:00
|
|
|
_string[0].ypos += (int)(((VAR(VAR_V5_TALK_STRING_Y) - s) / 2) + s);
|
2001-10-23 19:51:50 +00:00
|
|
|
} else {
|
2003-05-08 15:48:50 +00:00
|
|
|
_string[0].ypos = (int)VAR(VAR_V5_TALK_STRING_Y);
|
2001-10-23 19:51:50 +00:00
|
|
|
}
|
|
|
|
|
2001-10-09 14:30:12 +00:00
|
|
|
} else {
|
2005-03-11 01:10:06 +00:00
|
|
|
s = a->_scalex * a->_talkPosX / 0xFF;
|
|
|
|
_string[0].xpos += ((a->_talkPosX - s) / 2) + s;
|
2002-04-24 04:26:09 +00:00
|
|
|
|
2005-03-11 01:10:06 +00:00
|
|
|
s = a->_scaley * a->_talkPosY / 0xFF;
|
|
|
|
_string[0].ypos += ((a->_talkPosY - s) / 2) + s;
|
2004-08-18 12:20:26 +00:00
|
|
|
|
|
|
|
if (_string[0].ypos > _screenHeight - 40)
|
|
|
|
_string[0].ypos = _screenHeight - 40;
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
2003-05-12 22:08:39 +00:00
|
|
|
|
2008-04-30 04:39:58 +00:00
|
|
|
if (_string[0].ypos < 1)
|
|
|
|
_string[0].ypos = 1;
|
2003-05-12 22:08:39 +00:00
|
|
|
|
2008-04-30 04:39:58 +00:00
|
|
|
if (_string[0].xpos < 80)
|
|
|
|
_string[0].xpos = 80;
|
|
|
|
if (_string[0].xpos > _screenWidth - 80)
|
|
|
|
_string[0].xpos = _screenWidth - 80;
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
2001-10-23 19:51:50 +00:00
|
|
|
|
2003-09-09 20:54:19 +00:00
|
|
|
_charset->_top = _string[0].ypos + _screenTop;
|
2002-12-25 21:04:47 +00:00
|
|
|
_charset->_startLeft = _charset->_left = _string[0].xpos;
|
2004-08-14 16:30:39 +00:00
|
|
|
_charset->_right = _string[0].right;
|
|
|
|
_charset->_center = _string[0].center;
|
|
|
|
_charset->setColor(_charsetColor);
|
2001-10-09 14:30:12 +00:00
|
|
|
|
2005-03-11 01:10:06 +00:00
|
|
|
if (a && a->_charset)
|
|
|
|
_charset->setCurID(a->_charset);
|
2002-12-04 15:00:35 +00:00
|
|
|
else
|
2002-12-25 21:04:47 +00:00
|
|
|
_charset->setCurID(_string[0].charset);
|
2002-12-04 15:00:35 +00:00
|
|
|
|
2006-02-20 16:51:30 +00:00
|
|
|
if (_game.version >= 5)
|
2005-11-02 21:47:44 +00:00
|
|
|
memcpy(_charsetColorMap, _charsetData[_charset->getCurID()], 4);
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2010-10-05 19:04:52 +00:00
|
|
|
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
|
2021-02-28 01:05:44 +01:00
|
|
|
if (_game.platform == Common::kPlatformFMTowns && (_keepText || _haveMsg == 0xFF))
|
SCUMM/FM-TOWNS: fix palette and other graphics issues
This commit should fix at least the following bugs/feature requests: #1032859, #1252088, #1055391, #1315968, #1315938, #1742106, #812891.
The FM-Towns version of Scumm games use a mixed graphics mode with 2 layers (one with 32767 colors and one with 16 colors). Among other things I have added a screen output class which emulates this dual layer approach which allows specific hardware effects like enabling and disabling layers (e.g. in the voodoo priestess scene in MI1).
Old savegames (saved before this update) will load, but you’ll encounter palette glitches in the verb/inventory screen, since the 16 color palette for layer 2 is not contained in your savegame. This will be true at least for version 5 games. Certain scene change actions (which require the verb/inventory part to be redrawn) might correct this (e.g. try looking at the treasure map in MI1 and closing it). Version 3 games should be okay, since they use a static text palette which is never changed and which will be reset after loading a savegame.
This update requires a USE_RGB_COLORS setting for proper operation. 8 bit users will get a warning that they’ll have to expect palette glitches . Apart from that the engine in 8 bit mode should not only still work okay, but also benefit from some of the other (non palette related) improvements (e.g. bug #1032859 should be fixed even in 8 bit mode).
Japanese font drawing hasn’t been improved much yet. This will be a separate task.
svn-id: r52966
2010-10-01 19:24:52 +00:00
|
|
|
memcpy(&_charset->_str, &_curStringRect, sizeof(Common::Rect));
|
2010-10-05 19:04:52 +00:00
|
|
|
#endif
|
SCUMM/FM-TOWNS: fix palette and other graphics issues
This commit should fix at least the following bugs/feature requests: #1032859, #1252088, #1055391, #1315968, #1315938, #1742106, #812891.
The FM-Towns version of Scumm games use a mixed graphics mode with 2 layers (one with 32767 colors and one with 16 colors). Among other things I have added a screen output class which emulates this dual layer approach which allows specific hardware effects like enabling and disabling layers (e.g. in the voodoo priestess scene in MI1).
Old savegames (saved before this update) will load, but you’ll encounter palette glitches in the verb/inventory screen, since the 16 color palette for layer 2 is not contained in your savegame. This will be true at least for version 5 games. Certain scene change actions (which require the verb/inventory part to be redrawn) might correct this (e.g. try looking at the treasure map in MI1 and closing it). Version 3 games should be okay, since they use a static text palette which is never changed and which will be reset after loading a savegame.
This update requires a USE_RGB_COLORS setting for proper operation. 8 bit users will get a warning that they’ll have to expect palette glitches . Apart from that the engine in 8 bit mode should not only still work okay, but also benefit from some of the other (non palette related) improvements (e.g. bug #1032859 should be fixed even in 8 bit mode).
Japanese font drawing hasn’t been improved much yet. This will be a separate task.
svn-id: r52966
2010-10-01 19:24:52 +00:00
|
|
|
|
2002-06-02 12:07:12 +00:00
|
|
|
if (_talkDelay)
|
2001-10-09 14:30:12 +00:00
|
|
|
return;
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2006-05-06 22:49:57 +00:00
|
|
|
if ((_game.version <= 6 && _haveMsg == 1) ||
|
2019-12-31 21:43:03 +02:00
|
|
|
(_game.version == 7 && _haveMsg != 1)) {
|
2006-05-06 22:49:57 +00:00
|
|
|
|
2006-03-10 06:05:49 +00:00
|
|
|
if (_game.heversion >= 60) {
|
|
|
|
if (_sound->isSoundRunning(1) == 0)
|
|
|
|
stopTalk();
|
|
|
|
} else {
|
|
|
|
if ((_sound->_sfxMode & 2) == 0)
|
|
|
|
stopTalk();
|
|
|
|
}
|
2001-10-09 14:30:12 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-02-28 19:16:46 +01:00
|
|
|
// The second check is from LOOM DOS EGA disasm. It prevents weird speech animations
|
|
|
|
// with empty strings (bug #990). The same code is present in actorTalk(). The FM-Towns
|
|
|
|
// versions don't have such code, but I do not get the weird speech animations either.
|
|
|
|
// So apparently it is not needed there.
|
|
|
|
if (a && !_string[0].no_talk_anim && !(_game.id == GID_LOOM && _game.platform != Common::kPlatformFMTowns && !_charsetBuffer[_charsetBufPos])) {
|
2004-09-28 19:28:59 +00:00
|
|
|
a->runActorTalkScript(a->_talkStartFrame);
|
2001-11-05 19:21:49 +00:00
|
|
|
_useTalkAnims = true;
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
|
|
|
|
2005-11-02 21:47:44 +00:00
|
|
|
_talkDelay = (VAR_DEFAULT_TALK_DELAY != 0xFF) ? VAR(VAR_DEFAULT_TALK_DELAY) : 60;
|
2005-07-30 21:11:48 +00:00
|
|
|
|
2001-10-09 14:30:12 +00:00
|
|
|
if (!_keepText) {
|
2010-10-05 19:04:52 +00:00
|
|
|
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
|
SCUMM/FM-TOWNS: fix palette and other graphics issues
This commit should fix at least the following bugs/feature requests: #1032859, #1252088, #1055391, #1315968, #1315938, #1742106, #812891.
The FM-Towns version of Scumm games use a mixed graphics mode with 2 layers (one with 32767 colors and one with 16 colors). Among other things I have added a screen output class which emulates this dual layer approach which allows specific hardware effects like enabling and disabling layers (e.g. in the voodoo priestess scene in MI1).
Old savegames (saved before this update) will load, but you’ll encounter palette glitches in the verb/inventory screen, since the 16 color palette for layer 2 is not contained in your savegame. This will be true at least for version 5 games. Certain scene change actions (which require the verb/inventory part to be redrawn) might correct this (e.g. try looking at the treasure map in MI1 and closing it). Version 3 games should be okay, since they use a static text palette which is never changed and which will be reset after loading a savegame.
This update requires a USE_RGB_COLORS setting for proper operation. 8 bit users will get a warning that they’ll have to expect palette glitches . Apart from that the engine in 8 bit mode should not only still work okay, but also benefit from some of the other (non palette related) improvements (e.g. bug #1032859 should be fixed even in 8 bit mode).
Japanese font drawing hasn’t been improved much yet. This will be a separate task.
svn-id: r52966
2010-10-01 19:24:52 +00:00
|
|
|
if (_game.platform == Common::kPlatformFMTowns)
|
|
|
|
towns_restoreCharsetBg();
|
|
|
|
else
|
2010-10-05 19:04:52 +00:00
|
|
|
#endif
|
SCUMM/FM-TOWNS: fix palette and other graphics issues
This commit should fix at least the following bugs/feature requests: #1032859, #1252088, #1055391, #1315968, #1315938, #1742106, #812891.
The FM-Towns version of Scumm games use a mixed graphics mode with 2 layers (one with 32767 colors and one with 16 colors). Among other things I have added a screen output class which emulates this dual layer approach which allows specific hardware effects like enabling and disabling layers (e.g. in the voodoo priestess scene in MI1).
Old savegames (saved before this update) will load, but you’ll encounter palette glitches in the verb/inventory screen, since the 16 color palette for layer 2 is not contained in your savegame. This will be true at least for version 5 games. Certain scene change actions (which require the verb/inventory part to be redrawn) might correct this (e.g. try looking at the treasure map in MI1 and closing it). Version 3 games should be okay, since they use a static text palette which is never changed and which will be reset after loading a savegame.
This update requires a USE_RGB_COLORS setting for proper operation. 8 bit users will get a warning that they’ll have to expect palette glitches . Apart from that the engine in 8 bit mode should not only still work okay, but also benefit from some of the other (non palette related) improvements (e.g. bug #1032859 should be fixed even in 8 bit mode).
Japanese font drawing hasn’t been improved much yet. This will be a separate task.
svn-id: r52966
2010-10-01 19:24:52 +00:00
|
|
|
restoreCharsetBg();
|
2015-06-09 11:58:59 +10:00
|
|
|
_msgCount = 0;
|
|
|
|
} else if (_game.version <= 2) {
|
|
|
|
_talkDelay += _msgCount * _defaultTalkDelay;
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2007-01-29 21:06:39 +00:00
|
|
|
if (_game.version > 3) {
|
|
|
|
int maxwidth = _charset->_right - _string[0].xpos - 1;
|
|
|
|
if (_charset->_center) {
|
|
|
|
if (maxwidth > _nextLeft)
|
|
|
|
maxwidth = _nextLeft;
|
|
|
|
maxwidth *= 2;
|
|
|
|
}
|
2001-11-05 19:21:49 +00:00
|
|
|
|
2007-01-29 21:06:39 +00:00
|
|
|
_charset->addLinebreaks(0, _charsetBuffer + _charsetBufPos, 0, maxwidth);
|
|
|
|
}
|
2019-08-17 20:08:40 +03:00
|
|
|
|
2002-12-25 21:04:47 +00:00
|
|
|
if (_charset->_center) {
|
2007-01-28 20:11:31 +00:00
|
|
|
_nextLeft -= _charset->getStringWidth(0, _charsetBuffer + _charsetBufPos) / 2;
|
|
|
|
if (_nextLeft < 0)
|
2008-04-28 18:57:17 +00:00
|
|
|
_nextLeft = _game.version >= 6 ? _string[0].xpos : 0;
|
2022-05-05 16:35:02 +03:00
|
|
|
} else if (_isRTL) {
|
2022-07-25 22:24:17 +03:00
|
|
|
if (_game.id == GID_MANIAC || ((_game.id == GID_MONKEY || _game.id == GID_MONKEY2) && _charset->getCurID() == 4)) {
|
2018-10-20 19:12:04 +03:00
|
|
|
_nextLeft = _screenWidth - _charset->getStringWidth(0, _charsetBuffer + _charsetBufPos) - _nextLeft;
|
|
|
|
}
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
|
|
|
|
2002-12-25 21:04:47 +00:00
|
|
|
_charset->_disableOffsX = _charset->_firstChar = !_keepText;
|
2001-10-09 14:30:12 +00:00
|
|
|
|
2007-02-02 12:51:44 +00:00
|
|
|
int c = 0;
|
2018-10-01 15:38:49 +03:00
|
|
|
|
2022-05-05 16:35:02 +03:00
|
|
|
if (_isRTL)
|
2020-01-01 19:51:26 +02:00
|
|
|
fakeBidiString(_charsetBuffer + _charsetBufPos, true);
|
2018-10-01 15:38:49 +03:00
|
|
|
|
2021-06-22 10:06:50 +02:00
|
|
|
bool createTextBox = (_macScreen && _game.id == GID_INDY3);
|
|
|
|
bool drawTextBox = false;
|
2021-06-22 08:44:08 +02:00
|
|
|
|
2005-11-02 21:47:44 +00:00
|
|
|
while (handleNextCharsetCode(a, &c)) {
|
2002-04-11 17:19:16 +00:00
|
|
|
if (c == 0) {
|
2005-11-02 21:47:44 +00:00
|
|
|
// End of text reached, set _haveMsg accordingly
|
2021-09-10 03:35:37 +02:00
|
|
|
_haveMsg = 1;
|
2001-10-09 14:30:12 +00:00
|
|
|
_keepText = false;
|
2015-06-09 11:58:59 +10:00
|
|
|
_msgCount = 0;
|
2001-10-09 14:30:12 +00:00
|
|
|
break;
|
|
|
|
}
|
2005-07-30 21:11:48 +00:00
|
|
|
|
2001-10-16 10:01:48 +00:00
|
|
|
if (c == 13) {
|
2010-12-02 00:27:26 +00:00
|
|
|
if (!newLine())
|
2005-09-23 12:35:30 +00:00
|
|
|
break;
|
2003-05-14 13:30:52 +00:00
|
|
|
continue;
|
2001-10-16 10:01:48 +00:00
|
|
|
}
|
2001-10-09 14:30:12 +00:00
|
|
|
|
2021-02-28 01:28:23 -08:00
|
|
|
// Handle line overflow for V3. See also bug #2213.
|
2007-01-28 20:11:31 +00:00
|
|
|
if (_game.version == 3 && _nextLeft >= _screenWidth) {
|
|
|
|
_nextLeft = _screenWidth;
|
2006-05-07 11:47:17 +00:00
|
|
|
}
|
|
|
|
// Handle line breaks for V1-V2
|
2007-01-28 20:11:31 +00:00
|
|
|
if (_game.version <= 2 && _nextLeft >= _screenWidth) {
|
2010-12-02 00:27:26 +00:00
|
|
|
if (!newLine())
|
|
|
|
break; // FIXME: Is this necessary? Only would be relevant for v0 games
|
2006-05-07 11:47:17 +00:00
|
|
|
}
|
|
|
|
|
2007-01-28 20:11:31 +00:00
|
|
|
_charset->_left = _nextLeft;
|
|
|
|
_charset->_top = _nextTop;
|
2005-11-27 10:32:21 +00:00
|
|
|
|
2021-06-22 10:06:50 +02:00
|
|
|
if (createTextBox) {
|
2021-06-22 10:29:29 +02:00
|
|
|
if (!_keepText)
|
|
|
|
mac_createIndy3TextBox(a);
|
2021-06-22 10:06:50 +02:00
|
|
|
createTextBox = false;
|
|
|
|
drawTextBox = true;
|
2021-06-22 08:44:08 +02:00
|
|
|
}
|
|
|
|
|
2021-09-10 03:35:37 +02:00
|
|
|
if (c & 0x80 && _useCJKMode) {
|
|
|
|
if (is2ByteCharacter(_language, c)) {
|
|
|
|
byte *buffer = _charsetBuffer + _charsetBufPos;
|
|
|
|
c += *buffer++ * 256; //LE
|
|
|
|
_charsetBufPos = buffer - _charsetBuffer;
|
2003-06-09 00:35:32 +00:00
|
|
|
}
|
2021-09-10 03:35:37 +02:00
|
|
|
}
|
|
|
|
if (_game.version <= 3) {
|
|
|
|
_charset->printChar(c, false);
|
|
|
|
_msgCount += 1;
|
2005-11-03 10:06:17 +00:00
|
|
|
} else {
|
2021-09-10 03:35:37 +02:00
|
|
|
if (_game.features & GF_16BIT_COLOR) {
|
|
|
|
// HE games which use sprites for subtitles
|
|
|
|
} else if (_game.heversion >= 60 && !ConfMan.getBool("subtitles") && _sound->isSoundRunning(1)) {
|
|
|
|
// Special case for HE games
|
|
|
|
} else if (_game.id == GID_LOOM && !ConfMan.getBool("subtitles") && (_sound->pollCD())) {
|
|
|
|
// Special case for Loom (CD), since it only uses CD audio.for sound
|
|
|
|
} else if (!ConfMan.getBool("subtitles") && (!_haveActorSpeechMsg || _mixer->isSoundHandleActive(*_sound->_talkChannelHandle))) {
|
|
|
|
// Subtitles are turned off, and there is a voice version
|
|
|
|
// of this message -> don't print it.
|
2001-10-26 17:34:50 +00:00
|
|
|
} else {
|
2021-09-10 03:35:37 +02:00
|
|
|
_charset->printChar(c, false);
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
|
|
|
}
|
2021-09-10 03:35:37 +02:00
|
|
|
_nextLeft = _charset->_left;
|
|
|
|
_nextTop = _charset->_top;
|
2001-10-09 14:30:12 +00:00
|
|
|
|
2021-06-22 10:06:50 +02:00
|
|
|
if (drawTextBox)
|
|
|
|
mac_drawIndy3TextBox();
|
|
|
|
|
2006-02-20 16:51:30 +00:00
|
|
|
if (_game.version <= 2) {
|
2005-11-02 21:47:44 +00:00
|
|
|
_talkDelay += _defaultTalkDelay;
|
|
|
|
VAR(VAR_CHARCOUNT)++;
|
|
|
|
} else {
|
|
|
|
_talkDelay += (int)VAR(VAR_CHARINC);
|
|
|
|
}
|
|
|
|
}
|
2001-11-05 19:21:49 +00:00
|
|
|
|
2010-10-05 19:04:52 +00:00
|
|
|
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
|
SCUMM/FM-TOWNS: fix palette and other graphics issues
This commit should fix at least the following bugs/feature requests: #1032859, #1252088, #1055391, #1315968, #1315938, #1742106, #812891.
The FM-Towns version of Scumm games use a mixed graphics mode with 2 layers (one with 32767 colors and one with 16 colors). Among other things I have added a screen output class which emulates this dual layer approach which allows specific hardware effects like enabling and disabling layers (e.g. in the voodoo priestess scene in MI1).
Old savegames (saved before this update) will load, but you’ll encounter palette glitches in the verb/inventory screen, since the 16 color palette for layer 2 is not contained in your savegame. This will be true at least for version 5 games. Certain scene change actions (which require the verb/inventory part to be redrawn) might correct this (e.g. try looking at the treasure map in MI1 and closing it). Version 3 games should be okay, since they use a static text palette which is never changed and which will be reset after loading a savegame.
This update requires a USE_RGB_COLORS setting for proper operation. 8 bit users will get a warning that they’ll have to expect palette glitches . Apart from that the engine in 8 bit mode should not only still work okay, but also benefit from some of the other (non palette related) improvements (e.g. bug #1032859 should be fixed even in 8 bit mode).
Japanese font drawing hasn’t been improved much yet. This will be a separate task.
svn-id: r52966
2010-10-01 19:24:52 +00:00
|
|
|
if (_game.platform == Common::kPlatformFMTowns && (c == 0 || c == 2 || c == 3))
|
|
|
|
memcpy(&_curStringRect, &_charset->_str, sizeof(Common::Rect));
|
2010-10-05 19:04:52 +00:00
|
|
|
#endif
|
2002-05-25 08:53:08 +00:00
|
|
|
}
|
|
|
|
|
2004-03-15 03:09:48 +00:00
|
|
|
void ScummEngine::drawString(int a, const byte *msg) {
|
2005-04-13 06:30:02 +00:00
|
|
|
byte buf[270];
|
2002-12-04 15:00:35 +00:00
|
|
|
byte *space;
|
2003-06-04 14:37:43 +00:00
|
|
|
int i, c;
|
|
|
|
byte fontHeight = 0;
|
2001-10-26 17:34:50 +00:00
|
|
|
uint color;
|
2006-02-20 16:51:30 +00:00
|
|
|
int code = (_game.heversion >= 80) ? 127 : 64;
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2008-12-09 19:54:01 +00:00
|
|
|
// drawString is not used in SCUMM v7 and v8
|
|
|
|
assert(_game.version < 7);
|
2004-10-23 23:08:53 +00:00
|
|
|
|
2005-05-26 14:14:32 +00:00
|
|
|
convertMessageToString(msg, buf, sizeof(buf));
|
2001-10-09 14:30:12 +00:00
|
|
|
|
2022-05-05 16:35:02 +03:00
|
|
|
if (_isRTL)
|
2020-01-01 19:51:26 +02:00
|
|
|
fakeBidiString(buf, false);
|
2018-10-01 15:38:49 +03:00
|
|
|
|
2003-09-09 20:54:19 +00:00
|
|
|
_charset->_top = _string[a].ypos + _screenTop;
|
2002-12-25 21:04:47 +00:00
|
|
|
_charset->_startLeft = _charset->_left = _string[a].xpos;
|
|
|
|
_charset->_right = _string[a].right;
|
|
|
|
_charset->_center = _string[a].center;
|
2003-04-27 18:49:27 +00:00
|
|
|
_charset->setColor(_string[a].color);
|
2002-12-25 21:04:47 +00:00
|
|
|
_charset->_disableOffsX = _charset->_firstChar = true;
|
|
|
|
_charset->setCurID(_string[a].charset);
|
2002-02-16 19:03:14 +00:00
|
|
|
|
2007-12-17 22:36:16 +00:00
|
|
|
// HACK: Correct positions of text in books in Indy3 Mac.
|
2021-06-23 09:39:26 +02:00
|
|
|
// See also bug #8759. Not needed when using the Mac font.
|
2021-06-22 16:25:19 +02:00
|
|
|
if (_game.id == GID_INDY3 && _game.platform == Common::kPlatformMacintosh && a == 1 && !_macScreen) {
|
2007-12-16 21:46:34 +00:00
|
|
|
if (_currentRoom == 75) {
|
2007-12-17 22:36:16 +00:00
|
|
|
// Grail Diary Page 1 (Library)
|
2007-12-16 21:46:34 +00:00
|
|
|
if (_charset->_startLeft < 160)
|
|
|
|
_charset->_startLeft = _charset->_left = _string[a].xpos - 22;
|
|
|
|
else if (_charset->_startLeft < 200)
|
|
|
|
_charset->_startLeft = _charset->_left = _string[a].xpos - 10;
|
|
|
|
} else if (_currentRoom == 90) {
|
2007-12-17 22:36:16 +00:00
|
|
|
// Grail Diary Page 2 (Catacombs - Engravings)
|
2007-12-16 21:46:34 +00:00
|
|
|
if (_charset->_startLeft < 160)
|
|
|
|
_charset->_startLeft = _charset->_left = _string[a].xpos - 21;
|
|
|
|
else if (_charset->_startLeft < 200)
|
|
|
|
_charset->_startLeft = _charset->_left = _string[a].xpos - 15;
|
2007-12-17 22:36:16 +00:00
|
|
|
} else if (_currentRoom == 69) {
|
|
|
|
// Grail Diary Page 3 (Catacombs - Music)
|
|
|
|
if (_charset->_startLeft < 160)
|
|
|
|
_charset->_startLeft = _charset->_left = _string[a].xpos - 15;
|
|
|
|
else if (_charset->_startLeft < 200)
|
|
|
|
_charset->_startLeft = _charset->_left = _string[a].xpos - 10;
|
|
|
|
} else if (_currentRoom == 74) {
|
|
|
|
// Biplane Manual
|
|
|
|
_charset->_startLeft = _charset->_left = _string[a].xpos - 35;
|
2007-12-16 21:46:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-20 16:51:30 +00:00
|
|
|
if (_game.version >= 5)
|
2005-11-03 02:24:53 +00:00
|
|
|
memcpy(_charsetColorMap, _charsetData[_charset->getCurID()], 4);
|
2002-02-16 19:03:14 +00:00
|
|
|
|
2003-05-14 13:30:52 +00:00
|
|
|
fontHeight = _charset->getFontHeight();
|
|
|
|
|
2007-03-23 23:22:18 +00:00
|
|
|
if (_game.version >= 4) {
|
|
|
|
// trim from the right
|
|
|
|
byte *tmp = buf;
|
2021-11-13 23:40:38 +02:00
|
|
|
space = nullptr;
|
2007-03-23 23:22:18 +00:00
|
|
|
while (*tmp) {
|
|
|
|
if (*tmp == ' ') {
|
|
|
|
if (!space)
|
|
|
|
space = tmp;
|
|
|
|
} else {
|
2021-11-13 23:40:38 +02:00
|
|
|
space = nullptr;
|
2007-03-23 23:22:18 +00:00
|
|
|
}
|
|
|
|
tmp++;
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
2007-03-23 23:22:18 +00:00
|
|
|
if (space)
|
|
|
|
*space = '\0';
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
2007-03-23 23:22:18 +00:00
|
|
|
|
2002-12-25 21:04:47 +00:00
|
|
|
if (_charset->_center) {
|
2003-11-16 20:52:57 +00:00
|
|
|
_charset->_left -= _charset->getStringWidth(a, buf) / 2;
|
2022-05-04 00:33:28 +03:00
|
|
|
} else if (_isRTL && _game.id != GID_SAMNMAX && _game.id != GID_MANIAC) {
|
2019-12-31 21:43:03 +02:00
|
|
|
// Ignore INDY4 verbs (but allow dialogue)
|
2018-10-03 03:05:03 +03:00
|
|
|
if (_game.id != GID_INDY4 || buf[0] == 127) {
|
2021-03-20 22:52:09 +02:00
|
|
|
if (_game.id == GID_INDY4)
|
2018-10-03 03:05:03 +03:00
|
|
|
buf[0] = 32;
|
2021-12-14 07:47:18 +02:00
|
|
|
_charset->_left = _screenWidth - _charset->_startLeft - _charset->getStringWidth(1, buf);
|
2018-10-01 15:38:49 +03:00
|
|
|
}
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
|
|
|
|
2007-03-23 23:31:43 +00:00
|
|
|
if (!buf[0]) {
|
|
|
|
if (_game.version >= 5) {
|
|
|
|
buf[0] = ' ';
|
|
|
|
buf[1] = 0;
|
|
|
|
} else {
|
|
|
|
_charset->_str.left = _charset->_left;
|
|
|
|
_charset->_str.top = _charset->_top;
|
|
|
|
_charset->_str.right = _charset->_left;
|
|
|
|
_charset->_str.bottom = _charset->_top;
|
|
|
|
}
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
|
|
|
|
2003-06-04 14:37:43 +00:00
|
|
|
for (i = 0; (c = buf[i++]) != 0;) {
|
2006-02-20 16:51:30 +00:00
|
|
|
if (_game.heversion >= 72 && c == code) {
|
2004-09-19 12:22:47 +00:00
|
|
|
c = buf[i++];
|
|
|
|
switch (c) {
|
|
|
|
case 110:
|
|
|
|
if (_charset->_center) {
|
|
|
|
_charset->_left = _charset->_startLeft - _charset->getStringWidth(a, buf + i);
|
|
|
|
} else {
|
|
|
|
_charset->_left = _charset->_startLeft;
|
|
|
|
}
|
|
|
|
_charset->_top += fontHeight;
|
|
|
|
break;
|
2019-12-23 11:56:16 +00:00
|
|
|
default:
|
|
|
|
break;
|
2004-09-19 12:22:47 +00:00
|
|
|
}
|
2006-09-01 12:20:08 +00:00
|
|
|
} else if ((c == 0xFF || (_game.version <= 6 && c == 0xFE)) && (_game.heversion <= 71)) {
|
2003-06-04 14:37:43 +00:00
|
|
|
c = buf[i++];
|
|
|
|
switch (c) {
|
2001-10-09 14:30:12 +00:00
|
|
|
case 9:
|
2002-04-11 17:19:16 +00:00
|
|
|
case 10:
|
|
|
|
case 13:
|
|
|
|
case 14:
|
2001-10-09 14:30:12 +00:00
|
|
|
i += 2;
|
|
|
|
break;
|
2002-04-11 17:19:16 +00:00
|
|
|
case 1:
|
|
|
|
case 8:
|
2002-12-25 21:04:47 +00:00
|
|
|
if (_charset->_center) {
|
2002-12-25 21:42:22 +00:00
|
|
|
_charset->_left = _charset->_startLeft - _charset->getStringWidth(a, buf + i);
|
2022-05-05 16:35:02 +03:00
|
|
|
} else if (_isRTL) {
|
2021-12-14 07:47:18 +02:00
|
|
|
_charset->_left = _screenWidth - _charset->_startLeft - _charset->getStringWidth(1, buf + i);
|
2001-10-09 14:30:12 +00:00
|
|
|
} else {
|
2002-12-25 21:04:47 +00:00
|
|
|
_charset->_left = _charset->_startLeft;
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
2006-02-20 16:51:30 +00:00
|
|
|
if (!(_game.platform == Common::kPlatformFMTowns) && _string[0].height) {
|
2007-01-28 20:11:31 +00:00
|
|
|
_nextTop += _string[0].height;
|
2005-03-18 23:31:58 +00:00
|
|
|
} else {
|
|
|
|
_charset->_top += fontHeight;
|
|
|
|
}
|
2001-10-26 17:34:50 +00:00
|
|
|
break;
|
|
|
|
case 12:
|
2002-04-11 17:19:16 +00:00
|
|
|
color = buf[i] + (buf[i + 1] << 8);
|
|
|
|
i += 2;
|
|
|
|
if (color == 0xFF)
|
2003-04-27 18:49:27 +00:00
|
|
|
_charset->setColor(_string[a].color);
|
2001-10-26 17:34:50 +00:00
|
|
|
else
|
2003-04-27 18:49:27 +00:00
|
|
|
_charset->setColor(color);
|
2001-10-26 17:34:50 +00:00
|
|
|
break;
|
2019-12-23 11:56:16 +00:00
|
|
|
default:
|
|
|
|
break;
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
|
|
|
} else {
|
2006-02-20 16:51:30 +00:00
|
|
|
if (a == 1 && _game.version >= 6) {
|
2004-01-19 19:09:08 +00:00
|
|
|
// FIXME: The following code is a bit nasty. It is used for the
|
|
|
|
// Highway surfing game in Sam&Max; there, _blitAlso is set to
|
2004-01-25 15:08:41 +00:00
|
|
|
// true when writing the highscore numbers. It is also in DOTT
|
|
|
|
// for parts the intro and for drawing newspaper headlines. It
|
|
|
|
// is also used for scores in bowling mini game in fbear and
|
|
|
|
// for names in load/save screen of all HE games. Maybe it is
|
|
|
|
// also being used in other places.
|
|
|
|
//
|
2004-01-19 19:09:08 +00:00
|
|
|
// A better name for _blitAlso might be _imprintOnBackground
|
2004-01-25 15:08:41 +00:00
|
|
|
|
2004-01-06 12:02:18 +00:00
|
|
|
if (_string[a].no_talk_anim == false) {
|
2005-08-14 01:41:52 +00:00
|
|
|
//debug(0, "Would have set _charset->_blitAlso = true (wanted to print '%c' = %d)", c, c);
|
2004-01-19 19:09:08 +00:00
|
|
|
_charset->_blitAlso = true;
|
2004-01-06 12:02:18 +00:00
|
|
|
}
|
2002-10-02 17:23:19 +00:00
|
|
|
}
|
2004-10-23 23:08:53 +00:00
|
|
|
if (c & 0x80 && _useCJKMode) {
|
2020-10-28 20:45:04 +09:00
|
|
|
if (is2ByteCharacter(_language, c))
|
2004-10-23 23:08:53 +00:00
|
|
|
c += buf[i++] * 256;
|
|
|
|
}
|
2008-12-09 19:54:01 +00:00
|
|
|
_charset->printChar(c, true);
|
2002-12-25 21:04:47 +00:00
|
|
|
_charset->_blitAlso = false;
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
if (a == 0) {
|
2007-01-28 20:11:31 +00:00
|
|
|
_nextLeft = _charset->_left;
|
|
|
|
_nextTop = _charset->_top;
|
2003-06-09 00:35:32 +00:00
|
|
|
}
|
2002-10-02 17:23:19 +00:00
|
|
|
|
2007-03-23 23:22:18 +00:00
|
|
|
_string[a].xpos = _charset->_str.right;
|
2009-07-24 05:20:44 +00:00
|
|
|
|
|
|
|
if (_game.heversion >= 60) {
|
|
|
|
_string[a]._default.xpos = _string[a].xpos;
|
|
|
|
_string[a]._default.ypos = _string[a].ypos;
|
|
|
|
}
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
|
|
|
|
2006-09-01 12:04:40 +00:00
|
|
|
int ScummEngine::convertMessageToString(const byte *msg, byte *dst, int dstSize) {
|
|
|
|
uint num = 0;
|
|
|
|
uint32 val;
|
|
|
|
byte chr;
|
2010-10-17 23:52:33 +00:00
|
|
|
byte lastChr = 0;
|
2006-09-01 12:04:40 +00:00
|
|
|
const byte *src;
|
2021-09-10 18:17:04 +02:00
|
|
|
byte *start, *end;
|
2020-11-15 15:45:07 +09:00
|
|
|
byte transBuf[2048];
|
2006-09-01 12:04:40 +00:00
|
|
|
|
|
|
|
assert(dst);
|
2021-09-10 18:17:04 +02:00
|
|
|
start = dst;
|
2006-09-01 12:04:40 +00:00
|
|
|
end = dst + dstSize;
|
|
|
|
|
2021-11-13 23:40:38 +02:00
|
|
|
if (msg == nullptr) {
|
2006-09-01 12:04:40 +00:00
|
|
|
debug(0, "Bad message in convertMessageToString, ignoring");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-11-15 15:45:07 +09:00
|
|
|
if (_game.version >= 7 || isScummvmKorTarget()) {
|
2006-09-01 12:04:40 +00:00
|
|
|
translateText(msg, transBuf);
|
|
|
|
src = transBuf;
|
|
|
|
} else {
|
|
|
|
src = msg;
|
|
|
|
}
|
2019-08-17 20:28:57 +03:00
|
|
|
|
2006-09-01 12:04:40 +00:00
|
|
|
num = 0;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
chr = src[num++];
|
2002-04-11 17:19:16 +00:00
|
|
|
if (chr == 0)
|
2001-10-09 14:30:12 +00:00
|
|
|
break;
|
2009-11-22 13:34:58 +00:00
|
|
|
|
|
|
|
if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) {
|
|
|
|
// Code for TM character
|
|
|
|
if (chr == 0x0F && src[num] == 0x20) {
|
|
|
|
*dst++ = 0x5D;
|
|
|
|
*dst++ = 0x5E;
|
|
|
|
continue;
|
|
|
|
// Code for (C) character
|
|
|
|
} else if (chr == 0x1C && src[num] == 0x20) {
|
|
|
|
*dst++ = 0x3E;
|
|
|
|
*dst++ = 0x2A;
|
|
|
|
continue;
|
2009-12-02 03:55:06 +00:00
|
|
|
// Code for " character
|
|
|
|
} else if (chr == 0x19) {
|
|
|
|
*dst++ = 0x2F;
|
|
|
|
continue;
|
2009-11-22 13:34:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-10-09 14:30:12 +00:00
|
|
|
if (chr == 0xFF) {
|
2004-04-04 00:29:13 +00:00
|
|
|
chr = src[num++];
|
2004-08-02 20:55:35 +00:00
|
|
|
|
2021-06-07 17:44:05 -06:00
|
|
|
// WORKAROUND for bug #1675, a script bug in Indy3. Apparently,
|
2004-08-02 20:55:35 +00:00
|
|
|
// a german 'sz' was encoded incorrectly as 0xFF2E. We replace
|
|
|
|
// this by the correct encoding here. See also ScummEngine::resStrLen().
|
2006-02-20 16:51:30 +00:00
|
|
|
if (_game.id == GID_INDY3 && chr == 0x2E) {
|
2004-08-02 20:55:35 +00:00
|
|
|
*dst++ = 0xE1;
|
|
|
|
continue;
|
|
|
|
}
|
2007-09-19 08:40:12 +00:00
|
|
|
|
2021-02-28 01:28:23 -08:00
|
|
|
// WORKAROUND for bug #2715: Yet another script bug in Indy3.
|
2006-08-26 22:02:16 +00:00
|
|
|
// Once more a german 'sz' was encoded incorrectly, but this time
|
|
|
|
// they simply encoded it as 0xFF instead of 0xE1. Happens twice
|
|
|
|
// in script 71.
|
|
|
|
if (_game.id == GID_INDY3 && chr == 0x20 && vm.slot[_currentScript].number == 71) {
|
|
|
|
num--;
|
|
|
|
*dst++ = 0xE1;
|
|
|
|
continue;
|
|
|
|
}
|
2004-08-02 20:55:35 +00:00
|
|
|
|
2004-01-08 17:30:36 +00:00
|
|
|
if (chr == 1 || chr == 2 || chr == 3 || chr == 8) {
|
|
|
|
// Simply copy these special codes
|
2004-04-04 00:29:13 +00:00
|
|
|
*dst++ = 0xFF;
|
|
|
|
*dst++ = chr;
|
2004-01-08 17:30:36 +00:00
|
|
|
} else {
|
2006-02-20 16:51:30 +00:00
|
|
|
val = (_game.version == 8) ? READ_LE_UINT32(src + num) : READ_LE_UINT16(src + num);
|
2004-01-08 17:30:36 +00:00
|
|
|
switch (chr) {
|
|
|
|
case 4:
|
2005-05-26 14:14:32 +00:00
|
|
|
dst += convertIntMessage(dst, end - dst, val);
|
2004-01-08 17:30:36 +00:00
|
|
|
break;
|
|
|
|
case 5:
|
2005-05-26 14:14:32 +00:00
|
|
|
dst += convertVerbMessage(dst, end - dst, val);
|
2004-01-08 17:30:36 +00:00
|
|
|
break;
|
|
|
|
case 6:
|
2005-05-26 14:16:43 +00:00
|
|
|
dst += convertNameMessage(dst, end - dst, val);
|
2004-01-08 17:30:36 +00:00
|
|
|
break;
|
|
|
|
case 7:
|
2005-05-26 14:14:32 +00:00
|
|
|
dst += convertStringMessage(dst, end - dst, val);
|
2004-01-08 17:30:36 +00:00
|
|
|
break;
|
|
|
|
case 9:
|
|
|
|
case 10:
|
|
|
|
case 12:
|
|
|
|
case 13:
|
|
|
|
case 14:
|
|
|
|
// Simply copy these special codes
|
2004-04-04 00:29:13 +00:00
|
|
|
*dst++ = 0xFF;
|
|
|
|
*dst++ = chr;
|
|
|
|
*dst++ = src[num+0];
|
|
|
|
*dst++ = src[num+1];
|
2022-07-03 18:30:47 +02:00
|
|
|
|
|
|
|
// WORKAROUND: In some versions of Sam & Max, talking to Evelyn Morrison
|
|
|
|
// while wearing the bigfoot costume misattributes some lines to Max even
|
|
|
|
// though Sam is the one actually speaking. For example, the French and
|
|
|
|
// German releases use `startAnim(8)` while the English release correctly
|
|
|
|
// uses `startAnim(7)` for this.
|
|
|
|
if (_game.id == GID_SAMNMAX && _currentRoom == 52 && vm.slot[_currentScript].number == 102 &&
|
|
|
|
chr == 9 && readVar(0x8000 + 95) != 0 && (VAR(171) == 997 || VAR(171) == 998) &&
|
|
|
|
dst[-2] == 8 && _enableEnhancements) {
|
|
|
|
dst[-2] = 7;
|
|
|
|
}
|
|
|
|
|
2006-02-20 16:51:30 +00:00
|
|
|
if (_game.version == 8) {
|
2004-04-04 00:29:13 +00:00
|
|
|
*dst++ = src[num+2];
|
|
|
|
*dst++ = src[num+3];
|
2004-01-08 17:30:36 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2005-08-14 01:41:52 +00:00
|
|
|
error("convertMessageToString(): string escape sequence %d unknown", chr);
|
2002-12-28 01:57:19 +00:00
|
|
|
}
|
2006-02-20 16:51:30 +00:00
|
|
|
num += (_game.version == 8) ? 4 : 2;
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
|
|
|
} else {
|
2020-09-23 22:25:11 +02:00
|
|
|
if ((chr != '@') || (_game.version >= 7 && is2ByteCharacter(_language, lastChr)) ||
|
2010-10-17 13:08:00 +00:00
|
|
|
(_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine && _language == Common::JA_JPN) ||
|
2010-10-17 23:52:33 +00:00
|
|
|
(_game.platform == Common::kPlatformFMTowns && _language == Common::JA_JPN && checkSJISCode(lastChr))) {
|
2004-04-04 00:29:13 +00:00
|
|
|
*dst++ = chr;
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
2010-10-17 23:52:33 +00:00
|
|
|
lastChr = chr;
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
2005-07-30 21:11:48 +00:00
|
|
|
|
2004-03-15 02:21:04 +00:00
|
|
|
// Check for a buffer overflow
|
2004-04-04 00:29:13 +00:00
|
|
|
if (dst >= end)
|
2009-05-31 10:02:16 +00:00
|
|
|
error("convertMessageToString: buffer overflow");
|
2004-03-15 02:21:04 +00:00
|
|
|
}
|
2011-08-04 12:33:26 +01:00
|
|
|
|
2021-10-24 17:24:24 +02:00
|
|
|
// WORKAROUND bug #12249 (occurs also in original): Missing actor animation in German versions of SAMNMAX
|
2021-10-23 17:44:42 +02:00
|
|
|
// Adding the missing animation escape sequence while copying the text fixes it.
|
2021-10-24 17:24:24 +02:00
|
|
|
if (_game.id == GID_SAMNMAX && _currentRoom == 56 && vm.slot[_currentScript].number == 200 && _language == Common::DE_DEU) {
|
2021-10-24 14:06:33 +02:00
|
|
|
// 0xE5E6 is the CD version, 0xE373 is for the floppy version
|
|
|
|
if (vm.slot[_currentScript].offs == 0xE5E6 || vm.slot[_currentScript].offs == 0xE373) {
|
2021-10-23 17:44:42 +02:00
|
|
|
*dst++ = 0xFF;
|
|
|
|
*dst++ = 0x09;
|
|
|
|
*dst++ = 0x0E;
|
|
|
|
*dst++ = 0x00;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-04 12:33:26 +01:00
|
|
|
// WORKAROUND: Russian The Dig pads messages with 03. No idea why
|
|
|
|
// it does not work as is with our rendering code, thus fixing it
|
|
|
|
// with a workaround.
|
|
|
|
if (_game.id == GID_DIG) {
|
2021-09-10 18:17:04 +02:00
|
|
|
while (dst > start && *(dst - 1) == 0x03)
|
2011-08-04 12:33:26 +01:00
|
|
|
dst--;
|
|
|
|
}
|
2004-04-04 00:29:13 +00:00
|
|
|
*dst = 0;
|
2005-03-30 17:30:47 +00:00
|
|
|
|
2004-04-04 00:29:13 +00:00
|
|
|
return dstSize - (end - dst);
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
|
|
|
|
2008-05-06 03:00:26 +00:00
|
|
|
#ifdef ENABLE_HE
|
2006-09-02 05:49:59 +00:00
|
|
|
int ScummEngine_v72he::convertMessageToString(const byte *msg, byte *dst, int dstSize) {
|
|
|
|
uint num = 0;
|
|
|
|
byte chr;
|
|
|
|
const byte *src;
|
|
|
|
byte *end;
|
|
|
|
|
|
|
|
assert(dst);
|
|
|
|
end = dst + dstSize;
|
|
|
|
|
|
|
|
if (msg == NULL) {
|
|
|
|
debug(0, "Bad message in convertMessageToString, ignoring");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
src = msg;
|
|
|
|
num = 0;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
chr = src[num++];
|
|
|
|
if (_game.heversion >= 80 && src[num - 1] == '(' && (src[num] == 'p' || src[num] == 'P')) {
|
|
|
|
// Filter out the following prefixes in subtitles
|
|
|
|
// (pickup4)
|
|
|
|
// (PU1)
|
|
|
|
// (PU2)
|
2009-01-29 05:26:12 +00:00
|
|
|
while (src[num++] != ')')
|
|
|
|
;
|
2006-09-02 05:49:59 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ((_game.features & GF_HE_LOCALIZED) && chr == '[') {
|
2009-01-29 05:26:12 +00:00
|
|
|
while (src[num++] != ']')
|
|
|
|
;
|
2006-09-02 05:49:59 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chr == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
*dst++ = chr;
|
|
|
|
|
|
|
|
// Check for a buffer overflow
|
|
|
|
if (dst >= end)
|
2009-05-31 10:02:16 +00:00
|
|
|
error("convertMessageToString: buffer overflow");
|
2006-09-02 05:49:59 +00:00
|
|
|
}
|
|
|
|
*dst = 0;
|
|
|
|
|
|
|
|
return dstSize - (end - dst);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-05-26 14:14:32 +00:00
|
|
|
int ScummEngine::convertIntMessage(byte *dst, int dstSize, int var) {
|
2003-05-24 23:39:40 +00:00
|
|
|
int num;
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2001-10-09 14:30:12 +00:00
|
|
|
num = readVar(var);
|
2004-04-04 00:29:13 +00:00
|
|
|
return snprintf((char *)dst, dstSize, "%d", num);
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
|
|
|
|
2005-05-26 14:14:32 +00:00
|
|
|
int ScummEngine::convertVerbMessage(byte *dst, int dstSize, int var) {
|
2002-12-29 19:51:27 +00:00
|
|
|
int num, k;
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2020-10-28 23:03:23 +09:00
|
|
|
bool isKorVerbGlue = false;
|
|
|
|
|
2020-11-07 23:30:58 +09:00
|
|
|
if (isScummvmKorTarget() && _useCJKMode && var & (1 << 15)) {
|
2020-10-28 23:03:23 +09:00
|
|
|
isKorVerbGlue = true;
|
|
|
|
var &= ~(1 << 15);
|
|
|
|
}
|
|
|
|
|
2001-10-09 14:30:12 +00:00
|
|
|
num = readVar(var);
|
|
|
|
if (num) {
|
2003-12-26 23:11:35 +00:00
|
|
|
for (k = 1; k < _numVerbs; k++) {
|
2021-02-28 01:28:23 -08:00
|
|
|
// Fix ZAK FM-TOWNS bug #1734 by emulating exact (inconsistant?) behavior of the original code
|
2011-05-29 00:29:03 +02:00
|
|
|
if (num == _verbs[k].verbid && !_verbs[k].type && (!_verbs[k].saveid || (_game.version == 3 && _game.platform == Common::kPlatformFMTowns))) {
|
2020-11-10 00:45:54 +09:00
|
|
|
// Process variation of Korean postpositions
|
|
|
|
// Used by Korean fan translated games (monkey1, monkey2)
|
2020-10-28 23:03:23 +09:00
|
|
|
if (isKorVerbGlue) {
|
|
|
|
static const byte code0380[4] = {0xFF, 0x07, 0x03, 0x80}; // "eul/reul"
|
|
|
|
static const byte code0480[4] = {0xFF, 0x07, 0x04, 0x80}; // "wa/gwa"
|
|
|
|
static const byte codeWalkTo[9] = {0xFF, 0x07, 0x03, 0x80, 0x20, 0xC7, 0xE2, 0xC7, 0xD8}; // "eul/reul hyang-hae"
|
|
|
|
byte _transText[10];
|
|
|
|
memset(_transText, 0, sizeof(_transText));
|
|
|
|
// WORKAROUND: MI Korean verb parser
|
|
|
|
if (_game.id == GID_MONKEY_VGA) {
|
|
|
|
switch (_verbs[k].verbid) {
|
|
|
|
case 1: // Open
|
|
|
|
case 2: // Close
|
|
|
|
case 3: // Give
|
|
|
|
case 4: // Turn on
|
|
|
|
case 5: // Turn off
|
|
|
|
case 6: // Push
|
|
|
|
case 7: // Pull
|
|
|
|
case 8: // Use
|
|
|
|
case 9: // Look at
|
|
|
|
case 10: // Walk to
|
|
|
|
memcpy(_transText, codeWalkTo, 9);
|
|
|
|
break;
|
|
|
|
case 11: // Pick up
|
|
|
|
memcpy(_transText, code0380, 4);
|
|
|
|
break;
|
|
|
|
case 13: // Talk to
|
|
|
|
memcpy(_transText, code0480, 4);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return convertMessageToString(_transText, dst, dstSize);
|
|
|
|
}
|
|
|
|
// WORKAROUND: MI2 Korean verb parser
|
|
|
|
if (_game.id == GID_MONKEY2) {
|
|
|
|
if (_verbs[k].verbid <= 11) {
|
|
|
|
switch (_verbs[k].verbid) {
|
|
|
|
case 2: // Open
|
|
|
|
case 3: // Close
|
|
|
|
case 4: // Give
|
|
|
|
case 5: // Push
|
|
|
|
case 6: // Pull
|
|
|
|
case 7: // Use
|
|
|
|
case 8: // Look at
|
|
|
|
case 9: // Pick up
|
|
|
|
memcpy(_transText, code0380, 4);
|
|
|
|
break;
|
|
|
|
case 10: // Talk to
|
|
|
|
memcpy(_transText, code0480, 4);
|
|
|
|
break;
|
|
|
|
case 11: // Walk to
|
|
|
|
memcpy(_transText, codeWalkTo, 9);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return convertMessageToString(_transText, dst, dstSize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const byte *ptr = getResourceAddress(rtVerb, k);
|
|
|
|
return convertMessageToString(ptr, dst, dstSize);
|
|
|
|
}
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2004-04-04 00:29:13 +00:00
|
|
|
return 0;
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
|
|
|
|
2005-05-26 14:16:43 +00:00
|
|
|
int ScummEngine::convertNameMessage(byte *dst, int dstSize, int var) {
|
2001-10-09 14:30:12 +00:00
|
|
|
int num;
|
|
|
|
|
|
|
|
num = readVar(var);
|
2004-03-19 23:19:57 +00:00
|
|
|
if (num) {
|
|
|
|
const byte *ptr = getObjOrActorName(num);
|
2004-04-02 20:31:39 +00:00
|
|
|
if (ptr) {
|
2020-10-28 23:03:23 +09:00
|
|
|
int increment = convertMessageToString(ptr, dst, dstSize);
|
2020-11-10 00:45:54 +09:00
|
|
|
// Save the final consonant (jongsung) of the last Korean character
|
|
|
|
// Used by Korean fan translated games (monkey1, monkey2)
|
2020-11-07 23:30:58 +09:00
|
|
|
if (isScummvmKorTarget() && _useCJKMode) {
|
2020-10-28 23:03:23 +09:00
|
|
|
_krStrPost = 0;
|
|
|
|
int len = resStrLen(ptr);
|
|
|
|
if (len >= 2) {
|
|
|
|
for (int i = len; i > 1; i--) {
|
|
|
|
byte k1 = ptr[i - 2];
|
|
|
|
byte k2 = ptr[i - 1];
|
2020-11-08 17:41:22 +09:00
|
|
|
if (checkKSCode(k1, k2)) {
|
2020-10-28 23:03:23 +09:00
|
|
|
int jongsung = checkJongsung(k1, k2);
|
|
|
|
if (jongsung)
|
|
|
|
_krStrPost |= 1;
|
|
|
|
if (jongsung == 8) // 'ri-eul' final consonant
|
|
|
|
_krStrPost |= (1 << 1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return increment;
|
2004-04-02 20:31:39 +00:00
|
|
|
}
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
2004-04-04 00:29:13 +00:00
|
|
|
return 0;
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
|
|
|
|
2005-05-26 14:14:32 +00:00
|
|
|
int ScummEngine::convertStringMessage(byte *dst, int dstSize, int var) {
|
2003-07-15 21:30:53 +00:00
|
|
|
const byte *ptr;
|
2001-10-24 20:12:52 +00:00
|
|
|
|
2006-02-20 16:51:30 +00:00
|
|
|
if (_game.version <= 2) {
|
2004-03-19 23:19:57 +00:00
|
|
|
byte chr;
|
2004-04-04 00:29:13 +00:00
|
|
|
int i = 0;
|
2004-03-19 23:19:57 +00:00
|
|
|
while ((chr = (byte)_scummVars[var++])) {
|
2004-04-04 00:29:13 +00:00
|
|
|
if (chr != '@') {
|
|
|
|
*dst++ = chr;
|
|
|
|
i++;
|
|
|
|
}
|
2004-03-19 23:19:57 +00:00
|
|
|
}
|
|
|
|
|
2004-04-04 00:29:13 +00:00
|
|
|
return i;
|
2004-03-19 23:19:57 +00:00
|
|
|
}
|
|
|
|
|
2006-02-20 16:51:30 +00:00
|
|
|
if (_game.version == 3 || (_game.version >= 6 && _game.heversion < 72))
|
2002-07-12 21:36:20 +00:00
|
|
|
var = readVar(var);
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2020-11-10 00:45:54 +09:00
|
|
|
// Process variation of Korean postpositions
|
|
|
|
// Used by Korean fan translated games (monkey1, monkey2)
|
2020-11-07 23:30:58 +09:00
|
|
|
if (isScummvmKorTarget() && _useCJKMode && (var & (1 << 15))) {
|
2020-10-28 23:03:23 +09:00
|
|
|
int idx;
|
|
|
|
static const byte codeIdx[] = {0x00, 0x00, 0xC0, 0xB8, 0x00, 0x00, 0xC0, 0xCC, 0xB0, 0xA1, 0xC0, 0xCC, 0xB8, 0xA6, 0xC0, 0xBB, 0xBF, 0xCD, 0xB0, 0xFA, 0xB4, 0xC2, 0xC0, 0xBA};
|
|
|
|
|
|
|
|
if ((var & ~(1 << 15)) == 0)
|
|
|
|
idx = (2 * (var & ~(1 << 15)) + (_krStrPost & 1) - bool(_krStrPost & 2)) * 2;
|
|
|
|
else
|
|
|
|
idx = (2 * (var & ~(1 << 15)) + (_krStrPost & 1)) * 2;
|
|
|
|
|
|
|
|
byte _transText[3];
|
|
|
|
const byte byteIdx[] = {codeIdx[idx], codeIdx[idx + 1]};
|
|
|
|
|
|
|
|
memset(_transText, 0, sizeof(_transText));
|
|
|
|
memcpy(_transText, byteIdx, 2);
|
|
|
|
|
|
|
|
return convertMessageToString(_transText, dst, dstSize);
|
|
|
|
} else if (var) {
|
2001-10-24 20:12:52 +00:00
|
|
|
ptr = getStringAddress(var);
|
2001-10-09 14:30:12 +00:00
|
|
|
if (ptr) {
|
2020-10-28 23:03:23 +09:00
|
|
|
int increment = convertMessageToString(ptr, dst, dstSize);
|
2020-11-10 00:45:54 +09:00
|
|
|
// Save the final consonant (jongsung) of the last Korean character
|
|
|
|
// Used by Korean fan translated games (monkey1, monkey2)
|
2020-11-07 23:30:58 +09:00
|
|
|
if (isScummvmKorTarget() && _useCJKMode) {
|
2020-10-28 23:03:23 +09:00
|
|
|
_krStrPost = 0;
|
|
|
|
for (int i = resStrLen(ptr); i > 1; i--) {
|
|
|
|
byte k1 = ptr[i - 2];
|
|
|
|
byte k2 = ptr[i - 1];
|
2020-11-08 17:41:22 +09:00
|
|
|
if (checkKSCode(k1, k2)) {
|
2020-10-28 23:03:23 +09:00
|
|
|
int jongsung = checkJongsung(k1, k2);
|
|
|
|
if (jongsung)
|
|
|
|
_krStrPost |= 1;
|
|
|
|
if (jongsung == 8) // 'ri-eul' final consonant
|
|
|
|
_krStrPost |= (1 << 1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return increment;
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
2004-01-08 17:30:36 +00:00
|
|
|
}
|
2004-04-04 00:29:13 +00:00
|
|
|
return 0;
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
|
|
|
|
2006-01-10 00:34:13 +00:00
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
#pragma mark --- Charset initialisation ---
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
2008-05-06 03:00:26 +00:00
|
|
|
#ifdef ENABLE_HE
|
2005-05-05 13:16:50 +00:00
|
|
|
void ScummEngine_v80he::initCharset(int charsetno) {
|
|
|
|
ScummEngine::initCharset(charsetno);
|
|
|
|
VAR(VAR_CURRENT_CHARSET) = charsetno;
|
|
|
|
}
|
2005-05-14 22:56:41 +00:00
|
|
|
#endif
|
2005-05-05 13:16:50 +00:00
|
|
|
|
2003-10-02 22:42:03 +00:00
|
|
|
void ScummEngine::initCharset(int charsetno) {
|
2006-02-20 16:51:30 +00:00
|
|
|
if (_game.id == GID_FT) {
|
2006-09-17 20:36:48 +00:00
|
|
|
if (!_res->isResourceLoaded(rtCharset, charsetno))
|
2004-08-14 19:39:16 +00:00
|
|
|
loadCharset(charsetno);
|
|
|
|
} else {
|
|
|
|
if (!getResourceAddress(rtCharset, charsetno))
|
|
|
|
loadCharset(charsetno);
|
|
|
|
}
|
2001-10-09 14:30:12 +00:00
|
|
|
|
2004-04-05 23:54:58 +00:00
|
|
|
_string[0]._default.charset = charsetno;
|
|
|
|
_string[1]._default.charset = charsetno;
|
2001-10-09 14:30:12 +00:00
|
|
|
|
2006-01-10 21:13:43 +00:00
|
|
|
memcpy(_charsetColorMap, _charsetData[charsetno], sizeof(_charsetColorMap));
|
2001-10-09 14:30:12 +00:00
|
|
|
}
|
2002-12-25 21:42:22 +00:00
|
|
|
|
2003-06-02 23:18:52 +00:00
|
|
|
|
2006-01-10 00:34:13 +00:00
|
|
|
#pragma mark -
|
|
|
|
#pragma mark --- Translation/localization code ---
|
|
|
|
#pragma mark -
|
2003-06-02 23:18:52 +00:00
|
|
|
|
|
|
|
|
2008-05-06 03:00:26 +00:00
|
|
|
#ifdef ENABLE_SCUMM_7_8
|
2005-11-02 21:47:44 +00:00
|
|
|
static int indexCompare(const void *p1, const void *p2) {
|
2005-10-17 15:00:23 +00:00
|
|
|
const ScummEngine_v7::LangIndexNode *i1 = (const ScummEngine_v7::LangIndexNode *) p1;
|
|
|
|
const ScummEngine_v7::LangIndexNode *i2 = (const ScummEngine_v7::LangIndexNode *) p2;
|
2003-02-14 16:01:55 +00:00
|
|
|
|
|
|
|
return strcmp(i1->tag, i2->tag);
|
|
|
|
}
|
|
|
|
|
2004-03-15 02:21:04 +00:00
|
|
|
// Create an index of the language file.
|
2005-04-28 22:17:23 +00:00
|
|
|
void ScummEngine_v7::loadLanguageBundle() {
|
2020-11-15 15:45:07 +09:00
|
|
|
if (isScummvmKorTarget()) {
|
|
|
|
// Support language bundle for FT
|
|
|
|
ScummEngine::loadLanguageBundle();
|
|
|
|
return;
|
|
|
|
}
|
2004-07-26 23:15:01 +00:00
|
|
|
ScummFile file;
|
2003-02-14 16:01:55 +00:00
|
|
|
int32 size;
|
2002-09-17 09:18:47 +00:00
|
|
|
|
2006-02-20 16:51:30 +00:00
|
|
|
if (_game.id == GID_DIG) {
|
2004-07-26 23:15:01 +00:00
|
|
|
openFile(file, "language.bnd");
|
2006-02-20 16:51:30 +00:00
|
|
|
} else if (_game.id == GID_CMI) {
|
2004-07-26 23:15:01 +00:00
|
|
|
openFile(file, "language.tab");
|
2003-01-30 08:10:16 +00:00
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
2003-06-07 00:49:36 +00:00
|
|
|
if (file.isOpen() == false) {
|
2002-09-17 09:18:47 +00:00
|
|
|
_existLanguageFile = false;
|
|
|
|
return;
|
|
|
|
}
|
2005-07-30 21:11:48 +00:00
|
|
|
|
2003-05-30 23:24:29 +00:00
|
|
|
_existLanguageFile = true;
|
2003-01-30 08:10:16 +00:00
|
|
|
|
2003-02-14 16:01:55 +00:00
|
|
|
size = file.size();
|
2003-03-07 23:55:28 +00:00
|
|
|
_languageBuffer = (char *)calloc(1, size+1);
|
2003-02-14 16:01:55 +00:00
|
|
|
file.read(_languageBuffer, size);
|
2002-09-17 09:18:47 +00:00
|
|
|
file.close();
|
2003-02-14 16:01:55 +00:00
|
|
|
|
2003-05-30 23:24:29 +00:00
|
|
|
int32 i;
|
|
|
|
char *ptr = _languageBuffer;
|
2003-02-14 16:01:55 +00:00
|
|
|
|
2003-05-30 23:24:29 +00:00
|
|
|
// Count the number of lines in the language file.
|
2004-03-15 02:21:04 +00:00
|
|
|
for (_languageIndexSize = 0; ; _languageIndexSize++) {
|
2003-05-30 23:24:29 +00:00
|
|
|
ptr = strpbrk(ptr, "\n\r");
|
|
|
|
if (ptr == NULL)
|
|
|
|
break;
|
|
|
|
while (*ptr == '\n' || *ptr == '\r')
|
|
|
|
ptr++;
|
|
|
|
}
|
2003-02-14 16:01:55 +00:00
|
|
|
|
2003-05-30 23:24:29 +00:00
|
|
|
// Fill the language file index. This is just an array of
|
|
|
|
// tags and offsets. I did consider using a balanced tree
|
|
|
|
// instead, but the extra overhead in the node structure would
|
|
|
|
// easily have doubled the memory consumption of the index.
|
2005-06-04 21:21:18 +00:00
|
|
|
// And anyway, using qsort + bsearch gives us the exact same
|
|
|
|
// O(log(n)) access time anyway ;-).
|
2003-02-14 16:01:55 +00:00
|
|
|
|
2003-05-30 23:24:29 +00:00
|
|
|
_languageIndex = (LangIndexNode *)calloc(_languageIndexSize, sizeof(LangIndexNode));
|
2003-02-14 16:01:55 +00:00
|
|
|
|
2003-05-30 23:24:29 +00:00
|
|
|
ptr = _languageBuffer;
|
2003-02-14 16:01:55 +00:00
|
|
|
|
2006-02-20 16:51:30 +00:00
|
|
|
if (_game.id == GID_DIG) {
|
2003-05-30 23:24:29 +00:00
|
|
|
int lineCount = _languageIndexSize;
|
|
|
|
const char *baseTag = "";
|
|
|
|
byte enc = 0; // Initially assume the language file is not encoded
|
2005-07-30 21:11:48 +00:00
|
|
|
|
2003-05-30 23:24:29 +00:00
|
|
|
// We'll determine the real index size as we go.
|
|
|
|
_languageIndexSize = 0;
|
|
|
|
for (i = 0; i < lineCount; i++) {
|
|
|
|
if (*ptr == '!') {
|
|
|
|
// Don't know what a line with '!' means, just ignore it
|
2005-11-27 10:32:21 +00:00
|
|
|
} else if (*ptr == 'h') {
|
|
|
|
// File contains Korean text (Hangul). just ignore it
|
2008-04-08 20:04:51 +00:00
|
|
|
} else if (*ptr == 'j') {
|
|
|
|
// File contains Japanese text. just ignore it
|
2010-06-27 13:06:22 +00:00
|
|
|
} else if (*ptr == 'c') {
|
|
|
|
// File contains Chinese text. just ignore it
|
2003-05-30 23:24:29 +00:00
|
|
|
} else if (*ptr == 'e') {
|
|
|
|
// File is encoded!
|
|
|
|
enc = 0x13;
|
|
|
|
} else if (*ptr == '@') {
|
|
|
|
// A new 'base tag'
|
|
|
|
baseTag = ptr + 1;
|
|
|
|
} else if (*ptr == '#') {
|
|
|
|
// Number of subtags following a given basetag. We don't need that
|
|
|
|
// information so we just skip it
|
2012-02-20 16:03:39 +01:00
|
|
|
} else if (Common::isDigit(*ptr)) {
|
2003-05-30 23:24:29 +00:00
|
|
|
int idx = 0;
|
|
|
|
// A number (up to three digits)...
|
2012-02-20 16:03:39 +01:00
|
|
|
while (Common::isDigit(*ptr)) {
|
2003-05-30 23:24:29 +00:00
|
|
|
idx = idx * 10 + (*ptr - '0');
|
|
|
|
ptr++;
|
|
|
|
}
|
2005-07-30 21:11:48 +00:00
|
|
|
|
2003-05-30 23:24:29 +00:00
|
|
|
// ...followed by a slash...
|
|
|
|
assert(*ptr == '/');
|
|
|
|
ptr++;
|
2005-07-30 21:11:48 +00:00
|
|
|
|
2003-05-30 23:24:29 +00:00
|
|
|
// ...and then the translated message, possibly encoded
|
|
|
|
_languageIndex[_languageIndexSize].offset = ptr - _languageBuffer;
|
2005-07-30 21:11:48 +00:00
|
|
|
|
2003-05-30 23:24:29 +00:00
|
|
|
// Decode string if necessary.
|
|
|
|
if (enc) {
|
|
|
|
while (*ptr != '\n' && *ptr != '\r')
|
|
|
|
*ptr++ ^= enc;
|
|
|
|
}
|
2005-07-30 21:11:48 +00:00
|
|
|
|
2003-05-30 23:24:29 +00:00
|
|
|
// The tag is the basetag, followed by a dot and then the index
|
|
|
|
sprintf(_languageIndex[_languageIndexSize].tag, "%s.%03d", baseTag, idx);
|
2005-07-30 21:11:48 +00:00
|
|
|
|
2003-05-30 23:24:29 +00:00
|
|
|
// That was another index entry
|
|
|
|
_languageIndexSize++;
|
2005-03-30 17:30:47 +00:00
|
|
|
} else {
|
2009-05-31 10:02:16 +00:00
|
|
|
error("Unknown language.bnd entry found: '%s'", ptr);
|
2003-05-30 23:24:29 +00:00
|
|
|
}
|
2003-02-14 16:01:55 +00:00
|
|
|
|
2003-05-30 23:24:29 +00:00
|
|
|
// Skip over newlines (and turn them into null bytes)
|
|
|
|
ptr = strpbrk(ptr, "\n\r");
|
|
|
|
if (ptr == NULL)
|
|
|
|
break;
|
|
|
|
while (*ptr == '\n' || *ptr == '\r')
|
|
|
|
*ptr++ = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (i = 0; i < _languageIndexSize; i++) {
|
2003-05-30 22:57:19 +00:00
|
|
|
// First 8 chars in the line give the string ID / 'tag'
|
2003-05-30 23:32:02 +00:00
|
|
|
int j;
|
2012-02-20 16:03:39 +01:00
|
|
|
for (j = 0; j < 8 && !Common::isSpace(*ptr); j++, ptr++)
|
2003-05-30 22:57:19 +00:00
|
|
|
_languageIndex[i].tag[j] = toupper(*ptr);
|
2003-02-14 16:01:55 +00:00
|
|
|
_languageIndex[i].tag[j] = 0;
|
2005-07-30 21:11:48 +00:00
|
|
|
|
2003-05-30 22:57:19 +00:00
|
|
|
// After that follows a single space which we skip
|
2012-02-20 16:03:39 +01:00
|
|
|
assert(Common::isSpace(*ptr));
|
2003-05-30 22:57:19 +00:00
|
|
|
ptr++;
|
2005-06-04 21:21:18 +00:00
|
|
|
|
2003-05-30 22:57:19 +00:00
|
|
|
// Then comes the translated string: we record an offset to that.
|
2003-02-14 16:01:55 +00:00
|
|
|
_languageIndex[i].offset = ptr - _languageBuffer;
|
2005-07-30 21:11:48 +00:00
|
|
|
|
2003-05-30 22:57:19 +00:00
|
|
|
// Skip over newlines (and turn them into null bytes)
|
2003-02-14 16:01:55 +00:00
|
|
|
ptr = strpbrk(ptr, "\n\r");
|
|
|
|
if (ptr == NULL)
|
|
|
|
break;
|
|
|
|
while (*ptr == '\n' || *ptr == '\r')
|
2003-05-30 22:57:19 +00:00
|
|
|
*ptr++ = 0;
|
2005-06-04 21:21:18 +00:00
|
|
|
|
2021-06-07 17:44:05 -06:00
|
|
|
// Convert '\n' code to a newline. See also bug #1487.
|
2005-06-04 21:21:18 +00:00
|
|
|
char *src, *dst;
|
|
|
|
src = dst = _languageBuffer + _languageIndex[i].offset;
|
|
|
|
while (*src) {
|
|
|
|
if (src[0] == '\\' && src[1] == 'n') {
|
|
|
|
*dst++ = '\n';
|
|
|
|
src += 2;
|
|
|
|
} else {
|
|
|
|
*dst++ = *src++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*dst = 0;
|
2003-02-14 16:01:55 +00:00
|
|
|
}
|
|
|
|
}
|
2003-05-30 23:24:29 +00:00
|
|
|
|
|
|
|
// Sort the index nodes. We'll later use bsearch on it, which is just as efficient
|
|
|
|
// as using a binary tree, speed wise.
|
|
|
|
qsort(_languageIndex, _languageIndexSize, sizeof(LangIndexNode), indexCompare);
|
2002-09-17 09:18:47 +00:00
|
|
|
}
|
|
|
|
|
2005-04-28 22:17:23 +00:00
|
|
|
void ScummEngine_v7::playSpeech(const byte *ptr) {
|
2009-07-17 00:10:10 +00:00
|
|
|
if (_game.id == GID_DIG && (ConfMan.getBool("speech_mute") || VAR(VAR_VOICE_MODE) == 2))
|
|
|
|
return;
|
|
|
|
|
2006-02-20 16:51:30 +00:00
|
|
|
if ((_game.id == GID_DIG || _game.id == GID_CMI) && ptr[0]) {
|
2019-09-15 20:20:03 +01:00
|
|
|
Common::String pointerStr((const char *)ptr);
|
2003-07-15 21:30:53 +00:00
|
|
|
|
|
|
|
// Play speech
|
2006-02-20 16:51:30 +00:00
|
|
|
if (!(_game.features & GF_DEMO) && (_game.id == GID_CMI)) // CMI demo does not have .IMX for voice
|
2019-09-15 20:20:03 +01:00
|
|
|
pointerStr += ".IMX";
|
2003-07-15 21:30:53 +00:00
|
|
|
|
2004-03-19 23:19:57 +00:00
|
|
|
_sound->stopTalkSound();
|
2004-08-02 17:55:14 +00:00
|
|
|
_imuseDigital->stopSound(kTalkSoundID);
|
2021-11-15 15:05:18 -08:00
|
|
|
_imuseDigital->startVoice(kTalkSoundID, pointerStr.c_str(), _actorToPrintStrFor);
|
2004-07-24 14:24:34 +00:00
|
|
|
_sound->talkSound(0, 0, 2);
|
2003-07-15 21:30:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-28 22:17:23 +00:00
|
|
|
void ScummEngine_v7::translateText(const byte *text, byte *trans_buff) {
|
2020-11-15 15:45:07 +09:00
|
|
|
if (isScummvmKorTarget()) {
|
|
|
|
// Support language bundle for FT
|
|
|
|
ScummEngine::translateText(text, trans_buff);
|
|
|
|
return;
|
|
|
|
}
|
2004-04-02 21:42:42 +00:00
|
|
|
LangIndexNode target;
|
2004-04-02 21:55:30 +00:00
|
|
|
LangIndexNode *found = NULL;
|
2004-04-02 21:42:42 +00:00
|
|
|
int i;
|
2005-07-30 21:11:48 +00:00
|
|
|
|
2005-03-30 17:30:47 +00:00
|
|
|
trans_buff[0] = 0;
|
|
|
|
_lastStringTag[0] = 0;
|
2005-04-06 18:57:58 +00:00
|
|
|
|
2008-05-28 13:00:41 +00:00
|
|
|
if (_game.version >= 7 && text[0] == '/') {
|
|
|
|
// Extract the string tag from the text: /..../
|
|
|
|
for (i = 0; (i < 12) && (text[i + 1] != '/'); i++)
|
|
|
|
_lastStringTag[i] = toupper(text[i + 1]);
|
|
|
|
_lastStringTag[i] = 0;
|
|
|
|
}
|
|
|
|
|
2021-02-28 01:28:23 -08:00
|
|
|
// WORKAROUND for bug #1977.
|
2008-04-30 01:29:17 +00:00
|
|
|
if (_game.id == GID_DIG) {
|
2008-05-28 02:55:42 +00:00
|
|
|
// Based on the second release of The Dig
|
2008-05-28 13:00:41 +00:00
|
|
|
// Only applies to the subtitles and not speech
|
2005-04-06 18:57:58 +00:00
|
|
|
if (!strcmp((const char *)text, "faint light"))
|
|
|
|
text = (const byte *)"/NEW.007/faint light";
|
|
|
|
else if (!strcmp((const char *)text, "glowing crystal"))
|
|
|
|
text = (const byte *)"/NEW.008/glowing crystal";
|
|
|
|
else if (!strcmp((const char *)text, "glowing crystals"))
|
|
|
|
text = (const byte *)"/NEW.009/glowing crystals";
|
|
|
|
else if (!strcmp((const char *)text, "pit"))
|
|
|
|
text = (const byte *)"/NEW.010/pit";
|
2008-06-01 10:37:14 +00:00
|
|
|
else if (!strcmp((const char *)text, "You wish."))
|
2005-04-06 18:57:58 +00:00
|
|
|
text = (const byte *)"/NEW.011/You wish.";
|
2008-06-01 10:37:14 +00:00
|
|
|
else if (!strcmp((const char *)text, "In your dreams."))
|
2005-04-06 18:57:58 +00:00
|
|
|
text = (const byte *)"/NEW.012/In your dreams";
|
|
|
|
else if (!strcmp((const char *)text, "left"))
|
|
|
|
text = (const byte *)"/CATHPLAT.068/left";
|
|
|
|
else if (!strcmp((const char *)text, "right"))
|
|
|
|
text = (const byte *)"/CATHPLAT.070/right";
|
2007-02-05 22:49:58 +00:00
|
|
|
else if (!strcmp((const char *)text, "top"))
|
2005-04-06 18:57:58 +00:00
|
|
|
text = (const byte *)"/CATHPLAT.067/top";
|
|
|
|
else if (!strcmp((const char *)text, "exit"))
|
|
|
|
text = (const byte *)"/SKY.008/exit";
|
|
|
|
else if (!strcmp((const char *)text, "unattached lens"))
|
|
|
|
text = (const byte *)"/NEW.013/unattached lens";
|
|
|
|
else if (!strcmp((const char *)text, "lens slot"))
|
|
|
|
text = (const byte *)"/NEW.014/lens slot";
|
2008-04-30 01:29:17 +00:00
|
|
|
else if (!strcmp((const char *)text, "Jonathon Jackson"))
|
|
|
|
text = (const byte *)"Aram Gutowski";
|
|
|
|
else if (!strcmp((const char *)text, "Brink"))
|
|
|
|
text = (const byte *)"/CREVICE.049/Brink";
|
|
|
|
else if (!strcmp((const char *)text, "Robbins"))
|
|
|
|
text = (const byte *)"/NEST.061/Robbins";
|
2005-04-06 18:57:58 +00:00
|
|
|
}
|
|
|
|
|
2005-07-30 21:11:48 +00:00
|
|
|
|
2006-02-20 16:51:30 +00:00
|
|
|
if (_game.version >= 7 && text[0] == '/') {
|
2004-04-02 21:55:30 +00:00
|
|
|
// Extract the string tag from the text: /..../
|
2004-04-02 21:42:42 +00:00
|
|
|
for (i = 0; (i < 12) && (text[i + 1] != '/'); i++)
|
2008-05-28 13:00:41 +00:00
|
|
|
target.tag[i] = toupper(text[i + 1]);
|
|
|
|
target.tag[i] = 0;
|
2004-04-02 21:42:42 +00:00
|
|
|
text += i + 2;
|
|
|
|
|
2004-04-02 21:55:30 +00:00
|
|
|
// If a language file was loaded, try to find a translated version
|
|
|
|
// by doing a lookup on the string tag.
|
2004-04-02 21:42:42 +00:00
|
|
|
if (_existLanguageFile) {
|
2005-04-06 18:57:58 +00:00
|
|
|
// HACK: These are used for the object line in COMI when
|
2004-04-02 21:42:42 +00:00
|
|
|
// using one object on another. I don't know if the
|
|
|
|
// text in the language file is a placeholder or if
|
|
|
|
// we're supposed to use it, but at least in the
|
|
|
|
// English version things will work so much better if
|
|
|
|
// we can't find translations for these.
|
2005-07-30 21:11:48 +00:00
|
|
|
|
2005-04-06 18:57:58 +00:00
|
|
|
if (*text && strcmp(target.tag, "PU_M001") != 0 && strcmp(target.tag, "PU_M002") != 0)
|
2004-04-02 21:42:42 +00:00
|
|
|
found = (LangIndexNode *)bsearch(&target, _languageIndex, _languageIndexSize, sizeof(LangIndexNode), indexCompare);
|
2004-04-02 21:55:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (found != NULL) {
|
|
|
|
strcpy((char *)trans_buff, _languageBuffer + found->offset);
|
|
|
|
|
2006-02-20 16:51:30 +00:00
|
|
|
if ((_game.id == GID_DIG) && !(_game.features & GF_DEMO)) {
|
2004-04-02 21:55:30 +00:00
|
|
|
// Replace any '%___' by the corresponding special codes in the source text
|
|
|
|
const byte *src = text;
|
|
|
|
char *dst = (char *)trans_buff;
|
|
|
|
|
|
|
|
while ((dst = strstr(dst, "%___"))) {
|
|
|
|
// Search for a special code in the message.
|
|
|
|
while (*src && *src != 0xFF) {
|
|
|
|
src++;
|
2004-04-02 20:31:39 +00:00
|
|
|
}
|
2005-07-30 21:11:48 +00:00
|
|
|
|
2004-04-02 21:55:30 +00:00
|
|
|
// Replace the %___ by the special code. Luckily, we can do
|
|
|
|
// that in-place.
|
|
|
|
if (*src == 0xFF) {
|
|
|
|
memcpy(dst, src, 4);
|
|
|
|
src += 4;
|
|
|
|
dst += 4;
|
|
|
|
} else
|
|
|
|
break;
|
2003-03-08 17:39:18 +00:00
|
|
|
}
|
|
|
|
}
|
2004-04-02 21:55:30 +00:00
|
|
|
} else {
|
|
|
|
// Default: just copy the string
|
|
|
|
memcpy(trans_buff, text, resStrLen(text) + 1);
|
2002-09-17 09:18:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-05-14 22:56:41 +00:00
|
|
|
#endif
|
|
|
|
|
2020-11-15 15:45:07 +09:00
|
|
|
void ScummEngine::loadLanguageBundle() {
|
|
|
|
if (!isScummvmKorTarget()) {
|
|
|
|
_existLanguageFile = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ScummFile file;
|
|
|
|
openFile(file, "korean.trs");
|
|
|
|
|
|
|
|
if (!file.isOpen()) {
|
|
|
|
_existLanguageFile = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_existLanguageFile = true;
|
|
|
|
|
|
|
|
int size = file.size();
|
|
|
|
|
|
|
|
uint32 magic1 = file.readUint32BE();
|
|
|
|
uint32 magic2 = file.readUint32BE();
|
|
|
|
|
|
|
|
if (magic1 != MKTAG('S', 'C', 'V', 'M') || magic2 != MKTAG('T', 'R', 'S', ' ')) {
|
|
|
|
_existLanguageFile = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_numTranslatedLines = file.readUint16LE();
|
|
|
|
_translatedLines = new TranslatedLine[_numTranslatedLines];
|
|
|
|
_languageLineIndex = new uint16[_numTranslatedLines];
|
|
|
|
|
|
|
|
// sanity check
|
|
|
|
for (int i = 0; i < _numTranslatedLines; i++) {
|
|
|
|
_languageLineIndex[i] = 0xffff;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < _numTranslatedLines; i++) {
|
|
|
|
int idx = file.readUint16LE();
|
|
|
|
assert(idx < _numTranslatedLines);
|
|
|
|
_languageLineIndex[idx] = i;
|
|
|
|
_translatedLines[i].originalTextOffset = file.readUint32LE();
|
|
|
|
_translatedLines[i].translatedTextOffset = file.readUint32LE();
|
|
|
|
}
|
|
|
|
|
|
|
|
// sanity check
|
|
|
|
for (int i = 0; i < _numTranslatedLines; i++) {
|
|
|
|
if (_languageLineIndex[i] == 0xffff) {
|
|
|
|
error("Invalid language bundle file");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Room
|
|
|
|
byte numTranslatedRoom = file.readByte();
|
|
|
|
for (uint32 i = 0; i < numTranslatedRoom; i++) {
|
|
|
|
byte roomId = file.readByte();
|
|
|
|
|
2021-05-16 21:06:21 +09:00
|
|
|
TranslationRoom &room = _roomIndex.getOrCreateVal(roomId);
|
2020-11-15 15:45:07 +09:00
|
|
|
|
|
|
|
uint16 numScript = file.readUint16LE();
|
|
|
|
for (int sc = 0; sc < numScript; sc++) {
|
|
|
|
uint32 scrpKey = file.readUint32LE();
|
|
|
|
uint16 scrpLeft = file.readUint16LE();
|
|
|
|
uint16 scrpRight = file.readUint16LE();
|
|
|
|
|
|
|
|
room.scriptRanges.setVal(scrpKey, TranslationRange(scrpLeft, scrpRight));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int bodyPos = file.pos();
|
|
|
|
|
|
|
|
for (int i = 0; i < _numTranslatedLines; i++) {
|
|
|
|
_translatedLines[i].originalTextOffset -= bodyPos;
|
|
|
|
_translatedLines[i].translatedTextOffset -= bodyPos;
|
|
|
|
}
|
|
|
|
_languageBuffer = new byte[size - bodyPos];
|
|
|
|
file.read(_languageBuffer, size - bodyPos);
|
|
|
|
file.close();
|
|
|
|
|
|
|
|
debug(2, "loadLanguageBundle: Loaded %d entries", _numTranslatedLines);
|
|
|
|
}
|
|
|
|
|
|
|
|
const byte *ScummEngine::searchTranslatedLine(const byte *text, const TranslationRange &range, bool useIndex) {
|
|
|
|
int textLen = resStrLen(text);
|
|
|
|
|
|
|
|
int left = range.left;
|
|
|
|
int right = range.right;
|
|
|
|
|
|
|
|
int dbgIterationCount = 0;
|
|
|
|
|
|
|
|
while (left <= right) {
|
|
|
|
dbgIterationCount++;
|
|
|
|
debug(8, "searchTranslatedLine: Range: %d - %d", left, right);
|
|
|
|
int mid = (left + right) / 2;
|
|
|
|
int idx = useIndex ? _languageLineIndex[mid] : mid;
|
|
|
|
const byte *originalText = &_languageBuffer[_translatedLines[idx].originalTextOffset];
|
|
|
|
int originalLen = resStrLen(originalText);
|
|
|
|
int compare = memcmp(text, originalText, MIN(textLen + 1, originalLen + 1));
|
|
|
|
if (compare == 0) {
|
|
|
|
debug(8, "searchTranslatedLine: Found in %d iteration", dbgIterationCount);
|
|
|
|
const byte *translatedText = &_languageBuffer[_translatedLines[idx].translatedTextOffset];
|
|
|
|
return translatedText;
|
|
|
|
} else if (compare < 0) {
|
|
|
|
right = mid - 1;
|
|
|
|
} else if (compare > 0) {
|
|
|
|
left = mid + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
debug(8, "searchTranslatedLine: Not found in %d iteration", dbgIterationCount);
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2005-05-14 22:56:41 +00:00
|
|
|
void ScummEngine::translateText(const byte *text, byte *trans_buff) {
|
2020-11-15 15:45:07 +09:00
|
|
|
if (_existLanguageFile) {
|
|
|
|
if (_currentScript == 0xff) {
|
|
|
|
// used in drawVerb(), etc
|
|
|
|
debug(7, "translateText: Room=%d, CurrentScript == 0xff", _currentRoom);
|
|
|
|
} else {
|
|
|
|
// Use series of heuristics to preserve "the context of the conversation",
|
|
|
|
// since one English text can be translated differently depending on the context.
|
|
|
|
ScriptSlot *slot = &vm.slot[_currentScript];
|
|
|
|
debug(7, "translateText: Room=%d, Script=%d, WIO=%d", _currentRoom, slot->number, slot->where);
|
|
|
|
|
|
|
|
byte roomKey = 0;
|
|
|
|
if (slot->where != WIO_GLOBAL) {
|
|
|
|
roomKey = _currentRoom;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 scriptKey = slot->where << 16 | slot->number;
|
|
|
|
if (slot->where == WIO_ROOM) {
|
|
|
|
scriptKey = slot->where << 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
// First search by _currentRoom and _currentScript
|
|
|
|
Common::HashMap<byte, TranslationRoom>::const_iterator iterator = _roomIndex.find(roomKey);
|
|
|
|
if (iterator != _roomIndex.end()) {
|
|
|
|
const TranslationRoom &room = iterator->_value;
|
|
|
|
TranslationRange scrpRange;
|
|
|
|
if (room.scriptRanges.tryGetVal(scriptKey, scrpRange)) {
|
|
|
|
const byte *translatedText = searchTranslatedLine(text, scrpRange, true);
|
|
|
|
if (translatedText) {
|
|
|
|
debug(7, "translateText: Found by heuristic #1");
|
|
|
|
memcpy(trans_buff, translatedText, resStrLen(translatedText) + 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If not found, search for current room
|
|
|
|
roomKey = _currentRoom;
|
|
|
|
scriptKey = WIO_ROOM << 16;
|
|
|
|
iterator = _roomIndex.find(roomKey);
|
|
|
|
if (iterator != _roomIndex.end()) {
|
|
|
|
const TranslationRoom &room = iterator->_value;
|
|
|
|
TranslationRange scrpRange;
|
|
|
|
if (room.scriptRanges.tryGetVal(scriptKey, scrpRange)) {
|
|
|
|
const byte *translatedText = searchTranslatedLine(text, scrpRange, true);
|
|
|
|
if (translatedText) {
|
|
|
|
debug(7, "translateText: Found by heuristic #2");
|
|
|
|
memcpy(trans_buff, translatedText, resStrLen(translatedText) + 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try full search
|
|
|
|
const byte *translatedText = searchTranslatedLine(text, TranslationRange(0, _numTranslatedLines - 1), false);
|
|
|
|
if (translatedText) {
|
|
|
|
debug(7, "translateText: Found by full search");
|
|
|
|
memcpy(trans_buff, translatedText, resStrLen(translatedText) + 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
debug(7, "translateText: Not found");
|
|
|
|
}
|
|
|
|
|
2005-05-14 22:56:41 +00:00
|
|
|
// Default: just copy the string
|
|
|
|
memcpy(trans_buff, text, resStrLen(text) + 1);
|
|
|
|
}
|
|
|
|
|
2021-07-16 15:23:00 +03:00
|
|
|
bool ScummEngine::reverseIfNeeded(const byte *text, byte *reverseBuf) const {
|
|
|
|
if (_language != Common::HE_ISR)
|
|
|
|
return false;
|
|
|
|
if (_game.id != GID_LOOM && _game.id != GID_ZAK)
|
|
|
|
return false;
|
|
|
|
strcpy(reinterpret_cast<char *>(reverseBuf), reinterpret_cast<const char *>(text));
|
|
|
|
fakeBidiString(reverseBuf, true);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Common::CodePage ScummEngine::getDialogCodePage() const {
|
|
|
|
switch (_language) {
|
|
|
|
case Common::KO_KOR:
|
|
|
|
return Common::kWindows949;
|
|
|
|
case Common::JA_JPN:
|
|
|
|
return Common::kWindows932;
|
|
|
|
case Common::ZH_TWN:
|
2021-11-04 16:49:21 -04:00
|
|
|
case Common::ZH_CHN:
|
2021-07-16 15:23:00 +03:00
|
|
|
return Common::kWindows950;
|
|
|
|
case Common::RU_RUS:
|
|
|
|
return Common::kDos866;
|
|
|
|
case Common::HE_ISR:
|
|
|
|
switch (_game.id) {
|
|
|
|
case GID_LOOM:
|
|
|
|
case GID_ZAK:
|
|
|
|
return Common::kDos862;
|
|
|
|
default:
|
|
|
|
return Common::kWindows1255;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return Common::kCodePageInvalid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-30 22:31:35 +02:00
|
|
|
} // End of namespace Scumm
|