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