# win32.cpp

Go to the documentation of this file.

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