12 #include "../../stdafx.h" 13 #include "../../debug.h" 14 #include "../../gfx_func.h" 15 #include "../../textbuf_gui.h" 16 #include "../../fileio_func.h" 20 #define NO_SHOBJIDL_SORTDIRECTION // Avoid multiple definition of SORT_ASCENDING 24 #include "../../fios.h" 25 #include "../../core/alloc_func.hpp" 26 #include "../../openttd.h" 27 #include "../../core/random_func.hpp" 28 #include "../../string_func.h" 29 #include "../../crashlog.h" 32 #include "../../language.h" 33 #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;
60 while (*dll !=
'\0') {
62 lib = LoadLibrary(MB_TO_WIDE(dll));
64 if (lib ==
nullptr)
return false;
68 while (*dll++ !=
'\0') { }
69 if (*dll ==
'\0')
break;
70 p = GetProcAddress(lib, dll);
71 if (p ==
nullptr)
return false;
72 *proc++ = (Function)p;
79 void ShowOSErrorBox(
const char *buf,
bool system)
82 MessageBox(GetActiveWindow(),
OTTD2FS(buf), _T(
"Error!"), MB_ICONSTOP | MB_TASKMODAL);
85 void OSOpenBrowser(
const char *url)
87 ShellExecute(GetActiveWindow(), _T(
"open"),
OTTD2FS(url),
nullptr,
nullptr, SW_SHOWNORMAL);
112 static DIR _global_dir;
113 static LONG _global_dir_is_in_use =
false;
115 static inline DIR *dir_calloc()
119 if (InterlockedExchange(&_global_dir_is_in_use,
true) == (LONG)
true) {
123 memset(d, 0,
sizeof(*d));
128 static inline void dir_free(
DIR *d)
130 if (d == &_global_dir) {
131 _global_dir_is_in_use = (LONG)
false;
137 DIR *opendir(
const TCHAR *path)
140 UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS);
141 DWORD fa = GetFileAttributes(path);
143 if ((fa != INVALID_FILE_ATTRIBUTES) && (fa & FILE_ATTRIBUTE_DIRECTORY)) {
146 TCHAR search_path[MAX_PATH];
147 bool slash = path[_tcslen(path) - 1] ==
'\\';
151 _sntprintf(search_path,
lengthof(search_path), _T(
"%s%s*"), path, slash ? _T(
"") : _T(
"\\"));
152 *
lastof(search_path) =
'\0';
153 d->hFind = FindFirstFile(search_path, &d->fd);
155 if (d->hFind != INVALID_HANDLE_VALUE ||
156 GetLastError() == ERROR_NO_MORE_FILES) {
158 d->at_first_entry =
true;
176 struct dirent *readdir(
DIR *d)
178 DWORD prev_err = GetLastError();
180 if (d->at_first_entry) {
182 if (d->hFind == INVALID_HANDLE_VALUE)
return nullptr;
183 d->at_first_entry =
false;
184 }
else if (!FindNextFile(d->hFind, &d->fd)) {
185 if (GetLastError() == ERROR_NO_MORE_FILES) SetLastError(prev_err);
191 d->ent.d_name = d->fd.cFileName;
202 bool FiosIsRoot(
const char *file)
204 return file[3] ==
'\0';
207 void FiosGetDrives(
FileList &file_list)
212 GetLogicalDriveStrings(
lengthof(drives), drives);
213 for (s = drives; *s !=
'\0';) {
215 fios->type = FIOS_TYPE_DRIVE;
219 while (*s++ !=
'\0') { }
223 bool FiosIsValidFile(
const char *path,
const struct dirent *ent,
struct stat *sb)
226 static const int64 posix_epoch_hns = 0x019DB1DED53E8000LL;
227 const WIN32_FIND_DATA *fd = &ent->dir->fd;
229 sb->st_size = ((uint64) fd->nFileSizeHigh << 32) + fd->nFileSizeLow;
235 sb->st_mtime = (time_t)((*(
const uint64*)&fd->ftLastWriteTime - posix_epoch_hns) / 1E7);
236 sb->st_mode = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)? S_IFDIR : S_IFREG;
241 bool FiosIsHiddenFile(
const struct dirent *ent)
243 return (ent->dir->fd.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != 0;
246 bool FiosGetDiskFreeSpace(
const char *path, uint64 *tot)
248 UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS);
251 DWORD spc, bps, nfc, tnc;
253 _sntprintf(root,
lengthof(root), _T(
"%c:") _T(PATHSEP), path[0]);
254 if (tot !=
nullptr && GetDiskFreeSpace(root, &spc, &bps, &nfc, &tnc)) {
255 *tot = ((spc * bps) * (uint64)nfc);
263 static int ParseCommandLine(
char *line,
char **argv,
int max_argc)
269 while (*line ==
' ' || *line ==
'\t') line++;
272 if (*line ==
'\0')
break;
277 while (*line !=
'"') {
278 if (*line ==
'\0')
return n;
283 while (*line !=
' ' && *line !=
'\t') {
284 if (*line ==
'\0')
return n;
289 }
while (n != max_argc);
297 CONSOLE_SCREEN_BUFFER_INFO coninfo;
299 if (_has_console)
return;
302 if (!AllocConsole())
return;
304 hand = GetStdHandle(STD_OUTPUT_HANDLE);
305 GetConsoleScreenBufferInfo(hand, &coninfo);
306 coninfo.dwSize.Y = 500;
307 SetConsoleScreenBufferSize(hand, coninfo.dwSize);
310 #if !defined(__CYGWIN__) 313 int fd = _open_osfhandle((intptr_t)hand, _O_TEXT);
317 _has_console =
false;
321 ShowInfo(
"Unable to open an output handle to the console. Check known-bugs.txt for details.");
325 #if defined(_MSC_VER) && _MSC_VER >= 1900 326 freopen(
"CONOUT$",
"a", stdout);
327 freopen(
"CONIN$",
"r", stdin);
328 freopen(
"CONOUT$",
"a", stderr);
330 *stdout = *_fdopen(fd,
"w");
331 *stdin = *_fdopen(_open_osfhandle((intptr_t)GetStdHandle(STD_INPUT_HANDLE), _O_TEXT),
"r" );
332 *stderr = *_fdopen(_open_osfhandle((intptr_t)GetStdHandle(STD_ERROR_HANDLE), _O_TEXT),
"w" );
337 *stdout = *fdopen(1,
"w" );
338 *stdin = *fdopen(0,
"r" );
339 *stderr = *fdopen(2,
"w" );
342 setvbuf(stdin,
nullptr, _IONBF, 0);
343 setvbuf(stdout,
nullptr, _IONBF, 0);
344 setvbuf(stderr,
nullptr, _IONBF, 0);
351 static INT_PTR CALLBACK
HelpDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
354 case WM_INITDIALOG: {
358 while (q !=
lastof(help_msg) && *p !=
'\0') {
361 if (q ==
lastof(help_msg)) {
371 TCHAR help_msg_buf[8192];
373 SendDlgItemMessage(wnd, 11, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), FALSE);
377 if (wParam == 12) ExitProcess(0);
386 void ShowInfo(
const char *str)
389 fprintf(stderr,
"%s\n", str);
395 old = MyShowCursor(
true);
396 if (strlen(str) > 2048) {
401 DialogBox(GetModuleHandle(
nullptr), MAKEINTRESOURCE(101),
nullptr,
HelpDialogFunc);
405 TCHAR help_msg_buf[8192];
406 MessageBox(GetActiveWindow(),
convert_to_fs(str, help_msg_buf,
lengthof(help_msg_buf)), _T(
"OpenTTD"), MB_ICONINFORMATION | MB_OK);
412 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
int nCmdShow)
421 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.");
433 _set_error_mode(_OUT_TO_MSGBOX);
438 argc = ParseCommandLine(cmdline, argv,
lengthof(argv));
448 char *getcwd(
char *buf,
size_t size)
450 TCHAR path[MAX_PATH];
451 GetCurrentDirectory(MAX_PATH - 1, path);
460 TCHAR path[MAX_PATH];
461 #ifdef WITH_PERSONAL_DIR 462 if (SUCCEEDED(
OTTDSHGetFolderPath(
nullptr, CSIDL_PERSONAL,
nullptr, SHGFP_TYPE_CURRENT, path))) {
472 if (SUCCEEDED(
OTTDSHGetFolderPath(
nullptr, CSIDL_COMMON_DOCUMENTS,
nullptr, SHGFP_TYPE_CURRENT, path))) {
491 if (!GetModuleFileName(
nullptr, path,
lengthof(path))) {
492 DEBUG(misc, 0,
"GetModuleFileName failed (%lu)\n", GetLastError());
495 TCHAR exec_dir[MAX_PATH];
497 if (!GetFullPathName(path,
lengthof(exec_dir), exec_dir,
nullptr)) {
498 DEBUG(misc, 0,
"GetFullPathName failed (%lu)\n", GetLastError());
502 char *s = strrchr(tmp, PATHSEPCHAR);
518 if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
519 OpenClipboard(
nullptr);
520 cbuf = GetClipboardData(CF_UNICODETEXT);
522 ptr = (
const char*)GlobalLock(cbuf);
523 int out_len = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)ptr, -1, buffer, (last - buffer) + 1,
nullptr,
nullptr);
527 if (out_len == 0)
return false;
528 #if !defined(UNICODE) 529 }
else if (IsClipboardFormatAvailable(CF_TEXT)) {
530 OpenClipboard(
nullptr);
531 cbuf = GetClipboardData(CF_TEXT);
533 ptr = (
const char*)GlobalLock(cbuf);
562 static char utf8_buf[512];
578 const TCHAR *
OTTD2FS(
const char *name,
bool console_cp)
580 static TCHAR system_buf[512];
596 const WCHAR *wide_buf = name;
599 int wide_len = MultiByteToWideChar(CP_ACP, 0, name, -1,
nullptr, 0);
605 WCHAR *wide_buf =
AllocaM(WCHAR, wide_len);
606 MultiByteToWideChar(CP_ACP, 0, name, -1, wide_buf, wide_len);
610 int len = WideCharToMultiByte(CP_UTF8, 0, wide_buf, -1, utf8_buf, (
int)buflen,
nullptr,
nullptr);
611 if (len == 0) utf8_buf[0] =
'\0';
627 TCHAR *
convert_to_fs(
const char *name, TCHAR *system_buf,
size_t buflen,
bool console_cp)
630 int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, system_buf, (
int)buflen);
631 if (len == 0) system_buf[0] =
'\0';
633 int len = MultiByteToWideChar(CP_UTF8, 0, name, -1,
nullptr, 0);
635 system_buf[0] =
'\0';
639 WCHAR *wide_buf =
AllocaM(WCHAR, len);
640 MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_buf, len);
642 len = WideCharToMultiByte(console_cp ? CP_OEMCP : CP_ACP, 0, wide_buf, len, system_buf, (
int)buflen,
nullptr,
nullptr);
643 if (len == 0) system_buf[0] =
'\0';
657 static HRESULT (WINAPI *SHGetFolderPath)(HWND, int, HANDLE, DWORD, LPTSTR) =
nullptr;
658 static bool first_time =
true;
668 if (!
LoadLibraryList((Function*)&SHGetFolderPath,
"shell32.dll\0" W(
"SHGetFolderPath")
"\0\0")) {
669 if (!
LoadLibraryList((Function*)&SHGetFolderPath,
"SHFolder.dll\0" W(
"SHGetFolderPath")
"\0\0")) {
670 DEBUG(misc, 0,
"Unable to load " W(
"SHGetFolderPath")
"from either shell32.dll or SHFolder.dll");
677 if (SHGetFolderPath !=
nullptr)
return SHGetFolderPath(hwnd, csidl, hToken, dwFlags, pszPath);
690 ret = GetEnvironmentVariable(_T(
"WINDIR"), pszPath, MAX_PATH);
692 _tcsncat(pszPath, _T(
"\\Fonts"), MAX_PATH);
697 case CSIDL_COMMON_DOCUMENTS: {
699 if (RegOpenKeyEx(csidl == CSIDL_PERSONAL ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, REGSTR_PATH_SPECIAL_FOLDERS, 0, KEY_READ, &key) != ERROR_SUCCESS)
break;
700 DWORD len = MAX_PATH;
701 ret = RegQueryValueEx(key, csidl == CSIDL_PERSONAL ? _T(
"Personal") : _T(
"Common Documents"),
nullptr,
nullptr, (LPBYTE)pszPath, &len);
703 if (ret == ERROR_SUCCESS)
return (HRESULT)0;
717 char lang[9], country[9];
718 if (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, lang,
lengthof(lang)) == 0 ||
719 GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, country,
lengthof(country)) == 0) {
724 static char retbuf[6] = {lang[0], lang[1],
'_', country[0], country[1], 0};
729 static WCHAR _cur_iso_locale[16] = L
"";
731 void Win32SetCurrentLocaleName(
const char *iso_code)
735 if (strcmp(iso_code,
"zh_TW") == 0) {
737 }
else if (strcmp(iso_code,
"zh_CN") == 0) {
742 for (
char *c = iso; *c !=
'\0'; c++) {
743 if (*c ==
'_') *c =
'-';
747 MultiByteToWideChar(CP_UTF8, 0, iso, -1, _cur_iso_locale,
lengthof(_cur_iso_locale));
750 int OTTDStringCompare(
const char *s1,
const char *s2)
752 typedef int (WINAPI *PFNCOMPARESTRINGEX)(LPCWSTR, DWORD, LPCWCH, int, LPCWCH, int, LPVOID, LPVOID, LPARAM);
753 static PFNCOMPARESTRINGEX _CompareStringEx =
nullptr;
754 static bool first_time =
true;
756 #ifndef SORT_DIGITSASNUMBERS 757 # define SORT_DIGITSASNUMBERS 0x00000008 // use digits as numbers sort method 759 #ifndef LINGUISTIC_IGNORECASE 760 # define LINGUISTIC_IGNORECASE 0x00000010 // linguistically appropriate 'ignore case' 764 _CompareStringEx = (PFNCOMPARESTRINGEX)GetProcAddress(GetModuleHandle(_T(
"Kernel32")),
"CompareStringEx");
768 if (_CompareStringEx !=
nullptr) {
770 int len_s1 = MultiByteToWideChar(CP_UTF8, 0, s1, -1,
nullptr, 0);
771 int len_s2 = MultiByteToWideChar(CP_UTF8, 0, s2, -1,
nullptr, 0);
773 if (len_s1 != 0 && len_s2 != 0) {
774 LPWSTR str_s1 =
AllocaM(WCHAR, len_s1);
775 LPWSTR str_s2 =
AllocaM(WCHAR, len_s2);
777 MultiByteToWideChar(CP_UTF8, 0, s1, -1, str_s1, len_s1);
778 MultiByteToWideChar(CP_UTF8, 0, s2, -1, str_s2, len_s2);
780 int result = _CompareStringEx(_cur_iso_locale, LINGUISTIC_IGNORECASE | SORT_DIGITSASNUMBERS, str_s1, -1, str_s2, -1,
nullptr,
nullptr, 0);
781 if (result != 0)
return result;
785 TCHAR s1_buf[512], s2_buf[512];
794 const DWORD MS_VC_EXCEPTION = 0x406D1388;
796 PACK_N(
struct THREADNAME_INFO {
808 THREADNAME_INFO info;
809 info.dwType = 0x1000;
810 info.szName = threadName;
811 info.dwThreadID = -1;
814 #pragma warning(push) 815 #pragma warning(disable: 6320 6322) 817 RaiseException(MS_VC_EXCEPTION, 0,
sizeof(info) /
sizeof(ULONG_PTR), (ULONG_PTR*)&info);
818 } __except (EXCEPTION_EXECUTE_HANDLER) {
int openttd_main(int argc, char *argv[])
Main entry point for this lovely game.
static char * strecat(char *dst, const char *src, const char *last)
Appends characters from one string to another.
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.
const char * FS2OTTD(const TCHAR *name)
Convert to OpenTTD's encoding from that of the local environment.
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
Search in the directory where the binary resides.
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...
void SetRandomSeed(uint32 seed)
(Re)set the state of the random number generators.
const LanguageMetadata * _current_language
The currently loaded language.
#define lastof(x)
Get the last element of an fixed size array.
static void InitialiseCrashLog()
Initialiser for crash logs; do the appropriate things so crashes are handled by our crash handler ins...
#define AllocaM(T, num_elements)
alloca() has to be called in the parent function, so define AllocaM() as a macro
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.
const char * GetCurrentLocale(const char *)
Determine the current user's locale.
Deals with finding savegames.
bool _left_button_clicked
Is left mouse button clicked?
void SetCurrentThreadName(const char *)
Name the thread this function is called on for the debugger.
bool AppendPathSeparator(char *buf, const char *last)
Appends, if necessary, the path separator character to the end of the string.
bool LoadLibraryList(Function proc[], const char *dll)
Helper function needed by dynamically loading libraries XXX: Hurray for MS only having an ANSI GetPro...
bool _left_button_down
Is left mouse button pressed?
static const char * _help_msg
Temporary pointer to get the help message to the window.
static INT_PTR CALLBACK HelpDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
Callback function to handle the window.
void CDECL usererror(const char *s,...)
Error handling for fatal user errors.
char * stredup(const char *s, const char *last)
Create a duplicate of the given string.
bool GetClipboardContents(char *buffer, const char *last)
Try to retrieve the current clipboard contents.
#define lengthof(x)
Return the length of an fixed size array.
void DetermineBasePaths(const char *exe)
Determine the base (personal dir and game data dir) paths.
const TCHAR * OTTD2FS(const char *name, bool console_cp)
Convert from OpenTTD's encoding to that of the local environment.
#define DEBUG(name, level,...)
Output a line of debugging information.
Search in the personal directory.
List of file information.
const char * _searchpaths[NUM_SEARCHPATHS]
The search paths OpenTTD could search through.
char * strecpy(char *dst, const char *src, const char *last)
Copies characters from one buffer to another.
Search in the working directory.
Search in the installation directory.
Search within the application bundle.
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
static bool HasBit(const T x, const uint8 y)
Checks if a bit in a value is set.
void ValidateString(const char *str)
Scans the string for valid characters and if it finds invalid ones, replaces them with a question mar...
FiosItem * Append()
Construct a new entry in the file list.
declarations of functions for MS windows systems
Search in the shared directory, like 'Shared Files' under Windows.