1 #include <ctype.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <sys/stat.h>
6 #include <sys/types.h>
7 
8 #ifdef __HAIKU__
9 #include <Message.h>
10 #include <FindDirectory.h>
11 #include <Path.h>
12 #include <LocaleRoster.h>
13 #include <SupportDefs.h>
14 #define NO_UINT32_TYPES
15 #define NO_UINT64_TYPES
16 #endif
17 
18 #include "macros.h"
19 #include "simmain.h"
20 #include "simsys.h"
21 #include "pathes.h"
22 #include "simevent.h"
23 
24 
25 #ifdef _WIN32
26 #	include <windows.h>
27 #	include <winbase.h>
28 #	include <shellapi.h>
29 #	include <shlobj.h>
30 #	if !defined(__CYGWIN__)
31 #		include <direct.h>
32 #	else
33 #		include <sys\unistd.h>
34 #	endif
35 #	include "simdebug.h"
36 #else
37 #	include <limits.h>
38 #	include <dirent.h>
39 #	if !defined __AMIGA__ && !defined __BEOS__
40 #		include <unistd.h>
41 #	endif
42 #endif
43 
44 
45 struct sys_event sys_event;
46 
47 
48 #ifdef _WIN32
49 /**
50  * Utility class to handle conversion of UTF-8 to UTF-16 for use by Windows APIs.
51  * Constructs a UTF-16 view of the current contents of a UTF-8 C string.
52  */
53 class U16View {
54 private:
55 	LPCWSTR cu16str;
56 public:
57 	/**
58 	 * Constructs a UTF-16 view of a UTF-8 C string.
59 	 */
U16View(char const * const u8str)60 	U16View(char const *const u8str)
61 	{
62 		// Convert UTF-8 to UTF-16.
63 		int const size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, u8str, -1, NULL, 0);
64 		if(size == 0) {
65 			// It should never fail, since all file IO UTF8 comes either via keyborad input or filenames, i.e. a win32 api.
66 			// log may not even initialising at this point, so we just fail.
67 			dr_fatal_notify( "Unknown unicode character encountered!" );
68 			exit(0);
69 		}
70 		LPWSTR const u16str = new WCHAR[size];
71 		MultiByteToWideChar(CP_UTF8, 0, u8str, -1, u16str, size);
72 
73 		cu16str = u16str;
74 	}
75 
76 	/**
77 	 * Copy constructor to allow safe movement.
78 	 * Should also have move constructor to allow efficient movement but that requires C++11.
79 	 */
U16View(U16View const & from)80 	U16View(U16View const& from)
81 	{
82 		LPWSTR const u16str = new WCHAR[wcslen(from.cu16str) + 1];
83 		wcscpy(u16str, from.cu16str);
84 		cu16str = u16str;
85 	}
86 
~U16View()87 	~U16View()
88 	{
89 		delete[] cu16str;
90 	}
91 
92 	/**
93 	 * Typecast operator to allow implicit use as a LPCWSTR.
94 	 */
operator LPCWSTR() const95 	operator LPCWSTR() const
96 	{
97 		return cu16str;
98 	}
99 };
100 #endif
101 
102 // Platform specific path separators.
103 #ifdef _WIN32
104 char const PATH_SEPARATOR[] = "\\";
105 #else
106 char const PATH_SEPARATOR[] = "/";
107 #endif
108 
109 
110 
111 /**
112  * Get Mouse X-Position
113  * @author Hj. Malthaner
114  */
get_mouse_x()115 int get_mouse_x()
116 {
117 	return sys_event.mx;
118 }
119 
120 
121 /**
122  * Get Mouse y-Position
123  * @author Hj. Malthaner
124  */
get_mouse_y()125 int get_mouse_y()
126 {
127 	return sys_event.my;
128 }
129 
130 
131 
dr_mkdir(char const * const path)132 int dr_mkdir(char const* const path)
133 {
134 #ifdef _WIN32
135 	// Perform operation.
136 	int const result = CreateDirectoryW(U16View(path), NULL) ? 0 : -1;
137 
138 	// Translate error.
139 	if(result != 0) {
140 		DWORD const error = GetLastError();
141 		if(error == ERROR_ALREADY_EXISTS) {
142 			errno = EEXIST;
143 		} else if(error == ERROR_PATH_NOT_FOUND) {
144 			errno = ENOENT;
145 		}
146 	}
147 
148 	return result;
149 #else
150 	return mkdir(path, 0777);
151 #endif
152 }
153 
154 
155 
dr_movetotrash(const char * path)156 bool dr_movetotrash(const char *path)
157 {
158 #ifdef _WIN32
159 	// Convert to full path name to allow use of recycle bin.
160 	// Must be double null terminated for SHFILESTRUCT
161 	U16View const wpath(path);
162 	int const full_size = GetFullPathNameW(wpath, 0, NULL, NULL);
163 	wchar_t *const full_wpath = new wchar_t[full_size + 1];
164 	GetFullPathNameW(wpath, full_size, full_wpath, NULL);
165 	full_wpath[full_size] = L'\0';
166 
167 	// Initalize file operation structure.
168 	SHFILEOPSTRUCTW  FileOp;
169 	FileOp.hwnd = NULL;
170 	FileOp.wFunc = FO_DELETE;
171 	FileOp.pFrom = full_wpath;
172 	FileOp.pTo = NULL;
173 	FileOp.fFlags = FOF_ALLOWUNDO|FOF_NOCONFIRMATION;
174 
175 	// Perform operation.
176 	int success = SHFileOperationW(&FileOp);
177 
178 	delete[] full_wpath;
179 
180 	return success;
181 #else
182 	return remove(path);
183 #endif
184 }
185 
186 
187 
dr_cantrash()188 bool dr_cantrash()
189 {
190 #ifdef _WIN32
191 	return true;
192 #else
193 	return false;
194 #endif
195 }
196 
197 
198 
dr_remove(const char * path)199 int dr_remove(const char *path)
200 {
201 #ifdef _WIN32
202 	// Perform operation.
203 	bool success = DeleteFileW(U16View(path));
204 
205 	// Translate error.
206 	if(!success) {
207 		DWORD error = GetLastError();
208 		if(error == ERROR_FILE_NOT_FOUND) {
209 			errno = ENOENT;
210 		} else if(error == ERROR_ACCESS_DENIED) {
211 			errno = EACCES;
212 		}
213 	}
214 
215 	return success ? 0 : -1;
216 #else
217 	return remove(path);
218 #endif
219 }
220 
221 
222 
dr_rename(const char * existing_utf8,const char * new_utf8)223 int dr_rename(const char *existing_utf8, const char *new_utf8)
224 {
225 #ifdef _WIN32
226 	// Perform operation.
227 	bool success = MoveFileExW(U16View(existing_utf8), U16View(new_utf8), MOVEFILE_REPLACE_EXISTING);
228 
229 	// Translate error.
230 	if(!success) {
231 		DWORD error = GetLastError();
232 		if (error == ERROR_FILE_NOT_FOUND) {
233 			errno = ENOENT;
234 		} else if(error == ERROR_ACCESS_DENIED) {
235 			errno = EACCES;
236 		}
237 	}
238 
239 	return success ? 0 : -1;
240 #else
241 	remove( new_utf8 );
242 	return rename( existing_utf8, new_utf8 );
243 #endif
244 }
245 
dr_chdir(const char * path)246 int dr_chdir(const char *path)
247 {
248 #ifdef _WIN32
249 	// Perform operation.
250 	bool success = SetCurrentDirectoryW(U16View(path));
251 
252 	// Translate error.
253 	if(!success) {
254 		DWORD error = GetLastError();
255 		if (error == ERROR_FILE_NOT_FOUND) {
256 			errno = ENOENT;
257 		} else if(error == ERROR_ACCESS_DENIED) {
258 			errno = EACCES;
259 		}
260 	}
261 
262 	return success ? 0 : -1;
263 #else
264 	return chdir(path);
265 #endif
266 }
267 
dr_getcwd(char * buf,size_t size)268 char *dr_getcwd(char *buf, size_t size)
269 {
270 #ifdef _WIN32
271 	DWORD wsize = GetCurrentDirectoryW(0, NULL);
272 	WCHAR *const wpath = new WCHAR[wsize];
273 	DWORD success = GetCurrentDirectoryW(wsize, wpath);
274 
275 	// Translate error.
276 	if(!success) {
277 		delete[] wpath;
278 		return NULL;
279 	}
280 
281 	// Convert UTF-16 to UTF-8.
282 	int const convert_size = WideCharToMultiByte(CP_UTF8, 0, wpath, -1, buf, (int)size, NULL, NULL);
283 	delete[] wpath;
284 	if(convert_size == 0) {
285 		return NULL;
286 	}
287 
288 	return buf;
289 #else
290 	return getcwd(buf, size);
291 #endif
292 }
293 
dr_fopen(const char * filename,const char * mode)294 FILE *dr_fopen (const char *filename, const char *mode)
295 {
296 #ifdef _WIN32
297 	return _wfopen(U16View(filename), U16View(mode));
298 #else
299 	return fopen(filename, mode);
300 #endif
301 }
302 
dr_gzopen(const char * path,const char * mode)303 gzFile dr_gzopen(const char *path, const char *mode)
304 {
305 #ifdef _WIN32
306 	return gzopen_w(U16View(path), mode);
307 #else
308 	return gzopen(path, mode);
309 #endif
310 }
311 
dr_stat(const char * path,struct stat * buf)312 int dr_stat(const char *path, struct stat *buf)
313 {
314 #ifdef _WIN32
315 	return _wstat(U16View(path), (struct _stat*)buf);
316 #else
317 	return stat(path, buf);
318 #endif
319 }
320 
dr_query_homedir()321 char const *dr_query_homedir()
322 {
323 	static char buffer[PATH_MAX + 24];
324 
325 #if defined _WIN32
326 	WCHAR whomedir[MAX_PATH];
327 	if(FAILED(SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, whomedir))) {
328 		DWORD len = sizeof(whomedir);
329 		HKEY hHomeDir;
330 		if(RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 0, KEY_READ, &hHomeDir) != ERROR_SUCCESS) {
331 			return NULL;
332 		}
333 		LONG const status = RegQueryValueExW(hHomeDir, L"Personal", NULL, NULL, (LPBYTE)whomedir, &len);
334 		RegCloseKey(hHomeDir);
335 		if(status != ERROR_SUCCESS) {
336 			return NULL;
337 		}
338 	}
339 
340 	// Convert UTF-16 to UTF-8.
341 	int const convert_size = WideCharToMultiByte(CP_UTF8, 0, whomedir, -1, buffer, sizeof(buffer), NULL, NULL);
342 	if(convert_size == 0) {
343 		return NULL;
344 	}
345 
346 	// Append Simutrans folder.
347 	char const foldername[] = "Simutrans";
348 	if(lengthof(buffer) < strlen(buffer) + strlen(foldername) + 2 * strlen(PATH_SEPARATOR) + 1){
349 		return NULL;
350 	}
351 	strcat(buffer, PATH_SEPARATOR);
352 	strcat(buffer, foldername);
353 #elif defined __APPLE__
354 	sprintf(buffer, "%s/Library/Simutrans", getenv("HOME"));
355 #elif defined __HAIKU__
356 	BPath userDir;
357 	find_directory(B_USER_DIRECTORY, &userDir);
358 	sprintf(buffer, "%s/simutrans", userDir.Path());
359 #else
360 	sprintf(buffer, "%s/.simutrans", getenv("HOME"));
361 #endif
362 
363 	// create directory and subdirectories
364 	dr_mkdir(buffer);
365 	strcat(buffer, PATH_SEPARATOR);
366 
367 	return buffer;
368 }
369 
370 
dr_query_fontpath(int which)371 const char *dr_query_fontpath(int which)
372 {
373 #ifdef _WIN32
374 	if(  which>0  ) {
375 		return NULL;
376 	}
377 
378 	static char buffer[PATH_MAX];
379 	WCHAR fontdir[MAX_PATH];
380 	if(FAILED(SHGetFolderPathW(NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, fontdir))) {
381 		wcscpy(fontdir, L"C:\\Windows\\Fonts");
382 	}
383 
384 	// Convert UTF-16 to UTF-8.
385 	int const convert_size = WideCharToMultiByte(CP_UTF8, 0, fontdir, -1, buffer, sizeof(buffer), NULL, NULL);
386 	if(convert_size == 0) {
387 		return 0;
388 	}
389 
390 	strcat(buffer, PATH_SEPARATOR);
391 	return buffer;
392 #else
393 	// linux has more than one path
394 	// sometimes there is the file "/etc/fonts/fonts.conf" and we can read it
395 	// however, since we cannot rely on it, we just try a few candiates
396 
397 	// Apple only uses three locals, and Haiku has no standard ...
398 	const char *trypaths[] = {
399 #ifdef __APPLE__
400 		"~/Library/",
401 		"/Library/Fonts/",
402 		"/System/Library/Fonts/",
403 #elif defined __HAIKU__
404 		"/boot/system/non-packaged/data/fonts/",
405 		"/boot/home/config/non-packaged/data/fonts/",
406 		"~/config/non-packaged/data/fonts/",
407 		"/boot/system/data/fonts/ttfonts/",
408 #else
409 		"~/.fonts/",
410 		"~/.local/share/fonts/",
411 		"/usr/share/fonts/truetype/",
412 		"/usr/X11R6/lib/X11/fonts/ttfonts/",
413 		"/usr/local/sharefonts/truetype/",
414 		"/usr/share/fonts/",
415 		"/usr/X11R6/lib/X11/fonts/",
416 #endif
417 		NULL
418 	};
419 	for(  int i=0;  trypaths[i];  i++  ) {
420 		DIR *dir = opendir(trypaths[i]);
421 		if(  dir  ) {
422 			closedir( dir );
423 			if(  which==0  ) {
424 				return trypaths[i];
425 			}
426 			which--;
427 		}
428 	}
429 	return NULL;
430 #endif
431 }
432 
433 
434 /* this retrieves the 2 byte string for the default language
435  * I hope AmigaOS uses something like Linux for this ...
436  */
437 #ifdef _WIN32
438 
439 struct id2str_t { uint16 id; const char *name; };
440 
dr_get_locale_string()441 const char *dr_get_locale_string()
442 {
443 	static id2str_t id2str[] =
444 	{
445 		{ 0x0001,	"ar" },
446 		{ 0x0002,	"bg" },
447 		{ 0x0003,	"ca" },
448 		{ 0x0004,	"zh\0Hans" },
449 		{ 0x0005,	"cs" },
450 		{ 0x0006,	"da" },
451 		{ 0x0007,	"de" },
452 		{ 0x0008,	"el" },
453 		{ 0x0009,	"en" },
454 		{ 0x000a,	"es" },
455 		{ 0x000b,	"fi" },
456 		{ 0x000c,	"fr" },
457 		{ 0x000d,	"he" },
458 		{ 0x000e,	"hu" },
459 		{ 0x000f,	"is" },
460 		{ 0x0010,	"it" },
461 		{ 0x0011,	"ja" },
462 		{ 0x0012,	"ko" },
463 		{ 0x0013,	"nl" },
464 		{ 0x0014,	"no" },
465 		{ 0x0015,	"pl" },
466 		{ 0x0016,	"pt" },
467 		{ 0x0017,	"rm" },
468 		{ 0x0018,	"ro" },
469 		{ 0x0019,	"ru" },
470 		{ 0x001a,	"hr" },
471 		{ 0x001b,	"sk" },
472 		{ 0x001c,	"sq" },
473 		{ 0x001d,	"sv" },
474 		{ 0x001e,	"th" },
475 		{ 0x001f,	"tr" },
476 		{ 0x0020,	"ur" },
477 		{ 0x0021,	"id" },
478 		{ 0x0022,	"uk" },
479 		{ 0x0023,	"be" },
480 		{ 0x0024,	"sl" },
481 		{ 0x0025,	"et" },
482 		{ 0x0026,	"lv" },
483 		{ 0x0027,	"lt" },
484 		{ 0x0028,	"tg" },
485 		{ 0x0029,	"fa" },
486 		{ 0x002a,	"vi" },
487 		{ 0x002b,	"hy" },
488 		{ 0x002c,	"az" },
489 		{ 0x002d,	"eu" },
490 		{ 0x002e,	"hsb" },
491 		{ 0x002f,	"mk" },
492 		{ 0x0030,	"st" },
493 		{ 0x0031,	"ts" },
494 		{ 0x0032,	"tn" },
495 		{ 0x0033,	"ve" },
496 		{ 0x0034,	"xh" },
497 		{ 0x0035,	"zu" },
498 		{ 0x0036,	"af" },
499 		{ 0x0037,	"ka" },
500 		{ 0x0038,	"fo" },
501 		{ 0x0039,	"hi" },
502 		{ 0x003a,	"mt" },
503 		{ 0x003b,	"se" },
504 		{ 0x003c,	"ga" },
505 		{ 0x003d,	"yi" },
506 		{ 0x003e,	"ms" },
507 		{ 0x003f,	"kk" },
508 		{ 0x0040,	"ky" },
509 		{ 0x0041,	"sw" },
510 		{ 0x0042,	"tk" },
511 		{ 0x0043,	"uz" },
512 		{ 0x0044,	"tt" },
513 		{ 0x0045,	"bn" },
514 		{ 0x0046,	"pa" },
515 		{ 0x0047,	"gu" },
516 		{ 0x0048,	"or" },
517 		{ 0x0049,	"ta" },
518 		{ 0x004a,	"te" },
519 		{ 0x004b,	"kn" },
520 		{ 0x004c,	"ml" },
521 		{ 0x004d,	"as" },
522 		{ 0x004e,	"mr" },
523 		{ 0x004f,	"sa" },
524 		{ 0x0050,	"mn" },
525 		{ 0x0051,	"bo" },
526 		{ 0x0052,	"cy" },
527 		{ 0x0053,	"km" },
528 		{ 0x0054,	"lo" },
529 		{ 0x0055,	"my" },
530 		{ 0x0056,	"gl" },
531 		{ 0x0057,	"kok" },
532 		{ 0x0058,	"mni" },
533 		{ 0x0059,	"sd" },
534 		{ 0x005a,	"syr" },
535 		{ 0x005b,	"si" },
536 		{ 0x005c,	"chr" },
537 		{ 0x005d,	"iu" },
538 		{ 0x005e,	"am" },
539 		{ 0x005f,	"tzm" },
540 		{ 0x0060,	"ks" },
541 		{ 0x0061,	"ne" },
542 		{ 0x0062,	"fy" },
543 		{ 0x0063,	"ps" },
544 		{ 0x0064,	"fil" },
545 		{ 0x0065,	"dv" },
546 		{ 0x0066,	"bin" },
547 		{ 0x0067,	"ff" },
548 		{ 0x0068,	"ha" },
549 		{ 0x0069,	"ibb" },
550 		{ 0x006a,	"yo" },
551 		{ 0x006b,	"quz" },
552 		{ 0x006c,	"nso" },
553 		{ 0x006d,	"ba" },
554 		{ 0x006e,	"lb" },
555 		{ 0x006f,	"kl" },
556 		{ 0x0070,	"ig" },
557 		{ 0x0071,	"kr" },
558 		{ 0x0072,	"om" },
559 		{ 0x0073,	"ti" },
560 		{ 0x0074,	"gn" },
561 		{ 0x0075,	"haw" },
562 		{ 0x0076,	"la" },
563 		{ 0x0077,	"so" },
564 		{ 0x0078,	"ii" },
565 		{ 0x0079,	"pap" },
566 		{ 0x007a,	"arn" },
567 		{ 0x007c,	"moh" },
568 		{ 0x007e,	"br" },
569 		{ 0x0080,	"ug" },
570 		{ 0x0081,	"mi" },
571 		{ 0x0082,	"oc" },
572 		{ 0x0083,	"co" },
573 		{ 0x0084,	"gsw" },
574 		{ 0x0085,	"sah" },
575 		{ 0x0086,	"qut" },
576 		{ 0x0087,	"rw" },
577 		{ 0x0088,	"wo" },
578 		{ 0x008c,	"prs" },
579 		{ 0x0091,	"gd" },
580 		{ 0x0092,	"ku" },
581 		{ 0x0093,	"quc" },
582 		{ 0x0401,	"ar\0SA" },
583 		{ 0x0402,	"bg\0BG" },
584 		{ 0x0403,	"ca\0ES" },
585 		{ 0x0404,	"zh\0TW" },
586 		{ 0x0405,	"cs\0CZ" },
587 		{ 0x0406,	"da\0DK" },
588 		{ 0x0407,	"de\0DE" },
589 		{ 0x0408,	"el\0GR" },
590 		{ 0x0409,	"en\0US" },
591 		{ 0x040a,	"es\0ES_tradnl" },
592 		{ 0x040b,	"fi\0FI" },
593 		{ 0x040c,	"fr\0FR" },
594 		{ 0x040d,	"he\0IL" },
595 		{ 0x040e,	"hu\0HU" },
596 		{ 0x040f,	"is\0IS" },
597 		{ 0x0410,	"it\0IT" },
598 		{ 0x0411,	"ja\0JP" },
599 		{ 0x0412,	"ko\0KR" },
600 		{ 0x0413,	"nl\0NL" },
601 		{ 0x0414,	"nb\0NO" },
602 		{ 0x0415,	"pl\0PL" },
603 		{ 0x0416,	"pt\0BR" },
604 		{ 0x0417,	"rm\0CH" },
605 		{ 0x0418,	"ro\0RO" },
606 		{ 0x0419,	"ru\0RU" },
607 		{ 0x041a,	"hr\0HR" },
608 		{ 0x041b,	"sk\0SK" },
609 		{ 0x041c,	"sq\0AL" },
610 		{ 0x041d,	"sv\0SE" },
611 		{ 0x041e,	"th\0TH" },
612 		{ 0x041f,	"tr\0TR" },
613 		{ 0x0420,	"ur\0PK" },
614 		{ 0x0421,	"id\0ID" },
615 		{ 0x0422,	"uk\0UA" },
616 		{ 0x0423,	"be\0BY" },
617 		{ 0x0424,	"sl\0SI" },
618 		{ 0x0425,	"et\0EE" },
619 		{ 0x0426,	"lv\0LV" },
620 		{ 0x0427,	"lt\0LT" },
621 		{ 0x0428,	"tg\0Cyrl\0TJ" },
622 		{ 0x0429,	"fa\0IR" },
623 		{ 0x042a,	"vi\0VN" },
624 		{ 0x042b,	"hy\0AM" },
625 		{ 0x042c,	"az\0Latn\0AZ" },
626 		{ 0x042d,	"eu\0ES" },
627 		{ 0x042e,	"hsb\0DE" },
628 		{ 0x042f,	"mk\0MK" },
629 		{ 0x0430,	"st\0ZA" },
630 		{ 0x0431,	"ts\0ZA" },
631 		{ 0x0432,	"tn\0ZA" },
632 		{ 0x0433,	"ve\0ZA" },
633 		{ 0x0434,	"xh\0ZA" },
634 		{ 0x0435,	"zu\0ZA" },
635 		{ 0x0436,	"af\0ZA" },
636 		{ 0x0437,	"ka\0GE" },
637 		{ 0x0438,	"fo\0FO" },
638 		{ 0x0439,	"hi\0IN" },
639 		{ 0x043a,	"mt\0MT" },
640 		{ 0x043b,	"se\0NO" },
641 		{ 0x043d,	"yi\0Hebr" },
642 		{ 0x043e,	"ms\0MY" },
643 		{ 0x043f,	"kk\0KZ" },
644 		{ 0x0440,	"ky\0KG" },
645 		{ 0x0441,	"sw\0KE" },
646 		{ 0x0442,	"tk\0TM" },
647 		{ 0x0443,	"uz\0Latn\0UZ" },
648 		{ 0x0444,	"tt\0RU" },
649 		{ 0x0445,	"bn\0IN" },
650 		{ 0x0446,	"pa\0IN" },
651 		{ 0x0447,	"gu\0IN" },
652 		{ 0x0448,	"or\0IN" },
653 		{ 0x0449,	"ta\0IN" },
654 		{ 0x044a,	"te\0IN" },
655 		{ 0x044b,	"kn\0IN" },
656 		{ 0x044c,	"ml\0IN" },
657 		{ 0x044d,	"as\0IN" },
658 		{ 0x044e,	"mr\0IN" },
659 		{ 0x044f,	"sa\0IN" },
660 		{ 0x0450,	"mn\0MN" },
661 		{ 0x0451,	"bo\0CN" },
662 		{ 0x0452,	"cy\0GB" },
663 		{ 0x0453,	"km\0KH" },
664 		{ 0x0454,	"lo\0LA" },
665 		{ 0x0455,	"my\0MM" },
666 		{ 0x0456,	"gl\0ES" },
667 		{ 0x0457,	"kok\0IN" },
668 		{ 0x0458,	"mni\0IN" },
669 		{ 0x0459,	"sd\0Deva\0IN" },
670 		{ 0x045a,	"syr\0SY" },
671 		{ 0x045b,	"si\0LK" },
672 		{ 0x045c,	"chr\0Cher\0US" },
673 		{ 0x045d,	"iu\0Cans\0CA" },
674 		{ 0x045e,	"am\0ET" },
675 		{ 0x045f,	"tzm\0Arab\0MA" },
676 		{ 0x0460,	"ks\0Arab" },
677 		{ 0x0461,	"ne\0NP" },
678 		{ 0x0462,	"fy\0NL" },
679 		{ 0x0463,	"ps\0AF" },
680 		{ 0x0464,	"fil\0PH" },
681 		{ 0x0465,	"dv\0MV" },
682 		{ 0x0466,	"bin\0NG" },
683 		{ 0x0467,	"fuv\0NG" },
684 		{ 0x0468,	"ha\0Latn\0NG" },
685 		{ 0x0469,	"ibb\0NG" },
686 		{ 0x046a,	"yo\0NG" },
687 		{ 0x046b,	"quz\0BO" },
688 		{ 0x046c,	"nso\0ZA" },
689 		{ 0x046d,	"ba\0RU" },
690 		{ 0x046e,	"lb\0LU" },
691 		{ 0x046f,	"kl\0GL" },
692 		{ 0x0470,	"ig\0NG" },
693 		{ 0x0471,	"kr\0NG" },
694 		{ 0x0472,	"om\0Ethi\0ET" },
695 		{ 0x0473,	"ti\0ET" },
696 		{ 0x0474,	"gn\0PY" },
697 		{ 0x0475,	"haw\0US" },
698 		{ 0x0476,	"la\0Latn" },
699 		{ 0x0477,	"so\0SO" },
700 		{ 0x0478,	"ii\0CN" },
701 		{ 0x0479,	"pap\0029" },
702 		{ 0x047a,	"arn\0CL" },
703 		{ 0x047c,	"moh\0CA" },
704 		{ 0x047e,	"br\0FR" },
705 		{ 0x0480,	"ug\0CN" },
706 		{ 0x0481,	"mi\0NZ" },
707 		{ 0x0482,	"oc\0FR" },
708 		{ 0x0483,	"co\0FR" },
709 		{ 0x0484,	"gsw\0FR" },
710 		{ 0x0485,	"sah\0RU" },
711 		{ 0x0486,	"qut\0GT" },
712 		{ 0x0487,	"rw\0RW" },
713 		{ 0x0488,	"wo\0SN" },
714 		{ 0x048c,	"prs\0AF" },
715 		{ 0x048d,	"plt\0MG" },
716 		{ 0x048e,	"zh\0yue\0HK" },
717 		{ 0x048f,	"tdd\0Tale\0CN" },
718 		{ 0x0490,	"khb\0Talu\0CN" },
719 		{ 0x0491,	"gd\0GB" },
720 		{ 0x0492,	"ku\0Arab\0IQ" },
721 		{ 0x0493,	"quc\0CO" },
722 		{ 0x0501,	"qps\0ploc" },
723 		{ 0x05fe,	"qps\0ploca" },
724 		{ 0x0801,	"ar\0IQ" },
725 		{ 0x0803,	"ca\0ES\0valencia" },
726 		{ 0x0804,	"zh\0CN" },
727 		{ 0x0807,	"de\0CH" },
728 		{ 0x0809,	"en\0GB" },
729 		{ 0x080a,	"es\0MX" },
730 		{ 0x080c,	"fr\0BE" },
731 		{ 0x0810,	"it\0CH" },
732 		{ 0x0811,	"ja\0Ploc\0JP" },
733 		{ 0x0813,	"nl\0BE" },
734 		{ 0x0814,	"nn\0NO" },
735 		{ 0x0816,	"pt\0PT" },
736 		{ 0x0818,	"ro\0MO" },
737 		{ 0x0819,	"ru\0MO" },
738 		{ 0x081a,	"sr\0Latn\0CS" },
739 		{ 0x081d,	"sv\0FI" },
740 		{ 0x0820,	"ur\0IN" },
741 		{ 0x082c,	"az\0Cyrl\0AZ" },
742 		{ 0x082e,	"dsb\0DE" },
743 		{ 0x0832,	"tn\0BW" },
744 		{ 0x083b,	"se\0SE" },
745 		{ 0x083c,	"ga\0IE" },
746 		{ 0x083e,	"ms\0BN" },
747 		{ 0x0843,	"uz\0Cyrl\0UZ" },
748 		{ 0x0845,	"bn\0BD" },
749 		{ 0x0846,	"pa\0Arab\0PK" },
750 		{ 0x0849,	"ta\0LK" },
751 		{ 0x0850,	"mn\0Mong\0CN" },
752 		{ 0x0851,	"bo\0BT" },
753 		{ 0x0859,	"sd\0Arab\0PK" },
754 		{ 0x085d,	"iu\0Latn\0CA" },
755 		{ 0x085f,	"tzm\0Latn\0DZ" },
756 		{ 0x0860,	"ks\0Deva" },
757 		{ 0x0861,	"ne\0IN" },
758 		{ 0x0867,	"ff\0Latn\0SN" },
759 		{ 0x086b,	"quz\0EC" },
760 		{ 0x0873,	"ti\0ER" },
761 		{ 0x09ff,	"qps\0plocm" },
762 		{ 0x0c01,	"ar\0EG" },
763 		{ 0x0c04,	"zh\0HK" },
764 		{ 0x0c07,	"de\0AT" },
765 		{ 0x0c09,	"en\0AU" },
766 		{ 0x0c0a,	"es\0ES" },
767 		{ 0x0c0c,	"fr\0CA" },
768 		{ 0x0c1a,	"sr\0Cyrl\0CS" },
769 		{ 0x0c3b,	"se\0FI" },
770 		{ 0x0c5f,	"tmz\0MA" },
771 		{ 0x0c6b,	"quz\0PE" },
772 		{ 0x1001,	"ar\0LY" },
773 		{ 0x1004,	"zh\0SG" },
774 		{ 0x1007,	"de\0LU" },
775 		{ 0x1009,	"en\0CA" },
776 		{ 0x100a,	"es\0GT" },
777 		{ 0x100c,	"fr\0CH" },
778 		{ 0x101a,	"hr\0BA" },
779 		{ 0x103b,	"smj\0NO" },
780 		{ 0x1401,	"ar\0DZ" },
781 		{ 0x1404,	"zh\0MO" },
782 		{ 0x1407,	"de\0LI" },
783 		{ 0x1409,	"en\0NZ" },
784 		{ 0x140a,	"es\0CR" },
785 		{ 0x140c,	"fr\0LU" },
786 		{ 0x141a,	"bs\0Latn\0BA" },
787 		{ 0x143b,	"smj\0SE" },
788 		{ 0x1801,	"ar\0MA" },
789 		{ 0x1809,	"en\0IE" },
790 		{ 0x180a,	"es\0PA" },
791 		{ 0x180c,	"fr\0MC" },
792 		{ 0x181a,	"sr\0Latn\0BA" },
793 		{ 0x183b,	"sma\0NO" },
794 		{ 0x1c01,	"ar\0TN" },
795 		{ 0x1c09,	"en\0ZA" },
796 		{ 0x1c0a,	"es\0DO" },
797 		{ 0x1c1a,	"sr\0Cyrl\0BA" },
798 		{ 0x1c3b,	"sma\0SE" },
799 		{ 0x2001,	"ar\0OM" },
800 		{ 0x2009,	"en\0JM" },
801 		{ 0x200a,	"es\0VE" },
802 		{ 0x200c,	"fr\0RE" },
803 		{ 0x201a,	"bs\0Cyrl\0BA" },
804 		{ 0x203b,	"sms\0FI" },
805 		{ 0x2401,	"ar\0YE" },
806 		{ 0x2409,	"en\0029" },
807 		{ 0x240a,	"es\0CO" },
808 		{ 0x240c,	"fr\0CG" },
809 		{ 0x241a,	"sr\0Latn\0RS" },
810 		{ 0x243b,	"smn\0FI" },
811 		{ 0x2801,	"ar\0SY" },
812 		{ 0x2809,	"en\0BZ" },
813 		{ 0x280a,	"es\0PE" },
814 		{ 0x280c,	"fr\0SN" },
815 		{ 0x281a,	"sr\0Cyrl\0RS" },
816 		{ 0x2c01,	"ar\0JO" },
817 		{ 0x2c09,	"en\0TT" },
818 		{ 0x2c0a,	"es\0AR" },
819 		{ 0x2c0c,	"fr\0CM" },
820 		{ 0x2c1a,	"sr\0Latn\0ME" },
821 		{ 0x3001,	"ar\0LB" },
822 		{ 0x3009,	"en\0ZW" },
823 		{ 0x300a,	"es\0EC" },
824 		{ 0x300c,	"fr\0CI" },
825 		{ 0x301a,	"sr\0Cyrl\0ME" },
826 		{ 0x3401,	"ar\0KW" },
827 		{ 0x3409,	"en\0PH" },
828 		{ 0x340a,	"es\0CL" },
829 		{ 0x340c,	"fr\0ML" },
830 		{ 0x3801,	"ar\0AE" },
831 		{ 0x3809,	"en\0ID" },
832 		{ 0x380a,	"es\0UY" },
833 		{ 0x380c,	"fr\0MA" },
834 		{ 0x3c01,	"ar\0BH" },
835 		{ 0x3c09,	"en\0HK" },
836 		{ 0x3c0a,	"es\0PY" },
837 		{ 0x3c0c,	"fr\0HT" },
838 		{ 0x4001,	"ar\0QA" },
839 		{ 0x4009,	"en\0IN" },
840 		{ 0x400a,	"es\0BO" },
841 		{ 0x4401,	"ar\0Ploc\0SA" },
842 		{ 0x4409,	"en\0MY" },
843 		{ 0x440a,	"es\0SV" },
844 		{ 0x4801,	"ar\0145" },
845 		{ 0x4809,	"en\0SG" },
846 		{ 0x480a,	"es\0HN" },
847 		{ 0x4c09,	"en\0AE" },
848 		{ 0x4c0a,	"es\0NI" },
849 		{ 0x5009,	"en\0BH" },
850 		{ 0x500a,	"es\0PR" },
851 		{ 0x5409,	"en\0EG" },
852 		{ 0x540a,	"es\0US" },
853 		{ 0x5809,	"en\0JO" },
854 		{ 0x5c09,	"en\0KW" },
855 		{ 0x6009,	"en\0TR" },
856 		{ 0x6409,	"en\0YE" },
857 		{ 0x641a,	"bs\0Cyrl" },
858 		{ 0x681a,	"bs\0Latn" },
859 		{ 0x6c1a,	"sr\0Cyrl" },
860 		{ 0x701a,	"sr\0Latn" },
861 		{ 0x703b,	"smn" },
862 		{ 0x742c,	"az\0Cyrl" },
863 		{ 0x743b,	"sms" },
864 		{ 0x7804,	"zh" },
865 		{ 0x7814,	"nn" },
866 		{ 0x781a,	"bs" },
867 		{ 0x782c,	"az\0Latn" },
868 		{ 0x783b,	"sma" },
869 		{ 0x7843,	"uz\0Cyrl" },
870 		{ 0x7850,	"mn\0Cyrl" },
871 		{ 0x785d,	"iu\0Cans" },
872 		{ 0x7c04,	"zh\0Hant" },
873 		{ 0x7c14,	"nb" },
874 		{ 0x7c1a,	"sr" },
875 		{ 0x7c28,	"tg\0Cyrl" },
876 		{ 0x7c2e,	"dsb" },
877 		{ 0x7c3b,	"smj" },
878 		{ 0x7c43,	"uz\0Latn" },
879 		{ 0x7c46,	"pa\0Arab" },
880 		{ 0x7c50,	"mn\0Mong" },
881 		{ 0x7c59,	"sd\0Arab" },
882 		{ 0x7c5c,	"chr\0Cher" },
883 		{ 0x7c5d,	"iu\0Latn" },
884 		{ 0x7c5f,	"tzm\0Latn" },
885 		{ 0x7c67,	"ff\0Latn" },
886 		{ 0x7c68,	"ha\0Latn" },
887 		{ 0x7c92,	"ku\0Arab" }
888 	};
889 	const uint16 current_id = (0x000000FFFFul & GetThreadLocale());
890 
891 	for(  size_t i=0;  i<lengthof(id2str)  &&  id2str[i].id<=current_id;  i++  ) {
892 		if(  id2str[i].id == current_id  ) {
893 			return id2str[i].name;
894 		}
895 	}
896 	return NULL;
897 }
898 #elif __HAIKU__
899 #include <Message.h>
900 #include <LocaleRoster.h>
901 
dr_get_locale_string()902 const char *dr_get_locale_string()
903 {
904 	static char code[4];
905 	BMessage result;
906 	const char *str;
907 	code[0] = 0;
908 	if(  B_OK == BLocaleRoster::Default()->GetPreferredLanguages( &result )  ) {
909 		result.FindString( (const char *)"language", &str );
910 		for(  int i=0;  i<lengthof(code)-1  &&  isalpha(str[i]);  i++  ) {
911 			code[i] = tolower(str[i]);
912 			code[i+1] = 0;
913 		}
914 	}
915 	return code[0] ? code : NULL;
916 }
917 #else
918 #include <locale.h>
919 
dr_get_locale_string()920 const char *dr_get_locale_string()
921 {
922 	static char code[4];
923 	char *ptr;
924 	setlocale( LC_ALL, "" );
925 	ptr = setlocale( LC_ALL, NULL );
926 	code[0] = 0;
927 	for(  int i=0;  i<(int)lengthof(code)-1  &&  isalpha(ptr[i]);  i++  ) {
928 		code[i] = tolower(ptr[i]);
929 		code[i+1] = 0;
930 	}
931 	setlocale( LC_ALL, "C" );	// or the number output may be broken
932 	return code[0] ? code : NULL;
933 }
934 #endif
935 
936 
937 
dr_fatal_notify(char const * const msg)938 void dr_fatal_notify(char const* const msg)
939 {
940 #ifdef _WIN32
941 	MessageBoxA(0, msg, "Fatal Error", MB_ICONEXCLAMATION);
942 #else
943 	fputs(msg, stderr);
944 #endif
945 }
946 
947 /**
948  * Open a program/starts a script to download pak sets from sourceforge
949  * @param path_to_program : actual simutrans pakfile directory
950  * @return false, if nothing was downloaded
951  */
dr_download_pakset(const char * path_to_program,bool portable)952 bool dr_download_pakset( const char *path_to_program, bool portable )
953 {
954 #ifdef _WIN32
955 	std::string param(portable ? "/P /D=" : "/D=");
956 	param.append(path_to_program);
957 	U16View const wparam(param.c_str());
958 	U16View const wpath_to_program(path_to_program);
959 
960 	SHELLEXECUTEINFOW shExInfo;
961 	shExInfo.cbSize = sizeof(shExInfo);
962 	shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
963 	shExInfo.hwnd = 0;
964 	/* since the installer will run again a child process
965 	 * using "runas" will make sure it is already privileged.
966 	 * Otherwise the waiting for installation will fail!
967 	 * (no idea how this works on XP though)
968 	 */
969 	shExInfo.lpVerb = L"runas";
970 	shExInfo.lpFile = L"download-paksets.exe";
971 	shExInfo.lpParameters = wparam;
972 	shExInfo.lpDirectory = wpath_to_program;
973 	shExInfo.nShow = SW_SHOW;
974 	shExInfo.hInstApp = 0;
975 
976 	if(  ShellExecuteExW(&shExInfo)  ) {
977 		WaitForSingleObject( shExInfo.hProcess, INFINITE );
978 		CloseHandle( shExInfo.hProcess );
979 	}
980 	return true;
981 #else
982 	char command[2048];
983 	sprintf(command, "%s/get_pak.sh %i", path_to_program, portable);
984 	system( command );
985 	return true;
986 #endif
987 }
988 
989 
sysmain(int const argc,char ** const argv)990 int sysmain(int const argc, char** const argv)
991 {
992 #ifdef _WIN32
993 	// Guess required buffer length, if too small then dynamically expand up to around 65536 characters.
994 	// It is unlikely this loop will ever run more than once in practice but is required to cover flaws with the API.
995 	size_t buffersize = MAX_PATH;
996 	WCHAR *wpathname;
997 	while(true) {
998 		wpathname = new WCHAR[buffersize];
999 		DWORD result = GetModuleFileNameW(GetModuleHandleW(0), wpathname, buffersize);
1000 		if(result < buffersize  ||  buffersize >= 1 << 16) {
1001 			break;
1002 		}
1003 		delete[] wpathname;
1004 		buffersize<<= 1;
1005 	}
1006 
1007 	// Convert UTF-16 to UTF-8
1008 	int const pathnamesize = WideCharToMultiByte(CP_UTF8, 0, wpathname, -1, NULL, 0, NULL, NULL);
1009 	char *const pathname = new char[pathnamesize];
1010 	WideCharToMultiByte(CP_UTF8, 0, wpathname, -1, pathname, pathnamesize, NULL, NULL);
1011 	delete[] wpathname;
1012 
1013 	argv[0] = pathname;
1014 #elif !defined __BEOS__
1015 #	if defined __GLIBC__ && !defined __AMIGA__
1016 	/* glibc has a non-standard extension */
1017 	char* buffer2 = 0;
1018 #	else
1019 	char buffer2[PATH_MAX];
1020 #	endif
1021 #	ifndef __AMIGA__
1022 	char buffer[PATH_MAX];
1023 	ssize_t const length = readlink("/proc/self/exe", buffer, lengthof(buffer) - 1);
1024 	if (length != -1) {
1025 		buffer[length] = '\0'; /* readlink() does not NUL-terminate */
1026 		argv[0] = buffer;
1027 	} else if (strchr(argv[0], '/') == NULL) {
1028 		// no /proc, no '/' in argv[0] => search PATH
1029 		const char* path = getenv("PATH");
1030 		if (path != NULL) {
1031 			size_t flen = strlen(argv[0]);
1032 			for (;;) { /* for each directory in PATH */
1033 				size_t dlen = strcspn(path, ":");
1034 				if (dlen > 0 && dlen + flen + 2 < lengthof(buffer)) {
1035 					// buffer = dir '/' argv[0] '\0'
1036 					memcpy(buffer, path, dlen);
1037 					buffer[dlen] = '/';
1038 					memcpy(buffer + dlen + 1, argv[0], flen + 1);
1039 					if (access(buffer, X_OK) == 0) {
1040 						argv[0] = buffer;
1041 						break; /* found it! */
1042 					}
1043 				}
1044 				if (path[dlen] == '\0')
1045 					break;
1046 				path += dlen + 1; /* skip ':' */
1047 			}
1048 		}
1049 	}
1050 #	endif
1051 	// no process file system => need to parse argv[0]
1052 	/* should work on most unix or gnu systems */
1053 	argv[0] = realpath(argv[0], buffer2);
1054 #endif
1055 
1056 	return simu_main(argc, argv);
1057 
1058 #ifdef _WIN32
1059 	// Cleanup for dynamic allocation, probably unnescescary.
1060 	delete[] pathname;
1061 #endif
1062 }
1063