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