1 /*
2 Copyright (C) 2009-2021 Parallel Realities
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 */
19
20 #include "headers.h"
21
22 #include "init.h"
23
24 static HashTable table;
25
26 static int hashCode(char *);
27 static void put(char *, char *);
28 static void initTable(void);
29
setLanguage(char * applicationName,char * languageCode)30 void setLanguage(char *applicationName, char *languageCode)
31 {
32 char language[MAX_LINE_LENGTH], c[MAX_LINE_LENGTH];
33 char *lang, **key, **value;
34 int i, swap;
35 FILE *fp;
36 MOHeader header;
37 MOEntry *original, *translation;
38 #if DEV == 1
39 int read;
40 #endif
41
42 initTable();
43
44 language[0] = '\0';
45
46 if (languageCode == NULL)
47 {
48 #ifdef _WIN32
49 GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, c, MAX_LINE_LENGTH);
50
51 if (c[0] != '\0')
52 {
53 STRNCPY(language, c, MAX_LINE_LENGTH);
54
55 GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, c, MAX_LINE_LENGTH);
56
57 if (c[0] != '\0')
58 {
59 strncat(language, "_", MAX_MESSAGE_LENGTH - strlen(language) - 1);
60
61 strncat(language, c, MAX_MESSAGE_LENGTH - strlen(language) - 1);
62 }
63 }
64 #else
65 if ((lang = getenv("LC_ALL")) || (lang = getenv("LC_CTYPE")) || (lang = getenv("LANG")))
66 {
67 STRNCPY(language, lang, MAX_LINE_LENGTH);
68 }
69 #endif
70 }
71
72 else
73 {
74 STRNCPY(language, languageCode, MAX_LINE_LENGTH);
75 }
76
77 strtok(language, ".");
78
79 printf("Locale is %s\n", language);
80
81 SNPRINTF(c, MAX_LINE_LENGTH, "%s/%s/LC_MESSAGES/%s.mo", LOCALE_DIR, language, applicationName);
82
83 #if DEV == 1
84 printf("Opening %s\n", c);
85 #endif
86
87 fp = fopen(c, "rb");
88
89 if (fp == NULL)
90 {
91 #if DEV == 1
92 printf("Failed to open %s/%s/LC_MESSAGES/%s.mo\n", LOCALE_DIR, language, applicationName);
93 #endif
94
95 if (strstr(language, "_") == NULL)
96 {
97 return;
98 }
99
100 strtok(language, "_");
101
102 SNPRINTF(c, MAX_LINE_LENGTH, "%s/%s/LC_MESSAGES/%s.mo", LOCALE_DIR, language, applicationName);
103
104 #if DEV == 1
105 printf("Opening %s\n", c);
106 #endif
107
108 fp = fopen(c, "rb");
109
110 if (fp == NULL)
111 {
112 #if DEV == 1
113 printf("Failed to open %s/%s/LC_MESSAGES/%s.mo\n", LOCALE_DIR, language, applicationName);
114 #endif
115
116 return;
117 }
118 }
119
120 fread(&header, sizeof(header), 1, fp);
121
122 swap = header.magicNumber == 0x950412de ? FALSE : TRUE;
123
124 if (swap == TRUE)
125 {
126 header.stringCount = SDL_Swap32(header.stringCount);
127 header.originalOffset = SDL_Swap32(header.originalOffset);
128 header.translationOffset = SDL_Swap32(header.translationOffset);
129 }
130
131 original = malloc(sizeof(MOEntry) * header.stringCount);
132
133 translation = malloc(sizeof(MOEntry) * header.stringCount);
134
135 if (original == NULL || translation == NULL)
136 {
137 printf("Failed to allocate %d bytes for translation strings\n", (int)sizeof(MOEntry) * header.stringCount);
138
139 cleanup(1);
140 }
141
142 #if DEV == 1
143 printf("MO file has %d entries\n", header.stringCount);
144 #endif
145
146 fseek(fp, header.originalOffset, SEEK_SET);
147
148 key = malloc(sizeof(char *) * header.stringCount);
149
150 value = malloc(sizeof(char *) * header.stringCount);
151
152 if (key == NULL || value == NULL)
153 {
154 printf("Failed to allocate a whole %d bytes for translation strings\n", (int)sizeof(char *) * header.stringCount);
155
156 cleanup(1);
157 }
158
159 for (i=0;i<header.stringCount;i++)
160 {
161 fread(&original[i].length, sizeof(int32_t), 1, fp);
162 fread(&original[i].offset, sizeof(int32_t), 1, fp);
163
164 if (swap == TRUE)
165 {
166 original[i].length = SDL_Swap32(original[i].length);
167 original[i].offset = SDL_Swap32(original[i].offset);
168 }
169
170 key[i] = malloc(original[i].length + 1);
171
172 if (key[i] == NULL)
173 {
174 printf("Failed to allocate a whole %d bytes for translation string\n", original[i].length + 1);
175
176 cleanup(1);
177 }
178 }
179
180 fseek(fp, header.translationOffset, SEEK_SET);
181
182 for (i=0;i<header.stringCount;i++)
183 {
184 fread(&translation[i].length, sizeof(int32_t), 1, fp);
185 fread(&translation[i].offset, sizeof(int32_t), 1, fp);
186
187 if (swap == TRUE)
188 {
189 translation[i].length = SDL_Swap32(translation[i].length);
190 translation[i].offset = SDL_Swap32(translation[i].offset);
191 }
192
193 value[i] = malloc(translation[i].length + 1);
194
195 if (value[i] == NULL)
196 {
197 printf("Failed to allocate a whole %d bytes for translation string\n", translation[i].length + 1);
198
199 cleanup(1);
200 }
201 }
202
203 for (i=0;i<header.stringCount;i++)
204 {
205 fseek(fp, original[i].offset, SEEK_SET);
206
207 fread(key[i], original[i].length, 1, fp);
208
209 key[i][original[i].length] = '\0';
210 }
211
212 for (i=0;i<header.stringCount;i++)
213 {
214 fseek(fp, translation[i].offset, SEEK_SET);
215
216 fread(value[i], translation[i].length, 1, fp);
217
218 value[i][translation[i].length] = '\0';
219 }
220
221 fclose(fp);
222
223 for (i=0;i<header.stringCount;i++)
224 {
225 put(key[i], value[i]);
226
227 free(key[i]);
228
229 free(value[i]);
230 }
231
232 free(key);
233
234 free(value);
235
236 free(original);
237
238 free(translation);
239
240 #if DEV == 1
241 read = 0;
242
243 for (i=0;i<TABLE_SIZE;i++)
244 {
245 if (table.bucketCount[i] != 0)
246 {
247 read++;
248 }
249 }
250
251 printf("Using %d of %d buckets (%d%%)\n", read, TABLE_SIZE, (read * 100) / TABLE_SIZE);
252 #endif
253 }
254
hashCode(char * data)255 static int hashCode(char *data)
256 {
257 int i, length;
258 unsigned int hash;
259
260 length = strlen(data);
261
262 hash = 0;
263
264 for (i=0;i<length;i++)
265 {
266 hash = data[i] + (hash << 5) - hash;
267 }
268
269 return hash % TABLE_SIZE;
270 }
271
initTable()272 static void initTable()
273 {
274 int i;
275
276 table.bucket = malloc(sizeof(Bucket *) * TABLE_SIZE);
277
278 table.bucketCount = malloc(sizeof(int) * TABLE_SIZE);
279
280 if (table.bucket == NULL || table.bucketCount == NULL)
281 {
282 printf("Failed to allocate %d bytes for a HashTable\n", (int)sizeof(Bucket *) * TABLE_SIZE);
283
284 cleanup(1);
285 }
286
287 for (i=0;i<TABLE_SIZE;i++)
288 {
289 table.bucket[i] = malloc(sizeof(Bucket));
290
291 table.bucket[i]->next = NULL;
292
293 table.bucketCount[i] = 0;
294 }
295 }
296
put(char * key,char * value)297 static void put(char *key, char *value)
298 {
299 Bucket *bucket, *newBucket;
300 unsigned int hash = hashCode(key);
301
302 #if DEV == 1
303 printf("%s = %d\n", key, hash);
304 #endif
305
306 bucket = table.bucket[hash];
307
308 while (bucket->next != NULL)
309 {
310 bucket = bucket->next;
311 }
312
313 newBucket = malloc(sizeof(Bucket));
314
315 if (newBucket == NULL)
316 {
317 printf("Failed to allocate a whole %d bytes for a HashTable bucket\n", (int)sizeof(Bucket));
318
319 cleanup(1);
320 }
321
322 newBucket->key = malloc(strlen(key) + 1);
323 newBucket->value = malloc(strlen(value) + 1);
324
325 if (newBucket->key == NULL || newBucket->value == NULL)
326 {
327 printf("Failed to allocate a whole %d bytes for a translation\n", (int)strlen(newBucket->key) + 1);
328
329 cleanup(1);
330 }
331
332 STRNCPY(newBucket->key, key, strlen(key) + 1);
333 STRNCPY(newBucket->value, value, strlen(value) + 1);
334
335 newBucket->next = NULL;
336
337 bucket->next = newBucket;
338
339 table.bucketCount[hash]++;
340 }
341
getTranslatedString(char * key)342 char *getTranslatedString(char *key)
343 {
344 Bucket *bucket;
345 unsigned int hash = hashCode(key);
346
347 bucket = table.bucket[hash]->next;
348
349 for (;bucket!=NULL;bucket=bucket->next)
350 {
351 if (strcmpignorecase(key, bucket->key) == 0)
352 {
353 return strlen(bucket->value) == 0 ? key : bucket->value;
354 }
355 }
356
357 return key;
358 }
359
cleanupLanguage()360 void cleanupLanguage()
361 {
362 int i;
363 Bucket *bucket, *p, *q;
364
365 for (i=0;i<TABLE_SIZE;i++)
366 {
367 bucket = table.bucket[i];
368
369 for (p=bucket->next;p!=NULL;p=q)
370 {
371 free(p->key);
372 free(p->value);
373
374 q = p->next;
375
376 free(p);
377 }
378
379 free(bucket);
380 }
381
382 table.bucket = NULL;
383 }
384