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