OpenTTD
win32.cpp
Go to the documentation of this file.
1 /* $Id$ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * 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.
6  * 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.
7  * 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/>.
8  */
9 
12 #include "../../stdafx.h"
13 #include "../../debug.h"
14 #include "../../gfx_func.h"
15 #include "../../textbuf_gui.h"
16 #include "../../fileio_func.h"
17 #include <windows.h>
18 #include <fcntl.h>
19 #include <regstr.h>
20 #define NO_SHOBJIDL_SORTDIRECTION // Avoid multiple definition of SORT_ASCENDING
21 #include <shlobj.h> /* SHGetFolderPath */
22 #include <shellapi.h>
23 #include "win32.h"
24 #include "../../fios.h"
25 #include "../../core/alloc_func.hpp"
26 #include "../../openttd.h"
27 #include "../../core/random_func.hpp"
28 #include "../../string_func.h"
29 #include "../../crashlog.h"
30 #include <errno.h>
31 #include <sys/stat.h>
32 #include "../../language.h"
33 #include "../../thread.h"
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 
58 bool LoadLibraryList(Function proc[], const char *dll)
59 {
60  while (*dll != '\0') {
61  HMODULE lib;
62  lib = LoadLibrary(MB_TO_WIDE(dll));
63 
64  if (lib == nullptr) return false;
65  for (;;) {
66  FARPROC p;
67 
68  while (*dll++ != '\0') { /* Nothing */ }
69  if (*dll == '\0') break;
70  p = GetProcAddress(lib, dll);
71  if (p == nullptr) return false;
72  *proc++ = (Function)p;
73  }
74  dll++;
75  }
76  return true;
77 }
78 
79 void ShowOSErrorBox(const char *buf, bool system)
80 {
81  MyShowCursor(true);
82  MessageBox(GetActiveWindow(), OTTD2FS(buf), _T("Error!"), MB_ICONSTOP | MB_TASKMODAL);
83 }
84 
85 void OSOpenBrowser(const char *url)
86 {
87  ShellExecute(GetActiveWindow(), _T("open"), OTTD2FS(url), nullptr, nullptr, SW_SHOWNORMAL);
88 }
89 
90 /* Code below for windows version of opendir/readdir/closedir copied and
91  * modified from Jan Wassenberg's GPL implementation posted over at
92  * http://www.gamedev.net/community/forums/topic.asp?topic_id=364584&whichpage=1&#2398903 */
93 
94 struct DIR {
95  HANDLE hFind;
96  /* the dirent returned by readdir.
97  * note: having only one global instance is not possible because
98  * multiple independent opendir/readdir sequences must be supported. */
99  dirent ent;
100  WIN32_FIND_DATA fd;
101  /* since opendir calls FindFirstFile, we need a means of telling the
102  * first call to readdir that we already have a file.
103  * that's the case iff this is true */
104  bool at_first_entry;
105 };
106 
107 /* suballocator - satisfies most requests with a reusable static instance.
108  * this avoids hundreds of alloc/free which would fragment the heap.
109  * To guarantee concurrency, we fall back to malloc if the instance is
110  * already in use (it's important to avoid surprises since this is such a
111  * low-level routine). */
112 static DIR _global_dir;
113 static LONG _global_dir_is_in_use = false;
114 
115 static inline DIR *dir_calloc()
116 {
117  DIR *d;
118 
119  if (InterlockedExchange(&_global_dir_is_in_use, true) == (LONG)true) {
120  d = CallocT<DIR>(1);
121  } else {
122  d = &_global_dir;
123  memset(d, 0, sizeof(*d));
124  }
125  return d;
126 }
127 
128 static inline void dir_free(DIR *d)
129 {
130  if (d == &_global_dir) {
131  _global_dir_is_in_use = (LONG)false;
132  } else {
133  free(d);
134  }
135 }
136 
137 DIR *opendir(const TCHAR *path)
138 {
139  DIR *d;
140  UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS); // disable 'no-disk' message box
141  DWORD fa = GetFileAttributes(path);
142 
143  if ((fa != INVALID_FILE_ATTRIBUTES) && (fa & FILE_ATTRIBUTE_DIRECTORY)) {
144  d = dir_calloc();
145  if (d != nullptr) {
146  TCHAR search_path[MAX_PATH];
147  bool slash = path[_tcslen(path) - 1] == '\\';
148 
149  /* build search path for FindFirstFile, try not to append additional slashes
150  * as it throws Win9x off its groove for root directories */
151  _sntprintf(search_path, lengthof(search_path), _T("%s%s*"), path, slash ? _T("") : _T("\\"));
152  *lastof(search_path) = '\0';
153  d->hFind = FindFirstFile(search_path, &d->fd);
154 
155  if (d->hFind != INVALID_HANDLE_VALUE ||
156  GetLastError() == ERROR_NO_MORE_FILES) { // the directory is empty
157  d->ent.dir = d;
158  d->at_first_entry = true;
159  } else {
160  dir_free(d);
161  d = nullptr;
162  }
163  } else {
164  errno = ENOMEM;
165  }
166  } else {
167  /* path not found or not a directory */
168  d = nullptr;
169  errno = ENOENT;
170  }
171 
172  SetErrorMode(sem); // restore previous setting
173  return d;
174 }
175 
176 struct dirent *readdir(DIR *d)
177 {
178  DWORD prev_err = GetLastError(); // avoid polluting last error
179 
180  if (d->at_first_entry) {
181  /* the directory was empty when opened */
182  if (d->hFind == INVALID_HANDLE_VALUE) return nullptr;
183  d->at_first_entry = false;
184  } else if (!FindNextFile(d->hFind, &d->fd)) { // determine cause and bail
185  if (GetLastError() == ERROR_NO_MORE_FILES) SetLastError(prev_err);
186  return nullptr;
187  }
188 
189  /* This entry has passed all checks; return information about it.
190  * (note: d_name is a pointer; see struct dirent definition) */
191  d->ent.d_name = d->fd.cFileName;
192  return &d->ent;
193 }
194 
195 int closedir(DIR *d)
196 {
197  FindClose(d->hFind);
198  dir_free(d);
199  return 0;
200 }
201 
202 bool FiosIsRoot(const char *file)
203 {
204  return file[3] == '\0'; // C:\...
205 }
206 
207 void FiosGetDrives(FileList &file_list)
208 {
209  TCHAR drives[256];
210  const TCHAR *s;
211 
212  GetLogicalDriveStrings(lengthof(drives), drives);
213  for (s = drives; *s != '\0';) {
214  FiosItem *fios = file_list.Append();
215  fios->type = FIOS_TYPE_DRIVE;
216  fios->mtime = 0;
217  seprintf(fios->name, lastof(fios->name), "%c:", s[0] & 0xFF);
218  strecpy(fios->title, fios->name, lastof(fios->title));
219  while (*s++ != '\0') { /* Nothing */ }
220  }
221 }
222 
223 bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb)
224 {
225  /* hectonanoseconds between Windows and POSIX epoch */
226  static const int64 posix_epoch_hns = 0x019DB1DED53E8000LL;
227  const WIN32_FIND_DATA *fd = &ent->dir->fd;
228 
229  sb->st_size = ((uint64) fd->nFileSizeHigh << 32) + fd->nFileSizeLow;
230  /* UTC FILETIME to seconds-since-1970 UTC
231  * we just have to subtract POSIX epoch and scale down to units of seconds.
232  * http://www.gamedev.net/community/forums/topic.asp?topic_id=294070&whichpage=1&#1860504
233  * XXX - not entirely correct, since filetimes on FAT aren't UTC but local,
234  * this won't entirely be correct, but we use the time only for comparison. */
235  sb->st_mtime = (time_t)((*(const uint64*)&fd->ftLastWriteTime - posix_epoch_hns) / 1E7);
236  sb->st_mode = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)? S_IFDIR : S_IFREG;
237 
238  return true;
239 }
240 
241 bool FiosIsHiddenFile(const struct dirent *ent)
242 {
243  return (ent->dir->fd.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != 0;
244 }
245 
246 bool FiosGetDiskFreeSpace(const char *path, uint64 *tot)
247 {
248  UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS); // disable 'no-disk' message box
249  bool retval = false;
250  TCHAR root[4];
251  DWORD spc, bps, nfc, tnc;
252 
253  _sntprintf(root, lengthof(root), _T("%c:") _T(PATHSEP), path[0]);
254  if (tot != nullptr && GetDiskFreeSpace(root, &spc, &bps, &nfc, &tnc)) {
255  *tot = ((spc * bps) * (uint64)nfc);
256  retval = true;
257  }
258 
259  SetErrorMode(sem); // reset previous setting
260  return retval;
261 }
262 
263 static int ParseCommandLine(char *line, char **argv, int max_argc)
264 {
265  int n = 0;
266 
267  do {
268  /* skip whitespace */
269  while (*line == ' ' || *line == '\t') line++;
270 
271  /* end? */
272  if (*line == '\0') break;
273 
274  /* special handling when quoted */
275  if (*line == '"') {
276  argv[n++] = ++line;
277  while (*line != '"') {
278  if (*line == '\0') return n;
279  line++;
280  }
281  } else {
282  argv[n++] = line;
283  while (*line != ' ' && *line != '\t') {
284  if (*line == '\0') return n;
285  line++;
286  }
287  }
288  *line++ = '\0';
289  } while (n != max_argc);
290 
291  return n;
292 }
293 
294 void CreateConsole()
295 {
296  HANDLE hand;
297  CONSOLE_SCREEN_BUFFER_INFO coninfo;
298 
299  if (_has_console) return;
300  _has_console = true;
301 
302  if (!AllocConsole()) return;
303 
304  hand = GetStdHandle(STD_OUTPUT_HANDLE);
305  GetConsoleScreenBufferInfo(hand, &coninfo);
306  coninfo.dwSize.Y = 500;
307  SetConsoleScreenBufferSize(hand, coninfo.dwSize);
308 
309  /* redirect unbuffered STDIN, STDOUT, STDERR to the console */
310 #if !defined(__CYGWIN__)
311 
312  /* Check if we can open a handle to STDOUT. */
313  int fd = _open_osfhandle((intptr_t)hand, _O_TEXT);
314  if (fd == -1) {
315  /* Free everything related to the console. */
316  FreeConsole();
317  _has_console = false;
318  _close(fd);
319  CloseHandle(hand);
320 
321  ShowInfo("Unable to open an output handle to the console. Check known-bugs.txt for details.");
322  return;
323  }
324 
325 #if defined(_MSC_VER) && _MSC_VER >= 1900
326  freopen("CONOUT$", "a", stdout);
327  freopen("CONIN$", "r", stdin);
328  freopen("CONOUT$", "a", stderr);
329 #else
330  *stdout = *_fdopen(fd, "w");
331  *stdin = *_fdopen(_open_osfhandle((intptr_t)GetStdHandle(STD_INPUT_HANDLE), _O_TEXT), "r" );
332  *stderr = *_fdopen(_open_osfhandle((intptr_t)GetStdHandle(STD_ERROR_HANDLE), _O_TEXT), "w" );
333 #endif
334 
335 #else
336  /* open_osfhandle is not in cygwin */
337  *stdout = *fdopen(1, "w" );
338  *stdin = *fdopen(0, "r" );
339  *stderr = *fdopen(2, "w" );
340 #endif
341 
342  setvbuf(stdin, nullptr, _IONBF, 0);
343  setvbuf(stdout, nullptr, _IONBF, 0);
344  setvbuf(stderr, nullptr, _IONBF, 0);
345 }
346 
348 static const char *_help_msg;
349 
351 static INT_PTR CALLBACK HelpDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
352 {
353  switch (msg) {
354  case WM_INITDIALOG: {
355  char help_msg[8192];
356  const char *p = _help_msg;
357  char *q = help_msg;
358  while (q != lastof(help_msg) && *p != '\0') {
359  if (*p == '\n') {
360  *q++ = '\r';
361  if (q == lastof(help_msg)) {
362  q[-1] = '\0';
363  break;
364  }
365  }
366  *q++ = *p++;
367  }
368  *q = '\0';
369  /* We need to put the text in a separate buffer because the default
370  * buffer in OTTD2FS might not be large enough (512 chars). */
371  TCHAR help_msg_buf[8192];
372  SetDlgItemText(wnd, 11, convert_to_fs(help_msg, help_msg_buf, lengthof(help_msg_buf)));
373  SendDlgItemMessage(wnd, 11, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), FALSE);
374  } return TRUE;
375 
376  case WM_COMMAND:
377  if (wParam == 12) ExitProcess(0);
378  return TRUE;
379  case WM_CLOSE:
380  ExitProcess(0);
381  }
382 
383  return FALSE;
384 }
385 
386 void ShowInfo(const char *str)
387 {
388  if (_has_console) {
389  fprintf(stderr, "%s\n", str);
390  } else {
391  bool old;
392  ReleaseCapture();
394 
395  old = MyShowCursor(true);
396  if (strlen(str) > 2048) {
397  /* The minimum length of the help message is 2048. Other messages sent via
398  * ShowInfo are much shorter, or so long they need this way of displaying
399  * them anyway. */
400  _help_msg = str;
401  DialogBox(GetModuleHandle(nullptr), MAKEINTRESOURCE(101), nullptr, HelpDialogFunc);
402  } else {
403  /* We need to put the text in a separate buffer because the default
404  * buffer in OTTD2FS might not be large enough (512 chars). */
405  TCHAR help_msg_buf[8192];
406  MessageBox(GetActiveWindow(), convert_to_fs(str, help_msg_buf, lengthof(help_msg_buf)), _T("OpenTTD"), MB_ICONINFORMATION | MB_OK);
407  }
408  MyShowCursor(old);
409  }
410 }
411 
412 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
413 {
414  int argc;
415  char *argv[64]; // max 64 command line arguments
416 
418 
419 #if defined(UNICODE)
420  /* Check if a win9x user started the win32 version */
421  if (HasBit(GetVersion(), 31)) usererror("This version of OpenTTD doesn't run on windows 95/98/ME.\nPlease download the win9x binary and try again.");
422 #endif
423 
424  /* Convert the command line to UTF-8. We need a dedicated buffer
425  * for this because argv[] points into this buffer and this needs to
426  * be available between subsequent calls to FS2OTTD(). */
427  char *cmdline = stredup(FS2OTTD(GetCommandLine()));
428 
429 #if defined(_DEBUG)
430  CreateConsole();
431 #endif
432 
433  _set_error_mode(_OUT_TO_MSGBOX); // force assertion output to messagebox
434 
435  /* setup random seed to something quite random */
436  SetRandomSeed(GetTickCount());
437 
438  argc = ParseCommandLine(cmdline, argv, lengthof(argv));
439 
440  /* Make sure our arguments contain only valid UTF-8 characters. */
441  for (int i = 0; i < argc; i++) ValidateString(argv[i]);
442 
443  openttd_main(argc, argv);
444  free(cmdline);
445  return 0;
446 }
447 
448 char *getcwd(char *buf, size_t size)
449 {
450  TCHAR path[MAX_PATH];
451  GetCurrentDirectory(MAX_PATH - 1, path);
452  convert_from_fs(path, buf, size);
453  return buf;
454 }
455 
456 
457 void DetermineBasePaths(const char *exe)
458 {
459  char tmp[MAX_PATH];
460  TCHAR path[MAX_PATH];
461 #ifdef WITH_PERSONAL_DIR
462  if (SUCCEEDED(OTTDSHGetFolderPath(nullptr, CSIDL_PERSONAL, nullptr, SHGFP_TYPE_CURRENT, path))) {
463  strecpy(tmp, FS2OTTD(path), lastof(tmp));
464  AppendPathSeparator(tmp, lastof(tmp));
465  strecat(tmp, PERSONAL_DIR, lastof(tmp));
466  AppendPathSeparator(tmp, lastof(tmp));
468  } else {
469  _searchpaths[SP_PERSONAL_DIR] = nullptr;
470  }
471 
472  if (SUCCEEDED(OTTDSHGetFolderPath(nullptr, CSIDL_COMMON_DOCUMENTS, nullptr, SHGFP_TYPE_CURRENT, path))) {
473  strecpy(tmp, FS2OTTD(path), lastof(tmp));
474  AppendPathSeparator(tmp, lastof(tmp));
475  strecat(tmp, PERSONAL_DIR, lastof(tmp));
476  AppendPathSeparator(tmp, lastof(tmp));
478  } else {
479  _searchpaths[SP_SHARED_DIR] = nullptr;
480  }
481 #else
482  _searchpaths[SP_PERSONAL_DIR] = nullptr;
483  _searchpaths[SP_SHARED_DIR] = nullptr;
484 #endif
485 
486  /* Get the path to working directory of OpenTTD */
487  getcwd(tmp, lengthof(tmp));
488  AppendPathSeparator(tmp, lastof(tmp));
490 
491  if (!GetModuleFileName(nullptr, path, lengthof(path))) {
492  DEBUG(misc, 0, "GetModuleFileName failed (%lu)\n", GetLastError());
493  _searchpaths[SP_BINARY_DIR] = nullptr;
494  } else {
495  TCHAR exec_dir[MAX_PATH];
496  _tcsncpy(path, convert_to_fs(exe, path, lengthof(path)), lengthof(path));
497  if (!GetFullPathName(path, lengthof(exec_dir), exec_dir, nullptr)) {
498  DEBUG(misc, 0, "GetFullPathName failed (%lu)\n", GetLastError());
499  _searchpaths[SP_BINARY_DIR] = nullptr;
500  } else {
501  strecpy(tmp, convert_from_fs(exec_dir, tmp, lengthof(tmp)), lastof(tmp));
502  char *s = strrchr(tmp, PATHSEPCHAR);
503  *(s + 1) = '\0';
505  }
506  }
507 
510 }
511 
512 
513 bool GetClipboardContents(char *buffer, const char *last)
514 {
515  HGLOBAL cbuf;
516  const char *ptr;
517 
518  if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
519  OpenClipboard(nullptr);
520  cbuf = GetClipboardData(CF_UNICODETEXT);
521 
522  ptr = (const char*)GlobalLock(cbuf);
523  int out_len = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)ptr, -1, buffer, (last - buffer) + 1, nullptr, nullptr);
524  GlobalUnlock(cbuf);
525  CloseClipboard();
526 
527  if (out_len == 0) return false;
528 #if !defined(UNICODE)
529  } else if (IsClipboardFormatAvailable(CF_TEXT)) {
530  OpenClipboard(nullptr);
531  cbuf = GetClipboardData(CF_TEXT);
532 
533  ptr = (const char*)GlobalLock(cbuf);
534  strecpy(buffer, FS2OTTD(ptr), last);
535 
536  GlobalUnlock(cbuf);
537  CloseClipboard();
538 #endif /* UNICODE */
539  } else {
540  return false;
541  }
542 
543  return true;
544 }
545 
546 
560 const char *FS2OTTD(const TCHAR *name)
561 {
562  static char utf8_buf[512];
563  return convert_from_fs(name, utf8_buf, lengthof(utf8_buf));
564 }
565 
578 const TCHAR *OTTD2FS(const char *name, bool console_cp)
579 {
580  static TCHAR system_buf[512];
581  return convert_to_fs(name, system_buf, lengthof(system_buf), console_cp);
582 }
583 
584 
593 char *convert_from_fs(const TCHAR *name, char *utf8_buf, size_t buflen)
594 {
595 #if defined(UNICODE)
596  const WCHAR *wide_buf = name;
597 #else
598  /* Convert string from the local codepage to UTF-16. */
599  int wide_len = MultiByteToWideChar(CP_ACP, 0, name, -1, nullptr, 0);
600  if (wide_len == 0) {
601  utf8_buf[0] = '\0';
602  return utf8_buf;
603  }
604 
605  WCHAR *wide_buf = AllocaM(WCHAR, wide_len);
606  MultiByteToWideChar(CP_ACP, 0, name, -1, wide_buf, wide_len);
607 #endif
608 
609  /* Convert UTF-16 string to UTF-8. */
610  int len = WideCharToMultiByte(CP_UTF8, 0, wide_buf, -1, utf8_buf, (int)buflen, nullptr, nullptr);
611  if (len == 0) utf8_buf[0] = '\0';
612 
613  return utf8_buf;
614 }
615 
616 
627 TCHAR *convert_to_fs(const char *name, TCHAR *system_buf, size_t buflen, bool console_cp)
628 {
629 #if defined(UNICODE)
630  int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, system_buf, (int)buflen);
631  if (len == 0) system_buf[0] = '\0';
632 #else
633  int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, nullptr, 0);
634  if (len == 0) {
635  system_buf[0] = '\0';
636  return system_buf;
637  }
638 
639  WCHAR *wide_buf = AllocaM(WCHAR, len);
640  MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_buf, len);
641 
642  len = WideCharToMultiByte(console_cp ? CP_OEMCP : CP_ACP, 0, wide_buf, len, system_buf, (int)buflen, nullptr, nullptr);
643  if (len == 0) system_buf[0] = '\0';
644 #endif
645 
646  return system_buf;
647 }
648 
655 HRESULT OTTDSHGetFolderPath(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)
656 {
657  static HRESULT (WINAPI *SHGetFolderPath)(HWND, int, HANDLE, DWORD, LPTSTR) = nullptr;
658  static bool first_time = true;
659 
660  /* We only try to load the library one time; if it fails, it fails */
661  if (first_time) {
662 #if defined(UNICODE)
663 # define W(x) x "W"
664 #else
665 # define W(x) x "A"
666 #endif
667  /* The function lives in shell32.dll for all current Windows versions, but it first started to appear in SHFolder.dll. */
668  if (!LoadLibraryList((Function*)&SHGetFolderPath, "shell32.dll\0" W("SHGetFolderPath") "\0\0")) {
669  if (!LoadLibraryList((Function*)&SHGetFolderPath, "SHFolder.dll\0" W("SHGetFolderPath") "\0\0")) {
670  DEBUG(misc, 0, "Unable to load " W("SHGetFolderPath") "from either shell32.dll or SHFolder.dll");
671  }
672  }
673 #undef W
674  first_time = false;
675  }
676 
677  if (SHGetFolderPath != nullptr) return SHGetFolderPath(hwnd, csidl, hToken, dwFlags, pszPath);
678 
679  /* SHGetFolderPath doesn't exist, try a more conservative approach,
680  * eg environment variables. This is only included for legacy modes
681  * MSDN says: that 'pszPath' is a "Pointer to a null-terminated string of
682  * length MAX_PATH which will receive the path" so let's assume that
683  * Windows 95 with Internet Explorer 5.0, Windows 98 with Internet Explorer 5.0,
684  * Windows 98 Second Edition (SE), Windows NT 4.0 with Internet Explorer 5.0,
685  * Windows NT 4.0 with Service Pack 4 (SP4) */
686  {
687  DWORD ret;
688  switch (csidl) {
689  case CSIDL_FONTS: // Get the system font path, eg %WINDIR%\Fonts
690  ret = GetEnvironmentVariable(_T("WINDIR"), pszPath, MAX_PATH);
691  if (ret == 0) break;
692  _tcsncat(pszPath, _T("\\Fonts"), MAX_PATH);
693 
694  return (HRESULT)0;
695 
696  case CSIDL_PERSONAL:
697  case CSIDL_COMMON_DOCUMENTS: {
698  HKEY key;
699  if (RegOpenKeyEx(csidl == CSIDL_PERSONAL ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, REGSTR_PATH_SPECIAL_FOLDERS, 0, KEY_READ, &key) != ERROR_SUCCESS) break;
700  DWORD len = MAX_PATH;
701  ret = RegQueryValueEx(key, csidl == CSIDL_PERSONAL ? _T("Personal") : _T("Common Documents"), nullptr, nullptr, (LPBYTE)pszPath, &len);
702  RegCloseKey(key);
703  if (ret == ERROR_SUCCESS) return (HRESULT)0;
704  break;
705  }
706 
707  /* XXX - other types to go here when needed... */
708  }
709  }
710 
711  return E_INVALIDARG;
712 }
713 
715 const char *GetCurrentLocale(const char *)
716 {
717  char lang[9], country[9];
718  if (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, lang, lengthof(lang)) == 0 ||
719  GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, country, lengthof(country)) == 0) {
720  /* Unable to retrieve the locale. */
721  return nullptr;
722  }
723  /* Format it as 'en_us'. */
724  static char retbuf[6] = {lang[0], lang[1], '_', country[0], country[1], 0};
725  return retbuf;
726 }
727 
728 
729 static WCHAR _cur_iso_locale[16] = L"";
730 
731 void Win32SetCurrentLocaleName(const char *iso_code)
732 {
733  /* Convert the iso code into the format that windows expects. */
734  char iso[16];
735  if (strcmp(iso_code, "zh_TW") == 0) {
736  strecpy(iso, "zh-Hant", lastof(iso));
737  } else if (strcmp(iso_code, "zh_CN") == 0) {
738  strecpy(iso, "zh-Hans", lastof(iso));
739  } else {
740  /* Windows expects a '-' between language and country code, but we use a '_'. */
741  strecpy(iso, iso_code, lastof(iso));
742  for (char *c = iso; *c != '\0'; c++) {
743  if (*c == '_') *c = '-';
744  }
745  }
746 
747  MultiByteToWideChar(CP_UTF8, 0, iso, -1, _cur_iso_locale, lengthof(_cur_iso_locale));
748 }
749 
750 int OTTDStringCompare(const char *s1, const char *s2)
751 {
752  typedef int (WINAPI *PFNCOMPARESTRINGEX)(LPCWSTR, DWORD, LPCWCH, int, LPCWCH, int, LPVOID, LPVOID, LPARAM);
753  static PFNCOMPARESTRINGEX _CompareStringEx = nullptr;
754  static bool first_time = true;
755 
756 #ifndef SORT_DIGITSASNUMBERS
757 # define SORT_DIGITSASNUMBERS 0x00000008 // use digits as numbers sort method
758 #endif
759 #ifndef LINGUISTIC_IGNORECASE
760 # define LINGUISTIC_IGNORECASE 0x00000010 // linguistically appropriate 'ignore case'
761 #endif
762 
763  if (first_time) {
764  _CompareStringEx = (PFNCOMPARESTRINGEX)GetProcAddress(GetModuleHandle(_T("Kernel32")), "CompareStringEx");
765  first_time = false;
766  }
767 
768  if (_CompareStringEx != nullptr) {
769  /* CompareStringEx takes UTF-16 strings, even in ANSI-builds. */
770  int len_s1 = MultiByteToWideChar(CP_UTF8, 0, s1, -1, nullptr, 0);
771  int len_s2 = MultiByteToWideChar(CP_UTF8, 0, s2, -1, nullptr, 0);
772 
773  if (len_s1 != 0 && len_s2 != 0) {
774  LPWSTR str_s1 = AllocaM(WCHAR, len_s1);
775  LPWSTR str_s2 = AllocaM(WCHAR, len_s2);
776 
777  MultiByteToWideChar(CP_UTF8, 0, s1, -1, str_s1, len_s1);
778  MultiByteToWideChar(CP_UTF8, 0, s2, -1, str_s2, len_s2);
779 
780  int result = _CompareStringEx(_cur_iso_locale, LINGUISTIC_IGNORECASE | SORT_DIGITSASNUMBERS, str_s1, -1, str_s2, -1, nullptr, nullptr, 0);
781  if (result != 0) return result;
782  }
783  }
784 
785  TCHAR s1_buf[512], s2_buf[512];
786  convert_to_fs(s1, s1_buf, lengthof(s1_buf));
787  convert_to_fs(s2, s2_buf, lengthof(s2_buf));
788 
789  return CompareString(MAKELCID(_current_language->winlangid, SORT_DEFAULT), NORM_IGNORECASE, s1_buf, -1, s2_buf, -1);
790 }
791 
792 #ifdef _MSC_VER
793 /* Based on code from MSDN: https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
794 const DWORD MS_VC_EXCEPTION = 0x406D1388;
795 
796 PACK_N(struct THREADNAME_INFO {
797  DWORD dwType;
798  LPCSTR szName;
799  DWORD dwThreadID;
800  DWORD dwFlags;
801 }, 8);
802 
806 void SetCurrentThreadName(const char *threadName)
807 {
808  THREADNAME_INFO info;
809  info.dwType = 0x1000;
810  info.szName = threadName;
811  info.dwThreadID = -1;
812  info.dwFlags = 0;
813 
814 #pragma warning(push)
815 #pragma warning(disable: 6320 6322)
816  __try {
817  RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
818  } __except (EXCEPTION_EXECUTE_HANDLER) {
819  }
820 #pragma warning(pop)
821 }
822 #else
823 void SetCurrentThreadName(const char *) {}
824 #endif
int openttd_main(int argc, char *argv[])
Main entry point for this lovely game.
Definition: openttd.cpp:535
static char * strecat(char *dst, const char *src, const char *last)
Appends characters from one string to another.
Definition: depend.cpp:99
TCHAR * convert_to_fs(const char *name, TCHAR *system_buf, size_t buflen, bool console_cp)
Convert from OpenTTD&#39;s encoding to that of the environment in UNICODE.
Definition: win32.cpp:627
const char * FS2OTTD(const TCHAR *name)
Convert to OpenTTD&#39;s encoding from that of the local environment.
Definition: win32.cpp:560
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
Definition: string.cpp:409
Search in the directory where the binary resides.
Definition: fileio_type.h:141
HRESULT OTTDSHGetFolderPath(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)
Our very own SHGetFolderPath function for support of windows operating systems that don&#39;t have this f...
Definition: win32.cpp:655
void SetRandomSeed(uint32 seed)
(Re)set the state of the random number generators.
Definition: random_func.cpp:67
const LanguageMetadata * _current_language
The currently loaded language.
Definition: strings.cpp:48
#define lastof(x)
Get the last element of an fixed size array.
Definition: depend.cpp:50
static void InitialiseCrashLog()
Initialiser for crash logs; do the appropriate things so crashes are handled by our crash handler ins...
#define AllocaM(T, num_elements)
alloca() has to be called in the parent function, so define AllocaM() as a macro
Definition: alloc_func.hpp:134
char * convert_from_fs(const TCHAR *name, char *utf8_buf, size_t buflen)
Convert to OpenTTD&#39;s encoding from that of the environment in UNICODE.
Definition: win32.cpp:593
const char * GetCurrentLocale(const char *)
Determine the current user&#39;s locale.
Definition: win32.cpp:715
uint16 winlangid
Windows language ID: Windows cannot and will not convert isocodes to something it can use to determin...
Definition: language.h:53
Deals with finding savegames.
Definition: fios.h:105
bool _left_button_clicked
Is left mouse button clicked?
Definition: gfx.cpp:41
void SetCurrentThreadName(const char *)
Name the thread this function is called on for the debugger.
Definition: win32.cpp:823
bool AppendPathSeparator(char *buf, const char *last)
Appends, if necessary, the path separator character to the end of the string.
Definition: fileio.cpp:554
Definition: win32.cpp:94
bool LoadLibraryList(Function proc[], const char *dll)
Helper function needed by dynamically loading libraries XXX: Hurray for MS only having an ANSI GetPro...
Definition: win32.cpp:58
bool _left_button_down
Is left mouse button pressed?
Definition: gfx.cpp:40
static const char * _help_msg
Temporary pointer to get the help message to the window.
Definition: win32.cpp:348
static INT_PTR CALLBACK HelpDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
Callback function to handle the window.
Definition: win32.cpp:351
void CDECL usererror(const char *s,...)
Error handling for fatal user errors.
Definition: openttd.cpp:94
char * stredup(const char *s, const char *last)
Create a duplicate of the given string.
Definition: string.cpp:138
bool GetClipboardContents(char *buffer, const char *last)
Try to retrieve the current clipboard contents.
Definition: win32.cpp:513
#define lengthof(x)
Return the length of an fixed size array.
Definition: depend.cpp:42
void DetermineBasePaths(const char *exe)
Determine the base (personal dir and game data dir) paths.
Definition: fileio.cpp:1026
const TCHAR * OTTD2FS(const char *name, bool console_cp)
Convert from OpenTTD&#39;s encoding to that of the local environment.
Definition: win32.cpp:578
#define DEBUG(name, level,...)
Output a line of debugging information.
Definition: debug.h:37
Search in the personal directory.
Definition: fileio_type.h:139
List of file information.
Definition: fios.h:114
const char * _searchpaths[NUM_SEARCHPATHS]
The search paths OpenTTD could search through.
Definition: fileio.cpp:299
char * strecpy(char *dst, const char *src, const char *last)
Copies characters from one buffer to another.
Definition: depend.cpp:68
Search in the working directory.
Definition: fileio_type.h:135
Search in the installation directory.
Definition: fileio_type.h:142
Search within the application bundle.
Definition: fileio_type.h:143
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: depend.cpp:131
static bool HasBit(const T x, const uint8 y)
Checks if a bit in a value is set.
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:245
FiosItem * Append()
Construct a new entry in the file list.
Definition: fios.h:122
declarations of functions for MS windows systems
Search in the shared directory, like &#39;Shared Files&#39; under Windows.
Definition: fileio_type.h:140