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