1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "common/language.h"
24 #include "ags/engine/ac/asset_helper.h"
25 #include "ags/shared/ac/common.h"
26 #include "ags/engine/ac/game_setup.h"
27 #include "ags/shared/ac/game_setup_struct.h"
28 #include "ags/engine/ac/game_state.h"
29 #include "ags/engine/ac/global_game.h"
30 #include "ags/engine/ac/runtime_defines.h"
31 #include "ags/engine/ac/translation.h"
32 #include "ags/shared/ac/words_dictionary.h"
33 #include "ags/shared/debugging/out.h"
34 #include "ags/shared/game/tra_file.h"
35 #include "ags/shared/util/stream.h"
36 #include "ags/shared/core/asset_manager.h"
37 #include "ags/globals.h"
38
39 namespace AGS3 {
40
41 using namespace AGS::Shared;
42
43 // TODO: Since ScummVM can't use uconvert, this is a dummy implementation for now
convert_utf8_to_ascii(const char * mbstr,const char * loc_name)44 const char *convert_utf8_to_ascii(const char *mbstr, const char *loc_name) {
45 _G(mbbuf).resize(strlen(mbstr) + 1);
46 strcpy(&_G(mbbuf)[0], mbstr);
47 return &_G(mbbuf)[0];
48 }
49
close_translation()50 void close_translation() {
51 _GP(transtree).clear();
52 _GP(trans) = Translation();
53 _G(trans_name) = "";
54 _G(trans_filename) = "";
55
56 // Return back to default game's encoding
57 set_uformat(U_ASCII);
58 }
59
init_translation(const String & lang,const String & fallback_lang,bool quit_on_error)60 bool init_translation(const String &lang, const String &fallback_lang, bool quit_on_error) {
61
62 if (lang.IsEmpty())
63 return false;
64 _G(trans_filename) = String::FromFormat("%s.tra", lang.GetCStr());
65
66 std::unique_ptr<Stream> in(_GP(AssetMgr)->OpenAsset(_G(trans_filename)));
67 if (in == nullptr) {
68 Debug::Printf(kDbgMsg_Error, "Cannot open translation: %s", _G(trans_filename).GetCStr());
69 return false;
70 }
71
72 _GP(trans) = Translation();
73
74 // First test if the translation is meant for this game
75 HError err = TestTraGameID(_GP(game).uniqueid, _GP(game).gamename, in.get());
76 if (err) {
77 // If successful, then read translation data fully
78 in.reset(_GP(AssetMgr)->OpenAsset(_G(trans_filename)));
79 err = ReadTraData(_GP(trans), in.get());
80 }
81
82 // Process errors
83 if (!err) {
84 String err_msg = String::FromFormat("Failed to read translation file: %s:\n%s",
85 _G(trans_filename).GetCStr(),
86 err->FullMessage().GetCStr());
87 close_translation();
88 if (quit_on_error) {
89 quitprintf("!%s", err_msg.GetCStr());
90 } else {
91 Debug::Printf(kDbgMsg_Error, err_msg);
92 if (!fallback_lang.IsEmpty()) {
93 Debug::Printf("Fallback to translation: %s", fallback_lang.GetCStr());
94 init_translation(fallback_lang, "", false);
95 }
96 return false;
97 }
98 }
99
100 // Translation read successfully
101 // Configure new game settings
102 if (_GP(trans).NormalFont >= 0)
103 SetNormalFont(_GP(trans).NormalFont);
104 if (_GP(trans).SpeechFont >= 0)
105 SetSpeechFont(_GP(trans).SpeechFont);
106 if (_GP(trans).RightToLeft == 1) {
107 _GP(play).text_align = kHAlignLeft;
108 _GP(game).options[OPT_RIGHTLEFTWRITE] = 0;
109 } else if (_GP(trans).RightToLeft == 2) {
110 _GP(play).text_align = kHAlignRight;
111 _GP(game).options[OPT_RIGHTLEFTWRITE] = 1;
112 }
113
114 // Setup a text encoding mode depending on the translation data hint
115 String encoding = _GP(trans).StrOptions["encoding"];
116 if (encoding.CompareNoCase("utf-8") == 0)
117 set_uformat(U_UTF8);
118 else
119 set_uformat(U_ASCII);
120
121 // Mixed encoding support:
122 // original text unfortunately may contain extended ASCII chars (> 127);
123 // if translation is UTF-8 but game is extended ASCII, then the translation
124 // dictionary keys won't match.
125 // With that assumption we must convert dictionary keys into ASCII using
126 // provided locale hint.
127 String key_enc = _GP(trans).StrOptions["gameencoding"];
128 if (!key_enc.IsEmpty()) {
129 StringMap conv_map;
130 for (const auto &item : _GP(trans).Dict) {
131 String key = convert_utf8_to_ascii(item._key.GetCStr(), key_enc.GetCStr());
132 conv_map.insert(std::make_pair(key, item._value));
133 }
134 _GP(trans).Dict = conv_map;
135 }
136
137 Debug::Printf("Translation initialized: %s", _G(trans_filename).GetCStr());
138 return true;
139 }
140
get_translation_name()141 String get_translation_name() {
142 return _G(trans_name);
143 }
144
get_translation_path()145 String get_translation_path() {
146 return _G(trans_filename);
147 }
148
get_translation_tree()149 const StringMap &get_translation_tree() {
150 return _GP(trans).Dict;
151 }
152
153 } // namespace AGS3
154