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