10 #include "../../stdafx.h"
11 #include "../../debug.h"
12 #include "../../gfx_func.h"
13 #include "../../textbuf_gui.h"
14 #include "../../fileio_func.h"
18 #define NO_SHOBJIDL_SORTDIRECTION // Avoid multiple definition of SORT_ASCENDING
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"
30 #include "../../language.h"
31 #include "../../thread.h"
34 #include "../../safeguards.h"
36 static bool _has_console;
37 static bool _cursor_disable =
true;
38 static bool _cursor_visible =
true;
40 bool MyShowCursor(
bool show,
bool toggle)
42 if (toggle) _cursor_disable = !_cursor_disable;
43 if (_cursor_disable)
return show;
44 if (_cursor_visible == show)
return show;
46 _cursor_visible = show;
59 while (*dll !=
'\0') {
61 lib = LoadLibrary(MB_TO_WIDE(dll));
63 if (lib ==
nullptr)
return false;
67 while (*dll++ !=
'\0') { }
68 if (*dll ==
'\0')
break;
69 p = GetProcAddress(lib, dll);
70 if (p ==
nullptr)
return false;
71 *proc++ = (Function)p;
78 void ShowOSErrorBox(
const char *buf,
bool system)
81 MessageBox(GetActiveWindow(),
OTTD2FS(buf), _T(
"Error!"), MB_ICONSTOP | MB_TASKMODAL);
84 void OSOpenBrowser(
const char *url)
86 ShellExecute(GetActiveWindow(), _T(
"open"),
OTTD2FS(url),
nullptr,
nullptr, SW_SHOWNORMAL);
111 static DIR _global_dir;
112 static LONG _global_dir_is_in_use =
false;
114 static inline DIR *dir_calloc()
118 if (InterlockedExchange(&_global_dir_is_in_use,
true) == (LONG)
true) {
122 memset(d, 0,
sizeof(*d));
127 static inline void dir_free(
DIR *d)
129 if (d == &_global_dir) {
130 _global_dir_is_in_use = (LONG)
false;
136 DIR *opendir(
const TCHAR *path)
139 UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS);
140 DWORD fa = GetFileAttributes(path);
142 if ((fa != INVALID_FILE_ATTRIBUTES) && (fa & FILE_ATTRIBUTE_DIRECTORY)) {
145 TCHAR search_path[MAX_PATH];
146 bool slash = path[_tcslen(path) - 1] ==
'\\';
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);
154 if (d->hFind != INVALID_HANDLE_VALUE ||
155 GetLastError() == ERROR_NO_MORE_FILES) {
157 d->at_first_entry =
true;
175 struct dirent *readdir(
DIR *d)
177 DWORD prev_err = GetLastError();
179 if (d->at_first_entry) {
181 if (d->hFind == INVALID_HANDLE_VALUE)
return nullptr;
182 d->at_first_entry =
false;
183 }
else if (!FindNextFile(d->hFind, &d->fd)) {
184 if (GetLastError() == ERROR_NO_MORE_FILES) SetLastError(prev_err);
190 d->ent.d_name = d->fd.cFileName;
201 bool FiosIsRoot(
const char *file)
203 return file[3] ==
'\0';
206 void FiosGetDrives(
FileList &file_list)
211 GetLogicalDriveStrings(
lengthof(drives), drives);
212 for (s = drives; *s !=
'\0';) {
214 fios->type = FIOS_TYPE_DRIVE;
218 while (*s++ !=
'\0') { }
222 bool FiosIsValidFile(
const char *path,
const struct dirent *ent,
struct stat *sb)
225 static const int64 posix_epoch_hns = 0x019DB1DED53E8000LL;
226 const WIN32_FIND_DATA *fd = &ent->dir->fd;
228 sb->st_size = ((uint64) fd->nFileSizeHigh << 32) + fd->nFileSizeLow;
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;
240 bool FiosIsHiddenFile(
const struct dirent *ent)
242 return (ent->dir->fd.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != 0;
245 bool FiosGetDiskFreeSpace(
const char *path, uint64 *tot)
247 UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS);
250 DWORD spc, bps, nfc, tnc;
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);
262 static int ParseCommandLine(
char *line,
char **argv,
int max_argc)
268 while (*line ==
' ' || *line ==
'\t') line++;
271 if (*line ==
'\0')
break;
276 while (*line !=
'"') {
277 if (*line ==
'\0')
return n;
282 while (*line !=
' ' && *line !=
'\t') {
283 if (*line ==
'\0')
return n;
288 }
while (n != max_argc);
296 CONSOLE_SCREEN_BUFFER_INFO coninfo;
298 if (_has_console)
return;
301 if (!AllocConsole())
return;
303 hand = GetStdHandle(STD_OUTPUT_HANDLE);
304 GetConsoleScreenBufferInfo(hand, &coninfo);
305 coninfo.dwSize.Y = 500;
306 SetConsoleScreenBufferSize(hand, coninfo.dwSize);
309 #if !defined(__CYGWIN__)
312 int fd = _open_osfhandle((intptr_t)hand, _O_TEXT);
316 _has_console =
false;
320 ShowInfo(
"Unable to open an output handle to the console. Check known-bugs.txt for details.");
324 #if defined(_MSC_VER) && _MSC_VER >= 1900
325 freopen(
"CONOUT$",
"a", stdout);
326 freopen(
"CONIN$",
"r", stdin);
327 freopen(
"CONOUT$",
"a", stderr);
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" );
336 *stdout = *fdopen(1,
"w" );
337 *stdin = *fdopen(0,
"r" );
338 *stderr = *fdopen(2,
"w" );
341 setvbuf(stdin,
nullptr, _IONBF, 0);
342 setvbuf(stdout,
nullptr, _IONBF, 0);
343 setvbuf(stderr,
nullptr, _IONBF, 0);
350 static INT_PTR CALLBACK
HelpDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
353 case WM_INITDIALOG: {
357 while (q !=
lastof(help_msg) && *p !=
'\0') {
360 if (q ==
lastof(help_msg)) {
370 TCHAR help_msg_buf[8192];
372 SendDlgItemMessage(wnd, 11, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), FALSE);
376 if (wParam == 12) ExitProcess(0);
385 void ShowInfo(
const char *str)
388 fprintf(stderr,
"%s\n", str);
394 old = MyShowCursor(
true);
395 if (strlen(str) > 2048) {
400 DialogBox(GetModuleHandle(
nullptr), MAKEINTRESOURCE(101),
nullptr,
HelpDialogFunc);
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);
411 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
int nCmdShow)
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.");
432 _set_error_mode(_OUT_TO_MSGBOX);
437 argc = ParseCommandLine(cmdline, argv,
lengthof(argv));
447 char *getcwd(
char *buf,
size_t size)
449 TCHAR path[MAX_PATH];
450 GetCurrentDirectory(MAX_PATH - 1, path);
459 extern std::array<std::string, NUM_SEARCHPATHS>
_searchpaths;
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));
470 tmp +=
"content_download";
477 if (SUCCEEDED(
OTTDSHGetFolderPath(
nullptr, CSIDL_COMMON_DOCUMENTS,
nullptr, SHGFP_TYPE_CURRENT, path))) {
478 std::string tmp(
FS2OTTD(path));
494 std::string cwd_s(cwd);
499 TCHAR config_dir[MAX_PATH];
501 if (!GetFullPathName(path,
lengthof(config_dir), config_dir,
nullptr)) {
502 DEBUG(misc, 0,
"GetFullPathName failed (%lu)\n", GetLastError());
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);
513 if (!GetModuleFileName(
nullptr, path,
lengthof(path))) {
514 DEBUG(misc, 0,
"GetModuleFileName failed (%lu)\n", GetLastError());
517 TCHAR exec_dir[MAX_PATH];
519 if (!GetFullPathName(path,
lengthof(exec_dir), exec_dir,
nullptr)) {
520 DEBUG(misc, 0,
"GetFullPathName failed (%lu)\n", GetLastError());
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);
541 if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
542 OpenClipboard(
nullptr);
543 cbuf = GetClipboardData(CF_UNICODETEXT);
545 ptr = (
const char*)GlobalLock(cbuf);
546 int out_len = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)ptr, -1, buffer, (last - buffer) + 1,
nullptr,
nullptr);
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);
556 ptr = (
const char*)GlobalLock(cbuf);
585 static char utf8_buf[512];
601 const TCHAR *
OTTD2FS(
const char *name,
bool console_cp)
603 static TCHAR system_buf[512];
619 const WCHAR *wide_buf = name;
622 int wide_len = MultiByteToWideChar(CP_ACP, 0, name, -1,
nullptr, 0);
628 WCHAR *wide_buf =
AllocaM(WCHAR, wide_len);
629 MultiByteToWideChar(CP_ACP, 0, name, -1, wide_buf, wide_len);
633 int len = WideCharToMultiByte(CP_UTF8, 0, wide_buf, -1, utf8_buf, (
int)buflen,
nullptr,
nullptr);
634 if (len == 0) utf8_buf[0] =
'\0';
650 TCHAR *
convert_to_fs(
const char *name, TCHAR *system_buf,
size_t buflen,
bool console_cp)
653 int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, system_buf, (
int)buflen);
654 if (len == 0) system_buf[0] =
'\0';
656 int len = MultiByteToWideChar(CP_UTF8, 0, name, -1,
nullptr, 0);
658 system_buf[0] =
'\0';
662 WCHAR *wide_buf =
AllocaM(WCHAR, len);
663 MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_buf, len);
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';
680 static HRESULT (WINAPI *SHGetFolderPath)(HWND, int, HANDLE, DWORD, LPTSTR) =
nullptr;
681 static bool first_time =
true;
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");
700 if (SHGetFolderPath !=
nullptr)
return SHGetFolderPath(hwnd, csidl, hToken, dwFlags, pszPath);
713 ret = GetEnvironmentVariable(_T(
"WINDIR"), pszPath, MAX_PATH);
715 _tcsncat(pszPath, _T(
"\\Fonts"), MAX_PATH);
720 case CSIDL_COMMON_DOCUMENTS: {
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);
726 if (ret == ERROR_SUCCESS)
return (HRESULT)0;
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) {
747 static char retbuf[6] = {lang[0], lang[1],
'_', country[0], country[1], 0};
752 static WCHAR _cur_iso_locale[16] = L
"";
754 void Win32SetCurrentLocaleName(
const char *iso_code)
758 if (strcmp(iso_code,
"zh_TW") == 0) {
760 }
else if (strcmp(iso_code,
"zh_CN") == 0) {
765 for (
char *c = iso; *c !=
'\0'; c++) {
766 if (*c ==
'_') *c =
'-';
770 MultiByteToWideChar(CP_UTF8, 0, iso, -1, _cur_iso_locale,
lengthof(_cur_iso_locale));
773 int OTTDStringCompare(
const char *s1,
const char *s2)
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;
779 #ifndef SORT_DIGITSASNUMBERS
780 # define SORT_DIGITSASNUMBERS 0x00000008 // use digits as numbers sort method
782 #ifndef LINGUISTIC_IGNORECASE
783 # define LINGUISTIC_IGNORECASE 0x00000010 // linguistically appropriate 'ignore case'
787 _CompareStringEx = (PFNCOMPARESTRINGEX)GetProcAddress(GetModuleHandle(_T(
"Kernel32")),
"CompareStringEx");
791 if (_CompareStringEx !=
nullptr) {
793 int len_s1 = MultiByteToWideChar(CP_UTF8, 0, s1, -1,
nullptr, 0);
794 int len_s2 = MultiByteToWideChar(CP_UTF8, 0, s2, -1,
nullptr, 0);
796 if (len_s1 != 0 && len_s2 != 0) {
797 LPWSTR str_s1 =
AllocaM(WCHAR, len_s1);
798 LPWSTR str_s2 =
AllocaM(WCHAR, len_s2);
800 MultiByteToWideChar(CP_UTF8, 0, s1, -1, str_s1, len_s1);
801 MultiByteToWideChar(CP_UTF8, 0, s2, -1, str_s2, len_s2);
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;
808 TCHAR s1_buf[512], s2_buf[512];
817 const DWORD MS_VC_EXCEPTION = 0x406D1388;
819 PACK_N(
struct THREADNAME_INFO {
831 THREADNAME_INFO info;
832 info.dwType = 0x1000;
833 info.szName = threadName;
834 info.dwThreadID = -1;
837 #pragma warning(push)
838 #pragma warning(disable: 6320 6322)
840 RaiseException(MS_VC_EXCEPTION, 0,
sizeof(info) /
sizeof(ULONG_PTR), (ULONG_PTR*)&info);
841 } __except (EXCEPTION_EXECUTE_HANDLER) {