1 //=============================================================================
2 //
3 // Adventure Game Studio (AGS)
4 //
5 // Copyright (C) 1999-2011 Chris Jones and 2011-20xx others
6 // The full list of copyright holders can be found in the Copyright.txt
7 // file, which is part of this source code distribution.
8 //
9 // The AGS source code is provided under the Artistic License 2.0.
10 // A copy of this license can be found in the file License.txt and at
11 // http://www.opensource.org/licenses/artistic-license-2.0.php
12 //
13 //=============================================================================
14
15 #define USE_CLIB
16 #include "ac/asset_helper.h"
17 #include "ac/common.h"
18 #include "ac/gamesetup.h"
19 #include "ac/gamesetupstruct.h"
20 #include "ac/gamestate.h"
21 #include "ac/global_game.h"
22 #include "ac/runtime_defines.h"
23 #include "ac/translation.h"
24 #include "ac/tree_map.h"
25 #include "debug/out.h"
26 #include "util/misc.h"
27 #include "util/stream.h"
28 #include "core/assetmanager.h"
29
30 using namespace AGS::Common;
31
32 extern GameSetup usetup;
33 extern GameSetupStruct game;
34 extern GameState play;
35 extern char transFileName[MAX_PATH];
36
37
38 TreeMap *transtree = NULL;
39 long lang_offs_start = 0;
40 char transFileName[MAX_PATH] = "\0";
41
close_translation()42 void close_translation () {
43 if (transtree != NULL) {
44 delete transtree;
45 transtree = NULL;
46 }
47 }
48
49 bool parse_translation(Stream *language_file, String &parse_error);
50
init_translation(const String & lang,const String & fallback_lang,bool quit_on_error)51 bool init_translation (const String &lang, const String &fallback_lang, bool quit_on_error) {
52
53 if (lang.IsEmpty())
54 return false;
55 sprintf(transFileName, "%s.tra", lang.GetCStr());
56
57 Stream *language_file = find_open_asset(transFileName);
58 if (language_file == NULL)
59 {
60 Debug::Printf(kDbgMsg_Error, "Cannot open translation: %s", transFileName);
61 return false;
62 }
63 // in case it's inside a library file, record the offset
64 lang_offs_start = language_file->GetPosition();
65
66 char transsig[16] = {0};
67 language_file->Read(transsig, 15);
68 if (strcmp(transsig, "AGSTranslation") != 0) {
69 Debug::Printf(kDbgMsg_Error, "Translation signature mismatch: %s", transFileName);
70 delete language_file;
71 return false;
72 }
73
74 if (transtree != NULL)
75 {
76 close_translation();
77 }
78 transtree = new TreeMap();
79
80 String parse_error;
81 bool result = parse_translation(language_file, parse_error);
82 delete language_file;
83
84 if (!result)
85 {
86 close_translation();
87 parse_error.Prepend(String::FromFormat("Failed to read translation file: %s:\n", transFileName));
88 if (quit_on_error)
89 {
90 parse_error.PrependChar('!');
91 quit(parse_error);
92 }
93 else
94 {
95 Debug::Printf(kDbgMsg_Error, parse_error);
96 if (!fallback_lang.IsEmpty())
97 {
98 Debug::Printf("Fallback to translation: %s", fallback_lang.GetCStr());
99 init_translation(fallback_lang, "", false);
100 }
101 return false;
102 }
103 }
104 Debug::Printf("Translation initialized: %s", transFileName);
105 return true;
106 }
107
parse_translation(Stream * language_file,String & parse_error)108 bool parse_translation(Stream *language_file, String &parse_error)
109 {
110 while (!language_file->EOS()) {
111 int blockType = language_file->ReadInt32();
112 if (blockType == -1)
113 break;
114 // MACPORT FIX 9/6/5: remove warning
115 /* int blockSize = */ language_file->ReadInt32();
116
117 if (blockType == 1) {
118 char original[STD_BUFFER_SIZE], translation[STD_BUFFER_SIZE];
119 while (1) {
120 read_string_decrypt (language_file, original);
121 read_string_decrypt (language_file, translation);
122 if ((strlen (original) < 1) && (strlen(translation) < 1))
123 break;
124 if (language_file->EOS())
125 {
126 parse_error = "Translation file is corrupt";
127 return false;
128 }
129 transtree->addText (original, translation);
130 }
131
132 }
133 else if (blockType == 2) {
134 int uidfrom;
135 char wasgamename[100];
136 uidfrom = language_file->ReadInt32();
137 read_string_decrypt (language_file, wasgamename);
138 if ((uidfrom != game.uniqueid) || (strcmp (wasgamename, game.gamename) != 0)) {
139 parse_error.Format("The translation file is not compatible with this game. The translation is designed for '%s'.",
140 wasgamename);
141 return false;
142 }
143 }
144 else if (blockType == 3) {
145 // game settings
146 int temp = language_file->ReadInt32();
147 // normal font
148 if (temp >= 0)
149 SetNormalFont (temp);
150 temp = language_file->ReadInt32();
151 // speech font
152 if (temp >= 0)
153 SetSpeechFont (temp);
154 temp = language_file->ReadInt32();
155 // text direction
156 if (temp == 1) {
157 play.text_align = SCALIGN_LEFT;
158 game.options[OPT_RIGHTLEFTWRITE] = 0;
159 }
160 else if (temp == 2) {
161 play.text_align = SCALIGN_RIGHT;
162 game.options[OPT_RIGHTLEFTWRITE] = 1;
163 }
164 }
165 else
166 {
167 parse_error.Format("Unknown block type in translation file (%d).", blockType);
168 return false;
169 }
170 }
171
172 if (transtree->text == NULL)
173 {
174 parse_error = "The translation file was empty.";
175 return false;
176 }
177
178 return true;
179 }
180