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
24 #include "../../fios.h"
25 #include "../../core/alloc_func.hpp"
26 #include "../../string_func.h"
28 #include "../../language.h"
29 #include "../../thread.h"
30 #include "../../library_loader.h"
32 #include "../../safeguards.h"
34 static bool _has_console;
35 static bool _cursor_disable =
true;
36 static bool _cursor_visible =
true;
38 bool MyShowCursor(
bool show,
bool toggle)
40 if (toggle) _cursor_disable = !_cursor_disable;
41 if (_cursor_disable)
return show;
42 if (_cursor_visible == show)
return show;
44 _cursor_visible = show;
50 void ShowOSErrorBox(
const char *buf,
bool)
53 MessageBox(GetActiveWindow(),
OTTD2FS(buf).c_str(), L
"Error!", MB_ICONSTOP | MB_TASKMODAL);
56 void OSOpenBrowser(
const std::string &url)
58 ShellExecute(GetActiveWindow(), L
"open",
OTTD2FS(url).c_str(),
nullptr,
nullptr, SW_SHOWNORMAL);
83 static DIR _global_dir;
84 static LONG _global_dir_is_in_use =
false;
86 static inline DIR *dir_calloc()
90 if (InterlockedExchange(&_global_dir_is_in_use,
true) == (LONG)
true) {
94 memset(d, 0,
sizeof(*d));
99 static inline void dir_free(
DIR *d)
101 if (d == &_global_dir) {
102 _global_dir_is_in_use = (LONG)
false;
108 DIR *opendir(
const wchar_t *path)
111 UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS);
112 DWORD fa = GetFileAttributes(path);
114 if ((fa != INVALID_FILE_ATTRIBUTES) && (fa & FILE_ATTRIBUTE_DIRECTORY)) {
117 std::wstring search_path = path;
118 bool slash = path[wcslen(path) - 1] ==
'\\';
122 if (!slash) search_path += L
"\\";
124 d->hFind = FindFirstFile(search_path.c_str(), &d->fd);
126 if (d->hFind != INVALID_HANDLE_VALUE ||
127 GetLastError() == ERROR_NO_MORE_FILES) {
129 d->at_first_entry =
true;
147 struct dirent *readdir(
DIR *d)
149 DWORD prev_err = GetLastError();
151 if (d->at_first_entry) {
153 if (d->hFind == INVALID_HANDLE_VALUE)
return nullptr;
154 d->at_first_entry =
false;
155 }
else if (!FindNextFile(d->hFind, &d->fd)) {
156 if (GetLastError() == ERROR_NO_MORE_FILES) SetLastError(prev_err);
162 d->ent.d_name = d->fd.cFileName;
173 bool FiosIsRoot(
const std::string &file)
175 return file.size() == 3;
178 void FiosGetDrives(
FileList &file_list)
183 GetLogicalDriveStrings(
lengthof(drives), drives);
184 for (s = drives; *s !=
'\0';) {
185 FiosItem *fios = &file_list.emplace_back();
186 fios->type = FIOS_TYPE_DRIVE;
188 fios->name += (char)(s[0] & 0xFF);
190 fios->title = fios->name;
191 while (*s++ !=
'\0') { }
195 bool FiosIsValidFile(
const std::string &,
const struct dirent *ent,
struct stat *sb)
198 static const int64_t posix_epoch_hns = 0x019DB1DED53E8000LL;
199 const WIN32_FIND_DATA *fd = &ent->dir->fd;
201 sb->st_size = ((uint64_t) fd->nFileSizeHigh << 32) + fd->nFileSizeLow;
207 sb->st_mtime = (time_t)((*(
const uint64_t*)&fd->ftLastWriteTime - posix_epoch_hns) / 1E7);
208 sb->st_mode = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)? S_IFDIR : S_IFREG;
213 bool FiosIsHiddenFile(
const struct dirent *ent)
215 return (ent->dir->fd.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != 0;
218 std::optional<uint64_t> FiosGetDiskFreeSpace(
const std::string &path)
220 UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS);
222 ULARGE_INTEGER bytes_free;
223 bool retval = GetDiskFreeSpaceEx(
OTTD2FS(path).c_str(), &bytes_free,
nullptr,
nullptr);
227 if (retval)
return bytes_free.QuadPart;
234 CONSOLE_SCREEN_BUFFER_INFO coninfo;
236 if (_has_console)
return;
239 if (!AllocConsole())
return;
241 hand = GetStdHandle(STD_OUTPUT_HANDLE);
242 GetConsoleScreenBufferInfo(hand, &coninfo);
243 coninfo.dwSize.Y = 500;
244 SetConsoleScreenBufferSize(hand, coninfo.dwSize);
247 #if !defined(__CYGWIN__)
250 int fd = _open_osfhandle((intptr_t)hand, _O_TEXT);
254 _has_console =
false;
258 ShowInfo(
"Unable to open an output handle to the console. Check known-bugs.txt for details.");
262 #if defined(_MSC_VER) && _MSC_VER >= 1900
263 freopen(
"CONOUT$",
"a", stdout);
264 freopen(
"CONIN$",
"r", stdin);
265 freopen(
"CONOUT$",
"a", stderr);
267 *stdout = *_fdopen(fd,
"w");
268 *stdin = *_fdopen(_open_osfhandle((intptr_t)GetStdHandle(STD_INPUT_HANDLE), _O_TEXT),
"r" );
269 *stderr = *_fdopen(_open_osfhandle((intptr_t)GetStdHandle(STD_ERROR_HANDLE), _O_TEXT),
"w" );
274 *stdout = *fdopen(1,
"w" );
275 *stdin = *fdopen(0,
"r" );
276 *stderr = *fdopen(2,
"w" );
279 setvbuf(stdin,
nullptr, _IONBF, 0);
280 setvbuf(stdout,
nullptr, _IONBF, 0);
281 setvbuf(stderr,
nullptr, _IONBF, 0);
288 static INT_PTR CALLBACK
HelpDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM)
291 case WM_INITDIALOG: {
295 while (q !=
lastof(help_msg) && *p !=
'\0') {
298 if (q ==
lastof(help_msg)) {
308 wchar_t help_msg_buf[8192];
310 SendDlgItemMessage(wnd, 11, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), FALSE);
314 if (wParam == 12) ExitProcess(0);
323 void ShowInfoI(
const std::string &str)
326 fmt::print(stderr,
"{}\n", str);
332 old = MyShowCursor(
true);
333 if (str.size() > 2048) {
338 DialogBox(GetModuleHandle(
nullptr), MAKEINTRESOURCE(101),
nullptr,
HelpDialogFunc);
342 wchar_t help_msg_buf[8192];
343 MessageBox(GetActiveWindow(),
convert_to_fs(str, help_msg_buf,
lengthof(help_msg_buf)), L
"OpenTTD", MB_ICONINFORMATION | MB_OK);
349 char *getcwd(
char *buf,
size_t size)
351 wchar_t path[MAX_PATH];
352 GetCurrentDirectory(MAX_PATH - 1, path);
361 extern std::array<std::string, NUM_SEARCHPATHS>
_searchpaths;
363 wchar_t path[MAX_PATH];
364 #ifdef WITH_PERSONAL_DIR
365 if (SUCCEEDED(SHGetFolderPath(
nullptr, CSIDL_PERSONAL,
nullptr, SHGFP_TYPE_CURRENT, path))) {
366 std::string tmp(
FS2OTTD(path));
372 tmp +=
"content_download";
379 if (SUCCEEDED(SHGetFolderPath(
nullptr, CSIDL_COMMON_DOCUMENTS,
nullptr, SHGFP_TYPE_CURRENT, path))) {
380 std::string tmp(
FS2OTTD(path));
396 std::string cwd_s(cwd);
401 wchar_t config_dir[MAX_PATH];
403 if (!GetFullPathName(path,
lengthof(config_dir), config_dir,
nullptr)) {
404 Debug(misc, 0,
"GetFullPathName failed ({})", GetLastError());
407 std::string tmp(
FS2OTTD(config_dir));
408 auto pos = tmp.find_last_of(PATHSEPCHAR);
409 if (pos != std::string::npos) tmp.erase(pos + 1);
415 if (!GetModuleFileName(
nullptr, path,
lengthof(path))) {
416 Debug(misc, 0,
"GetModuleFileName failed ({})", GetLastError());
419 wchar_t exec_dir[MAX_PATH];
421 if (!GetFullPathName(path,
lengthof(exec_dir), exec_dir,
nullptr)) {
422 Debug(misc, 0,
"GetFullPathName failed ({})", GetLastError());
425 std::string tmp(
FS2OTTD(exec_dir));
426 auto pos = tmp.find_last_of(PATHSEPCHAR);
427 if (pos != std::string::npos) tmp.erase(pos + 1);
440 if (!IsClipboardFormatAvailable(CF_UNICODETEXT))
return std::nullopt;
442 OpenClipboard(
nullptr);
443 HGLOBAL cbuf = GetClipboardData(CF_UNICODETEXT);
445 std::string result =
FS2OTTD(
static_cast<LPCWSTR
>(GlobalLock(cbuf)));
449 if (result.empty())
return std::nullopt;
464 int name_len = (name.length() >= INT_MAX) ? INT_MAX : (
int)name.length();
465 int len = WideCharToMultiByte(CP_UTF8, 0, name.c_str(), name_len,
nullptr, 0,
nullptr,
nullptr);
466 if (len <= 0)
return std::string();
467 std::string utf8_buf(len,
'\0');
468 WideCharToMultiByte(CP_UTF8, 0, name.c_str(), name_len, utf8_buf.data(), len,
nullptr,
nullptr);
481 int name_len = (name.length() >= INT_MAX) ? INT_MAX : (
int)name.length();
482 int len = MultiByteToWideChar(CP_UTF8, 0, name.c_str(), name_len,
nullptr, 0);
483 if (len <= 0)
return std::wstring();
484 std::wstring system_buf(len, L
'\0');
485 MultiByteToWideChar(CP_UTF8, 0, name.c_str(), name_len, system_buf.data(), len);
501 int len = WideCharToMultiByte(CP_UTF8, 0, name, -1, utf8_buf, (
int)buflen,
nullptr,
nullptr);
502 if (len == 0) utf8_buf[0] =
'\0';
518 wchar_t *
convert_to_fs(
const std::string_view name,
wchar_t *system_buf,
size_t buflen)
520 int len = MultiByteToWideChar(CP_UTF8, 0, name.data(), (
int)name.size(), system_buf, (
int)buflen);
521 system_buf[len] =
'\0';
529 const LANGID userUiLang = GetUserDefaultUILanguage();
530 const LCID userUiLocale = MAKELCID(userUiLang, SORT_DEFAULT);
532 char lang[9], country[9];
533 if (GetLocaleInfoA(userUiLocale, LOCALE_SISO639LANGNAME, lang,
lengthof(lang)) == 0 ||
534 GetLocaleInfoA(userUiLocale, LOCALE_SISO3166CTRYNAME, country,
lengthof(country)) == 0) {
539 static char retbuf[6] = {lang[0], lang[1],
'_', country[0], country[1], 0};
544 static WCHAR _cur_iso_locale[16] = L
"";
546 void Win32SetCurrentLocaleName(std::string iso_code)
549 if (iso_code ==
"zh_TW") {
550 iso_code =
"zh-Hant";
551 }
else if (iso_code ==
"zh_CN") {
552 iso_code =
"zh-Hans";
555 for (
char &c : iso_code) {
556 if (c ==
'_') c =
'-';
560 MultiByteToWideChar(CP_UTF8, 0, iso_code.c_str(), -1, _cur_iso_locale,
lengthof(_cur_iso_locale));
563 int OTTDStringCompare(std::string_view s1, std::string_view s2)
565 typedef int (WINAPI *PFNCOMPARESTRINGEX)(LPCWSTR, DWORD, LPCWCH, int, LPCWCH, int, LPVOID, LPVOID, LPARAM);
566 static PFNCOMPARESTRINGEX _CompareStringEx =
nullptr;
567 static bool first_time =
true;
569 #ifndef SORT_DIGITSASNUMBERS
570 # define SORT_DIGITSASNUMBERS 0x00000008 // use digits as numbers sort method
572 #ifndef LINGUISTIC_IGNORECASE
573 # define LINGUISTIC_IGNORECASE 0x00000010 // linguistically appropriate 'ignore case'
578 _CompareStringEx = _kernel32.GetFunction(
"CompareStringEx");
582 if (_CompareStringEx !=
nullptr) {
584 int len_s1 = MultiByteToWideChar(CP_UTF8, 0, s1.data(), (
int)s1.size(),
nullptr, 0);
585 int len_s2 = MultiByteToWideChar(CP_UTF8, 0, s2.data(), (
int)s2.size(),
nullptr, 0);
587 if (len_s1 != 0 && len_s2 != 0) {
588 std::wstring str_s1(len_s1, L
'\0');
589 std::wstring str_s2(len_s2, L
'\0');
591 MultiByteToWideChar(CP_UTF8, 0, s1.data(), (
int)s1.size(), str_s1.data(), len_s1);
592 MultiByteToWideChar(CP_UTF8, 0, s2.data(), (
int)s2.size(), str_s2.data(), len_s2);
594 int result = _CompareStringEx(_cur_iso_locale, LINGUISTIC_IGNORECASE | SORT_DIGITSASNUMBERS, str_s1.c_str(), -1, str_s2.c_str(), -1,
nullptr,
nullptr, 0);
595 if (result != 0)
return result;
599 wchar_t s1_buf[512], s2_buf[512];
616 typedef int (WINAPI *PFNFINDNLSSTRINGEX)(LPCWSTR, DWORD, LPCWSTR, int, LPCWSTR, int, LPINT, LPNLSVERSIONINFO, LPVOID, LPARAM);
617 static PFNFINDNLSSTRINGEX _FindNLSStringEx =
nullptr;
618 static bool first_time =
true;
622 _FindNLSStringEx = _kernel32.
GetFunction(
"FindNLSStringEx");
626 if (_FindNLSStringEx !=
nullptr) {
627 int len_str = MultiByteToWideChar(CP_UTF8, 0, str.data(), (
int)str.size(),
nullptr, 0);
628 int len_value = MultiByteToWideChar(CP_UTF8, 0, value.data(), (
int)value.size(),
nullptr, 0);
630 if (len_str != 0 && len_value != 0) {
631 std::wstring str_str(len_str, L
'\0');
632 std::wstring str_value(len_value, L
'\0');
634 MultiByteToWideChar(CP_UTF8, 0, str.data(), (
int)str.size(), str_str.data(), len_str);
635 MultiByteToWideChar(CP_UTF8, 0, value.data(), (
int)value.size(), str_value.data(), len_value);
637 return _FindNLSStringEx(_cur_iso_locale, FIND_FROMSTART | (case_insensitive ? LINGUISTIC_IGNORECASE : 0), str_str.data(), -1, str_value.data(), -1,
nullptr,
nullptr,
nullptr, 0) >= 0 ? 1 : 0;
646 const DWORD MS_VC_EXCEPTION = 0x406D1388;
648 PACK_N(
struct THREADNAME_INFO {
660 THREADNAME_INFO info;
661 info.dwType = 0x1000;
662 info.szName = threadName;
663 info.dwThreadID = -1;
666 #pragma warning(push)
667 #pragma warning(disable: 6320 6322)
669 RaiseException(MS_VC_EXCEPTION, 0,
sizeof(info) /
sizeof(ULONG_PTR), (ULONG_PTR*)&info);
670 } __except (EXCEPTION_EXECUTE_HANDLER) {