win32.cpp

Go to the documentation of this file.
00001 /* $Id: win32.cpp 15113 2009-01-16 16:32:48Z rubidium $ */
00002 
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "debug.h"
00008 #include "saveload/saveload.h"
00009 #include "gfx_func.h"
00010 #include "textbuf_gui.h"
00011 #include "fileio_func.h"
00012 #include "fios.h"
00013 #include "rev.h"
00014 #include <windows.h>
00015 #include <winnt.h>
00016 #include <wininet.h>
00017 #include <io.h>
00018 #include <fcntl.h>
00019 #include <shlobj.h> // SHGetFolderPath
00020 #include "variables.h"
00021 #include "win32.h"
00022 #include "core/alloc_func.hpp"
00023 #include "functions.h"
00024 #include "core/random_func.hpp"
00025 #include "core/bitmath_func.hpp"
00026 #include "string_func.h"
00027 #include "gamelog.h"
00028 #include <ctype.h>
00029 #include <tchar.h>
00030 #include <errno.h>
00031 #include <sys/types.h>
00032 #include <sys/stat.h>
00033 #if defined(_MSC_VER) && !defined(WINCE)
00034   #include <dbghelp.h>
00035   #include "strings_func.h"
00036 #endif
00037 
00038 static bool _has_console;
00039 
00040 static bool cursor_visible = true;
00041 
00042 bool MyShowCursor(bool show)
00043 {
00044   if (cursor_visible == show) return show;
00045 
00046   cursor_visible = show;
00047   ShowCursor(show);
00048 
00049   return !show;
00050 }
00051 
00055 bool LoadLibraryList(Function proc[], const char *dll)
00056 {
00057   while (*dll != '\0') {
00058     HMODULE lib;
00059     lib = LoadLibrary(MB_TO_WIDE(dll));
00060 
00061     if (lib == NULL) return false;
00062     for (;;) {
00063       FARPROC p;
00064 
00065       while (*dll++ != '\0') { /* Nothing */ }
00066       if (*dll == '\0') break;
00067 #if defined(WINCE)
00068       p = GetProcAddress(lib, MB_TO_WIDE(dll));
00069 #else
00070       p = GetProcAddress(lib, dll);
00071 #endif
00072       if (p == NULL) return false;
00073       *proc++ = (Function)p;
00074     }
00075     dll++;
00076   }
00077   return true;
00078 }
00079 
00080 #ifdef _MSC_VER
00081 static const char *_exception_string = NULL;
00082 void SetExceptionString(const char *s, ...)
00083 {
00084   va_list va;
00085   char buf[512];
00086 
00087   va_start(va, s);
00088   vsnprintf(buf, lengthof(buf), s, va);
00089   va_end(va);
00090 
00091   _exception_string = strdup(buf);
00092 }
00093 #endif
00094 
00095 void ShowOSErrorBox(const char *buf, bool system)
00096 {
00097   MyShowCursor(true);
00098   MessageBox(GetActiveWindow(), MB_TO_WIDE(buf), _T("Error!"), MB_ICONSTOP);
00099 
00100 /* if exception tracker is enabled, we crash here to let the exception handler handle it. */
00101 #if defined(WIN32_EXCEPTION_TRACKER) && !defined(_DEBUG)
00102   if (system) {
00103     _exception_string = buf;
00104     *(byte*)0 = 0;
00105   }
00106 #endif
00107 }
00108 
00109 #if defined(_MSC_VER) && !defined(WINCE)
00110 
00111 static void *_safe_esp;
00112 static char *_crash_msg;
00113 static bool _expanded;
00114 static bool _did_emerg_save;
00115 static int _ident;
00116 
00117 struct DebugFileInfo {
00118   uint32 size;
00119   uint32 crc32;
00120   SYSTEMTIME file_time;
00121 };
00122 
00123 static uint32 *_crc_table;
00124 
00125 static void MakeCRCTable(uint32 *table)
00126 {
00127   uint32 crc, poly = 0xEDB88320L;
00128   int i;
00129   int j;
00130 
00131   _crc_table = table;
00132 
00133   for (i = 0; i != 256; i++) {
00134     crc = i;
00135     for (j = 8; j != 0; j--) {
00136       crc = (crc & 1 ? (crc >> 1) ^ poly : crc >> 1);
00137     }
00138     table[i] = crc;
00139   }
00140 }
00141 
00142 static uint32 CalcCRC(byte *data, uint size, uint32 crc)
00143 {
00144   for (; size > 0; size--) {
00145     crc = ((crc >> 8) & 0x00FFFFFF) ^ _crc_table[(crc ^ *data++) & 0xFF];
00146   }
00147   return crc;
00148 }
00149 
00150 static void GetFileInfo(DebugFileInfo *dfi, const TCHAR *filename)
00151 {
00152   HANDLE file;
00153   memset(dfi, 0, sizeof(dfi));
00154 
00155   file = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
00156   if (file != INVALID_HANDLE_VALUE) {
00157     byte buffer[1024];
00158     DWORD numread;
00159     uint32 filesize = 0;
00160     FILETIME write_time;
00161     uint32 crc = (uint32)-1;
00162 
00163     for (;;) {
00164       if (ReadFile(file, buffer, sizeof(buffer), &numread, NULL) == 0 || numread == 0)
00165         break;
00166       filesize += numread;
00167       crc = CalcCRC(buffer, numread, crc);
00168     }
00169     dfi->size = filesize;
00170     dfi->crc32 = crc ^ (uint32)-1;
00171 
00172     if (GetFileTime(file, NULL, NULL, &write_time)) {
00173       FileTimeToSystemTime(&write_time, &dfi->file_time);
00174     }
00175     CloseHandle(file);
00176   }
00177 }
00178 
00179 
00180 static char *PrintModuleInfo(char *output, const char *last, HMODULE mod)
00181 {
00182   TCHAR buffer[MAX_PATH];
00183   DebugFileInfo dfi;
00184 
00185   GetModuleFileName(mod, buffer, MAX_PATH);
00186   GetFileInfo(&dfi, buffer);
00187   output += seprintf(output, last, " %-20s handle: %p size: %d crc: %.8X date: %d-%.2d-%.2d %.2d:%.2d:%.2d\r\n",
00188     WIDE_TO_MB(buffer),
00189     mod,
00190     dfi.size,
00191     dfi.crc32,
00192     dfi.file_time.wYear,
00193     dfi.file_time.wMonth,
00194     dfi.file_time.wDay,
00195     dfi.file_time.wHour,
00196     dfi.file_time.wMinute,
00197     dfi.file_time.wSecond
00198   );
00199   return output;
00200 }
00201 
00202 static char *PrintModuleList(char *output, const char *last)
00203 {
00204   BOOL (WINAPI *EnumProcessModules)(HANDLE, HMODULE*, DWORD, LPDWORD);
00205 
00206   if (LoadLibraryList((Function*)&EnumProcessModules, "psapi.dll\0EnumProcessModules\0\0")) {
00207     HMODULE modules[100];
00208     DWORD needed;
00209     BOOL res;
00210 
00211     HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
00212     if (proc != NULL) {
00213       res = EnumProcessModules(proc, modules, sizeof(modules), &needed);
00214       CloseHandle(proc);
00215       if (res) {
00216         size_t count = min(needed / sizeof(HMODULE), lengthof(modules));
00217 
00218         for (size_t i = 0; i != count; i++) output = PrintModuleInfo(output, last, modules[i]);
00219         return output;
00220       }
00221     }
00222   }
00223   output = PrintModuleInfo(output, last, NULL);
00224   return output;
00225 }
00226 
00227 static const TCHAR _crash_desc[] =
00228   _T("A serious fault condition occured in the game. The game will shut down.\n")
00229   _T("Please send the crash information and the crash.dmp file (if any) to the developers.\n")
00230   _T("This will greatly help debugging. The correct place to do this is http://bugs.openttd.org. ")
00231   _T("The information contained in the report is displayed below.\n")
00232   _T("Press \"Emergency save\" to attempt saving the game.");
00233 
00234 static const TCHAR _save_succeeded[] =
00235   _T("Emergency save succeeded.\n")
00236   _T("Be aware that critical parts of the internal game state may have become ")
00237   _T("corrupted. The saved game is not guaranteed to work.");
00238 
00239 static bool EmergencySave()
00240 {
00241   SaveOrLoad("crash.sav", SL_SAVE, BASE_DIR);
00242   return true;
00243 }
00244 
00245 /* Disable the crash-save submit code as it's not used */
00246 #if 0
00247 
00248 struct WinInetProcs {
00249   HINTERNET (WINAPI *InternetOpen)(LPCTSTR, DWORD, LPCTSTR, LPCTSTR, DWORD);
00250   HINTERNET (WINAPI *InternetConnect)(HINTERNET, LPCTSTR, INTERNET_PORT, LPCTSTR, LPCTSTR, DWORD, DWORD, DWORD);
00251   HINTERNET (WINAPI *HttpOpenRequest)(HINTERNET, LPCTSTR, LPCTSTR, LPCTSTR, LPCTSTR, LPCTSTR *, DWORD, DWORD);
00252   BOOL (WINAPI *HttpSendRequest)(HINTERNET, LPCTSTR, DWORD, LPVOID, DWORD);
00253   BOOL (WINAPI *InternetCloseHandle)(HINTERNET);
00254   BOOL (WINAPI *HttpQueryInfo)(HINTERNET, DWORD, LPVOID, LPDWORD, LPDWORD);
00255 };
00256 
00257 #define M(x) x "\0"
00258 #if defined(UNICODE)
00259 # define W(x) x "W"
00260 #else
00261 # define W(x) x "A"
00262 #endif
00263 static const char wininet_files[] =
00264   M("wininet.dll")
00265   M(W("InternetOpen"))
00266   M(W("InternetConnect"))
00267   M(W("HttpOpenRequest"))
00268   M(W("HttpSendRequest"))
00269   M("InternetCloseHandle")
00270   M(W("HttpQueryInfo"))
00271   M("");
00272 #undef W
00273 #undef M
00274 
00275 static WinInetProcs _wininet;
00276 
00277 static const TCHAR *SubmitCrashReport(HWND wnd, void *msg, size_t msglen, const TCHAR *arg)
00278 {
00279   HINTERNET inet, conn, http;
00280   const TCHAR *err = NULL;
00281   DWORD code, len;
00282   static TCHAR buf[100];
00283   TCHAR buff[100];
00284 
00285   if (_wininet.InternetOpen == NULL && !LoadLibraryList((Function*)&_wininet, wininet_files)) return _T("can't load wininet.dll");
00286 
00287   inet = _wininet.InternetOpen(_T("OTTD"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0 );
00288   if (inet == NULL) { err = _T("internetopen failed"); goto error1; }
00289 
00290   conn = _wininet.InternetConnect(inet, _T("www.openttd.org"), INTERNET_DEFAULT_HTTP_PORT, _T(""), _T(""), INTERNET_SERVICE_HTTP, 0, 0);
00291   if (conn == NULL) { err = _T("internetconnect failed"); goto error2; }
00292 
00293   _sntprintf(buff, lengthof(buff), _T("/crash.php?file=%s&ident=%d"), arg, _ident);
00294 
00295   http = _wininet.HttpOpenRequest(conn, _T("POST"), buff, NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE , 0);
00296   if (http == NULL) { err = _T("httpopenrequest failed"); goto error3; }
00297 
00298   if (!_wininet.HttpSendRequest(http, _T("Content-type: application/binary"), -1, msg, (DWORD)msglen)) { err = _T("httpsendrequest failed"); goto error4; }
00299 
00300   len = sizeof(code);
00301   if (!_wininet.HttpQueryInfo(http, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &code, &len, 0)) { err = _T("httpqueryinfo failed"); goto error4; }
00302 
00303   if (code != 200) {
00304     int l = _sntprintf(buf, lengthof(buf), _T("Server said: %d "), code);
00305     len = sizeof(buf) - l;
00306     _wininet.HttpQueryInfo(http, HTTP_QUERY_STATUS_TEXT, buf + l, &len, 0);
00307     err = buf;
00308   }
00309 
00310 error4:
00311   _wininet.InternetCloseHandle(http);
00312 error3:
00313   _wininet.InternetCloseHandle(conn);
00314 error2:
00315   _wininet.InternetCloseHandle(inet);
00316 error1:
00317   return err;
00318 }
00319 
00320 static void SubmitFile(HWND wnd, const TCHAR *file)
00321 {
00322   HANDLE h;
00323   unsigned long size;
00324   unsigned long read;
00325   void *mem;
00326 
00327   h = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
00328   if (h == NULL) return;
00329 
00330   size = GetFileSize(h, NULL);
00331   if (size > 500000) goto error1;
00332 
00333   mem = MallocT<byte>(size);
00334   if (mem == NULL) goto error1;
00335 
00336   if (!ReadFile(h, mem, size, &read, NULL) || read != size) goto error2;
00337 
00338   SubmitCrashReport(wnd, mem, size, file);
00339 
00340 error2:
00341   free(mem);
00342 error1:
00343   CloseHandle(h);
00344 }
00345 
00346 #endif /* Disabled crash-submit procedures */
00347 
00348 static const TCHAR * const _expand_texts[] = {_T("S&how report >>"), _T("&Hide report <<") };
00349 
00350 static void SetWndSize(HWND wnd, int mode)
00351 {
00352   RECT r, r2;
00353   int offs;
00354 
00355   GetWindowRect(wnd, &r);
00356 
00357   SetDlgItemText(wnd, 15, _expand_texts[mode == 1]);
00358 
00359   if (mode >= 0) {
00360     GetWindowRect(GetDlgItem(wnd, 11), &r2);
00361     offs = r2.bottom - r2.top + 10;
00362     if (!mode) offs = -offs;
00363     SetWindowPos(wnd, HWND_TOPMOST, 0, 0,
00364       r.right - r.left, r.bottom - r.top + offs, SWP_NOMOVE | SWP_NOZORDER);
00365   } else {
00366     SetWindowPos(wnd, HWND_TOPMOST,
00367       (GetSystemMetrics(SM_CXSCREEN) - (r.right - r.left)) / 2,
00368       (GetSystemMetrics(SM_CYSCREEN) - (r.bottom - r.top)) / 2,
00369       0, 0, SWP_NOSIZE);
00370   }
00371 }
00372 
00373 static bool DoEmergencySave(HWND wnd)
00374 {
00375   bool b = false;
00376 
00377   EnableWindow(GetDlgItem(wnd, 13), FALSE);
00378   _did_emerg_save = true;
00379   __try {
00380     b = EmergencySave();
00381   } __except (1) {}
00382   return b;
00383 }
00384 
00385 static INT_PTR CALLBACK CrashDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
00386 {
00387   switch (msg) {
00388     case WM_INITDIALOG: {
00389 #if defined(UNICODE)
00390       /* We need to put the crash-log in a seperate buffer because the default
00391        * buffer in MB_TO_WIDE is not large enough (512 chars) */
00392       wchar_t crash_msgW[8096];
00393 #endif
00394       SetDlgItemText(wnd, 10, _crash_desc);
00395       SetDlgItemText(wnd, 11, MB_TO_WIDE_BUFFER(_crash_msg, crash_msgW, lengthof(crash_msgW)));
00396       SendDlgItemMessage(wnd, 11, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), FALSE);
00397       SetWndSize(wnd, -1);
00398     } return TRUE;
00399     case WM_COMMAND:
00400       switch (wParam) {
00401         case 12: /* Close */
00402           ExitProcess(0);
00403         case 13: /* Emergency save */
00404           if (DoEmergencySave(wnd)) {
00405             MessageBox(wnd, _save_succeeded, _T("Save successful"), MB_ICONINFORMATION);
00406           } else {
00407             MessageBox(wnd, _T("Save failed"), _T("Save failed"), MB_ICONINFORMATION);
00408           }
00409           break;
00410 /* Disable the crash-save submit code as it's not used */
00411 #if 0
00412         case 14: { /* Submit crash report */
00413           const TCHAR *s;
00414 
00415           SetCursor(LoadCursor(NULL, IDC_WAIT));
00416 
00417           s = SubmitCrashReport(wnd, _crash_msg, strlen(_crash_msg), _T(""));
00418           if (s != NULL) {
00419             MessageBox(wnd, s, _T("Error"), MB_ICONSTOP);
00420             break;
00421           }
00422 
00423           // try to submit emergency savegame
00424           if (_did_emerg_save || DoEmergencySave(wnd)) SubmitFile(wnd, _T("crash.sav"));
00425 
00426           // try to submit the autosaved game
00427           if (_opt.autosave) {
00428             TCHAR buf[40];
00429             _sntprintf(buf, lengthof(buf), _T("autosave%d.sav"), (_autosave_ctr - 1) & 3);
00430             SubmitFile(wnd, buf);
00431           }
00432           EnableWindow(GetDlgItem(wnd, 14), FALSE);
00433           SetCursor(LoadCursor(NULL, IDC_ARROW));
00434           MessageBox(wnd, _T("Crash report submitted. Thank you."), _T("Crash Report"), MB_ICONINFORMATION);
00435         } break;
00436 #endif /* Disabled crash-submit procedures */
00437         case 15: /* Expand window to show crash-message */
00438           _expanded ^= 1;
00439           SetWndSize(wnd, _expanded);
00440           break;
00441       }
00442       return TRUE;
00443     case WM_CLOSE: ExitProcess(0);
00444   }
00445 
00446   return FALSE;
00447 }
00448 
00449 static void Handler2()
00450 {
00451   ShowCursor(TRUE);
00452   ShowWindow(GetActiveWindow(), FALSE);
00453   DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(100), NULL, CrashDialogFunc);
00454 }
00455 
00456 extern bool CloseConsoleLogIfActive();
00457 
00458 static HANDLE _file_crash_log;
00459 
00460 static void GamelogPrintCrashLogProc(const char *s)
00461 {
00462   DWORD num_written;
00463   WriteFile(_file_crash_log, s, (DWORD)strlen(s), &num_written, NULL);
00464   WriteFile(_file_crash_log, "\r\n", (DWORD)strlen("\r\n"), &num_written, NULL);
00465 }
00466 
00468 static const int EXCEPTION_OUTPUT_SIZE = 8192;
00469 
00470 static LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS *ep)
00471 {
00472   char *output;
00473   static bool had_exception = false;
00474 
00475   if (had_exception) ExitProcess(0);
00476   had_exception = true;
00477 
00478   _ident = GetTickCount(); // something pretty unique
00479 
00480   MakeCRCTable(AllocaM(uint32, 256));
00481   _crash_msg = output = (char*)LocalAlloc(LMEM_FIXED, EXCEPTION_OUTPUT_SIZE);
00482   const char *last = output + EXCEPTION_OUTPUT_SIZE - 1;
00483 
00484   {
00485     SYSTEMTIME time;
00486     GetLocalTime(&time);
00487     output += seprintf(output, last,
00488       "*** OpenTTD Crash Report ***\r\n"
00489       "Date: %d-%.2d-%.2d %.2d:%.2d:%.2d\r\n"
00490       "Build: %s built on " __DATE__ " " __TIME__ "\r\n",
00491       time.wYear,
00492       time.wMonth,
00493       time.wDay,
00494       time.wHour,
00495       time.wMinute,
00496       time.wSecond,
00497       _openttd_revision
00498     );
00499   }
00500 
00501   if (_exception_string)
00502     output += seprintf(output, last, "Reason: %s\r\n", _exception_string);
00503 
00504   output += seprintf(output, last, "Language: %s\r\n", _dynlang.curr_file);
00505 
00506 #ifdef _M_AMD64
00507   output += seprintf(output, last, "Exception %.8X at %.16IX\r\n"
00508     "Registers:\r\n"
00509     "RAX: %.16llX RBX: %.16llX RCX: %.16llX RDX: %.16llX\r\n"
00510     "RSI: %.16llX RDI: %.16llX RBP: %.16llX RSP: %.16llX\r\n"
00511     "R8:  %.16llX R9:  %.16llX R10: %.16llX R11: %.16llX\r\n"
00512     "R12: %.16llX R13: %.16llX R14: %.16llX R15: %.16llX\r\n"
00513     "RIP: %.16llX EFLAGS: %.8X\r\n"
00514     "\r\nBytes at CS:RIP:\r\n",
00515     ep->ExceptionRecord->ExceptionCode,
00516     ep->ExceptionRecord->ExceptionAddress,
00517     ep->ContextRecord->Rax,
00518     ep->ContextRecord->Rbx,
00519     ep->ContextRecord->Rcx,
00520     ep->ContextRecord->Rdx,
00521     ep->ContextRecord->Rsi,
00522     ep->ContextRecord->Rdi,
00523     ep->ContextRecord->Rbp,
00524     ep->ContextRecord->Rsp,
00525     ep->ContextRecord->R8,
00526     ep->ContextRecord->R9,
00527     ep->ContextRecord->R10,
00528     ep->ContextRecord->R11,
00529     ep->ContextRecord->R12,
00530     ep->ContextRecord->R13,
00531     ep->ContextRecord->R14,
00532     ep->ContextRecord->R15,
00533     ep->ContextRecord->Rip,
00534     ep->ContextRecord->EFlags
00535   );
00536 #else
00537   output += seprintf(output, last, "Exception %.8X at %.8p\r\n"
00538     "Registers:\r\n"
00539     " EAX: %.8X EBX: %.8X ECX: %.8X EDX: %.8X\r\n"
00540     " ESI: %.8X EDI: %.8X EBP: %.8X ESP: %.8X\r\n"
00541     " EIP: %.8X EFLAGS: %.8X\r\n"
00542     "\r\nBytes at CS:EIP:\r\n",
00543     ep->ExceptionRecord->ExceptionCode,
00544     ep->ExceptionRecord->ExceptionAddress,
00545     ep->ContextRecord->Eax,
00546     ep->ContextRecord->Ebx,
00547     ep->ContextRecord->Ecx,
00548     ep->ContextRecord->Edx,
00549     ep->ContextRecord->Esi,
00550     ep->ContextRecord->Edi,
00551     ep->ContextRecord->Ebp,
00552     ep->ContextRecord->Esp,
00553     ep->ContextRecord->Eip,
00554     ep->ContextRecord->EFlags
00555   );
00556 #endif
00557 
00558   {
00559 #ifdef _M_AMD64
00560     byte *b = (byte*)ep->ContextRecord->Rip;
00561 #else
00562     byte *b = (byte*)ep->ContextRecord->Eip;
00563 #endif
00564     int i;
00565     for (i = 0; i != 24; i++) {
00566       if (IsBadReadPtr(b, 1)) {
00567         output += seprintf(output, last, " ??"); // OCR: WAS: , 0);
00568       } else {
00569         output += seprintf(output, last, " %.2X", *b);
00570       }
00571       b++;
00572     }
00573     output += seprintf(output, last,
00574       "\r\n"
00575       "\r\nStack trace: \r\n"
00576     );
00577   }
00578 
00579   {
00580     int i, j;
00581 #ifdef _M_AMD64
00582     uint32 *b = (uint32*)ep->ContextRecord->Rsp;
00583 #else
00584     uint32 *b = (uint32*)ep->ContextRecord->Esp;
00585 #endif
00586     for (j = 0; j != 24; j++) {
00587       for (i = 0; i != 8; i++) {
00588         if (IsBadReadPtr(b, sizeof(uint32))) {
00589           output += seprintf(output, last, " ????????"); //OCR: WAS - , 0);
00590         } else {
00591           output += seprintf(output, last, " %.8X", *b);
00592         }
00593         b++;
00594       }
00595       output += seprintf(output, last, "\r\n");
00596     }
00597   }
00598 
00599   output += seprintf(output, last, "\r\nModule information:\r\n");
00600   output = PrintModuleList(output, last);
00601 
00602   {
00603     _OSVERSIONINFOA os;
00604     os.dwOSVersionInfoSize = sizeof(os);
00605     GetVersionExA(&os);
00606     output += seprintf(output, last, "\r\nSystem information:\r\n"
00607       " Windows version %d.%d %d %s\r\n\r\n",
00608       os.dwMajorVersion, os.dwMinorVersion, os.dwBuildNumber, os.szCSDVersion);
00609   }
00610 
00611   _file_crash_log = CreateFile(_T("crash.log"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
00612 
00613   if (_file_crash_log != INVALID_HANDLE_VALUE) {
00614     DWORD num_written;
00615     WriteFile(_file_crash_log, _crash_msg, output - _crash_msg, &num_written, NULL);
00616   }
00617 
00618 #if !defined(_DEBUG)
00619   HMODULE dbghelp = LoadLibrary(_T("dbghelp.dll"));
00620   if (dbghelp != NULL) {
00621     typedef BOOL (WINAPI *MiniDumpWriteDump_t)(HANDLE, DWORD, HANDLE,
00622         MINIDUMP_TYPE,
00623         CONST PMINIDUMP_EXCEPTION_INFORMATION,
00624         CONST PMINIDUMP_USER_STREAM_INFORMATION,
00625         CONST PMINIDUMP_CALLBACK_INFORMATION);
00626     MiniDumpWriteDump_t funcMiniDumpWriteDump = (MiniDumpWriteDump_t)GetProcAddress(dbghelp, "MiniDumpWriteDump");
00627     if (funcMiniDumpWriteDump != NULL) {
00628       HANDLE file  = CreateFile(_T("crash.dmp"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
00629       HANDLE proc  = GetCurrentProcess();
00630       DWORD procid = GetCurrentProcessId();
00631       MINIDUMP_EXCEPTION_INFORMATION mdei;
00632       MINIDUMP_USER_STREAM userstream;
00633       MINIDUMP_USER_STREAM_INFORMATION musi;
00634       char msg[] = "****** Built on " __DATE__ " " __TIME__ ". ******";
00635 
00636       userstream.Type        = LastReservedStream + 1;
00637       userstream.Buffer      = msg;
00638       userstream.BufferSize  = sizeof(msg);
00639 
00640       musi.UserStreamCount   = 1;
00641       musi.UserStreamArray   = &userstream;
00642 
00643       mdei.ThreadId = GetCurrentThreadId();
00644       mdei.ExceptionPointers  = ep;
00645       mdei.ClientPointers     = false;
00646 
00647       funcMiniDumpWriteDump(proc, procid, file, MiniDumpWithDataSegs, &mdei, &musi, NULL);
00648     }
00649     FreeLibrary(dbghelp);
00650   }
00651 #endif
00652 
00653   if (_file_crash_log != INVALID_HANDLE_VALUE) {
00654     GamelogPrint(&GamelogPrintCrashLogProc);
00655     CloseHandle(_file_crash_log);
00656   }
00657 
00658   /* Close any possible log files */
00659   CloseConsoleLogIfActive();
00660 
00661   if (_safe_esp) {
00662 #ifdef _M_AMD64
00663     ep->ContextRecord->Rip = (DWORD64)Handler2;
00664     ep->ContextRecord->Rsp = (DWORD64)_safe_esp;
00665 #else
00666     ep->ContextRecord->Eip = (DWORD)Handler2;
00667     ep->ContextRecord->Esp = (DWORD)_safe_esp;
00668 #endif
00669     return EXCEPTION_CONTINUE_EXECUTION;
00670   }
00671 
00672 
00673   return EXCEPTION_EXECUTE_HANDLER;
00674 }
00675 
00676 #ifdef _M_AMD64
00677 extern "C" void *_get_safe_esp();
00678 #endif
00679 
00680 static void Win32InitializeExceptions()
00681 {
00682 #ifdef _M_AMD64
00683   _safe_esp = _get_safe_esp();
00684 #else
00685   _asm {
00686     mov _safe_esp, esp
00687   }
00688 #endif
00689 
00690   SetUnhandledExceptionFilter(ExceptionHandler);
00691 }
00692 #endif /* _MSC_VER */
00693 
00694 /* Code below for windows version of opendir/readdir/closedir copied and
00695  * modified from Jan Wassenberg's GPL implementation posted over at
00696  * http://www.gamedev.net/community/forums/topic.asp?topic_id=364584&whichpage=1&#2398903 */
00697 
00698 /* suballocator - satisfies most requests with a reusable static instance.
00699  * this avoids hundreds of alloc/free which would fragment the heap.
00700  * To guarantee concurrency, we fall back to malloc if the instance is
00701  * already in use (it's important to avoid suprises since this is such a
00702  * low-level routine). */
00703 static DIR _global_dir;
00704 static LONG _global_dir_is_in_use = false;
00705 
00706 static inline DIR *dir_calloc()
00707 {
00708   DIR *d;
00709 
00710   if (InterlockedExchange(&_global_dir_is_in_use, true) == (LONG)true) {
00711     d = CallocT<DIR>(1);
00712   } else {
00713     d = &_global_dir;
00714     memset(d, 0, sizeof(*d));
00715   }
00716   return d;
00717 }
00718 
00719 static inline void dir_free(DIR *d)
00720 {
00721   if (d == &_global_dir) {
00722     _global_dir_is_in_use = (LONG)false;
00723   } else {
00724     free(d);
00725   }
00726 }
00727 
00728 DIR *opendir(const TCHAR *path)
00729 {
00730   DIR *d;
00731   UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS); // disable 'no-disk' message box
00732   DWORD fa = GetFileAttributes(path);
00733 
00734   if ((fa != INVALID_FILE_ATTRIBUTES) && (fa & FILE_ATTRIBUTE_DIRECTORY)) {
00735     d = dir_calloc();
00736     if (d != NULL) {
00737       TCHAR search_path[MAX_PATH];
00738       bool slash = path[_tcslen(path) - 1] == '\\';
00739 
00740       /* build search path for FindFirstFile, try not to append additional slashes
00741        * as it throws Win9x off its groove for root directories */
00742       _sntprintf(search_path, lengthof(search_path), _T("%s%s*"), path, slash ? _T("") : _T("\\"));
00743       *lastof(search_path) = '\0';
00744       d->hFind = FindFirstFile(search_path, &d->fd);
00745 
00746       if (d->hFind != INVALID_HANDLE_VALUE ||
00747           GetLastError() == ERROR_NO_MORE_FILES) { // the directory is empty
00748         d->ent.dir = d;
00749         d->at_first_entry = true;
00750       } else {
00751         dir_free(d);
00752         d = NULL;
00753       }
00754     } else {
00755       errno = ENOMEM;
00756     }
00757   } else {
00758     /* path not found or not a directory */
00759     d = NULL;
00760     errno = ENOENT;
00761   }
00762 
00763   SetErrorMode(sem); // restore previous setting
00764   return d;
00765 }
00766 
00767 struct dirent *readdir(DIR *d)
00768 {
00769   DWORD prev_err = GetLastError(); // avoid polluting last error
00770 
00771   if (d->at_first_entry) {
00772     /* the directory was empty when opened */
00773     if (d->hFind == INVALID_HANDLE_VALUE) return NULL;
00774     d->at_first_entry = false;
00775   } else if (!FindNextFile(d->hFind, &d->fd)) { // determine cause and bail
00776     if (GetLastError() == ERROR_NO_MORE_FILES) SetLastError(prev_err);
00777     return NULL;
00778   }
00779 
00780   /* This entry has passed all checks; return information about it.
00781    * (note: d_name is a pointer; see struct dirent definition) */
00782   d->ent.d_name = d->fd.cFileName;
00783   return &d->ent;
00784 }
00785 
00786 int closedir(DIR *d)
00787 {
00788   FindClose(d->hFind);
00789   dir_free(d);
00790   return 0;
00791 }
00792 
00793 bool FiosIsRoot(const char *file)
00794 {
00795   return file[3] == '\0'; // C:\...
00796 }
00797 
00798 void FiosGetDrives()
00799 {
00800 #if defined(WINCE)
00801   /* WinCE only knows one drive: / */
00802   FiosItem *fios = _fios_items.Append();
00803   fios->type = FIOS_TYPE_DRIVE;
00804   fios->mtime = 0;
00805   snprintf(fios->name, lengthof(fios->name), PATHSEP "");
00806   strecpy(fios->title, fios->name, lastof(fios->title));
00807 #else
00808   TCHAR drives[256];
00809   const TCHAR *s;
00810 
00811   GetLogicalDriveStrings(lengthof(drives), drives);
00812   for (s = drives; *s != '\0';) {
00813     FiosItem *fios = _fios_items.Append();
00814     fios->type = FIOS_TYPE_DRIVE;
00815     fios->mtime = 0;
00816     snprintf(fios->name, lengthof(fios->name),  "%c:", s[0] & 0xFF);
00817     strecpy(fios->title, fios->name, lastof(fios->title));
00818     while (*s++ != '\0') { /* Nothing */ }
00819   }
00820 #endif
00821 }
00822 
00823 bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb)
00824 {
00825   /* hectonanoseconds between Windows and POSIX epoch */
00826   static const int64 posix_epoch_hns = 0x019DB1DED53E8000LL;
00827   const WIN32_FIND_DATA *fd = &ent->dir->fd;
00828 
00829   sb->st_size  = ((uint64) fd->nFileSizeHigh << 32) + fd->nFileSizeLow;
00830   /* UTC FILETIME to seconds-since-1970 UTC
00831    * we just have to subtract POSIX epoch and scale down to units of seconds.
00832    * http://www.gamedev.net/community/forums/topic.asp?topic_id=294070&whichpage=1&#1860504
00833    * XXX - not entirely correct, since filetimes on FAT aren't UTC but local,
00834    * this won't entirely be correct, but we use the time only for comparsion. */
00835   sb->st_mtime = (time_t)((*(uint64*)&fd->ftLastWriteTime - posix_epoch_hns) / 1E7);
00836   sb->st_mode  = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)? S_IFDIR : S_IFREG;
00837 
00838   return true;
00839 }
00840 
00841 bool FiosIsHiddenFile(const struct dirent *ent)
00842 {
00843   return (ent->dir->fd.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != 0;
00844 }
00845 
00846 bool FiosGetDiskFreeSpace(const char *path, uint64 *tot)
00847 {
00848   UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS);  // disable 'no-disk' message box
00849   bool retval = false;
00850   TCHAR root[4];
00851   DWORD spc, bps, nfc, tnc;
00852 
00853   _sntprintf(root, lengthof(root), _T("%c:") _T(PATHSEP), path[0]);
00854   if (tot != NULL && GetDiskFreeSpace(root, &spc, &bps, &nfc, &tnc)) {
00855     *tot = ((spc * bps) * (uint64)nfc);
00856     retval = true;
00857   }
00858 
00859   SetErrorMode(sem); // reset previous setting
00860   return retval;
00861 }
00862 
00863 static int ParseCommandLine(char *line, char **argv, int max_argc)
00864 {
00865   int n = 0;
00866 
00867   do {
00868     /* skip whitespace */
00869     while (*line == ' ' || *line == '\t') line++;
00870 
00871     /* end? */
00872     if (*line == '\0') break;
00873 
00874     /* special handling when quoted */
00875     if (*line == '"') {
00876       argv[n++] = ++line;
00877       while (*line != '"') {
00878         if (*line == '\0') return n;
00879         line++;
00880       }
00881     } else {
00882       argv[n++] = line;
00883       while (*line != ' ' && *line != '\t') {
00884         if (*line == '\0') return n;
00885         line++;
00886       }
00887     }
00888     *line++ = '\0';
00889   } while (n != max_argc);
00890 
00891   return n;
00892 }
00893 
00894 void CreateConsole()
00895 {
00896 #if defined(WINCE)
00897   /* WinCE doesn't support console stuff */
00898 #else
00899   HANDLE hand;
00900   CONSOLE_SCREEN_BUFFER_INFO coninfo;
00901 
00902   if (_has_console) return;
00903   _has_console = true;
00904 
00905   AllocConsole();
00906 
00907   hand = GetStdHandle(STD_OUTPUT_HANDLE);
00908   GetConsoleScreenBufferInfo(hand, &coninfo);
00909   coninfo.dwSize.Y = 500;
00910   SetConsoleScreenBufferSize(hand, coninfo.dwSize);
00911 
00912   /* redirect unbuffered STDIN, STDOUT, STDERR to the console */
00913 #if !defined(__CYGWIN__)
00914   *stdout = *_fdopen( _open_osfhandle((intptr_t)hand, _O_TEXT), "w" );
00915   *stdin = *_fdopen(_open_osfhandle((intptr_t)GetStdHandle(STD_INPUT_HANDLE), _O_TEXT), "r" );
00916   *stderr = *_fdopen(_open_osfhandle((intptr_t)GetStdHandle(STD_ERROR_HANDLE), _O_TEXT), "w" );
00917 #else
00918   /* open_osfhandle is not in cygwin */
00919   *stdout = *fdopen(1, "w" );
00920   *stdin = *fdopen(0, "r" );
00921   *stderr = *fdopen(2, "w" );
00922 #endif
00923 
00924   setvbuf(stdin, NULL, _IONBF, 0);
00925   setvbuf(stdout, NULL, _IONBF, 0);
00926   setvbuf(stderr, NULL, _IONBF, 0);
00927 #endif
00928 }
00929 
00930 void ShowInfo(const char *str)
00931 {
00932   if (_has_console) {
00933     fprintf(stderr, "%s\n", str);
00934   } else {
00935     bool old;
00936 #if defined(UNICODE)
00937     /* We need to put the text in a seperate buffer because the default
00938      * buffer in MB_TO_WIDE might not be large enough (512 chars) */
00939     wchar_t help_msgW[8192];
00940 #endif
00941     ReleaseCapture();
00942     _left_button_clicked = _left_button_down = false;
00943 
00944     old = MyShowCursor(true);
00945     if (MessageBox(GetActiveWindow(), MB_TO_WIDE_BUFFER(str, help_msgW, lengthof(help_msgW)), _T("OpenTTD"), MB_ICONINFORMATION | MB_OKCANCEL) == IDCANCEL) {
00946       CreateConsole();
00947     }
00948     MyShowCursor(old);
00949   }
00950 }
00951 
00952 #if defined(WINCE)
00953 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
00954 #else
00955 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
00956 #endif
00957 {
00958   int argc;
00959   char *argv[64]; // max 64 command line arguments
00960   char *cmdline;
00961 
00962 #if !defined(UNICODE)
00963   _codepage = GetACP(); // get system codepage as some kind of a default
00964 #endif /* UNICODE */
00965 
00966 #if defined(UNICODE)
00967 
00968 #if !defined(WINCE)
00969   /* Check if a win9x user started the win32 version */
00970   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.");
00971 #endif
00972 
00973   /* For UNICODE we need to convert the commandline to char* _AND_
00974    * save it because argv[] points into this buffer and thus needs to
00975    * be available between subsequent calls to FS2OTTD() */
00976   char cmdlinebuf[MAX_PATH];
00977 #endif /* UNICODE */
00978 
00979   cmdline = WIDE_TO_MB_BUFFER(GetCommandLine(), cmdlinebuf, lengthof(cmdlinebuf));
00980 
00981 #if defined(_DEBUG)
00982   CreateConsole();
00983 #endif
00984 
00985 #if !defined(WINCE)
00986   _set_error_mode(_OUT_TO_MSGBOX); // force assertion output to messagebox
00987 #endif
00988 
00989   /* setup random seed to something quite random */
00990   SetRandomSeed(GetTickCount());
00991 
00992   argc = ParseCommandLine(cmdline, argv, lengthof(argv));
00993 
00994 #if defined(WIN32_EXCEPTION_TRACKER)
00995   Win32InitializeExceptions();
00996 #endif
00997 
00998 #if defined(WIN32_EXCEPTION_TRACKER_DEBUG)
00999   _try {
01000     LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS *ep);
01001 #endif
01002     ttd_main(argc, argv);
01003 
01004 #if defined(WIN32_EXCEPTION_TRACKER_DEBUG)
01005   } _except (ExceptionHandler(_exception_info())) {}
01006 #endif
01007 
01008   return 0;
01009 }
01010 
01011 #if defined(WINCE)
01012 void GetCurrentDirectoryW(int length, wchar_t *path)
01013 {
01014   /* Get the name of this module */
01015   GetModuleFileName(NULL, path, length);
01016 
01017   /* Remove the executable name, this we call CurrentDir */
01018   wchar_t *pDest = wcsrchr(path, '\\');
01019   if (pDest != NULL) {
01020     int result = pDest - path + 1;
01021     path[result] = '\0';
01022   }
01023 }
01024 #endif
01025 
01026 char *getcwd(char *buf, size_t size)
01027 {
01028 #if defined(WINCE)
01029   TCHAR path[MAX_PATH];
01030   GetModuleFileName(NULL, path, MAX_PATH);
01031   convert_from_fs(path, buf, size);
01032   /* GetModuleFileName returns dir with file, so remove everything behind latest '\\' */
01033   char *p = strrchr(buf, '\\');
01034   if (p != NULL) *p = '\0';
01035 #elif defined(UNICODE)
01036   TCHAR path[MAX_PATH];
01037   GetCurrentDirectory(MAX_PATH - 1, path);
01038   convert_from_fs(path, buf, size);
01039 #else
01040   GetCurrentDirectory(size, buf);
01041 #endif
01042   return buf;
01043 }
01044 
01045 
01046 void DetermineBasePaths(const char *exe)
01047 {
01048   char tmp[MAX_PATH];
01049   TCHAR path[MAX_PATH];
01050 #ifdef WITH_PERSONAL_DIR
01051   SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, path);
01052   strecpy(tmp, WIDE_TO_MB_BUFFER(path, tmp, lengthof(tmp)), lastof(tmp));
01053   AppendPathSeparator(tmp, MAX_PATH);
01054   ttd_strlcat(tmp, PERSONAL_DIR, MAX_PATH);
01055   AppendPathSeparator(tmp, MAX_PATH);
01056   _searchpaths[SP_PERSONAL_DIR] = strdup(tmp);
01057 
01058   SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, SHGFP_TYPE_CURRENT, path);
01059   strecpy(tmp, WIDE_TO_MB_BUFFER(path, tmp, lengthof(tmp)), lastof(tmp));
01060   AppendPathSeparator(tmp, MAX_PATH);
01061   ttd_strlcat(tmp, PERSONAL_DIR, MAX_PATH);
01062   AppendPathSeparator(tmp, MAX_PATH);
01063   _searchpaths[SP_SHARED_DIR] = strdup(tmp);
01064 #else
01065   _searchpaths[SP_PERSONAL_DIR] = NULL;
01066   _searchpaths[SP_SHARED_DIR]   = NULL;
01067 #endif
01068 
01069   /* Get the path to working directory of OpenTTD */
01070   getcwd(tmp, lengthof(tmp));
01071   AppendPathSeparator(tmp, MAX_PATH);
01072   _searchpaths[SP_WORKING_DIR] = strdup(tmp);
01073 
01074   if (!GetModuleFileName(NULL, path, lengthof(path))) {
01075     DEBUG(misc, 0, "GetModuleFileName failed (%d)\n", GetLastError());
01076     _searchpaths[SP_BINARY_DIR] = NULL;
01077   } else {
01078     TCHAR exec_dir[MAX_PATH];
01079     _tcsncpy(path, MB_TO_WIDE_BUFFER(exe, path, lengthof(path)), lengthof(path));
01080     if (!GetFullPathName(path, lengthof(exec_dir), exec_dir, NULL)) {
01081       DEBUG(misc, 0, "GetFullPathName failed (%d)\n", GetLastError());
01082       _searchpaths[SP_BINARY_DIR] = NULL;
01083     } else {
01084       strecpy(tmp, WIDE_TO_MB_BUFFER(exec_dir, tmp, lengthof(tmp)), lastof(tmp));
01085       char *s = strrchr(tmp, PATHSEPCHAR);
01086       *(s + 1) = '\0';
01087       _searchpaths[SP_BINARY_DIR] = strdup(tmp);
01088     }
01089   }
01090 
01091   _searchpaths[SP_INSTALLATION_DIR]       = NULL;
01092   _searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
01093 }
01094 
01102 bool InsertTextBufferClipboard(Textbuf *tb)
01103 {
01104   HGLOBAL cbuf;
01105   char utf8_buf[512];
01106   const char *ptr;
01107 
01108   WChar c;
01109   uint16 width, length;
01110 
01111   if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
01112     OpenClipboard(NULL);
01113     cbuf = GetClipboardData(CF_UNICODETEXT);
01114 
01115     ptr = (const char*)GlobalLock(cbuf);
01116     const char *ret = convert_from_fs((wchar_t*)ptr, utf8_buf, lengthof(utf8_buf));
01117     GlobalUnlock(cbuf);
01118     CloseClipboard();
01119 
01120     if (*ret == '\0') return false;
01121 #if !defined(UNICODE)
01122   } else if (IsClipboardFormatAvailable(CF_TEXT)) {
01123     OpenClipboard(NULL);
01124     cbuf = GetClipboardData(CF_TEXT);
01125 
01126     ptr = (const char*)GlobalLock(cbuf);
01127     strecpy(utf8_buf, FS2OTTD(ptr), lastof(utf8_buf));
01128 
01129     GlobalUnlock(cbuf);
01130     CloseClipboard();
01131 #endif /* UNICODE */
01132   } else {
01133     return false;
01134   }
01135 
01136   width = length = 0;
01137 
01138   for (ptr = utf8_buf; (c = Utf8Consume(&ptr)) != '\0';) {
01139     if (!IsPrintable(c)) break;
01140 
01141     byte len = Utf8CharLen(c);
01142     if (tb->size + length + len > tb->maxsize) break;
01143 
01144     byte charwidth = GetCharacterWidth(FS_NORMAL, c);
01145     if (tb->maxwidth != 0 && width + tb->width + charwidth > tb->maxwidth) break;
01146 
01147     width += charwidth;
01148     length += len;
01149   }
01150 
01151   if (length == 0) return false;
01152 
01153   memmove(tb->buf + tb->caretpos + length, tb->buf + tb->caretpos, tb->size - tb->caretpos);
01154   memcpy(tb->buf + tb->caretpos, utf8_buf, length);
01155   tb->width += width;
01156   tb->caretxoffs += width;
01157 
01158   tb->size += length;
01159   tb->caretpos += length;
01160   assert(tb->size <= tb->maxsize);
01161   tb->buf[tb->size - 1] = '\0'; // terminating zero
01162 
01163   return true;
01164 }
01165 
01166 
01167 void CSleep(int milliseconds)
01168 {
01169   Sleep(milliseconds);
01170 }
01171 
01172 
01175 int64 GetTS()
01176 {
01177   static double freq;
01178   __int64 value;
01179   if (!freq) {
01180     QueryPerformanceFrequency((LARGE_INTEGER*)&value);
01181     freq = (double)1000000 / value;
01182   }
01183   QueryPerformanceCounter((LARGE_INTEGER*)&value);
01184   return (__int64)(value * freq);
01185 }
01186 
01187 
01200 const char *FS2OTTD(const TCHAR *name)
01201 {
01202   static char utf8_buf[512];
01203 #if defined(UNICODE)
01204   return convert_from_fs(name, utf8_buf, lengthof(utf8_buf));
01205 #else
01206   char *s = utf8_buf;
01207 
01208   for (; *name != '\0'; name++) {
01209     wchar_t w;
01210     int len = MultiByteToWideChar(_codepage, 0, name, 1, &w, 1);
01211     if (len != 1) {
01212       DEBUG(misc, 0, "[utf8] M2W error converting '%c'. Errno %d", *name, GetLastError());
01213       continue;
01214     }
01215 
01216     if (s + Utf8CharLen(w) >= lastof(utf8_buf)) break;
01217     s += Utf8Encode(s, w);
01218   }
01219 
01220   *s = '\0';
01221   return utf8_buf;
01222 #endif /* UNICODE */
01223 }
01224 
01237 const TCHAR *OTTD2FS(const char *name)
01238 {
01239   static TCHAR system_buf[512];
01240 #if defined(UNICODE)
01241   return convert_to_fs(name, system_buf, lengthof(system_buf));
01242 #else
01243   char *s = system_buf;
01244 
01245   for (WChar c; (c = Utf8Consume(&name)) != '\0';) {
01246     if (s >= lastof(system_buf)) break;
01247 
01248     char mb;
01249     int len = WideCharToMultiByte(_codepage, 0, (wchar_t*)&c, 1, &mb, 1, NULL, NULL);
01250     if (len != 1) {
01251       DEBUG(misc, 0, "[utf8] W2M error converting '0x%X'. Errno %d", c, GetLastError());
01252       continue;
01253     }
01254 
01255     *s++ = mb;
01256   }
01257 
01258   *s = '\0';
01259   return system_buf;
01260 #endif /* UNICODE */
01261 }
01262 
01263 
01270 char *convert_from_fs(const wchar_t *name, char *utf8_buf, size_t buflen)
01271 {
01272   int len = WideCharToMultiByte(CP_UTF8, 0, name, -1, utf8_buf, (int)buflen, NULL, NULL);
01273   if (len == 0) {
01274     DEBUG(misc, 0, "[utf8] W2M error converting wide-string. Errno %d", GetLastError());
01275     utf8_buf[0] = '\0';
01276   }
01277 
01278   return utf8_buf;
01279 }
01280 
01281 
01289 wchar_t *convert_to_fs(const char *name, wchar_t *utf16_buf, size_t buflen)
01290 {
01291   int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, utf16_buf, (int)buflen);
01292   if (len == 0) {
01293     DEBUG(misc, 0, "[utf8] M2W error converting '%s'. Errno %d", name, GetLastError());
01294     utf16_buf[0] = '\0';
01295   }
01296 
01297   return utf16_buf;
01298 }
01299 
01304 HRESULT OTTDSHGetFolderPath(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)
01305 {
01306   static HRESULT (WINAPI *SHGetFolderPath)(HWND, int, HANDLE, DWORD, LPTSTR) = NULL;
01307   static bool first_time = true;
01308 
01309   /* We only try to load the library one time; if it fails, it fails */
01310   if (first_time) {
01311 #if defined(UNICODE)
01312 # define W(x) x "W"
01313 #else
01314 # define W(x) x "A"
01315 #endif
01316     if (!LoadLibraryList((Function*)&SHGetFolderPath, "SHFolder.dll\0" W("SHGetFolderPath") "\0\0")) {
01317       DEBUG(misc, 0, "Unable to load " W("SHGetFolderPath") "from SHFolder.dll");
01318     }
01319 #undef W
01320     first_time = false;
01321   }
01322 
01323   if (SHGetFolderPath != NULL) return SHGetFolderPath(hwnd, csidl, hToken, dwFlags, pszPath);
01324 
01325   /* SHGetFolderPath doesn't exist, try a more conservative approach,
01326    * eg environment variables. This is only included for legacy modes
01327    * MSDN says: that 'pszPath' is a "Pointer to a null-terminated string of
01328    * length MAX_PATH which will receive the path" so let's assume that
01329    * Windows 95 with Internet Explorer 5.0, Windows 98 with Internet Explorer 5.0,
01330    * Windows 98 Second Edition (SE), Windows NT 4.0 with Internet Explorer 5.0,
01331    * Windows NT 4.0 with Service Pack 4 (SP4) */
01332   {
01333     DWORD ret;
01334     switch (csidl) {
01335       case CSIDL_FONTS: /* Get the system font path, eg %WINDIR%\Fonts */
01336         ret = GetEnvironmentVariable(_T("WINDIR"), pszPath, MAX_PATH);
01337         if (ret == 0) break;
01338         _tcsncat(pszPath, _T("\\Fonts"), MAX_PATH);
01339 
01340         return (HRESULT)0;
01341         break;
01342       /* XXX - other types to go here when needed... */
01343     }
01344   }
01345 
01346   return E_INVALIDARG;
01347 }
01348 
01350 const char *GetCurrentLocale(const char *)
01351 {
01352   char lang[9], country[9];
01353   if (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, lang, lengthof(lang)) == 0 ||
01354       GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, country, lengthof(country)) == 0) {
01355     /* Unable to retrieve the locale. */
01356     return NULL;
01357   }
01358   /* Format it as 'en_us'. */
01359   static char retbuf[6] = {lang[0], lang[1], '_', country[0], country[1], 0};
01360   return retbuf;
01361 }

Generated on Mon Feb 16 23:12:12 2009 for openttd by  doxygen 1.5.6