diff --git a/engines/scumm/he/localizer.cpp b/engines/scumm/he/localizer.cpp new file mode 100644 index 00000000000..f5289531ea7 --- /dev/null +++ b/engines/scumm/he/localizer.cpp @@ -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; +} + +} diff --git a/engines/scumm/he/localizer.h b/engines/scumm/he/localizer.h new file mode 100644 index 00000000000..25be5eeabfa --- /dev/null +++ b/engines/scumm/he/localizer.h @@ -0,0 +1,48 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef 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 _translationMap; + Common::HashMap _talkMap; + bool _isValid; +}; + +} + +#endif diff --git a/engines/scumm/he/script_v72he.cpp b/engines/scumm/he/script_v72he.cpp index 304e76faf23..2b21f3cdfa0 100644 --- a/engines/scumm/he/script_v72he.cpp +++ b/engines/scumm/he/script_v72he.cpp @@ -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) { diff --git a/engines/scumm/metaengine.cpp b/engines/scumm/metaengine.cpp index 58bcf9201cd..aaeb6677151 100644 --- a/engines/scumm/metaengine.cpp +++ b/engines/scumm/metaengine.cpp @@ -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)); diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk index b12597be23e..36d7871da8b 100644 --- a/engines/scumm/module.mk +++ b/engines/scumm/module.mk @@ -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 \ diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 5afa175515d..6b60875b2bf 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -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(); diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index eb8aa5d1511..d8f4351d59b 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -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(); diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp index 755396c2713..26c19cd6d68 100644 --- a/engines/scumm/string.cpp +++ b/engines/scumm/string.cpp @@ -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;