SCUMM: Support Lokalizator plugin.

This was used by Russian translation of Pajama Sam 2 and Spy Fox 3
This commit is contained in:
Vladimir Serbinenko 2020-12-07 18:27:18 +01:00 committed by Eugene Sandulenko
parent cc375694be
commit 5c91fb8d6a
8 changed files with 202 additions and 9 deletions

View file

@ -0,0 +1,122 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "common/endian.h"
#include "scumm/he/localizer.h"
#include "common/file.h"
#include "common/debug.h"
namespace Scumm {
Localizer::Localizer() {
Common::File _file;
if (!_file.open("lokalizator.big"))
return;
uint _fileSize = _file.size();
if (_fileSize < 0x18)
return;
byte *lkBig = new byte[_fileSize];
_file.read(lkBig, _fileSize);
// Obfuscation round 1
for (uint i = 0; i < _fileSize; i++)
lkBig[i] ^= (214013 * i + 2531011) >> 16;
uint32 numFiles = READ_LE_UINT32(lkBig + 0x14);
uint32 localeMsgOffset = 0, localeMsgSize = 0;
uint32 talkieDatOffset = 0, talkieDatSize = 0;
for (uint i = 0; i < numFiles; i++) {
byte *fileHdr = lkBig + 0x18 + 0x4c * i;
if (strcmp((char *) fileHdr, "locale.msg") == 0) {
localeMsgOffset = READ_LE_UINT32(fileHdr + 0x48);
localeMsgSize = READ_LE_UINT32(fileHdr + 0x44);
continue;
}
if (strcmp((char *) fileHdr, "talkie.dat") == 0) {
talkieDatOffset = READ_LE_UINT32(fileHdr + 0x48);
talkieDatSize = READ_LE_UINT32(fileHdr + 0x44);
continue;
}
}
_isValid = true;
if (localeMsgSize > 4) {
uint32 msgCount = READ_LE_UINT32(lkBig + localeMsgOffset);
// Obfuscation round 2
uint32 st = 0x12345678;
for (uint i = 0; i < localeMsgSize - 4; i++) {
byte x = 0;
switch (i & 3) {
case 0:
x = st;
break;
case 1:
x = st + 35;
break;
case 2:
x = st + 70;
break;
case 3:
x = st + 105;
st += 45707404;
break;
}
lkBig[i + localeMsgOffset + 4] ^= x;
}
uint32 cur = localeMsgOffset + 4;
for (uint i = 0; i < msgCount && cur < localeMsgOffset + localeMsgSize; i++) {
cur += 4; // Domain id or something like this, always zero
uint32 lenOrig = READ_LE_UINT32(lkBig + cur); cur += 4;
Common::String orig((char *) lkBig + cur, (char *) lkBig + cur + lenOrig);
cur += lenOrig;
uint32 lenTrans = READ_LE_UINT32(lkBig + cur); cur += 4;
Common::String trans((char *) lkBig + cur, (char *) lkBig + cur + lenTrans);
cur += lenTrans;
_translationMap[orig] = trans;
}
}
for (uint32 cur = talkieDatOffset; cur < talkieDatOffset + talkieDatSize; cur += 16) {
_talkMap[READ_LE_UINT32(lkBig+cur+4)] = READ_LE_UINT32(lkBig+cur+12);
}
}
Common::String Localizer::translate(const Common::String &orig) {
if (_translationMap.contains(orig)) {
return _translationMap[orig];
}
return orig;
}
uint32 Localizer::mapTalk(uint32 orig) {
if (_talkMap.contains(orig)) {
return _talkMap[orig];
}
return orig;
}
}

View file

@ -0,0 +1,48 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef SCUMM_LOCALIZER_H
#define SCUMM_LOCALIZER_H
#include "common/str.h"
#include "common/hashmap.h"
#include "common/hash-str.h"
namespace Scumm {
class Localizer {
public:
Localizer();
Common::String translate(const Common::String &original);
uint32 mapTalk(uint32 orig);
bool isValid() {
return _isValid;
}
private:
Common::HashMap<Common::String, Common::String> _translationMap;
Common::HashMap<uint32, uint32> _talkMap;
bool _isValid;
};
}
#endif

View file

