OpenTTD Source: src/os/windows/win32.cpp Source File

OpenTTD Source 1.11.0-RC1

win32.cpp
Go to the documentation of this file.
1 /*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10 #include "../../stdafx.h"
11 #include "../../debug.h"
12 #include "../../gfx_func.h"
13 #include "../../textbuf_gui.h"
14 #include "../../fileio_func.h"
15 #include <windows.h>
16 #include <fcntl.h>
17 #include <mmsystem.h>
18 #include <regstr.h>
19 #define NO_SHOBJIDL_SORTDIRECTION // Avoid multiple definition of SORT_ASCENDING
20 #include <shlobj.h> /* SHGetFolderPath */
21 #include <shellapi.h>
22 #include "win32.h"
23 #include "../../fios.h"
24 #include "../../core/alloc_func.hpp"
25 #include "../../openttd.h"
26 #include "../../core/random_func.hpp"
27 #include "../../string_func.h"
28 #include "../../crashlog.h"
29 #include <errno.h>
30 #include <sys/stat.h>
31 #include "../../language.h"
32 #include "../../thread.h"
33 #include <array>
34
35 #include "../../safeguards.h"
36
37 static bool _has_console;
38 static bool _cursor_disable = true;
39 static bool _cursor_visible = true;
40
41 bool MyShowCursor(bool show, bool toggle)
42 {
43 if (toggle) _cursor_disable = !_cursor_disable;
44 if (_cursor_disable) return show;
45 if (_cursor_visible == show) return show;
46
47 _cursor_visible = show;
48 ShowCursor(show);
49
50 return !show;
51 }
52
56 bool LoadLibraryList(Function proc[], const char *dll)
57 {
58 while (*dll != '\0') {
59 HMODULE lib;
60 lib = LoadLibrary(OTTD2FS(dll));
61
62 if (lib == nullptr) return false;
63 for (;;) {
64 FARPROC p;
65
66 while (*dll++ != '\0') { /* Nothing */ }
67 if (*dll == '\0') break;
68 p = GetProcAddress(lib, dll);
69 if (p == nullptr) return false;
70 *proc++ = (Function)p;
71 }
72 dll++;
73 }
74 return true;
75 }
76
77 void ShowOSErrorBox(const char *buf, bool system)
78 {
79 MyShowCursor(true);
80 MessageBox(GetActiveWindow(), OTTD2FS(buf), L"Error!", MB_ICONSTOP | MB_TASKMODAL);
81 }
82
83 void OSOpenBrowser(const char *url)
84 {
85 ShellExecute(GetActiveWindow(), L"open", OTTD2FS(url), nullptr, nullptr, SW_SHOWNORMAL);
86 }
87
88 /* Code below for windows version of opendir/readdir/closedir copied and
89 * modified from Jan Wassenberg's GPL implementation posted over at
90 * http://www.gamedev.net/community/forums/topic.asp?topic_id=364584&whichpage=1&#2398903 */
91
92 struct DIR {
93 HANDLE hFind;
94 /* the dirent returned by readdir.
95 * note: having only one global instance is not possible because
96 * multiple independent opendir/readdir sequences must be supported. */
97 dirent ent;
98 WIN32_FIND_DATA fd;
99 /* since opendir calls FindFirstFile, we need a means of telling the
100 * first call to readdir that we already have a file.
101 * that's the case iff this is true */
102 bool at_first_entry;
103 };
104
105 /* suballocator - satisfies most requests with a reusable static instance.
106 * this avoids hundreds of alloc/free which would fragment the heap.
107 * To guarantee concurrency, we fall back to malloc if the instance is
108 * already in use (it's important to avoid surprises since this is such a
109 * low-level routine). */
110 static DIR _global_dir;
111 static LONG _global_dir_is_in_use = false;
112
113 static inline DIR *dir_calloc()
114 {
115 DIR *d;
116
117 if (InterlockedExchange(&_global_dir_is_in_use, true) == (LONG)true) {
118 d = CallocT<DIR>(1);
119 } else {
120 d = &_global_dir;
121 memset(d, 0, sizeof(*d));
122 }
123 return d;
124 }
125
126 static inline void dir_free(DIR *d)
127 {
128 if (d == &_global_dir) {
129 _global_dir_is_in_use = (LONG)false;
130 } else {
131 free(d);
132 }
133 }
134
135 DIR *opendir(const wchar_t *path)
136 {
137 DIR *d;
138 UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS); // disable 'no-disk' message box
139 DWORD fa = GetFileAttributes(path);
140
141 if ((fa != INVALID_FILE_ATTRIBUTES) && (fa & FILE_ATTRIBUTE_DIRECTORY)) {
142 d = dir_calloc();
143 if (d != nullptr) {
144 wchar_t search_path[MAX_PATH];
145 bool slash = path[wcslen(path) - 1] == '\\';
146
147 /* build search path for FindFirstFile, try not to append additional slashes
148 * as it throws Win9x off its groove for root directories */
149 _snwprintf(search_path, lengthof(search_path), L"%s%s*", path, slash ? L"" : L"\\");
150 *lastof(search_path) = '\0';
151 d->hFind = FindFirstFile(search_path, &d->fd);
152
153 if (d->hFind != INVALID_HANDLE_VALUE ||
154 GetLastError() == ERROR_NO_MORE_FILES) { // the directory is empty
155 d->ent.dir = d;
156 d->at_first_entry = true;
157 } else {
158 dir_free(d);
159 d = nullptr;
160 }
161 } else {
162 errno = ENOMEM;
163 }
164 } else {
165 /* path not found or not a directory */
166 d = nullptr;
167 errno = ENOENT;
168 }
169
170 SetErrorMode(sem); // restore previous setting
171 return d;
172 }
173
174 struct dirent *readdir(DIR *d)
175 {
176 DWORD prev_err = GetLastError(); // avoid polluting last error
177
178 if (d->at_first_entry) {
179 /* the directory was empty when opened */
180 if (d->hFind == INVALID_HANDLE_VALUE) return nullptr;
181 d->at_first_entry = false;
182 } else if (!FindNextFile(d->hFind, &d->fd)) { // determine cause and bail
183 if (GetLastError() == ERROR_NO_MORE_FILES) SetLastError(prev_err);
184 return nullptr;
185 }
186
187 /* This entry has passed all checks; return information about it.
188 * (note: d_name is a pointer; see struct dirent definition) */
189 d->ent.d_name = d->fd.cFileName;
190 return &d->ent;
191 }
192
193 int closedir(DIR *d)
194 {
195 FindClose(d->hFind);
196 dir_free(d);
197 return 0;
198 }
199
200 bool FiosIsRoot(const char *file)
201 {
202 return file[3] == '\0'; // C:\...
203 }
204
205 void FiosGetDrives(FileList &file_list)
206 {
207 wchar_t drives[256];
208 const wchar_t *s;
209
210 GetLogicalDriveStrings(lengthof(drives), drives);
211 for (s = drives; *s != '\0';) {
212 FiosItem *fios = file_list.Append();
213 fios->type = FIOS_TYPE_DRIVE;
214 fios->mtime = 0;
215 seprintf(fios->name, lastof(fios->name), "%c:", s[0] & 0xFF);
216 strecpy(fios->title, fios->name, lastof(fios->title));
217 while (*s++ != '\0') { /* Nothing */ }
218 }
219 }
220
221 bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb)
222 {
223 /* hectonanoseconds between Windows and POSIX epoch */
224 static const int64 posix_epoch_hns = 0x019DB1DED53E8000LL;
225 const WIN32_FIND_DATA *fd = &ent->dir->fd;
226
227 sb->st_size = ((uint64) fd->nFileSizeHigh << 32) + fd->nFileSizeLow;
228 /* UTC FILETIME to seconds-since-1970 UTC
229 * we just have to subtract POSIX epoch and scale down to units of seconds.
230 * http://www.gamedev.net/community/forums/topic.asp?topic_id=294070&whichpage=1&#1860504
231 * XXX - not entirely correct, since filetimes on FAT aren't UTC but local,
232 * this won't entirely be correct, but we use the time only for comparison. */
233 sb->st_mtime = (time_t)((*(const uint64*)&fd->ftLastWriteTime - posix_epoch_hns) / 1E7);
234 sb->st_mode = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)? S_IFDIR : S_IFREG;
235
236 return true;
237 }
238
239 bool FiosIsHiddenFile(const struct dirent *ent)
240 {
241 return (ent->dir->fd.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != 0;
242 }
243
244 bool FiosGetDiskFreeSpace(const char *path, uint64 *tot)
245 {
246 UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS); // disable 'no-disk' message box
247
248 ULARGE_INTEGER bytes_free;
249 bool retval = GetDiskFreeSpaceEx(OTTD2FS(path), &bytes_free, nullptr, nullptr);
250 if (retval) *tot = bytes_free.QuadPart;
251
252 SetErrorMode(sem); // reset previous setting
253 return retval;
254 }
255
256 static int ParseCommandLine(char *line, char **argv, int max_argc)
257 {
258 int n = 0;
259
260 do {
261 /* skip whitespace */
262 while (*line == ' ' || *line == '\t') line++;
263
264 /* end? */
265 if (*line == '\0') break;
266
267 /* special handling when quoted */
268 if (*line == '"') {
269 argv[n++] = ++line;
270 while (*line != '"') {
271 if (*line == '\0') return n;
272 line++;
273 }
274 } else {
275 argv[n++] = line;
276 while (*line != ' ' && *line != '\t') {
277 if (*line == '\0') return n;
278 line++;
279 }
280 }
281 *line++ = '\0';
282 } while (n != max_argc);
283
284 return n;
285 }
286
287 void CreateConsole()
288 {
289 HANDLE hand;
290 CONSOLE_SCREEN_BUFFER_INFO coninfo;
291
292 if (_has_console) return;
293 _has_console = true;
294
295 if (!AllocConsole()) return;
296
297 hand = GetStdHandle(STD_OUTPUT_HANDLE);
298 GetConsoleScreenBufferInfo(hand, &coninfo);
299 coninfo.dwSize.Y = 500;
300 SetConsoleScreenBufferSize(hand, coninfo.dwSize);
301
302 /* redirect unbuffered STDIN, STDOUT, STDERR to the console */
303 #if !defined(__CYGWIN__)
304
305 /* Check if we can open a handle to STDOUT. */
306 int fd = _open_osfhandle((intptr_t)hand, _O_TEXT);
307 if (fd == -1) {
308 /* Free everything related to the console. */
309 FreeConsole();
310 _has_console = false;
311 _close(fd);
312 CloseHandle(hand);
313
314 ShowInfo("Unable to open an output handle to the console. Check known-bugs.txt for details.");
315 return;
316 }
317
318 #if defined(_MSC_VER) && _MSC_VER >= 1900
319 freopen("CONOUT$", "a", stdout);
320 freopen("CONIN$", "r", stdin);
321 freopen("CONOUT$", "a", stderr);
322 #else
323 *stdout = *_fdopen(fd, "w");
324 *stdin = *_fdopen(_open_osfhandle((intptr_t)GetStdHandle(STD_INPUT_HANDLE), _O_TEXT), "r" );
325 *stderr = *_fdopen(_open_osfhandle((intptr_t)GetStdHandle(STD_ERROR_HANDLE), _O_TEXT), "w" );
326 #endif
327
328 #else
329 /* open_osfhandle is not in cygwin */
330 *stdout = *fdopen(1, "w" );
331 *stdin = *fdopen(0, "r" );
332 *stderr = *fdopen(2, "w" );
333 #endif
334
335 setvbuf(stdin, nullptr, _IONBF, 0);
336 setvbuf(stdout, nullptr, _IONBF, 0);
337 setvbuf(stderr, nullptr, _IONBF, 0);
338 }
339
341 static const char *_help_msg;
342
344 static INT_PTR CALLBACK HelpDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
345 {
346 switch (msg) {
347 case WM_INITDIALOG: {
348 char help_msg[8192];
349 const char *p = _help_msg;
350 char *q = help_msg;
351 while (q != lastof(help_msg) && *p != '\0') {
352 if (*p == '\n') {
353 *q++ = '\r';
354 if (q == lastof(help_msg)) {
355 q[-1] = '\0';
356 break;
357 }
358 }
359 *q++ = *p++;
360 }
361 *q = '\0';
362 /* We need to put the text in a separate buffer because the default
363 * buffer in OTTD2FS might not be large enough (512 chars). */
364 wchar_t help_msg_buf[8192];
365 SetDlgItemText(wnd, 11, convert_to_fs(help_msg, help_msg_buf, lengthof(help_msg_buf)));
366 SendDlgItemMessage(wnd, 11, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), FALSE);
367 } return TRUE;
368
369 case WM_COMMAND:
370 if (wParam == 12) ExitProcess(0);
371 return TRUE;
372 case WM_CLOSE:
373 ExitProcess(0);
374 }
375
376 return FALSE;
377 }
378
379 void ShowInfo(const char *str)
380 {
381 if (_has_console) {
382 fprintf(stderr, "%s\n", str);
383 } else {
384 bool old;
385 ReleaseCapture();
386 _left_button_clicked = _left_button_down = false;
387
388 old = MyShowCursor(true);
389 if (strlen(str) > 2048) {
390 /* The minimum length of the help message is 2048. Other messages sent via
391 * ShowInfo are much shorter, or so long they need this way of displaying
392 * them anyway. */
393 _help_msg = str;
394 DialogBox(GetModuleHandle(nullptr), MAKEINTRESOURCE(101), nullptr, HelpDialogFunc);
395 } else {
396 /* We need to put the text in a separate buffer because the default
397 * buffer in OTTD2FS might not be large enough (512 chars). */
398 wchar_t help_msg_buf[8192];
399 MessageBox(GetActiveWindow(), convert_to_fs(str, help_msg_buf, lengthof(help_msg_buf)), L"OpenTTD", MB_ICONINFORMATION | MB_OK);
400 }
401 MyShowCursor(old);
402 }
403 }
404
405 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
406 {
407 int argc;
408 char *argv[64]; // max 64 command line arguments
409
410 /* Set system timer resolution to 1ms. */
411 timeBeginPeriod(1);
412
413 CrashLog::InitialiseCrashLog();
414
415 /* Convert the command line to UTF-8. We need a dedicated buffer
416 * for this because argv[] points into this buffer and this needs to
417 * be available between subsequent calls to FS2OTTD(). */
418 char *cmdline = stredup(FS2OTTD(GetCommandLine()));
419
420 #if defined(_DEBUG)
421 CreateConsole();
422 #endif
423
424 _set_error_mode(_OUT_TO_MSGBOX); // force assertion output to messagebox
425
426 /* setup random seed to something quite random */
427 SetRandomSeed(GetTickCount());
428
429 argc = ParseCommandLine(cmdline, argv, lengthof(argv));
430
431 /* Make sure our arguments contain only valid UTF-8 characters. */
432 for (int i = 0; i < argc; i++) ValidateString(argv[i]);
433
434 openttd_main(argc, argv);
435
436 /* Restore system timer resolution. */
437 timeEndPeriod(1);
438
439 free(cmdline);
440 return 0;
441 }
442
443 char *getcwd(char *buf, size_t size)
444 {
445 wchar_t path[MAX_PATH];
446 GetCurrentDirectory(MAX_PATH - 1, path);
447 convert_from_fs(path, buf, size);
448 return buf;
449 }
450
451 extern std::string _config_file;
452
453 void DetermineBasePaths(const char *exe)
454 {
455 extern std::array<std::string, NUM_SEARCHPATHS> _searchpaths;
456
457 wchar_t path[MAX_PATH];
458 #ifdef WITH_PERSONAL_DIR
459 if (SUCCEEDED(SHGetFolderPath(nullptr, CSIDL_PERSONAL, nullptr, SHGFP_TYPE_CURRENT, path))) {
460 std::string tmp(FS2OTTD(path));
461 AppendPathSeparator(tmp);
462 tmp += PERSONAL_DIR;
463 AppendPathSeparator(tmp);
464 _searchpaths[SP_PERSONAL_DIR] = tmp;
465
466 tmp += "content_download";
467 AppendPathSeparator(tmp);
468 _searchpaths[SP_AUTODOWNLOAD_PERSONAL_DIR] = tmp;
469 } else {
470 _searchpaths[SP_PERSONAL_DIR].clear();
471 }
472
473 if (SUCCEEDED(SHGetFolderPath(nullptr, CSIDL_COMMON_DOCUMENTS, nullptr, SHGFP_TYPE_CURRENT, path))) {
474 std::string tmp(FS2OTTD(path));
475 AppendPathSeparator(tmp);
476 tmp += PERSONAL_DIR;
477 AppendPathSeparator(tmp);
478 _searchpaths[SP_SHARED_DIR] = tmp;
479 } else {
480 _searchpaths[SP_SHARED_DIR].clear();
481 }
482 #else
483 _searchpaths[SP_PERSONAL_DIR].clear();
484 _searchpaths[SP_SHARED_DIR].clear();
485 #endif
486
487 if (_config_file.empty()) {
488 char cwd[MAX_PATH];
489 getcwd(cwd, lengthof(cwd));
490 std::string cwd_s(cwd);
491 AppendPathSeparator(cwd_s);
492 _searchpaths[SP_WORKING_DIR] = cwd_s;
493 } else {
494 /* Use the folder of the config file as working directory. */
495 wchar_t config_dir[MAX_PATH];
496 wcsncpy(path, convert_to_fs(_config_file.c_str(), path, lengthof(path)), lengthof(path));
497 if (!GetFullPathName(path, lengthof(config_dir), config_dir, nullptr)) {
498 DEBUG(misc, 0, "GetFullPathName failed (%lu)\n", GetLastError());
499 _searchpaths[SP_WORKING_DIR].clear();
500 } else {
501 std::string tmp(FS2OTTD(config_dir));
502 auto pos = tmp.find_last_of(PATHSEPCHAR);
503 if (pos != std::string::npos) tmp.erase(pos + 1);
504
505 _searchpaths[SP_WORKING_DIR] = tmp;
506 }
507 }
508
509 if (!GetModuleFileName(nullptr, path, lengthof(path))) {
510 DEBUG(misc, 0, "GetModuleFileName failed (%lu)\n", GetLastError());
511 _searchpaths[SP_BINARY_DIR].clear();
512 } else {
513 wchar_t exec_dir[MAX_PATH];
514 wcsncpy(path, convert_to_fs(exe, path, lengthof(path)), lengthof(path));
515 if (!GetFullPathName(path, lengthof(exec_dir), exec_dir, nullptr)) {
516 DEBUG(misc, 0, "GetFullPathName failed (%lu)\n", GetLastError());
517 _searchpaths[SP_BINARY_DIR].clear();
518 } else {
519 std::string tmp(FS2OTTD(exec_dir));
520 auto pos = tmp.find_last_of(PATHSEPCHAR);
521 if (pos != std::string::npos) tmp.erase(pos + 1);
522
523 _searchpaths[SP_BINARY_DIR] = tmp;
524 }
525 }
526
527 _searchpaths[SP_INSTALLATION_DIR].clear();
528 _searchpaths[SP_APPLICATION_BUNDLE_DIR].clear();
529 }
530
531
532 bool GetClipboardContents(char *buffer, const char *last)
533 {
534 HGLOBAL cbuf;
535 const char *ptr;
536
537 if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
538 OpenClipboard(nullptr);
539 cbuf = GetClipboardData(CF_UNICODETEXT);
540
541 ptr = (const char*)GlobalLock(cbuf);
542 int out_len = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)ptr, -1, buffer, (last - buffer) + 1, nullptr, nullptr);
543 GlobalUnlock(cbuf);
544 CloseClipboard();
545
546 if (out_len == 0) return false;
547 } else {
548 return false;
549 }
550
551 return true;
552 }
553
554
565 const char *FS2OTTD(const wchar_t *name)
566 {
567 static char utf8_buf[512];
568 return convert_from_fs(name, utf8_buf, lengthof(utf8_buf));
569 }
570
580 const wchar_t *OTTD2FS(const char *name, bool console_cp)
581 {
582 static wchar_t system_buf[512];
583 return convert_to_fs(name, system_buf, lengthof(system_buf), console_cp);
584 }
585
586
595 char *convert_from_fs(const wchar_t *name, char *utf8_buf, size_t buflen)
596 {
597 const wchar_t *wide_buf = name;
598
599 /* Convert UTF-16 string to UTF-8. */
600 int len = WideCharToMultiByte(CP_UTF8, 0, wide_buf, -1, utf8_buf, (int)buflen, nullptr, nullptr);
601 if (len == 0) utf8_buf[0] = '\0';
602
603 return utf8_buf;
604 }
605
606
617 wchar_t *convert_to_fs(const char *name, wchar_t *system_buf, size_t buflen, bool console_cp)
618 {
619 int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, system_buf, (int)buflen);
620 if (len == 0) system_buf[0] = '\0';
621
622 return system_buf;
623 }
624
626 const char *GetCurrentLocale(const char *)
627 {
628 char lang[9], country[9];
629 if (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, lang, lengthof(lang)) == 0 ||
630 GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, country, lengthof(country)) == 0) {
631 /* Unable to retrieve the locale. */
632 return nullptr;
633 }
634 /* Format it as 'en_us'. */
635 static char retbuf[6] = {lang[0], lang[1], '_', country[0], country[1], 0};
636 return retbuf;
637 }
638
639
640 static WCHAR _cur_iso_locale[16] = L"";
641
642 void Win32SetCurrentLocaleName(const char *iso_code)
643 {
644 /* Convert the iso code into the format that windows expects. */
645 char iso[16];
646 if (strcmp(iso_code, "zh_TW") == 0) {
647 strecpy(iso, "zh-Hant", lastof(iso));
648 } else if (strcmp(iso_code, "zh_CN") == 0) {
649 strecpy(iso, "zh-Hans", lastof(iso));
650 } else {
651 /* Windows expects a '-' between language and country code, but we use a '_'. */
652 strecpy(iso, iso_code, lastof(iso));
653 for (char *c = iso; *c != '\0'; c++) {
654 if (*c == '_') *c = '-';
655 }
656 }
657
658 MultiByteToWideChar(CP_UTF8, 0, iso, -1, _cur_iso_locale, lengthof(_cur_iso_locale));
659 }
660
661 int OTTDStringCompare(const char *s1, const char *s2)
662 {
663 typedef int (WINAPI *PFNCOMPARESTRINGEX)(LPCWSTR, DWORD, LPCWCH, int, LPCWCH, int, LPVOID, LPVOID, LPARAM);
664 static PFNCOMPARESTRINGEX _CompareStringEx = nullptr;
665 static bool first_time = true;
666
667 #ifndef SORT_DIGITSASNUMBERS
668 # define SORT_DIGITSASNUMBERS 0x00000008 // use digits as numbers sort method
669 #endif
670 #ifndef LINGUISTIC_IGNORECASE
671 # define LINGUISTIC_IGNORECASE 0x00000010 // linguistically appropriate 'ignore case'
672 #endif
673
674 if (first_time) {
675 _CompareStringEx = (PFNCOMPARESTRINGEX)GetProcAddress(GetModuleHandle(L"Kernel32"), "CompareStringEx");
676 first_time = false;
677 }
678
679 if (_CompareStringEx != nullptr) {
680 /* CompareStringEx takes UTF-16 strings, even in ANSI-builds. */
681 int len_s1 = MultiByteToWideChar(CP_UTF8, 0, s1, -1, nullptr, 0);
682 int len_s2 = MultiByteToWideChar(CP_UTF8, 0, s2, -1, nullptr, 0);
683
684 if (len_s1 != 0 && len_s2 != 0) {
685 LPWSTR str_s1 = AllocaM(WCHAR, len_s1);
686 LPWSTR str_s2 = AllocaM(WCHAR, len_s2);
687
688 MultiByteToWideChar(CP_UTF8, 0, s1, -1, str_s1, len_s1);
689 MultiByteToWideChar(CP_UTF8, 0, s2, -1, str_s2, len_s2);
690
691 int result = _CompareStringEx(_cur_iso_locale, LINGUISTIC_IGNORECASE | SORT_DIGITSASNUMBERS, str_s1, -1, str_s2, -1, nullptr, nullptr, 0);
692 if (result != 0) return result;
693 }
694 }
695
696 wchar_t s1_buf[512], s2_buf[512];
697 convert_to_fs(s1, s1_buf, lengthof(s1_buf));
698 convert_to_fs(s2, s2_buf, lengthof(s2_buf));
699
700 return CompareString(MAKELCID(_current_language->winlangid, SORT_DEFAULT), NORM_IGNORECASE, s1_buf, -1, s2_buf, -1);
701 }
702
707 bool IsWindowsVistaOrGreater()
708 {
709 typedef BOOL (WINAPI * LPVERIFYVERSIONINFO)(LPOSVERSIONINFOEX, DWORD, DWORDLONG);
710 typedef ULONGLONG (NTAPI * LPVERSETCONDITIONMASK)(ULONGLONG, DWORD, BYTE);
711 #ifdef UNICODE
712 static LPVERIFYVERSIONINFO _VerifyVersionInfo = (LPVERIFYVERSIONINFO)GetProcAddress(GetModuleHandle(_T("Kernel32")), "VerifyVersionInfoW");
713 #else
714 static LPVERIFYVERSIONINFO _VerifyVersionInfo = (LPVERIFYVERSIONINFO)GetProcAddress(GetModuleHandle(_T("Kernel32")), "VerifyVersionInfoA");
715 #endif
716 static LPVERSETCONDITIONMASK _VerSetConditionMask = (LPVERSETCONDITIONMASK)GetProcAddress(GetModuleHandle(_T("Kernel32")), "VerSetConditionMask");
717
718 if (_VerifyVersionInfo != nullptr && _VerSetConditionMask != nullptr) {
719 OSVERSIONINFOEX osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0 };
720 DWORDLONG dwlConditionMask = 0;
721 dwlConditionMask = _VerSetConditionMask(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
722 dwlConditionMask = _VerSetConditionMask(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
723 dwlConditionMask = _VerSetConditionMask(dwlConditionMask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
724
725 osvi.dwMajorVersion = 6;
726 osvi.dwMinorVersion = 0;
727 osvi.wServicePackMajor = 0;
728
729 return _VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;
730 } else {
731 return LOBYTE(GetVersion()) >= 6;
732 }
733 }
734
735 #ifdef _MSC_VER
736 /* Based on code from MSDN: https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
737 const DWORD MS_VC_EXCEPTION = 0x406D1388;
738
739 PACK_N(struct THREADNAME_INFO {
740 DWORD dwType;
741 LPCSTR szName;
742 DWORD dwThreadID;
743 DWORD dwFlags;
744 }, 8);
745
749 void SetCurrentThreadName(const char *threadName)
750 {
751 THREADNAME_INFO info;
752 info.dwType = 0x1000;
753 info.szName = threadName;
754 info.dwThreadID = -1;
755 info.dwFlags = 0;
756
757 #pragma warning(push)
758 #pragma warning(disable: 6320 6322)
759 __try {
760 RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
761 } __except (EXCEPTION_EXECUTE_HANDLER) {
762 }
763 #pragma warning(pop)
764 }
765 #else
766 void SetCurrentThreadName(const char *) {}
767 #endif
openttd_main
int openttd_main(int argc, char *argv[])
Main entry point for this lovely game.
Definition: openttd.cpp:546
FS2OTTD
const char * FS2OTTD(const wchar_t *name)
Convert to OpenTTD's encoding from wide characters.
Definition: win32.cpp:565
_left_button_down
bool _left_button_down
Is left mouse button pressed?
Definition: gfx.cpp:38
win32.h
SP_PERSONAL_DIR
@ SP_PERSONAL_DIR
Search in the personal directory.
Definition: fileio_type.h:137
SP_AUTODOWNLOAD_PERSONAL_DIR
@ SP_AUTODOWNLOAD_PERSONAL_DIR
Search within the autodownload directory located in the personal directory.
Definition: fileio_type.h:143
LoadLibraryList
bool LoadLibraryList(Function proc[], const char *dll)
Helper function needed by dynamically loading libraries.
Definition: win32.cpp:56
HelpDialogFunc
static INT_PTR CALLBACK HelpDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
Callback function to handle the window.
Definition: win32.cpp:344
SP_INSTALLATION_DIR
@ SP_INSTALLATION_DIR
Search in the installation directory.
Definition: fileio_type.h:140
FileList
List of file information.
Definition: fios.h:112
convert_from_fs
char * convert_from_fs(const wchar_t *name, char *utf8_buf, size_t buflen)
Convert to OpenTTD's encoding from that of the environment in UNICODE.
Definition: win32.cpp:595
_searchpaths
std::array< std::string, NUM_SEARCHPATHS > _searchpaths
The search paths OpenTTD could search through.
Definition: fileio.cpp:235
DEBUG
#define DEBUG(name, level,...)
Output a line of debugging information.
Definition: debug.h:35
convert_to_fs
wchar_t * convert_to_fs(const char *name, wchar_t *system_buf, size_t buflen, bool console_cp)
Convert from OpenTTD's encoding to that of the environment in UNICODE.
Definition: win32.cpp:617
FiosItem
Deals with finding savegames.
Definition: fios.h:103
SP_APPLICATION_BUNDLE_DIR
@ SP_APPLICATION_BUNDLE_DIR
Search within the application bundle.
Definition: fileio_type.h:141
SetCurrentThreadName
void SetCurrentThreadName(const char *)
Name the thread this function is called on for the debugger.
Definition: win32.cpp:766
_current_language
const LanguageMetadata * _current_language
The currently loaded language.
Definition: strings.cpp:46
SP_SHARED_DIR
@ SP_SHARED_DIR
Search in the shared directory, like 'Shared Files' under Windows.
Definition: fileio_type.h:138
ValidateString
void ValidateString(const char *str)
Scans the string for valid characters and if it finds invalid ones, replaces them with a question mar...
Definition: string.cpp:267
GetCurrentLocale
const char * GetCurrentLocale(const char *)
Determine the current user's locale.
Definition: win32.cpp:626
SP_WORKING_DIR
@ SP_WORKING_DIR
Search in the working directory.
Definition: fileio_type.h:133
AppendPathSeparator
void AppendPathSeparator(std::string &buf)
Appends, if necessary, the path separator character to the end of the string.
Definition: fileio.cpp:520
FileList::Append
FiosItem * Append()
Construct a new entry in the file list.
Definition: fios.h:120
_help_msg
static const char * _help_msg
Temporary pointer to get the help message to the window.
Definition: win32.cpp:341
CrashLog::InitialiseCrashLog
static void InitialiseCrashLog()
Initialiser for crash logs; do the appropriate things so crashes are handled by our crash handler ins...
Definition: crashlog_osx.cpp:254
SP_BINARY_DIR
@ SP_BINARY_DIR
Search in the directory where the binary resides.
Definition: fileio_type.h:139
SetRandomSeed
void SetRandomSeed(uint32 seed)
(Re)set the state of the random number generators.
Definition: random_func.cpp:65
GetClipboardContents
bool GetClipboardContents(char *buffer, const char *last)
Try to retrieve the current clipboard contents.
Definition: win32.cpp:532
DIR
Definition: win32.cpp:92
LanguagePackHeader::winlangid
uint16 winlangid
Windows language ID: Windows cannot and will not convert isocodes to something it can use to determin...
Definition: language.h:51
seprintf
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
Definition: string.cpp:442
DetermineBasePaths
void DetermineBasePaths(const char *exe)
Determine the base (personal dir and game data dir) paths.
Definition: fileio.cpp:1000
_config_file
std::string _config_file
Configuration file of OpenTTD.
Definition: settings.cpp:83
stredup
char * stredup(const char *s, const char *last)
Create a duplicate of the given string.
Definition: string.cpp:137
lengthof
#define lengthof(x)
Return the length of an fixed size array.
Definition: stdafx.h:367
strecpy
char * strecpy(char *dst, const char *src, const char *last)
Copies characters from one buffer to another.
Definition: string.cpp:112
free
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: stdafx.h:454
IsWindowsVistaOrGreater
bool IsWindowsVistaOrGreater()
Is the current Windows version Vista or later?
Definition: win32.cpp:707
lastof
#define lastof(x)
Get the last element of an fixed size array.
Definition: stdafx.h:383
_left_button_clicked
bool _left_button_clicked
Is left mouse button clicked?
Definition: gfx.cpp:39
OTTD2FS
const wchar_t * OTTD2FS(const char *name, bool console_cp)
Convert from OpenTTD's encoding to wide characters.
Definition: win32.cpp:580
AllocaM
#define AllocaM(T, num_elements)
alloca() has to be called in the parent function, so define AllocaM() as a macro
Definition: alloc_func.hpp:132