win32.cpp

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