@ -31,6 +31,7 @@
#include "scumm/dialogs.h"
#include "scumm/file.h"
#include "scumm/he/intern_he.h"
#include "scumm/he/localizer.h"
#include "scumm/object.h"
#include "scumm/resource.h"
#include "scumm/scumm.h"
@ -317,6 +318,7 @@ void ScummEngine_v72he::decodeScriptString(byte *dst, bool scriptString) {
int args[31];
int num, len, val;
byte chr, string[1024];
byte *dst0 = dst;
memset(args, 0, sizeof(args));
memset(string, 0, sizeof(string));
@ -335,6 +337,10 @@ void ScummEngine_v72he::decodeScriptString(byte *dst, bool scriptString) {
len = resStrLen(string) + 1;
}
if (_localizer) {
strncpy((char *) string, _localizer->translate((char *) string).c_str(), sizeof(string) - 1);
}
// Decode string
num = 0;
val = 0;
@ -372,6 +378,10 @@ void ScummEngine_v72he::decodeScriptString(byte *dst, bool scriptString) {
}
}
*dst = 0;
if (_localizer) {
strncpy((char *) dst0, _localizer->translate((char *) dst0).c_str(), sizeof(string) - 1);
}
}
int ScummEngine_v72he::findObject(int x, int y, int num, int *args) {

View file

@ -333,13 +333,6 @@ Common::Error ScummMetaEngine::createInstance(OSystem *syst, Engine **engine) co
return Common::kUnsupportedGameidError;
}
// We don't support yet the
// the full game.
if (!strcmp(res.game.gameid, "pajama2") && !strcmp(res.extra, "Russobit")) {
GUIErrorMessage(_("The Russian version of Pajama Sam 2 is not supported yet due to incomplete code."));
return Common::kUnsupportedGameidError;
}
// If the GUI options were updated, we catch this here and update them in the users config
// file transparently.
Common::updateGameGUIOptions(res.game.guioptions, getGameGUIOptionsDescriptionLanguage(res.language));

View file

@ -137,6 +137,7 @@ MODULE_OBJS += \
he/script_v100he.o \
he/sprite_he.o \
he/wiz_he.o \
he/localizer.o \
he/logic/baseball2001.o \
he/logic/basketball.o \
he/logic/football.o \

View file

@ -72,6 +72,7 @@
#include "scumm/scumm_v8.h"
#include "scumm/sound.h"
#include "scumm/imuse/sysex.h"
#include "scumm/he/localizer.h"
#include "scumm/he/sprite_he.h"
#include "scumm/he/cup_player_he.h"
#include "scumm/util.h"
@ -124,6 +125,8 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
_rnd("scumm")
{
_localizer = nullptr;
#ifdef USE_RGB_COLOR
if (_game.features & GF_16BIT_COLOR) {
if (_game.platform == Common::kPlatformPCEngine)
@ -1337,6 +1340,14 @@ Common::Error ScummEngine::init() {
}
}
#ifdef ENABLE_HE
Localizer *loc = new Localizer();
if (!loc->isValid())
delete loc;
else
_localizer = loc;
#endif
_outputPixelFormat = _system->getScreenFormat();
setupScumm();

View file

@ -90,6 +90,7 @@ class Player_Towns;
class ScummEngine;
class ScummDebugger;
class Sound;
class Localizer;
struct Box;
struct BoxCoords;
@ -1108,6 +1109,8 @@ protected:
int _nextLeft, _nextTop;
Localizer *_localizer;
void restoreCharsetBg();
void clearCharsetMask();
void clearTextSurface();

View file

@ -32,6 +32,7 @@
#include "scumm/imuse_digi/dimuse.h"
#ifdef ENABLE_HE
#include "scumm/he/intern_he.h"
#include "scumm/he/localizer.h"
#endif
#include "scumm/resource.h"
#include "scumm/scumm.h"
@ -336,7 +337,11 @@ bool ScummEngine::handleNextCharsetCode(Actor *a, int *code) {
talk_sound_b = buffer[8] | (buffer[9] << 8) | (buffer[12] << 16) | (buffer[13] << 24);
buffer += 14;
if (_game.heversion >= 60) {
#ifdef ENABLE_HE
((SoundHE *)_sound)->startHETalkSound(_localizer ? _localizer->mapTalk(talk_sound_a) : talk_sound_a);
#else
((SoundHE *)_sound)->startHETalkSound(talk_sound_a);
#endif
} else {
_sound->talkSound(talk_sound_a, talk_sound_b, 2);
}
@ -406,7 +411,7 @@ bool ScummEngine_v72he::handleNextCharsetCode(Actor *a, int *code) {
}
value[i] = 0;
//talk_sound_b = atoi(value);
((SoundHE *)_sound)->startHETalkSound(talk_sound_a);
((SoundHE *)_sound)->startHETalkSound(_localizer ? _localizer->mapTalk(talk_sound_a) : talk_sound_a);
break;
case 104:
_haveMsg = 0;
@ -429,7 +434,7 @@ bool ScummEngine_v72he::handleNextCharsetCode(Actor *a, int *code) {
value[i] = 0;
talk_sound_a = atoi(value);
//talk_sound_b = 0;
((SoundHE *)_sound)->startHETalkSound(talk_sound_a);
((SoundHE *)_sound)->startHETalkSound(_localizer ? _localizer->mapTalk(talk_sound_a) : talk_sound_a);
break;
case 119:
_haveMsg = 0xFF;