10 #include "../../stdafx.h"
11 #include "../../debug.h"
12 #include "../../gfx_func.h"
13 #include "../../textbuf_gui.h"
14 #include "../../fileio_func.h"
19 #define NO_SHOBJIDL_SORTDIRECTION // Avoid multiple definition of SORT_ASCENDING
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"
31 #include "../../language.h"
32 #include "../../thread.h"
35 #include "../../safeguards.h"
37 static bool _has_console;
38 static bool _cursor_disable =
true;
39 static bool _cursor_visible =
true;
41 bool MyShowCursor(
bool show,
bool toggle)
43 if (toggle) _cursor_disable = !_cursor_disable;
44 if (_cursor_disable)
return show;
45 if (_cursor_visible == show)
return show;
47 _cursor_visible = show;
58 while (*dll !=
'\0') {
60 lib = LoadLibrary(
OTTD2FS(dll));
62 if (lib ==
nullptr)
return false;
66 while (*dll++ !=
'\0') { }
67 if (*dll ==
'\0')
break;
68 p = GetProcAddress(lib, dll);
69 if (p ==
nullptr)
return false;
70 *proc++ = (Function)p;
77 void ShowOSErrorBox(
const char *buf,
bool system)
80 MessageBox(GetActiveWindow(),
OTTD2FS(buf), L
"Error!", MB_ICONSTOP | MB_TASKMODAL);
83 void OSOpenBrowser(
const char *url)
85 ShellExecute(GetActiveWindow(), L
"open",
OTTD2FS(url),
nullptr,
nullptr, SW_SHOWNORMAL);
110 static DIR _global_dir;
111 static LONG _global_dir_is_in_use =
false;
113 static inline DIR *dir_calloc()
117 if (InterlockedExchange(&_global_dir_is_in_use,
true) == (LONG)
true) {
121 memset(d, 0,
sizeof(*d));
126 static inline void dir_free(
DIR *d)
128 if (d == &_global_dir) {
129 _global_dir_is_in_use = (LONG)
false;
135 DIR *opendir(
const wchar_t *path)
138 UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS);
139 DWORD fa = GetFileAttributes(path);
141 if ((fa != INVALID_FILE_ATTRIBUTES) && (fa & FILE_ATTRIBUTE_DIRECTORY)) {
144 wchar_t search_path[MAX_PATH];
145 bool slash = path[wcslen(path) - 1] ==
'\\';
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);
153 if (d->hFind != INVALID_HANDLE_VALUE ||
154 GetLastError() == ERROR_NO_MORE_FILES) {
156 d->at_first_entry =
true;
174 struct dirent *readdir(
DIR *d)
176 DWORD prev_err = GetLastError();
178 if (d->at_first_entry) {
180 if (d->hFind == INVALID_HANDLE_VALUE)
return nullptr;
181 d->at_first_entry =
false;
182 }
else if (!FindNextFile(d->hFind, &d->fd)) {
183 if (GetLastError() == ERROR_NO_MORE_FILES) SetLastError(prev_err);
189 d->ent.d_name = d->fd.cFileName;
200 bool FiosIsRoot(
const char *file)
202 return file[3] ==
'\0';
205 void FiosGetDrives(
FileList &file_list)
210 GetLogicalDriveStrings(
lengthof(drives), drives);
211 for (s = drives; *s !=
'\0';) {
213 fios->type = FIOS_TYPE_DRIVE;
217 while (*s++ !=
'\0') { }
221 bool FiosIsValidFile(
const char *path,
const struct dirent *ent,
struct stat *sb)
224 static const int64 posix_epoch_hns = 0x019DB1DED53E8000LL;
225 const WIN32_FIND_DATA *fd = &ent->dir->fd;
227 sb->st_size = ((uint64) fd->nFileSizeHigh << 32) + fd->nFileSizeLow;
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;
239 bool FiosIsHiddenFile(
const struct dirent *ent)
241 return (ent->dir->fd.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != 0;
244 bool FiosGetDiskFreeSpace(
const char *path, uint64 *tot)
246 UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS);
248 ULARGE_INTEGER bytes_free;
249 bool retval = GetDiskFreeSpaceEx(
OTTD2FS(path), &bytes_free,
nullptr,
nullptr);
250 if (retval) *tot = bytes_free.QuadPart;
256 static int ParseCommandLine(
char *line,
char **argv,
int max_argc)
262 while (*line ==
' ' || *line ==
'\t') line++;
265 if (*line ==
'\0')
break;
270 while (*line !=
'"') {
271 if (*line ==
'\0')
return n;
276 while (*line !=
' ' && *line !=
'\t') {
277 if (*line ==
'\0')
return n;
282 }
while (n != max_argc);
290 CONSOLE_SCREEN_BUFFER_INFO coninfo;
292 if (_has_console)
return;
295 if (!AllocConsole())
return;
297 hand = GetStdHandle(STD_OUTPUT_HANDLE);
298 GetConsoleScreenBufferInfo(hand, &coninfo);
299 coninfo.dwSize.Y = 500;
300 SetConsoleScreenBufferSize(hand, coninfo.dwSize);
303 #if !defined(__CYGWIN__)
306 int fd = _open_osfhandle((intptr_t)hand, _O_TEXT);
310 _has_console =
false;
314 ShowInfo(
"Unable to open an output handle to the console. Check known-bugs.txt for details.");
318 #if defined(_MSC_VER) && _MSC_VER >= 1900
319 freopen(
"CONOUT$",
"a", stdout);
320 freopen(
"CONIN$",
"r", stdin);
321 freopen(
"CONOUT$",
"a", stderr);
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" );
330 *stdout = *fdopen(1,
"w" );
331 *stdin = *fdopen(0,
"r" );
332 *stderr = *fdopen(2,
"w" );
335 setvbuf(stdin,
nullptr, _IONBF, 0);
336 setvbuf(stdout,
nullptr, _IONBF, 0);
337 setvbuf(stderr,
nullptr, _IONBF, 0);
344 static INT_PTR CALLBACK
HelpDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
347 case WM_INITDIALOG: {
351 while (q !=
lastof(help_msg) && *p !=
'\0') {
354 if (q ==
lastof(help_msg)) {
364 wchar_t help_msg_buf[8192];
366 SendDlgItemMessage(wnd, 11, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), FALSE);
370 if (wParam == 12) ExitProcess(0);
379 void ShowInfo(
const char *str)
382 fprintf(stderr,
"%s\n", str);
388 old = MyShowCursor(
true);
389 if (strlen(str) > 2048) {
394 DialogBox(GetModuleHandle(
nullptr), MAKEINTRESOURCE(101),
nullptr,
HelpDialogFunc);
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);
405 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
int nCmdShow)
424 _set_error_mode(_OUT_TO_MSGBOX);
429 argc = ParseCommandLine(cmdline, argv,
lengthof(argv));
443 char *getcwd(
char *buf,
size_t size)
445 wchar_t path[MAX_PATH];
446 GetCurrentDirectory(MAX_PATH - 1, path);
455 extern std::array<std::string, NUM_SEARCHPATHS>
_searchpaths;
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));
466 tmp +=
"content_download";
473 if (SUCCEEDED(SHGetFolderPath(
nullptr, CSIDL_COMMON_DOCUMENTS,
nullptr, SHGFP_TYPE_CURRENT, path))) {
474 std::string tmp(
FS2OTTD(path));
490 std::string cwd_s(cwd);
495 wchar_t config_dir[MAX_PATH];
497 if (!GetFullPathName(path,
lengthof(config_dir), config_dir,
nullptr)) {
498 DEBUG(misc, 0,
"GetFullPathName failed (%lu)\n", GetLastError());
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);
509 if (!GetModuleFileName(
nullptr, path,
lengthof(path))) {
510 DEBUG(misc, 0,
"GetModuleFileName failed (%lu)\n", GetLastError());
513 wchar_t exec_dir[MAX_PATH];
515 if (!GetFullPathName(path,
lengthof(exec_dir), exec_dir,
nullptr)) {
516 DEBUG(misc, 0,
"GetFullPathName failed (%lu)\n", GetLastError());
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);
537 if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
538 OpenClipboard(
nullptr);
539 cbuf = GetClipboardData(CF_UNICODETEXT);
541 ptr = (
const char*)GlobalLock(cbuf);
542 int out_len = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)ptr, -1, buffer, (last - buffer) + 1,
nullptr,
nullptr);
546 if (out_len == 0)
return false;
567 static char utf8_buf[512];
580 const wchar_t *
OTTD2FS(
const char *name,
bool console_cp)
582 static wchar_t system_buf[512];
597 const wchar_t *wide_buf = name;
600 int len = WideCharToMultiByte(CP_UTF8, 0, wide_buf, -1, utf8_buf, (
int)buflen,
nullptr,
nullptr);
601 if (len == 0) utf8_buf[0] =
'\0';
617 wchar_t *
convert_to_fs(
const char *name,
wchar_t *system_buf,
size_t buflen,
bool console_cp)
619 int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, system_buf, (
int)buflen);
620 if (len == 0) system_buf[0] =
'\0';
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) {
635 static char retbuf[6] = {lang[0], lang[1],
'_', country[0], country[1], 0};
640 static WCHAR _cur_iso_locale[16] = L
"";
642 void Win32SetCurrentLocaleName(
const char *iso_code)
646 if (strcmp(iso_code,
"zh_TW") == 0) {
648 }
else if (strcmp(iso_code,
"zh_CN") == 0) {
653 for (
char *c = iso; *c !=
'\0'; c++) {
654 if (*c ==
'_') *c =
'-';
658 MultiByteToWideChar(CP_UTF8, 0, iso, -1, _cur_iso_locale,
lengthof(_cur_iso_locale));
661 int OTTDStringCompare(
const char *s1,
const char *s2)
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;
667 #ifndef SORT_DIGITSASNUMBERS
668 # define SORT_DIGITSASNUMBERS 0x00000008 // use digits as numbers sort method
670 #ifndef LINGUISTIC_IGNORECASE
671 # define LINGUISTIC_IGNORECASE 0x00000010 // linguistically appropriate 'ignore case'
675 _CompareStringEx = (PFNCOMPARESTRINGEX)GetProcAddress(GetModuleHandle(L
"Kernel32"),
"CompareStringEx");
679 if (_CompareStringEx !=
nullptr) {
681 int len_s1 = MultiByteToWideChar(CP_UTF8, 0, s1, -1,
nullptr, 0);
682 int len_s2 = MultiByteToWideChar(CP_UTF8, 0, s2, -1,
nullptr, 0);
684 if (len_s1 != 0 && len_s2 != 0) {
685 LPWSTR str_s1 =
AllocaM(WCHAR, len_s1);
686 LPWSTR str_s2 =
AllocaM(WCHAR, len_s2);
688 MultiByteToWideChar(CP_UTF8, 0, s1, -1, str_s1, len_s1);
689 MultiByteToWideChar(CP_UTF8, 0, s2, -1, str_s2, len_s2);
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;
696 wchar_t s1_buf[512], s2_buf[512];
709 typedef BOOL (WINAPI * LPVERIFYVERSIONINFO)(LPOSVERSIONINFOEX, DWORD, DWORDLONG);
710 typedef ULONGLONG (NTAPI * LPVERSETCONDITIONMASK)(ULONGLONG, DWORD, BYTE);
712 static LPVERIFYVERSIONINFO _VerifyVersionInfo = (LPVERIFYVERSIONINFO)GetProcAddress(GetModuleHandle(_T(
"Kernel32")),
"VerifyVersionInfoW");
714 static LPVERIFYVERSIONINFO _VerifyVersionInfo = (LPVERIFYVERSIONINFO)GetProcAddress(GetModuleHandle(_T(
"Kernel32")),
"VerifyVersionInfoA");
716 static LPVERSETCONDITIONMASK _VerSetConditionMask = (LPVERSETCONDITIONMASK)GetProcAddress(GetModuleHandle(_T(
"Kernel32")),
"VerSetConditionMask");
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);
725 osvi.dwMajorVersion = 6;
726 osvi.dwMinorVersion = 0;
727 osvi.wServicePackMajor = 0;
729 return _VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;
731 return LOBYTE(GetVersion()) >= 6;
737 const DWORD MS_VC_EXCEPTION = 0x406D1388;
739 PACK_N(
struct THREADNAME_INFO {
751 THREADNAME_INFO info;
752 info.dwType = 0x1000;
753 info.szName = threadName;
754 info.dwThreadID = -1;
757 #pragma warning(push)
758 #pragma warning(disable: 6320 6322)
760 RaiseException(MS_VC_EXCEPTION, 0,
sizeof(info) /
sizeof(ULONG_PTR), (ULONG_PTR*)&info);
761 } __except (EXCEPTION_EXECUTE_HANDLER) {