# win32.cpp

Go to the documentation of this file.

    00001 /* $Id: win32.cpp 25676 2013-08-05 20:36:55Z michi_cc $ */
    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(), OTTD2FS(buf), _T("Error!"), MB_ICONSTOP);
    00082 }
    00083
    00084 void OSOpenBrowser(const char *url)
    00085 {
    00086   ShellExecute(GetActiveWindow(), _T("open"), OTTD2FS(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       /* We need to put the text in a separate buffer because the default
    00375        * buffer in OTTD2FS might not be large enough (512 chars). */
    00376       TCHAR help_msg_buf[8192];
    00377       SetDlgItemText(wnd, 11, convert_to_fs(help_msg, help_msg_buf, lengthof(help_msg_buf)));
    00378       SendDlgItemMessage(wnd, 11, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), FALSE);
    00379     } return TRUE;
    00380
    00381     case WM_COMMAND:
    00382       if (wParam == 12) ExitProcess(0);
    00383       return TRUE;
    00384     case WM_CLOSE:
    00385       ExitProcess(0);
    00386   }
    00387
    00388   return FALSE;
    00389 }
    00390
    00391 void ShowInfo(const char *str)
    00392 {
    00393   if (_has_console) {
    00394     fprintf(stderr, "%s\n", str);
    00395   } else {
    00396     bool old;
    00397     ReleaseCapture();
    00398     _left_button_clicked = _left_button_down = false;
    00399
    00400     old = MyShowCursor(true);
    00401     if (strlen(str) > 2048) {
    00402       /* The minimum length of the help message is 2048. Other messages sent via
    00403        * ShowInfo are much shorter, or so long they need this way of displaying
    00404        * them anyway. */
    00405       _help_msg = str;
    00406       DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(101), NULL, HelpDialogFunc);
    00407     } else {
    00408       /* We need to put the text in a separate buffer because the default
    00409        * buffer in OTTD2FS might not be large enough (512 chars). */
    00410       TCHAR help_msg_buf[8192];
    00411       MessageBox(GetActiveWindow(), convert_to_fs(str, help_msg_buf, lengthof(help_msg_buf)), _T("OpenTTD"), MB_ICONINFORMATION | MB_OK);
    00412     }
    00413     MyShowCursor(old);
    00414   }
    00415 }
    00416
    00417 #if defined(WINCE)
    00418 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
    00419 #else
    00420 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    00421 #endif
    00422 {
    00423   int argc;
    00424   char *argv[64]; // max 64 command line arguments
    00425
    00426   CrashLog::InitialiseCrashLog();
    00427
    00428 #if defined(UNICODE) && !defined(WINCE)
    00429   /* Check if a win9x user started the win32 version */
    00430   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.");
    00431 #endif
    00432
    00433   /* Convert the command line to UTF-8. We need a dedicated buffer
    00434    * for this because argv[] points into this buffer and this needs to
    00435    * be available between subsequent calls to FS2OTTD(). */
    00436   char *cmdline = strdup(FS2OTTD(GetCommandLine()));
    00437
    00438 #if defined(_DEBUG)
    00439   CreateConsole();
    00440 #endif
    00441
    00442 #if !defined(WINCE)
    00443   _set_error_mode(_OUT_TO_MSGBOX); // force assertion output to messagebox
    00444 #endif
    00445
    00446   /* setup random seed to something quite random */
    00447   SetRandomSeed(GetTickCount());
    00448
    00449   argc = ParseCommandLine(cmdline, argv, lengthof(argv));
    00450
    00451   openttd_main(argc, argv);
    00452   free(cmdline);
    00453   return 0;
    00454 }
    00455
    00456 #if defined(WINCE)
    00457 void GetCurrentDirectoryW(int length, wchar_t *path)
    00458 {
    00459   /* Get the name of this module */
    00460   GetModuleFileName(NULL, path, length);
    00461
    00462   /* Remove the executable name, this we call CurrentDir */
    00463   wchar_t *pDest = wcsrchr(path, '\\');
    00464   if (pDest != NULL) {
    00465     int result = pDest - path + 1;
    00466     path[result] = '\0';
    00467   }
    00468 }
    00469 #endif
    00470
    00471 char *getcwd(char *buf, size_t size)
    00472 {
    00473 #if defined(WINCE)
    00474   TCHAR path[MAX_PATH];
    00475   GetModuleFileName(NULL, path, MAX_PATH);
    00476   convert_from_fs(path, buf, size);
    00477   /* GetModuleFileName returns dir with file, so remove everything behind latest '\\' */
    00478   char *p = strrchr(buf, '\\');
    00479   if (p != NULL) *p = '\0';
    00480 #else
    00481   TCHAR path[MAX_PATH];
    00482   GetCurrentDirectory(MAX_PATH - 1, path);
    00483   convert_from_fs(path, buf, size);
    00484 #endif
    00485   return buf;
    00486 }
    00487
    00488
    00489 void DetermineBasePaths(const char *exe)
    00490 {
    00491   char tmp[MAX_PATH];
    00492   TCHAR path[MAX_PATH];
    00493 #ifdef WITH_PERSONAL_DIR
    00494   if (SUCCEEDED(OTTDSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, path))) {
    00495     strecpy(tmp, FS2OTTD(path), lastof(tmp));
    00496     AppendPathSeparator(tmp, MAX_PATH);
    00497     ttd_strlcat(tmp, PERSONAL_DIR, MAX_PATH);
    00498     AppendPathSeparator(tmp, MAX_PATH);
    00499     _searchpaths[SP_PERSONAL_DIR] = strdup(tmp);
    00500   } else {
    00501     _searchpaths[SP_PERSONAL_DIR] = NULL;
    00502   }
    00503
    00504   if (SUCCEEDED(OTTDSHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, SHGFP_TYPE_CURRENT, path))) {
    00505     strecpy(tmp, FS2OTTD(path), lastof(tmp));
    00506     AppendPathSeparator(tmp, MAX_PATH);
    00507     ttd_strlcat(tmp, PERSONAL_DIR, MAX_PATH);
    00508     AppendPathSeparator(tmp, MAX_PATH);
    00509     _searchpaths[SP_SHARED_DIR] = strdup(tmp);
    00510   } else {
    00511     _searchpaths[SP_SHARED_DIR] = NULL;
    00512   }
    00513 #else
    00514   _searchpaths[SP_PERSONAL_DIR] = NULL;
    00515   _searchpaths[SP_SHARED_DIR]   = NULL;
    00516 #endif
    00517
    00518   /* Get the path to working directory of OpenTTD */
    00519   getcwd(tmp, lengthof(tmp));
    00520   AppendPathSeparator(tmp, MAX_PATH);
    00521   _searchpaths[SP_WORKING_DIR] = strdup(tmp);
    00522
    00523   if (!GetModuleFileName(NULL, path, lengthof(path))) {
    00524     DEBUG(misc, 0, "GetModuleFileName failed (%lu)\n", GetLastError());
    00525     _searchpaths[SP_BINARY_DIR] = NULL;
    00526   } else {
    00527     TCHAR exec_dir[MAX_PATH];
    00528     _tcsncpy(path, convert_to_fs(exe, path, lengthof(path)), lengthof(path));
    00529     if (!GetFullPathName(path, lengthof(exec_dir), exec_dir, NULL)) {
    00530       DEBUG(misc, 0, "GetFullPathName failed (%lu)\n", GetLastError());
    00531       _searchpaths[SP_BINARY_DIR] = NULL;
    00532     } else {
    00533       strecpy(tmp, convert_from_fs(exec_dir, tmp, lengthof(tmp)), lastof(tmp));
    00534       char *s = strrchr(tmp, PATHSEPCHAR);
    00535       *(s + 1) = '\0';
    00536       _searchpaths[SP_BINARY_DIR] = strdup(tmp);
    00537     }
    00538   }
    00539
    00540   _searchpaths[SP_INSTALLATION_DIR]       = NULL;
    00541   _searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
    00542 }
    00543
    00544
    00545 bool GetClipboardContents(char *buffer, size_t buff_len)
    00546 {
    00547   HGLOBAL cbuf;
    00548   const char *ptr;
    00549
    00550   if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
    00551     OpenClipboard(NULL);
    00552     cbuf = GetClipboardData(CF_UNICODETEXT);
    00553
    00554     ptr = (const char*)GlobalLock(cbuf);
    00555     int out_len = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)ptr, -1, buffer, (int)buff_len, NULL, NULL);
    00556     GlobalUnlock(cbuf);
    00557     CloseClipboard();
    00558
    00559     if (out_len == 0) return false;
    00560 #if !defined(UNICODE)
    00561   } else if (IsClipboardFormatAvailable(CF_TEXT)) {
    00562     OpenClipboard(NULL);
    00563     cbuf = GetClipboardData(CF_TEXT);
    00564
    00565     ptr = (const char*)GlobalLock(cbuf);
    00566     ttd_strlcpy(buffer, FS2OTTD(ptr), buff_len);
    00567
    00568     GlobalUnlock(cbuf);
    00569     CloseClipboard();
    00570 #endif /* UNICODE */
    00571   } else {
    00572     return false;
    00573   }
    00574
    00575   return true;
    00576 }
    00577
    00578
    00579 void CSleep(int milliseconds)
    00580 {
    00581   Sleep(milliseconds);
    00582 }
    00583
    00584
    00598 const char *FS2OTTD(const TCHAR *name)
    00599 {
    00600   static char utf8_buf[512];
    00601   return convert_from_fs(name, utf8_buf, lengthof(utf8_buf));
    00602 }
    00603
    00616 const TCHAR *OTTD2FS(const char *name, bool console_cp)
    00617 {
    00618   static TCHAR system_buf[512];
    00619   return convert_to_fs(name, system_buf, lengthof(system_buf), console_cp);
    00620 }
    00621
    00622
    00631 char *convert_from_fs(const TCHAR *name, char *utf8_buf, size_t buflen)
    00632 {
    00633 #if defined(UNICODE)
    00634   const WCHAR *wide_buf = name;
    00635 #else
    00636   /* Convert string from the local codepage to UTF-16. */
    00637   int wide_len = MultiByteToWideChar(CP_ACP, 0, name, -1, NULL, 0);
    00638   if (wide_len == 0) {
    00639     utf8_buf[0] = '\0';
    00640     return utf8_buf;
    00641   }
    00642
    00643   WCHAR *wide_buf = AllocaM(WCHAR, wide_len);
    00644   MultiByteToWideChar(CP_ACP, 0, name, -1, wide_buf, wide_len);
    00645 #endif
    00646
    00647   /* Convert UTF-16 string to UTF-8. */
    00648   int len = WideCharToMultiByte(CP_UTF8, 0, wide_buf, -1, utf8_buf, (int)buflen, NULL, NULL);
    00649   if (len == 0) utf8_buf[0] = '\0';
    00650
    00651   return utf8_buf;
    00652 }
    00653
    00654
    00665 TCHAR *convert_to_fs(const char *name, TCHAR *system_buf, size_t buflen, bool console_cp)
    00666 {
    00667 #if defined(UNICODE)
    00668   int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, system_buf, (int)buflen);
    00669   if (len == 0) system_buf[0] = '\0';
    00670 #else
    00671   int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0);
    00672   if (len == 0) {
    00673     system_buf[0] = '\0';
    00674     return system_buf;
    00675   }
    00676
    00677   WCHAR *wide_buf = AllocaM(WCHAR, len);
    00678   MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_buf, len);
    00679
    00680   len = WideCharToMultiByte(console_cp ? CP_OEMCP : CP_ACP, 0, wide_buf, len, system_buf, (int)buflen, NULL, NULL);
    00681   if (len == 0) system_buf[0] = '\0';
    00682 #endif
    00683
    00684   return system_buf;
    00685 }
    00686
    00693 HRESULT OTTDSHGetFolderPath(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)
    00694 {
    00695   static HRESULT (WINAPI *SHGetFolderPath)(HWND, int, HANDLE, DWORD, LPTSTR) = NULL;
    00696   static bool first_time = true;
    00697
    00698   /* We only try to load the library one time; if it fails, it fails */
    00699   if (first_time) {
    00700 #if defined(UNICODE)
    00701 # define W(x) x "W"
    00702 #else
    00703 # define W(x) x "A"
    00704 #endif
    00705     /* The function lives in shell32.dll for all current Windows versions, but it first started to appear in SHFolder.dll. */
    00706     if (!LoadLibraryList((Function*)&SHGetFolderPath, "shell32.dll\0" W("SHGetFolderPath") "\0\0")) {
    00707       if (!LoadLibraryList((Function*)&SHGetFolderPath, "SHFolder.dll\0" W("SHGetFolderPath") "\0\0")) {
    00708         DEBUG(misc, 0, "Unable to load " W("SHGetFolderPath") "from either shell32.dll or SHFolder.dll");
    00709       }
    00710     }
    00711 #undef W
    00712     first_time = false;
    00713   }
    00714
    00715   if (SHGetFolderPath != NULL) return SHGetFolderPath(hwnd, csidl, hToken, dwFlags, pszPath);
    00716
    00717   /* SHGetFolderPath doesn't exist, try a more conservative approach,
    00718    * eg environment variables. This is only included for legacy modes
    00719    * MSDN says: that 'pszPath' is a "Pointer to a null-terminated string of
    00720    * length MAX_PATH which will receive the path" so let's assume that
    00721    * Windows 95 with Internet Explorer 5.0, Windows 98 with Internet Explorer 5.0,
    00722    * Windows 98 Second Edition (SE), Windows NT 4.0 with Internet Explorer 5.0,
    00723    * Windows NT 4.0 with Service Pack 4 (SP4) */
    00724   {
    00725     DWORD ret;
    00726     switch (csidl) {
    00727       case CSIDL_FONTS: // Get the system font path, eg %WINDIR%\Fonts
    00728         ret = GetEnvironmentVariable(_T("WINDIR"), pszPath, MAX_PATH);
    00729         if (ret == 0) break;
    00730         _tcsncat(pszPath, _T("\\Fonts"), MAX_PATH);
    00731
    00732         return (HRESULT)0;
    00733
    00734       case CSIDL_PERSONAL:
    00735       case CSIDL_COMMON_DOCUMENTS: {
    00736         HKEY key;
    00737         if (RegOpenKeyEx(csidl == CSIDL_PERSONAL ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, REGSTR_PATH_SPECIAL_FOLDERS, 0, KEY_READ, &key) != ERROR_SUCCESS) break;
    00738         DWORD len = MAX_PATH;
    00739         ret = RegQueryValueEx(key, csidl == CSIDL_PERSONAL ? _T("Personal") : _T("Common Documents"), NULL, NULL, (LPBYTE)pszPath, &len);
    00740         RegCloseKey(key);
    00741         if (ret == ERROR_SUCCESS) return (HRESULT)0;
    00742         break;
    00743       }
    00744
    00745       /* XXX - other types to go here when needed... */
    00746     }
    00747   }
    00748
    00749   return E_INVALIDARG;
    00750 }
    00751
    00753 const char *GetCurrentLocale(const char *)
    00754 {
    00755   char lang[9], country[9];
    00756   if (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, lang, lengthof(lang)) == 0 ||
    00757       GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, country, lengthof(country)) == 0) {
    00758     /* Unable to retrieve the locale. */
    00759     return NULL;
    00760   }
    00761   /* Format it as 'en_us'. */
    00762   static char retbuf[6] = {lang[0], lang[1], '_', country[0], country[1], 0};
    00763   return retbuf;
    00764 }
    00765
    00766 uint GetCPUCoreCount()
    00767 {
    00768   SYSTEM_INFO info;
    00769
    00770   GetSystemInfo(&info);
    00771   return info.dwNumberOfProcessors;
    00772 }