win32.cpp

Go to the documentation of this file.
00001 /* $Id: win32.cpp 24606 2012-10-17 19:11:03Z rubidium $ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #include "../../stdafx.h"
00013 #include "../../debug.h"
00014 #include "../../gfx_func.h"
00015 #include "../../textbuf_gui.h"
00016 #include "../../fileio_func.h"
00017 #include "../../fios.h"
00018 #include <windows.h>
00019 #include <fcntl.h>
00020 #include <shlobj.h> /* SHGetFolderPath */
00021 #include <Shellapi.h>
00022 #include "win32.h"
00023 #include "../../core/alloc_func.hpp"
00024 #include "../../openttd.h"
00025 #include "../../core/random_func.hpp"
00026 #include "../../string_func.h"
00027 #include "../../crashlog.h"
00028 #include <errno.h>
00029 #include <sys/stat.h>
00030 
00031 static bool _has_console;
00032 static bool _cursor_disable = true;
00033 static bool _cursor_visible = true;
00034 
00035 bool MyShowCursor(bool show, bool toggle)
00036 {
00037   if (toggle) _cursor_disable = !_cursor_disable;
00038   if (_cursor_disable) return show;
00039   if (_cursor_visible == show) return show;
00040 
00041   _cursor_visible = show;
00042   ShowCursor(show);
00043 
00044   return !show;
00045 }
00046 
00052 bool LoadLibraryList(Function proc[], const char *dll)
00053 {
00054   while (*dll != '\0') {
00055     HMODULE lib;
00056     lib = LoadLibrary(MB_TO_WIDE(dll));
00057 
00058     if (lib == NULL) return false;
00059     for (;;) {
00060       FARPROC p;
00061 
00062       while (*dll++ != '\0') { /* Nothing */ }
00063       if (*dll == '\0') break;
00064 #if defined(WINCE)
00065       p = GetProcAddress(lib, MB_TO_WIDE(dll));
00066 #else
00067       p = GetProcAddress(lib, dll);
00068 #endif
00069       if (p == NULL) return false;
00070       *proc++ = (Function)p;
00071     }
00072     dll++;
00073   }
00074   return true;
00075 }
00076 
00077 void ShowOSErrorBox(const char *buf, bool system)
00078 {
00079   MyShowCursor(true);
00080   MessageBox(GetActiveWindow(), MB_TO_WIDE(buf), _T("Error!"), MB_ICONSTOP);
00081 }
00082 
00083 void OSOpenBrowser(const char *url)
00084 {
00085   ShellExecute(GetActiveWindow(), _T("open"), MB_TO_WIDE(url), NULL, NULL, SW_SHOWNORMAL);
00086 }
00087 
00088 /* Code below for windows version of opendir/readdir/closedir copied and
00089  * modified from Jan Wassenberg's GPL implementation posted over at
00090  * http://www.gamedev.net/community/forums/topic.asp?topic_id=364584&whichpage=1&#2398903 */
00091 
00092 struct DIR {
00093   HANDLE hFind;
00094   /* the dirent returned by readdir.
00095    * note: having only one global instance is not possible because
00096    * multiple independent opendir/readdir sequences must be supported. */
00097   dirent ent;
00098   WIN32_FIND_DATA fd;
00099   /* since opendir calls FindFirstFile, we need a means of telling the
00100    * first call to readdir that we already have a file.
00101    * that's the case iff this is true */
00102   bool at_first_entry;
00103 };
00104 
00105 /* suballocator - satisfies most requests with a reusable static instance.
00106  * this avoids hundreds of alloc/free which would fragment the heap.
00107  * To guarantee concurrency, we fall back to malloc if the instance is
00108  * already in use (it's important to avoid surprises since this is such a
00109  * low-level routine). */
00110 static DIR _global_dir;
00111 static LONG _global_dir_is_in_use = false;
00112 
00113 static inline DIR *dir_calloc()
00114 {
00115   DIR *d;
00116 
00117   if (InterlockedExchange(&_global_dir_is_in_use, true) == (LONG)true) {
00118     d = CallocT<DIR>(1);
00119   } else {
00120     d = &_global_dir;
00121     memset(d, 0, sizeof(*d));
00122   }
00123   return d;
00124 }
00125 
00126 static inline void dir_free(DIR *d)
00127 {
00128   if (d == &_global_dir) {
00129     _global_dir_is_in_use = (LONG)false;
00130   } else {
00131     free(d);
00132   }
00133 }
00134 
00135 DIR *opendir(const TCHAR *path)
00136 {
00137   DIR *d;
00138   UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS); // disable 'no-disk' message box
00139   DWORD fa = GetFileAttributes(path);
00140 
00141   if ((fa != INVALID_FILE_ATTRIBUTES) && (fa & FILE_ATTRIBUTE_DIRECTORY)) {
00142     d = dir_calloc();
00143     if (d != NULL) {
00144       TCHAR search_path[MAX_PATH];
00145       bool slash = path[_tcslen(path) - 1] == '\\';
00146 
00147       /* build search path for FindFirstFile, try not to append additional slashes
00148        * as it throws Win9x off its groove for root directories */
00149       _sntprintf(search_path, lengthof(search_path), _T("%s%s*"), path, slash ? _T("") : _T("\\"));
00150       *lastof(search_path) = '\0';
00151       d->hFind = FindFirstFile(search_path, &d->fd);
00152 
00153       if (d->hFind != INVALID_HANDLE_VALUE ||
00154           GetLastError() == ERROR_NO_MORE_FILES) { // the directory is empty
00155         d->ent.dir = d;
00156         d->at_first_entry = true;
00157       } else {
00158         dir_free(d);
00159         d = NULL;
00160       }
00161     } else {
00162       errno = ENOMEM;
00163     }
00164   } else {
00165     /* path not found or not a directory */
00166     d = NULL;
00167     errno = ENOENT;
00168   }
00169 
00170   SetErrorMode(sem); // restore previous setting
00171   return d;
00172 }
00173 
00174 struct dirent *readdir(DIR *d)
00175 {
00176   DWORD prev_err = GetLastError(); // avoid polluting last error
00177 
00178   if (d->at_first_entry) {
00179     /* the directory was empty when opened */
00180     if (d->hFind == INVALID_HANDLE_VALUE) return NULL;
00181     d->at_first_entry = false;
00182   } else if (!FindNextFile(d->hFind, &d->fd)) { // determine cause and bail
00183     if (GetLastError() == ERROR_NO_MORE_FILES) SetLastError(prev_err);
00184     return NULL;
00185   }
00186 
00187   /* This entry has passed all checks; return information about it.
00188    * (note: d_name is a pointer; see struct dirent definition) */
00189   d->ent.d_name = d->fd.cFileName;
00190   return &d->ent;
00191 }
00192 
00193 int closedir(DIR *d)
00194 {
00195   FindClose(d->hFind);
00196   dir_free(d);
00197   return 0;
00198 }
00199 
00200 bool FiosIsRoot(const char *file)
00201 {
00202   return file[3] == '\0'; // C:\...
00203 }
00204 
00205 void FiosGetDrives()
00206 {
00207 #if defined(WINCE)
00208   /* WinCE only knows one drive: / */
00209   FiosItem *fios = _fios_items.Append();
00210   fios->type = FIOS_TYPE_DRIVE;
00211   fios->mtime = 0;
00212   snprintf(fios->name, lengthof(fios->name), PATHSEP "");
00213   strecpy(fios->title, fios->name, lastof(fios->title));
00214 #else
00215   TCHAR drives[256];
00216   const TCHAR *s;
00217 
00218   GetLogicalDriveStrings(lengthof(drives), drives);
00219   for (s = drives; *s != '\0';) {
00220     FiosItem *fios = _fios_items.Append();
00221     fios->type = FIOS_TYPE_DRIVE;
00222     fios->mtime = 0;
00223     snprintf(fios->name, lengthof(fios->name),  "%c:", s[0] & 0xFF);
00224     strecpy(fios->title, fios->name, lastof(fios->title));
00225     while (*s++ != '\0') { /* Nothing */ }
00226   }
00227 #endif
00228 }
00229 
00230 bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb)
00231 {
00232   /* hectonanoseconds between Windows and POSIX epoch */
00233   static const int64 posix_epoch_hns = 0x019DB1DED53E8000LL;
00234   const WIN32_FIND_DATA *fd = &ent->dir->fd;
00235 
00236   sb->st_size  = ((uint64) fd->nFileSizeHigh << 32) + fd->nFileSizeLow;
00237   /* UTC FILETIME to seconds-since-1970 UTC
00238    * we just have to subtract POSIX epoch and scale down to units of seconds.
00239    * http://www.gamedev.net/community/forums/topic.asp?topic_id=294070&whichpage=1&#1860504
00240    * XXX - not entirely correct, since filetimes on FAT aren't UTC but local,
00241    * this won't entirely be correct, but we use the time only for comparsion. */
00242   sb->st_mtime = (time_t)((*(const uint64*)&fd->ftLastWriteTime - posix_epoch_hns) / 1E7);
00243   sb->st_mode  = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)? S_IFDIR : S_IFREG;
00244 
00245   return true;
00246 }
00247 
00248 bool FiosIsHiddenFile(const struct dirent *ent)
00249 {
00250   return (ent->dir->fd.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != 0;
00251 }
00252 
00253 bool FiosGetDiskFreeSpace(const char *path, uint64 *tot)
00254 {
00255   UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS);  // disable 'no-disk' message box
00256   bool retval = false;
00257   TCHAR root[4];
00258   DWORD spc, bps, nfc, tnc;
00259 
00260   _sntprintf(root, lengthof(root), _T("%c:") _T(PATHSEP), path[0]);
00261   if (tot != NULL && GetDiskFreeSpace(root, &spc, &bps, &nfc, &tnc)) {
00262     *tot = ((spc * bps) * (uint64)nfc);
00263     retval = true;
00264   }
00265 
00266   SetErrorMode(sem); // reset previous setting
00267   return retval;
00268 }
00269 
00270 static int ParseCommandLine(char *line, char **argv, int max_argc)
00271 {
00272   int n = 0;
00273 
00274   do {
00275     /* skip whitespace */
00276     while (*line == ' ' || *line == '\t') line++;
00277 
00278     /* end? */
00279     if (*line == '\0') break;
00280 
00281     /* special handling when quoted */
00282     if (*line == '"') {
00283       argv[n++] = ++line;
00284       while (*line != '"') {
00285         if (*line == '\0') return n;
00286         line++;
00287       }
00288     } else {
00289       argv[n++] = line;
00290       while (*line != ' ' && *line != '\t') {
00291         if (*line == '\0') return n;
00292         line++;
00293       }
00294     }
00295     *line++ = '\0';
00296   } while (n != max_argc);
00297 
00298   return n;
00299 }
00300 
00301 void CreateConsole()
00302 {
00303 #if defined(WINCE)
00304   /* WinCE doesn't support console stuff */
00305 #else
00306   HANDLE hand;
00307   CONSOLE_SCREEN_BUFFER_INFO coninfo;
00308 
00309   if (_has_console) return;
00310   _has_console = true;
00311 
00312   AllocConsole();
00313 
00314   hand = GetStdHandle(STD_OUTPUT_HANDLE);
00315   GetConsoleScreenBufferInfo(hand, &coninfo);
00316   coninfo.dwSize.Y = 500;
00317   SetConsoleScreenBufferSize(hand, coninfo.dwSize);
00318 
00319   /* redirect unbuffered STDIN, STDOUT, STDERR to the console */
00320 #if !defined(__CYGWIN__)
00321 
00322   /* Check if we can open a handle to STDOUT. */
00323   int fd = _open_osfhandle((intptr_t)hand, _O_TEXT);
00324   if (fd == -1) {
00325     /* Free everything related to the console. */
00326     FreeConsole();
00327     _has_console = false;
00328     _close(fd);
00329     CloseHandle(hand);
00330 
00331     ShowInfo("Unable to open an output handle to the console. Check known-bugs.txt for details.");
00332     return;
00333   }
00334 
00335   *stdout = *_fdopen(fd, "w");
00336   *stdin = *_fdopen(_open_osfhandle((intptr_t)GetStdHandle(STD_INPUT_HANDLE), _O_TEXT), "r" );
00337   *stderr = *_fdopen(_open_osfhandle((intptr_t)GetStdHandle(STD_ERROR_HANDLE), _O_TEXT), "w" );
00338 #else
00339   /* open_osfhandle is not in cygwin */
00340   *stdout = *fdopen(1, "w" );
00341   *stdin = *fdopen(0, "r" );
00342   *stderr = *fdopen(2, "w" );
00343 #endif
00344 
00345   setvbuf(stdin, NULL, _IONBF, 0);
00346   setvbuf(stdout, NULL, _IONBF, 0);
00347   setvbuf(stderr, NULL, _IONBF, 0);
00348 #endif
00349 }
00350 
00352 static const char *_help_msg;
00353 
00355 static INT_PTR CALLBACK HelpDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
00356 {
00357   switch (msg) {
00358     case WM_INITDIALOG: {
00359       char help_msg[8192];
00360       const char *p = _help_msg;
00361       char *q = help_msg;
00362       while (q != lastof(help_msg) && *p != '\0') {
00363         if (*p == '\n') {
00364           *q++ = '\r';
00365           if (q == lastof(help_msg)) {
00366             q[-1] = '\0';
00367             break;
00368           }
00369         }
00370         *q++ = *p++;
00371       }
00372       *q = '\0';
00373 #if defined(UNICODE)
00374       /* We need to put the text in a seperate buffer because the default
00375        * buffer in MB_TO_WIDE might not be large enough (512 chars) */
00376       wchar_t help_msgW[8192];
00377 #endif
00378       SetDlgItemText(wnd, 11, MB_TO_WIDE_BUFFER(help_msg, help_msgW, lengthof(help_msgW)));
00379       SendDlgItemMessage(wnd, 11, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), FALSE);
00380     } return TRUE;
00381 
00382     case WM_COMMAND:
00383       if (wParam == 12) ExitProcess(0);
00384       return TRUE;
00385     case WM_CLOSE:
00386       ExitProcess(0);
00387   }
00388 
00389   return FALSE;
00390 }
00391 
00392 void ShowInfo(const char *str)
00393 {
00394   if (_has_console) {
00395     fprintf(stderr, "%s\n", str);
00396   } else {
00397     bool old;
00398     ReleaseCapture();
00399     _left_button_clicked = _left_button_down = false;
00400 
00401     old = MyShowCursor(true);
00402     if (strlen(str) > 2048) {
00403       /* The minimum length of the help message is 2048. Other messages sent via
00404        * ShowInfo are much shorter, or so long they need this way of displaying
00405        * them anyway. */
00406       _help_msg = str;
00407       DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(101), NULL, HelpDialogFunc);
00408     } else {
00409 #if defined(UNICODE)
00410       /* We need to put the text in a seperate buffer because the default
00411        * buffer in MB_TO_WIDE might not be large enough (512 chars) */
00412       wchar_t help_msgW[8192];
00413 #endif
00414       MessageBox(GetActiveWindow(), MB_TO_WIDE_BUFFER(str, help_msgW, lengthof(help_msgW)), _T("OpenTTD"), MB_ICONINFORMATION | MB_OK);
00415     }
00416     MyShowCursor(old);
00417   }
00418 }
00419 
00420 #if defined(WINCE)
00421 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
00422 #else
00423 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
00424 #endif
00425 {
00426   int argc;
00427   char *argv[64]; // max 64 command line arguments
00428   char *cmdline;
00429 
00430 #if !defined(UNICODE)
00431   _codepage = GetACP(); // get system codepage as some kind of a default
00432 #endif /* UNICODE */
00433 
00434   CrashLog::InitialiseCrashLog();
00435 
00436 #if defined(UNICODE)
00437 
00438 #if !defined(WINCE)
00439   /* Check if a win9x user started the win32 version */
00440   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.");
00441 #endif
00442 
00443   /* For UNICODE we need to convert the commandline to char* _AND_
00444    * save it because argv[] points into this buffer and thus needs to
00445    * be available between subsequent calls to FS2OTTD() */
00446   char cmdlinebuf[MAX_PATH];
00447 #endif /* UNICODE */
00448 
00449   cmdline = WIDE_TO_MB_BUFFER(GetCommandLine(), cmdlinebuf, lengthof(cmdlinebuf));
00450 
00451 #if defined(_DEBUG)
00452   CreateConsole();
00453 #endif
00454 
00455 #if !defined(WINCE)
00456   _set_error_mode(_OUT_TO_MSGBOX); // force assertion output to messagebox
00457 #endif
00458 
00459   /* setup random seed to something quite random */
00460   SetRandomSeed(GetTickCount());
00461 
00462   argc = ParseCommandLine(cmdline, argv, lengthof(argv));
00463 
00464   ttd_main(argc, argv);
00465   return 0;
00466 }
00467 
00468 #if defined(WINCE)
00469 void GetCurrentDirectoryW(int length, wchar_t *path)
00470 {
00471   /* Get the name of this module */
00472   GetModuleFileName(NULL, path, length);
00473 
00474   /* Remove the executable name, this we call CurrentDir */
00475   wchar_t *pDest = wcsrchr(path, '\\');
00476   if (pDest != NULL) {
00477     int result = pDest - path + 1;
00478     path[result] = '\0';
00479   }
00480 }
00481 #endif
00482 
00483 char *getcwd(char *buf, size_t size)
00484 {
00485 #if defined(WINCE)
00486   TCHAR path[MAX_PATH];
00487   GetModuleFileName(NULL, path, MAX_PATH);
00488   convert_from_fs(path, buf, size);
00489   /* GetModuleFileName returns dir with file, so remove everything behind latest '\\' */
00490   char *p = strrchr(buf, '\\');
00491   if (p != NULL) *p = '\0';
00492 #elif defined(UNICODE)
00493   TCHAR path[MAX_PATH];
00494   GetCurrentDirectory(MAX_PATH - 1, path);
00495   convert_from_fs(path, buf, size);
00496 #else
00497   GetCurrentDirectory(size, buf);
00498 #endif
00499   return buf;
00500 }
00501 
00502 
00503 void DetermineBasePaths(const char *exe)
00504 {
00505   char tmp[MAX_PATH];
00506   TCHAR path[MAX_PATH];
00507 #ifdef WITH_PERSONAL_DIR
00508   SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, path);
00509   strecpy(tmp, WIDE_TO_MB_BUFFER(path, tmp, lengthof(tmp)), lastof(tmp));
00510   AppendPathSeparator(tmp, MAX_PATH);
00511   ttd_strlcat(tmp, PERSONAL_DIR, MAX_PATH);
00512   AppendPathSeparator(tmp, MAX_PATH);
00513   _searchpaths[SP_PERSONAL_DIR] = strdup(tmp);
00514 
00515   SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, SHGFP_TYPE_CURRENT, path);
00516   strecpy(tmp, WIDE_TO_MB_BUFFER(path, tmp, lengthof(tmp)), lastof(tmp));
00517   AppendPathSeparator(tmp, MAX_PATH);
00518   ttd_strlcat(tmp, PERSONAL_DIR, MAX_PATH);
00519   AppendPathSeparator(tmp, MAX_PATH);
00520   _searchpaths[SP_SHARED_DIR] = strdup(tmp);
00521 #else
00522   _searchpaths[SP_PERSONAL_DIR] = NULL;
00523   _searchpaths[SP_SHARED_DIR]   = NULL;
00524 #endif
00525 
00526   /* Get the path to working directory of OpenTTD */
00527   getcwd(tmp, lengthof(tmp));
00528   AppendPathSeparator(tmp, MAX_PATH);
00529   _searchpaths[SP_WORKING_DIR] = strdup(tmp);
00530 
00531   if (!GetModuleFileName(NULL, path, lengthof(path))) {
00532     DEBUG(misc, 0, "GetModuleFileName failed (%lu)\n", GetLastError());
00533     _searchpaths[SP_BINARY_DIR] = NULL;
00534   } else {
00535     TCHAR exec_dir[MAX_PATH];
00536     _tcsncpy(path, MB_TO_WIDE_BUFFER(exe, path, lengthof(path)), lengthof(path));
00537     if (!GetFullPathName(path, lengthof(exec_dir), exec_dir, NULL)) {
00538       DEBUG(misc, 0, "GetFullPathName failed (%lu)\n", GetLastError());
00539       _searchpaths[SP_BINARY_DIR] = NULL;
00540     } else {
00541       strecpy(tmp, WIDE_TO_MB_BUFFER(exec_dir, tmp, lengthof(tmp)), lastof(tmp));
00542       char *s = strrchr(tmp, PATHSEPCHAR);
00543       *(s + 1) = '\0';
00544       _searchpaths[SP_BINARY_DIR] = strdup(tmp);
00545     }
00546   }
00547 
00548   _searchpaths[SP_INSTALLATION_DIR]       = NULL;
00549   _searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
00550 }
00551 
00552 
00553 bool GetClipboardContents(char *buffer, size_t buff_len)
00554 {
00555   HGLOBAL cbuf;
00556   const char *ptr;
00557 
00558   if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
00559     OpenClipboard(NULL);
00560     cbuf = GetClipboardData(CF_UNICODETEXT);
00561 
00562     ptr = (const char*)GlobalLock(cbuf);
00563     const char *ret = convert_from_fs((const wchar_t*)ptr, buffer, buff_len);
00564     GlobalUnlock(cbuf);
00565     CloseClipboard();
00566 
00567     if (*ret == '\0') return false;
00568 #if !defined(UNICODE)
00569   } else if (IsClipboardFormatAvailable(CF_TEXT)) {
00570     OpenClipboard(NULL);
00571     cbuf = GetClipboardData(CF_TEXT);
00572 
00573     ptr = (const char*)GlobalLock(cbuf);
00574     ttd_strlcpy(buffer, FS2OTTD(ptr), buff_len);
00575 
00576     GlobalUnlock(cbuf);
00577     CloseClipboard();
00578 #endif /* UNICODE */
00579   } else {
00580     return false;
00581   }
00582 
00583   return true;
00584 }
00585 
00586 
00587 void CSleep(int milliseconds)
00588 {
00589   Sleep(milliseconds);
00590 }
00591 
00592 
00606 const char *FS2OTTD(const TCHAR *name)
00607 {
00608   static char utf8_buf[512];
00609 #if defined(UNICODE)
00610   return convert_from_fs(name, utf8_buf, lengthof(utf8_buf));
00611 #else
00612   char *s = utf8_buf;
00613 
00614   for (; *name != '\0'; name++) {
00615     wchar_t w;
00616     int len = MultiByteToWideChar(_codepage, 0, name, 1, &w, 1);
00617     if (len != 1) {
00618       DEBUG(misc, 0, "[utf8] M2W error converting '%c'. Errno %lu", *name, GetLastError());
00619       continue;
00620     }
00621 
00622     if (s + Utf8CharLen(w) >= lastof(utf8_buf)) break;
00623     s += Utf8Encode(s, w);
00624   }
00625 
00626   *s = '\0';
00627   return utf8_buf;
00628 #endif /* UNICODE */
00629 }
00630 
00644 const TCHAR *OTTD2FS(const char *name)
00645 {
00646   static TCHAR system_buf[512];
00647 #if defined(UNICODE)
00648   return convert_to_fs(name, system_buf, lengthof(system_buf));
00649 #else
00650   char *s = system_buf;
00651 
00652   for (WChar c; (c = Utf8Consume(&name)) != '\0';) {
00653     if (s >= lastof(system_buf)) break;
00654 
00655     char mb;
00656     int len = WideCharToMultiByte(_codepage, 0, (wchar_t*)&c, 1, &mb, 1, NULL, NULL);
00657     if (len != 1) {
00658       DEBUG(misc, 0, "[utf8] W2M error converting '0x%X'. Errno %lu", c, GetLastError());
00659       continue;
00660     }
00661 
00662     *s++ = mb;
00663   }
00664 
00665   *s = '\0';
00666   return system_buf;
00667 #endif /* UNICODE */
00668 }
00669 
00670 
00679 char *convert_from_fs(const wchar_t *name, char *utf8_buf, size_t buflen)
00680 {
00681   int len = WideCharToMultiByte(CP_UTF8, 0, name, -1, utf8_buf, (int)buflen, NULL, NULL);
00682   if (len == 0) {
00683     DEBUG(misc, 0, "[utf8] W2M error converting wide-string. Errno %lu", GetLastError());
00684     utf8_buf[0] = '\0';
00685   }
00686 
00687   return utf8_buf;
00688 }
00689 
00690 
00700 wchar_t *convert_to_fs(const char *name, wchar_t *utf16_buf, size_t buflen)
00701 {
00702   int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, utf16_buf, (int)buflen);
00703   if (len == 0) {
00704     DEBUG(misc, 0, "[utf8] M2W error converting '%s'. Errno %lu", name, GetLastError());
00705     utf16_buf[0] = '\0';
00706   }
00707 
00708   return utf16_buf;
00709 }
00710 
00717 HRESULT OTTDSHGetFolderPath(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)
00718 {
00719   static HRESULT (WINAPI *SHGetFolderPath)(HWND, int, HANDLE, DWORD, LPTSTR) = NULL;
00720   static bool first_time = true;
00721 
00722   /* We only try to load the library one time; if it fails, it fails */
00723   if (first_time) {
00724 #if defined(UNICODE)
00725 # define W(x) x "W"
00726 #else
00727 # define W(x) x "A"
00728 #endif
00729     if (!LoadLibraryList((Function*)&SHGetFolderPath, "SHFolder.dll\0" W("SHGetFolderPath") "\0\0")) {
00730       DEBUG(misc, 0, "Unable to load " W("SHGetFolderPath") "from SHFolder.dll");
00731     }
00732 #undef W
00733     first_time = false;
00734   }
00735 
00736   if (SHGetFolderPath != NULL) return SHGetFolderPath(hwnd, csidl, hToken, dwFlags, pszPath);
00737 
00738   /* SHGetFolderPath doesn't exist, try a more conservative approach,
00739    * eg environment variables. This is only included for legacy modes
00740    * MSDN says: that 'pszPath' is a "Pointer to a null-terminated string of
00741    * length MAX_PATH which will receive the path" so let's assume that
00742    * Windows 95 with Internet Explorer 5.0, Windows 98 with Internet Explorer 5.0,
00743    * Windows 98 Second Edition (SE), Windows NT 4.0 with Internet Explorer 5.0,
00744    * Windows NT 4.0 with Service Pack 4 (SP4) */
00745   {
00746     DWORD ret;
00747     switch (csidl) {
00748       case CSIDL_FONTS: // Get the system font path, eg %WINDIR%\Fonts
00749         ret = GetEnvironmentVariable(_T("WINDIR"), pszPath, MAX_PATH);
00750         if (ret == 0) break;
00751         _tcsncat(pszPath, _T("\\Fonts"), MAX_PATH);
00752 
00753         return (HRESULT)0;
00754 
00755       /* XXX - other types to go here when needed... */
00756     }
00757   }
00758 
00759   return E_INVALIDARG;
00760 }
00761 
00763 const char *GetCurrentLocale(const char *)
00764 {
00765   char lang[9], country[9];
00766   if (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, lang, lengthof(lang)) == 0 ||
00767       GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, country, lengthof(country)) == 0) {
00768     /* Unable to retrieve the locale. */
00769     return NULL;
00770   }
00771   /* Format it as 'en_us'. */
00772   static char retbuf[6] = {lang[0], lang[1], '_', country[0], country[1], 0};
00773   return retbuf;
00774 }
00775 
00776 uint GetCPUCoreCount()
00777 {
00778   SYSTEM_INFO info;
00779 
00780   GetSystemInfo(&info);
00781   return info.dwNumberOfProcessors;
00782 }