# win32.cpp

Go to the documentation of this file.

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