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;
53 void ShowOSErrorBox(
const char *buf,
bool system)
56 MessageBox(GetActiveWindow(),
OTTD2FS(buf).c_str(), L
"Error!", MB_ICONSTOP | MB_TASKMODAL);
59 void OSOpenBrowser(
const char *url)
61 ShellExecute(GetActiveWindow(), L
"open",
OTTD2FS(url).c_str(),
nullptr,
nullptr, SW_SHOWNORMAL);
86 static DIR _global_dir;
87 static LONG _global_dir_is_in_use =
false;
89 static inline DIR *dir_calloc()
93 if (InterlockedExchange(&_global_dir_is_in_use,
true) == (LONG)
true) {
97 memset(d, 0,
sizeof(*d));
102 static inline void dir_free(
DIR *d)
104 if (d == &_global_dir) {
105 _global_dir_is_in_use = (LONG)
false;
111 DIR *opendir(
const wchar_t *path)
114 UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS);
115 DWORD fa = GetFileAttributes(path);
117 if ((fa != INVALID_FILE_ATTRIBUTES) && (fa & FILE_ATTRIBUTE_DIRECTORY)) {
120 std::wstring search_path = path;
121 bool slash = path[wcslen(path) - 1] ==
'\\';
125 if (!slash) search_path += L
"\\";
127 d->hFind = FindFirstFile(search_path.c_str(), &d->fd);
129 if (d->hFind != INVALID_HANDLE_VALUE ||
130 GetLastError() == ERROR_NO_MORE_FILES) {
132 d->at_first_entry =
true;
150 struct dirent *readdir(
DIR *d)
152 DWORD prev_err = GetLastError();
154 if (d->at_first_entry) {
156 if (d->hFind == INVALID_HANDLE_VALUE)
return nullptr;
157 d->at_first_entry =
false;
158 }
else if (!FindNextFile(d->hFind, &d->fd)) {
159 if (GetLastError() == ERROR_NO_MORE_FILES) SetLastError(prev_err);
165 d->ent.d_name = d->fd.cFileName;
176 bool FiosIsRoot(
const char *file)
178 return file[3] ==
'\0';
181 void FiosGetDrives(
FileList &file_list)
186 GetLogicalDriveStrings(
lengthof(drives), drives);
187 for (s = drives; *s !=
'\0';) {
188 FiosItem *fios = &file_list.emplace_back();
189 fios->type = FIOS_TYPE_DRIVE;
193 while (*s++ !=
'\0') { }
197 bool FiosIsValidFile(
const char *path,
const struct dirent *ent,
struct stat *sb)
200 static const int64 posix_epoch_hns = 0x019DB1DED53E8000LL;
201 const WIN32_FIND_DATA *fd = &ent->dir->fd;
203 sb->st_size = ((uint64) fd->nFileSizeHigh << 32) + fd->nFileSizeLow;
209 sb->st_mtime = (time_t)((*(
const uint64*)&fd->ftLastWriteTime - posix_epoch_hns) / 1E7);
210 sb->st_mode = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)? S_IFDIR : S_IFREG;
215 bool FiosIsHiddenFile(
const struct dirent *ent)
217 return (ent->dir->fd.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != 0;
220 bool FiosGetDiskFreeSpace(
const char *path, uint64 *tot)
222 UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS);
224 ULARGE_INTEGER bytes_free;
225 bool retval = GetDiskFreeSpaceEx(
OTTD2FS(path).c_str(), &bytes_free,
nullptr,
nullptr);
226 if (retval && tot !=
nullptr) *tot = bytes_free.QuadPart;
232 static int ParseCommandLine(
char *line,
char **argv,
int max_argc)
238 while (*line ==
' ' || *line ==
'\t') line++;
241 if (*line ==
'\0')
break;
246 while (*line !=
'"') {
247 if (*line ==
'\0')
return n;
252 while (*line !=
' ' && *line !=
'\t') {
253 if (*line ==
'\0')
return n;
258 }
while (n != max_argc);
266 CONSOLE_SCREEN_BUFFER_INFO coninfo;
268 if (_has_console)
return;
271 if (!AllocConsole())
return;
273 hand = GetStdHandle(STD_OUTPUT_HANDLE);
274 GetConsoleScreenBufferInfo(hand, &coninfo);
275 coninfo.dwSize.Y = 500;
276 SetConsoleScreenBufferSize(hand, coninfo.dwSize);
279 #if !defined(__CYGWIN__)
282 int fd = _open_osfhandle((intptr_t)hand, _O_TEXT);
286 _has_console =
false;
290 ShowInfo(
"Unable to open an output handle to the console. Check known-bugs.txt for details.");
294 #if defined(_MSC_VER) && _MSC_VER >= 1900
295 freopen(
"CONOUT$",
"a", stdout);
296 freopen(
"CONIN$",
"r", stdin);
297 freopen(
"CONOUT$",
"a", stderr);
299 *stdout = *_fdopen(fd,
"w");
300 *stdin = *_fdopen(_open_osfhandle((intptr_t)GetStdHandle(STD_INPUT_HANDLE), _O_TEXT),
"r" );
301 *stderr = *_fdopen(_open_osfhandle((intptr_t)GetStdHandle(STD_ERROR_HANDLE), _O_TEXT),
"w" );
306 *stdout = *fdopen(1,
"w" );
307 *stdin = *fdopen(0,
"r" );
308 *stderr = *fdopen(2,
"w" );
311 setvbuf(stdin,
nullptr, _IONBF, 0);
312 setvbuf(stdout,
nullptr, _IONBF, 0);
313 setvbuf(stderr,
nullptr, _IONBF, 0);
320 static INT_PTR CALLBACK
HelpDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
323 case WM_INITDIALOG: {
327 while (q !=
lastof(help_msg) && *p !=
'\0') {
330 if (q ==
lastof(help_msg)) {
340 wchar_t help_msg_buf[8192];
342 SendDlgItemMessage(wnd, 11, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), FALSE);
346 if (wParam == 12) ExitProcess(0);
355 void ShowInfo(
const char *str)
358 fprintf(stderr,
"%s\n", str);
364 old = MyShowCursor(
true);
365 if (strlen(str) > 2048) {
370 DialogBox(GetModuleHandle(
nullptr), MAKEINTRESOURCE(101),
nullptr,
HelpDialogFunc);
374 wchar_t help_msg_buf[8192];
375 MessageBox(GetActiveWindow(),
convert_to_fs(str, help_msg_buf,
lengthof(help_msg_buf)), L
"OpenTTD", MB_ICONINFORMATION | MB_OK);
381 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
int nCmdShow)
397 SetConsoleOutputCP(CP_UTF8);
403 _set_error_mode(_OUT_TO_MSGBOX);
408 argc = ParseCommandLine(cmdline, argv,
lengthof(argv));
422 char *getcwd(
char *buf,
size_t size)
424 wchar_t path[MAX_PATH];
425 GetCurrentDirectory(MAX_PATH - 1, path);
434 extern std::array<std::string, NUM_SEARCHPATHS>
_searchpaths;
436 wchar_t path[MAX_PATH];
437 #ifdef WITH_PERSONAL_DIR
438 if (SUCCEEDED(SHGetFolderPath(
nullptr, CSIDL_PERSONAL,
nullptr, SHGFP_TYPE_CURRENT, path))) {
439 std::string tmp(
FS2OTTD(path));
445 tmp +=
"content_download";
452 if (SUCCEEDED(SHGetFolderPath(
nullptr, CSIDL_COMMON_DOCUMENTS,
nullptr, SHGFP_TYPE_CURRENT, path))) {
453 std::string tmp(
FS2OTTD(path));
469 std::string cwd_s(cwd);
474 wchar_t config_dir[MAX_PATH];
476 if (!GetFullPathName(path,
lengthof(config_dir), config_dir,
nullptr)) {
477 Debug(misc, 0,
"GetFullPathName failed ({})", GetLastError());
480 std::string tmp(
FS2OTTD(config_dir));
481 auto pos = tmp.find_last_of(PATHSEPCHAR);
482 if (pos != std::string::npos) tmp.erase(pos + 1);
488 if (!GetModuleFileName(
nullptr, path,
lengthof(path))) {
489 Debug(misc, 0,
"GetModuleFileName failed ({})", GetLastError());
492 wchar_t exec_dir[MAX_PATH];
494 if (!GetFullPathName(path,
lengthof(exec_dir), exec_dir,
nullptr)) {
495 Debug(misc, 0,
"GetFullPathName failed ({})", GetLastError());
498 std::string tmp(
FS2OTTD(exec_dir));
499 auto pos = tmp.find_last_of(PATHSEPCHAR);
500 if (pos != std::string::npos) tmp.erase(pos + 1);
516 if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
517 OpenClipboard(
nullptr);
518 cbuf = GetClipboardData(CF_UNICODETEXT);
520 ptr = (
const char*)GlobalLock(cbuf);
521 int out_len = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)ptr, -1, buffer, (last - buffer) + 1,
nullptr,
nullptr);
525 if (out_len == 0)
return false;
544 int name_len = (name.length() >= INT_MAX) ? INT_MAX : (
int)name.length();
545 int len = WideCharToMultiByte(CP_UTF8, 0, name.c_str(), name_len,
nullptr, 0,
nullptr,
nullptr);
546 if (len <= 0)
return std::string();
547 char *utf8_buf =
AllocaM(
char, len + 1);
548 utf8_buf[len] =
'\0';
549 WideCharToMultiByte(CP_UTF8, 0, name.c_str(), name_len, utf8_buf, len,
nullptr,
nullptr);
550 return std::string(utf8_buf,
static_cast<size_t>(len));
562 int name_len = (name.length() >= INT_MAX) ? INT_MAX : (
int)name.length();
563 int len = MultiByteToWideChar(CP_UTF8, 0, name.c_str(), name_len,
nullptr, 0);
564 if (len <= 0)
return std::wstring();
565 wchar_t *system_buf =
AllocaM(
wchar_t, len + 1);
566 system_buf[len] = L
'\0';
567 MultiByteToWideChar(CP_UTF8, 0, name.c_str(), name_len, system_buf, len);
568 return std::wstring(system_buf,
static_cast<size_t>(len));
583 int len = WideCharToMultiByte(CP_UTF8, 0, name, -1, utf8_buf, (
int)buflen,
nullptr,
nullptr);
584 if (len == 0) utf8_buf[0] =
'\0';
600 wchar_t *
convert_to_fs(
const char *name,
wchar_t *system_buf,
size_t buflen)
602 int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, system_buf, (
int)buflen);
603 if (len == 0) system_buf[0] =
'\0';
611 const LANGID userUiLang = GetUserDefaultUILanguage();
612 const LCID userUiLocale = MAKELCID(userUiLang, SORT_DEFAULT);
614 char lang[9], country[9];
615 if (GetLocaleInfoA(userUiLocale, LOCALE_SISO639LANGNAME, lang,
lengthof(lang)) == 0 ||
616 GetLocaleInfoA(userUiLocale, LOCALE_SISO3166CTRYNAME, country,
lengthof(country)) == 0) {
621 static char retbuf[6] = {lang[0], lang[1],
'_', country[0], country[1], 0};
626 static WCHAR _cur_iso_locale[16] = L
"";
628 void Win32SetCurrentLocaleName(
const char *iso_code)
632 if (strcmp(iso_code,
"zh_TW") == 0) {
634 }
else if (strcmp(iso_code,
"zh_CN") == 0) {
639 for (
char *c = iso; *c !=
'\0'; c++) {
640 if (*c ==
'_') *c =
'-';
644 MultiByteToWideChar(CP_UTF8, 0, iso, -1, _cur_iso_locale,
lengthof(_cur_iso_locale));
647 int OTTDStringCompare(
const char *s1,
const char *s2)
649 typedef int (WINAPI *PFNCOMPARESTRINGEX)(LPCWSTR, DWORD, LPCWCH, int, LPCWCH, int, LPVOID, LPVOID, LPARAM);
650 static PFNCOMPARESTRINGEX _CompareStringEx =
nullptr;
651 static bool first_time =
true;
653 #ifndef SORT_DIGITSASNUMBERS
654 # define SORT_DIGITSASNUMBERS 0x00000008 // use digits as numbers sort method
656 #ifndef LINGUISTIC_IGNORECASE
657 # define LINGUISTIC_IGNORECASE 0x00000010 // linguistically appropriate 'ignore case'
661 static DllLoader _kernel32(L
"Kernel32.dll");
662 _CompareStringEx = _kernel32.GetProcAddress(
"CompareStringEx");
666 if (_CompareStringEx !=
nullptr) {
668 int len_s1 = MultiByteToWideChar(CP_UTF8, 0, s1, -1,
nullptr, 0);
669 int len_s2 = MultiByteToWideChar(CP_UTF8, 0, s2, -1,
nullptr, 0);
671 if (len_s1 != 0 && len_s2 != 0) {
672 LPWSTR str_s1 =
AllocaM(WCHAR, len_s1);
673 LPWSTR str_s2 =
AllocaM(WCHAR, len_s2);
675 MultiByteToWideChar(CP_UTF8, 0, s1, -1, str_s1, len_s1);
676 MultiByteToWideChar(CP_UTF8, 0, s2, -1, str_s2, len_s2);
678 int result = _CompareStringEx(_cur_iso_locale, LINGUISTIC_IGNORECASE | SORT_DIGITSASNUMBERS, str_s1, -1, str_s2, -1,
nullptr,
nullptr, 0);
679 if (result != 0)
return result;
683 wchar_t s1_buf[512], s2_buf[512];
692 const DWORD MS_VC_EXCEPTION = 0x406D1388;
694 PACK_N(
struct THREADNAME_INFO {
706 THREADNAME_INFO info;
707 info.dwType = 0x1000;
708 info.szName = threadName;
709 info.dwThreadID = -1;
712 #pragma warning(push)
713 #pragma warning(disable: 6320 6322)
715 RaiseException(MS_VC_EXCEPTION, 0,
sizeof(info) /
sizeof(ULONG_PTR), (ULONG_PTR*)&info);
716 } __except (EXCEPTION_EXECUTE_HANDLER) {