win32.cpp

Go to the documentation of this file.
00001 /* $Id: win32.cpp 16309 2009-05-15 10:17: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 
00940 void ShowInfo(const char *str)
00941 {
00942   if (_has_console) {
00943     fprintf(stderr, "%s\n", str);
00944   } else {
00945     bool old;
00946 #if defined(UNICODE)
00947     /* We need to put the text in a seperate buffer because the default
00948      * buffer in MB_TO_WIDE might not be large enough (512 chars) */
00949     wchar_t help_msgW[8192];
00950 #endif
00951     ReleaseCapture();
00952     _left_button_clicked = _left_button_down = false;
00953 
00954     old = MyShowCursor(true);
00955     if (MessageBox(GetActiveWindow(), MB_TO_WIDE_BUFFER(str, help_msgW, lengthof(help_msgW)), _T("OpenTTD"), MB_ICONINFORMATION | MB_OKCANCEL) == IDCANCEL) {
00956       CreateConsole();
00957     }
00958     MyShowCursor(old);
00959   }
00960 }
00961 
00962 #if defined(WINCE)
00963 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
00964 #else
00965 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
00966 #endif
00967 {
00968   int argc;
00969   char *argv[64]; // max 64 command line arguments
00970   char *cmdline;
00971 
00972 #if !defined(UNICODE)
00973   _codepage = GetACP(); // get system codepage as some kind of a default
00974 #endif /* UNICODE */
00975 
00976 #if defined(UNICODE)
00977 
00978 #if !defined(WINCE)
00979   /* Check if a win9x user started the win32 version */
00980   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.");
00981 #endif
00982 
00983   /* For UNICODE we need to convert the commandline to char* _AND_
00984    * save it because argv[] points into this buffer and thus needs to
00985    * be available between subsequent calls to FS2OTTD() */
00986   char cmdlinebuf[MAX_PATH];
00987 #endif /* UNICODE */
00988 
00989   cmdline = WIDE_TO_MB_BUFFER(GetCommandLine(), cmdlinebuf, lengthof(cmdlinebuf));
00990 
00991 #if defined(_DEBUG)
00992   CreateConsole();
00993 #endif
00994 
00995 #if !defined(WINCE)
00996   _set_error_mode(_OUT_TO_MSGBOX); // force assertion output to messagebox
00997 #endif
00998 
00999   /* setup random seed to something quite random */
01000   SetRandomSeed(GetTickCount());
01001 
01002   argc = ParseCommandLine(cmdline, argv, lengthof(argv));
01003 
01004 #if defined(WIN32_EXCEPTION_TRACKER)
01005   Win32InitializeExceptions();
01006 #endif
01007 
01008 #if defined(WIN32_EXCEPTION_TRACKER_DEBUG)
01009   _try {
01010     LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS *ep);
01011 #endif
01012     ttd_main(argc, argv);
01013 
01014 #if defined(WIN32_EXCEPTION_TRACKER_DEBUG)
01015   } _except (ExceptionHandler(_exception_info())) {}
01016 #endif
01017 
01018   return 0;
01019 }
01020 
01021 #if defined(WINCE)
01022 void GetCurrentDirectoryW(int length, wchar_t *path)
01023 {
01024   /* Get the name of this module */
01025   GetModuleFileName(NULL, path, length);
01026 
01027   /* Remove the executable name, this we call CurrentDir */
01028   wchar_t *pDest = wcsrchr(path, '\\');
01029   if (pDest != NULL) {
01030     int result = pDest - path + 1;
01031     path[result] = '\0';
01032   }
01033 }
01034 #endif
01035 
01036 char *getcwd(char *buf, size_t size)
01037 {
01038 #if defined(WINCE)
01039   TCHAR path[MAX_PATH];
01040   GetModuleFileName(NULL, path, MAX_PATH);
01041   convert_from_fs(path, buf, size);
01042   /* GetModuleFileName returns dir with file, so remove everything behind latest '\\' */
01043   char *p = strrchr(buf, '\\');
01044   if (p != NULL) *p = '\0';
01045 #elif defined(UNICODE)
01046   TCHAR path[MAX_PATH];
01047   GetCurrentDirectory(MAX_PATH - 1, path);
01048   convert_from_fs(path, buf, size);
01049 #else
01050   GetCurrentDirectory(size, buf);
01051 #endif
01052   return buf;
01053 }
01054 
01055 
01056 void DetermineBasePaths(const char *exe)
01057 {
01058   char tmp[MAX_PATH];
01059   TCHAR path[MAX_PATH];
01060 #ifdef WITH_PERSONAL_DIR
01061   SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, path);
01062   strecpy(tmp, WIDE_TO_MB_BUFFER(path, tmp, lengthof(tmp)), lastof(tmp));
01063   AppendPathSeparator(tmp, MAX_PATH);
01064   ttd_strlcat(tmp, PERSONAL_DIR, MAX_PATH);
01065   AppendPathSeparator(tmp, MAX_PATH);
01066   _searchpaths[SP_PERSONAL_DIR] = strdup(tmp);
01067 
01068   SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, SHGFP_TYPE_CURRENT, path);
01069   strecpy(tmp, WIDE_TO_MB_BUFFER(path, tmp, lengthof(tmp)), lastof(tmp));
01070   AppendPathSeparator(tmp, MAX_PATH);
01071   ttd_strlcat(tmp, PERSONAL_DIR, MAX_PATH);
01072   AppendPathSeparator(tmp, MAX_PATH);
01073   _searchpaths[SP_SHARED_DIR] = strdup(tmp);
01074 #else
01075   _searchpaths[SP_PERSONAL_DIR] = NULL;
01076   _searchpaths[SP_SHARED_DIR]   = NULL;
01077 #endif
01078 
01079   /* Get the path to working directory of OpenTTD */
01080   getcwd(tmp, lengthof(tmp));
01081   AppendPathSeparator(tmp, MAX_PATH);
01082   _searchpaths[SP_WORKING_DIR] = strdup(tmp);
01083 
01084   if (!GetModuleFileName(NULL, path, lengthof(path))) {
01085     DEBUG(misc, 0, "GetModuleFileName failed (%lu)\n", GetLastError());
01086     _searchpaths[SP_BINARY_DIR] = NULL;
01087   } else {
01088     TCHAR exec_dir[MAX_PATH];
01089     _tcsncpy(path, MB_TO_WIDE_BUFFER(exe, path, lengthof(path)), lengthof(path));
01090     if (!GetFullPathName(path, lengthof(exec_dir), exec_dir, NULL)) {
01091       DEBUG(misc, 0, "GetFullPathName failed (%lu)\n", GetLastError());
01092       _searchpaths[SP_BINARY_DIR] = NULL;
01093     } else {
01094       strecpy(tmp, WIDE_TO_MB_BUFFER(exec_dir, tmp, lengthof(tmp)), lastof(tmp));
01095       char *s = strrchr(tmp, PATHSEPCHAR);
01096       *(s + 1) = '\0';
01097       _searchpaths[SP_BINARY_DIR] = strdup(tmp);
01098     }
01099   }
01100 
01101   _searchpaths[SP_INSTALLATION_DIR]       = NULL;
01102   _searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
01103 }
01104 
01112 bool InsertTextBufferClipboard(Textbuf *tb)
01113 {
01114   HGLOBAL cbuf;
01115   char utf8_buf[512];
01116   const char *ptr;
01117 
01118   WChar c;
01119   uint16 width, length;
01120 
01121   if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
01122     OpenClipboard(NULL);
01123     cbuf = GetClipboardData(CF_UNICODETEXT);
01124 
01125     ptr = (const char*)GlobalLock(cbuf);
01126     const char *ret = convert_from_fs((wchar_t*)ptr, utf8_buf, lengthof(utf8_buf));
01127     GlobalUnlock(cbuf);
01128     CloseClipboard();
01129 
01130     if (*ret == '\0') return false;
01131 #if !defined(UNICODE)
01132   } else if (IsClipboardFormatAvailable(CF_TEXT)) {
01133     OpenClipboard(NULL);
01134     cbuf = GetClipboardData(CF_TEXT);
01135 
01136     ptr = (const char*)GlobalLock(cbuf);
01137     strecpy(utf8_buf, FS2OTTD(ptr), lastof(utf8_buf));
01138 
01139     GlobalUnlock(cbuf);
01140     CloseClipboard();
01141 #endif /* UNICODE */
01142   } else {
01143     return false;
01144   }
01145 
01146   width = length = 0;
01147 
01148   for (ptr = utf8_buf; (c = Utf8Consume(&ptr)) != '\0';) {
01149     if (!IsPrintable(c)) break;
01150 
01151     byte len = Utf8CharLen(c);
01152     if (tb->size + length + len > tb->maxsize) break;
01153 
01154     byte charwidth = GetCharacterWidth(FS_NORMAL, c);
01155     if (tb->maxwidth != 0 && width + tb->width + charwidth > tb->maxwidth) break;
01156 
01157     width += charwidth;
01158     length += len;
01159   }
01160 
01161   if (length == 0) return false;
01162 
01163   memmove(tb->buf + tb->caretpos + length, tb->buf + tb->caretpos, tb->size - tb->caretpos);
01164   memcpy(tb->buf + tb->caretpos, utf8_buf, length);
01165   tb->width += width;
01166   tb->caretxoffs += width;
01167 
01168   tb->size += length;
01169   tb->caretpos += length;
01170   assert(tb->size <= tb->maxsize);
01171   tb->buf[tb->size - 1] = '\0'; // terminating zero
01172 
01173   return true;
01174 }
01175 
01176 
01177 void CSleep(int milliseconds)
01178 {
01179   Sleep(milliseconds);
01180 }
01181 
01182 
01185 int64 GetTS()
01186 {
01187   static double freq;
01188   __int64 value;
01189   if (!freq) {
01190     QueryPerformanceFrequency((LARGE_INTEGER*)&value);
01191     freq = (double)1000000 / value;
01192   }
01193   QueryPerformanceCounter((LARGE_INTEGER*)&value);
01194   return (__int64)(value * freq);
01195 }
01196 
01197 
01210 const char *FS2OTTD(const TCHAR *name)
01211 {
01212   static char utf8_buf[512];
01213 #if defined(UNICODE)
01214   return convert_from_fs(name, utf8_buf, lengthof(utf8_buf));
01215 #else
01216   char *s = utf8_buf;
01217 
01218   for (; *name != '\0'; name++) {
01219     wchar_t w;
01220     int len = MultiByteToWideChar(_codepage, 0, name, 1, &w, 1);
01221     if (len != 1) {
01222       DEBUG(misc, 0, "[utf8] M2W error converting '%c'. Errno %lu", *name, GetLastError());
01223       continue;
01224     }
01225 
01226     if (s + Utf8CharLen(w) >= lastof(utf8_buf)) break;
01227     s += Utf8Encode(s, w);
01228   }
01229 
01230   *s = '\0';
01231   return utf8_buf;
01232 #endif /* UNICODE */
01233 }
01234 
01247 const TCHAR *OTTD2FS(const char *name)
01248 {
01249   static TCHAR system_buf[512];
01250 #if defined(UNICODE)
01251   return convert_to_fs(name, system_buf, lengthof(system_buf));
01252 #else
01253   char *s = system_buf;
01254 
01255   for (WChar c; (c = Utf8Consume(&name)) != '\0';) {
01256     if (s >= lastof(system_buf)) break;
01257 
01258     char mb;
01259     int len = WideCharToMultiByte(_codepage, 0, (wchar_t*)&c, 1, &mb, 1, NULL, NULL);
01260     if (len != 1) {
01261       DEBUG(misc, 0, "[utf8] W2M error converting '0x%X'. Errno %lu", c, GetLastError());
01262       continue;
01263     }
01264 
01265     *s++ = mb;
01266   }
01267 
01268   *s = '\0';
01269   return system_buf;
01270 #endif /* UNICODE */
01271 }
01272 
01273 
01280 char *convert_from_fs(const wchar_t *name, char *utf8_buf, size_t buflen)
01281 {
01282   int len = WideCharToMultiByte(CP_UTF8, 0, name, -1, utf8_buf, (int)buflen, NULL, NULL);
01283   if (len == 0) {
01284     DEBUG(misc, 0, "[utf8] W2M error converting wide-string. Errno %lu", GetLastError());
01285     utf8_buf[0] = '\0';
01286   }
01287 
01288   return utf8_buf;
01289 }
01290 
01291 
01299 wchar_t *convert_to_fs(const char *name, wchar_t *utf16_buf, size_t buflen)
01300 {
01301   int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, utf16_buf, (int)buflen);
01302   if (len == 0) {
01303     DEBUG(misc, 0, "[utf8] M2W error converting '%s'. Errno %lu", name, GetLastError());
01304     utf16_buf[0] = '\0';
01305   }
01306 
01307   return utf16_buf;
01308 }
01309 
01314 HRESULT OTTDSHGetFolderPath(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)
01315 {
01316   static HRESULT (WINAPI *SHGetFolderPath)(HWND, int, HANDLE, DWORD, LPTSTR) = NULL;
01317   static bool first_time = true;
01318 
01319   /* We only try to load the library one time; if it fails, it fails */
01320   if (first_time) {
01321 #if defined(UNICODE)
01322 # define W(x) x "W"
01323 #else
01324 # define W(x) x "A"
01325 #endif
01326     if (!LoadLibraryList((Function*)&SHGetFolderPath, "SHFolder.dll\0" W("SHGetFolderPath") "\0\0")) {
01327       DEBUG(misc, 0, "Unable to load " W("SHGetFolderPath") "from SHFolder.dll");
01328     }
01329 #undef W
01330     first_time = false;
01331   }
01332 
01333   if (SHGetFolderPath != NULL) return SHGetFolderPath(hwnd, csidl, hToken, dwFlags, pszPath);
01334 
01335   /* SHGetFolderPath doesn't exist, try a more conservative approach,
01336    * eg environment variables. This is only included for legacy modes
01337    * MSDN says: that 'pszPath' is a "Pointer to a null-terminated string of
01338    * length MAX_PATH which will receive the path" so let's assume that
01339    * Windows 95 with Internet Explorer 5.0, Windows 98 with Internet Explorer 5.0,
01340    * Windows 98 Second Edition (SE), Windows NT 4.0 with Internet Explorer 5.0,
01341    * Windows NT 4.0 with Service Pack 4 (SP4) */
01342   {
01343     DWORD ret;
01344     switch (csidl) {
01345       case CSIDL_FONTS: // Get the system font path, eg %WINDIR%\Fonts
01346         ret = GetEnvironmentVariable(_T("WINDIR"), pszPath, MAX_PATH);
01347         if (ret == 0) break;
01348         _tcsncat(pszPath, _T("\\Fonts"), MAX_PATH);
01349 
01350         return (HRESULT)0;
01351         break;
01352       /* XXX - other types to go here when needed... */
01353     }
01354   }
01355 
01356   return E_INVALIDARG;
01357 }
01358 
01360 const char *GetCurrentLocale(const char *)
01361 {
01362   char lang[9], country[9];
01363   if (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, lang, lengthof(lang)) == 0 ||
01364       GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, country, lengthof(country)) == 0) {
01365     /* Unable to retrieve the locale. */
01366     return NULL;
01367   }
01368   /* Format it as 'en_us'. */
01369   static char retbuf[6] = {lang[0], lang[1], '_', country[0], country[1], 0};
01370   return retbuf;
01371 }

Generated on Wed Jul 15 20:36:04 2009 for OpenTTD by  doxygen 1.5.6