00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "../../stdafx.h"
00013 #include "../../debug.h"
00014 #include "../../gfx_func.h"
00015 #include "../../textbuf_gui.h"
00016 #include "../../fileio_func.h"
00017 #include "../../fios.h"
00018 #include <windows.h>
00019 #include <fcntl.h>
00020 #include <shlobj.h>
00021 #include <Shellapi.h>
00022 #include "win32.h"
00023 #include "../../core/alloc_func.hpp"
00024 #include "../../openttd.h"
00025 #include "../../core/random_func.hpp"
00026 #include "../../string_func.h"
00027 #include "../../crashlog.h"
00028 #include <errno.h>
00029 #include <sys/stat.h>
00030
00031 static bool _has_console;
00032 static bool _cursor_disable = true;
00033 static bool _cursor_visible = true;
00034
00035 bool MyShowCursor(bool show, bool toggle)
00036 {
00037 if (toggle) _cursor_disable = !_cursor_disable;
00038 if (_cursor_disable) return show;
00039 if (_cursor_visible == show) return show;
00040
00041 _cursor_visible = show;
00042 ShowCursor(show);
00043
00044 return !show;
00045 }
00046
00052 bool LoadLibraryList(Function proc[], const char *dll)
00053 {
00054 while (*dll != '\0') {
00055 HMODULE lib;
00056 lib = LoadLibrary(MB_TO_WIDE(dll));
00057
00058 if (lib == NULL) return false;
00059 for (;;) {
00060 FARPROC p;
00061
00062 while (*dll++ != '\0') { }
00063 if (*dll == '\0') break;
00064 #if defined(WINCE)
00065 p = GetProcAddress(lib, MB_TO_WIDE(dll));
00066 #else
00067 p = GetProcAddress(lib, dll);
00068 #endif
00069 if (p == NULL) return false;
00070 *proc++ = (Function)p;
00071 }
00072 dll++;
00073 }
00074 return true;
00075 }
00076
00077 void ShowOSErrorBox(const char *buf, bool system)
00078 {
00079 MyShowCursor(true);
00080 MessageBox(GetActiveWindow(), MB_TO_WIDE(buf), _T("Error!"), MB_ICONSTOP);
00081 }
00082
00083 void OSOpenBrowser(const char *url)
00084 {
00085 ShellExecute(GetActiveWindow(), _T("open"), MB_TO_WIDE(url), NULL, NULL, SW_SHOWNORMAL);
00086 }
00087
00088
00089
00090
00091
00092 struct DIR {
00093 HANDLE hFind;
00094
00095
00096
00097 dirent ent;
00098 WIN32_FIND_DATA fd;
00099
00100
00101
00102 bool at_first_entry;
00103 };
00104
00105
00106
00107
00108
00109
00110 static DIR _global_dir;
00111 static LONG _global_dir_is_in_use = false;
00112
00113 static inline DIR *dir_calloc()
00114 {
00115 DIR *d;
00116
00117 if (InterlockedExchange(&_global_dir_is_in_use, true) == (LONG)true) {
00118 d = CallocT<DIR>(1);
00119 } else {
00120 d = &_global_dir;
00121 memset(d, 0, sizeof(*d));
00122 }
00123 return d;
00124 }
00125
00126 static inline void dir_free(DIR *d)
00127 {
00128 if (d == &_global_dir) {
00129 _global_dir_is_in_use = (LONG)false;
00130 } else {
00131 free(d);
00132 }
00133 }
00134
00135 DIR *opendir(const TCHAR *path)
00136 {
00137 DIR *d;
00138 UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS);
00139 DWORD fa = GetFileAttributes(path);
00140
00141 if ((fa != INVALID_FILE_ATTRIBUTES) && (fa & FILE_ATTRIBUTE_DIRECTORY)) {
00142 d = dir_calloc();
00143 if (d != NULL) {
00144 TCHAR search_path[MAX_PATH];
00145 bool slash = path[_tcslen(path) - 1] == '\\';
00146
00147
00148
00149 _sntprintf(search_path, lengthof(search_path), _T("%s%s*"), path, slash ? _T("") : _T("\\"));
00150 *lastof(search_path) = '\0';
00151 d->hFind = FindFirstFile(search_path, &d->fd);
00152
00153 if (d->hFind != INVALID_HANDLE_VALUE ||
00154 GetLastError() == ERROR_NO_MORE_FILES) {
00155 d->ent.dir = d;
00156 d->at_first_entry = true;
00157 } else {
00158 dir_free(d);
00159 d = NULL;
00160 }
00161 } else {
00162 errno = ENOMEM;
00163 }
00164 } else {
00165
00166 d = NULL;
00167 errno = ENOENT;
00168 }
00169
00170 SetErrorMode(sem);
00171 return d;
00172 }
00173
00174 struct dirent *readdir(DIR *d)
00175 {
00176 DWORD prev_err = GetLastError();
00177
00178 if (d->at_first_entry) {
00179
00180 if (d->hFind == INVALID_HANDLE_VALUE) return NULL;
00181 d->at_first_entry = false;
00182 } else if (!FindNextFile(d->hFind, &d->fd)) {
00183 if (GetLastError() == ERROR_NO_MORE_FILES) SetLastError(prev_err);
00184 return NULL;
00185 }
00186
00187
00188
00189 d->ent.d_name = d->fd.cFileName;
00190 return &d->ent;
00191 }
00192
00193 int closedir(DIR *d)
00194 {
00195 FindClose(d->hFind);
00196 dir_free(d);
00197 return 0;
00198 }
00199
00200 bool FiosIsRoot(const char *file)
00201 {
00202 return file[3] == '\0';
00203 }
00204
00205 void FiosGetDrives()
00206 {
00207 #if defined(WINCE)
00208
00209 FiosItem *fios = _fios_items.Append();
00210 fios->type = FIOS_TYPE_DRIVE;
00211 fios->mtime = 0;
00212 snprintf(fios->name, lengthof(fios->name), PATHSEP "");
00213 strecpy(fios->title, fios->name, lastof(fios->title));
00214 #else
00215 TCHAR drives[256];
00216 const TCHAR *s;
00217
00218 GetLogicalDriveStrings(lengthof(drives), drives);
00219 for (s = drives; *s != '\0';) {
00220 FiosItem *fios = _fios_items.Append();
00221 fios->type = FIOS_TYPE_DRIVE;
00222 fios->mtime = 0;
00223 snprintf(fios->name, lengthof(fios->name), "%c:", s[0] & 0xFF);
00224 strecpy(fios->title, fios->name, lastof(fios->title));
00225 while (*s++ != '\0') { }
00226 }
00227 #endif
00228 }
00229
00230 bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb)
00231 {
00232
00233 static const int64 posix_epoch_hns = 0x019DB1DED53E8000LL;
00234 const WIN32_FIND_DATA *fd = &ent->dir->fd;
00235
00236 sb->st_size = ((uint64) fd->nFileSizeHigh << 32) + fd->nFileSizeLow;
00237
00238
00239
00240
00241
00242 sb->st_mtime = (time_t)((*(const uint64*)&fd->ftLastWriteTime - posix_epoch_hns) / 1E7);
00243 sb->st_mode = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)? S_IFDIR : S_IFREG;
00244
00245 return true;
00246 }
00247
00248 bool FiosIsHiddenFile(const struct dirent *ent)
00249 {
00250 return (ent->dir->fd.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != 0;
00251 }
00252
00253 bool FiosGetDiskFreeSpace(const char *path, uint64 *tot)
00254 {
00255 UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS);
00256 bool retval = false;
00257 TCHAR root[4];
00258 DWORD spc, bps, nfc, tnc;
00259
00260 _sntprintf(root, lengthof(root), _T("%c:") _T(PATHSEP), path[0]);
00261 if (tot != NULL && GetDiskFreeSpace(root, &spc, &bps, &nfc, &tnc)) {
00262 *tot = ((spc * bps) * (uint64)nfc);
00263 retval = true;
00264 }
00265
00266 SetErrorMode(sem);
00267 return retval;
00268 }
00269
00270 static int ParseCommandLine(char *line, char **argv, int max_argc)
00271 {
00272 int n = 0;
00273
00274 do {
00275
00276 while (*line == ' ' || *line == '\t') line++;
00277
00278
00279 if (*line == '\0') break;
00280
00281
00282 if (*line == '"') {
00283 argv[n++] = ++line;
00284 while (*line != '"') {
00285 if (*line == '\0') return n;
00286 line++;
00287 }
00288 } else {
00289 argv[n++] = line;
00290 while (*line != ' ' && *line != '\t') {
00291 if (*line == '\0') return n;
00292 line++;
00293 }
00294 }
00295 *line++ = '\0';
00296 } while (n != max_argc);
00297
00298 return n;
00299 }
00300
00301 void CreateConsole()
00302 {
00303 #if defined(WINCE)
00304
00305 #else
00306 HANDLE hand;
00307 CONSOLE_SCREEN_BUFFER_INFO coninfo;
00308
00309 if (_has_console) return;
00310 _has_console = true;
00311
00312 AllocConsole();
00313
00314 hand = GetStdHandle(STD_OUTPUT_HANDLE);
00315 GetConsoleScreenBufferInfo(hand, &coninfo);
00316 coninfo.dwSize.Y = 500;
00317 SetConsoleScreenBufferSize(hand, coninfo.dwSize);
00318
00319
00320 #if !defined(__CYGWIN__)
00321
00322
00323 int fd = _open_osfhandle((intptr_t)hand, _O_TEXT);
00324 if (fd == -1) {
00325
00326 FreeConsole();
00327 _has_console = false;
00328 _close(fd);
00329 CloseHandle(hand);
00330
00331 ShowInfo("Unable to open an output handle to the console. Check known-bugs.txt for details.");
00332 return;
00333 }
00334
00335 *stdout = *_fdopen(fd, "w");
00336 *stdin = *_fdopen(_open_osfhandle((intptr_t)GetStdHandle(STD_INPUT_HANDLE), _O_TEXT), "r" );
00337 *stderr = *_fdopen(_open_osfhandle((intptr_t)GetStdHandle(STD_ERROR_HANDLE), _O_TEXT), "w" );
00338 #else
00339
00340 *stdout = *fdopen(1, "w" );
00341 *stdin = *fdopen(0, "r" );
00342 *stderr = *fdopen(2, "w" );
00343 #endif
00344
00345 setvbuf(stdin, NULL, _IONBF, 0);
00346 setvbuf(stdout, NULL, _IONBF, 0);
00347 setvbuf(stderr, NULL, _IONBF, 0);
00348 #endif
00349 }
00350
00352 static const char *_help_msg;
00353
00355 static INT_PTR CALLBACK HelpDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
00356 {
00357 switch (msg) {
00358 case WM_INITDIALOG: {
00359 char help_msg[8192];
00360 const char *p = _help_msg;
00361 char *q = help_msg;
00362 while (q != lastof(help_msg) && *p != '\0') {
00363 if (*p == '\n') {
00364 *q++ = '\r';
00365 if (q == lastof(help_msg)) {
00366 q[-1] = '\0';
00367 break;
00368 }
00369 }
00370 *q++ = *p++;
00371 }
00372 *q = '\0';
00373 #if defined(UNICODE)
00374
00375
00376 wchar_t help_msgW[8192];
00377 #endif
00378 SetDlgItemText(wnd, 11, MB_TO_WIDE_BUFFER(help_msg, help_msgW, lengthof(help_msgW)));
00379 SendDlgItemMessage(wnd, 11, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), FALSE);
00380 } return TRUE;
00381
00382 case WM_COMMAND:
00383 if (wParam == 12) ExitProcess(0);
00384 return TRUE;
00385 case WM_CLOSE:
00386 ExitProcess(0);
00387 }
00388
00389 return FALSE;
00390 }
00391
00392 void ShowInfo(const char *str)
00393 {
00394 if (_has_console) {
00395 fprintf(stderr, "%s\n", str);
00396 } else {
00397 bool old;
00398 ReleaseCapture();
00399 _left_button_clicked = _left_button_down = false;
00400
00401 old = MyShowCursor(true);
00402 if (strlen(str) > 2048) {
00403
00404
00405
00406 _help_msg = str;
00407 DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(101), NULL, HelpDialogFunc);
00408 } else {
00409 #if defined(UNICODE)
00410
00411
00412 wchar_t help_msgW[8192];
00413 #endif
00414 MessageBox(GetActiveWindow(), MB_TO_WIDE_BUFFER(str, help_msgW, lengthof(help_msgW)), _T("OpenTTD"), MB_ICONINFORMATION | MB_OK);
00415 }
00416 MyShowCursor(old);
00417 }
00418 }
00419
00420 #if defined(WINCE)
00421 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
00422 #else
00423 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
00424 #endif
00425 {
00426 int argc;
00427 char *argv[64];
00428 char *cmdline;
00429
00430 #if !defined(UNICODE)
00431 _codepage = GetACP();
00432 #endif
00433
00434 CrashLog::InitialiseCrashLog();
00435
00436 #if defined(UNICODE)
00437
00438 #if !defined(WINCE)
00439
00440 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.");
00441 #endif
00442
00443
00444
00445
00446 char cmdlinebuf[MAX_PATH];
00447 #endif
00448
00449 cmdline = WIDE_TO_MB_BUFFER(GetCommandLine(), cmdlinebuf, lengthof(cmdlinebuf));
00450
00451 #if defined(_DEBUG)
00452 CreateConsole();
00453 #endif
00454
00455 #if !defined(WINCE)
00456 _set_error_mode(_OUT_TO_MSGBOX);
00457 #endif
00458
00459
00460 SetRandomSeed(GetTickCount());
00461
00462 argc = ParseCommandLine(cmdline, argv, lengthof(argv));
00463
00464 ttd_main(argc, argv);
00465 return 0;
00466 }
00467
00468 #if defined(WINCE)
00469 void GetCurrentDirectoryW(int length, wchar_t *path)
00470 {
00471
00472 GetModuleFileName(NULL, path, length);
00473
00474
00475 wchar_t *pDest = wcsrchr(path, '\\');
00476 if (pDest != NULL) {
00477 int result = pDest - path + 1;
00478 path[result] = '\0';
00479 }
00480 }
00481 #endif
00482
00483 char *getcwd(char *buf, size_t size)
00484 {
00485 #if defined(WINCE)
00486 TCHAR path[MAX_PATH];
00487 GetModuleFileName(NULL, path, MAX_PATH);
00488 convert_from_fs(path, buf, size);
00489
00490 char *p = strrchr(buf, '\\');
00491 if (p != NULL) *p = '\0';
00492 #elif defined(UNICODE)
00493 TCHAR path[MAX_PATH];
00494 GetCurrentDirectory(MAX_PATH - 1, path);
00495 convert_from_fs(path, buf, size);
00496 #else
00497 GetCurrentDirectory(size, buf);
00498 #endif
00499 return buf;
00500 }
00501
00502
00503 void DetermineBasePaths(const char *exe)
00504 {
00505 char tmp[MAX_PATH];
00506 TCHAR path[MAX_PATH];
00507 #ifdef WITH_PERSONAL_DIR
00508 SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, path);
00509 strecpy(tmp, WIDE_TO_MB_BUFFER(path, tmp, lengthof(tmp)), lastof(tmp));
00510 AppendPathSeparator(tmp, MAX_PATH);
00511 ttd_strlcat(tmp, PERSONAL_DIR, MAX_PATH);
00512 AppendPathSeparator(tmp, MAX_PATH);
00513 _searchpaths[SP_PERSONAL_DIR] = strdup(tmp);
00514
00515 SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, SHGFP_TYPE_CURRENT, path);
00516 strecpy(tmp, WIDE_TO_MB_BUFFER(path, tmp, lengthof(tmp)), lastof(tmp));
00517 AppendPathSeparator(tmp, MAX_PATH);
00518 ttd_strlcat(tmp, PERSONAL_DIR, MAX_PATH);
00519 AppendPathSeparator(tmp, MAX_PATH);
00520 _searchpaths[SP_SHARED_DIR] = strdup(tmp);
00521 #else
00522 _searchpaths[SP_PERSONAL_DIR] = NULL;
00523 _searchpaths[SP_SHARED_DIR] = NULL;
00524 #endif
00525
00526
00527 getcwd(tmp, lengthof(tmp));
00528 AppendPathSeparator(tmp, MAX_PATH);
00529 _searchpaths[SP_WORKING_DIR] = strdup(tmp);
00530
00531 if (!GetModuleFileName(NULL, path, lengthof(path))) {
00532 DEBUG(misc, 0, "GetModuleFileName failed (%lu)\n", GetLastError());
00533 _searchpaths[SP_BINARY_DIR] = NULL;
00534 } else {
00535 TCHAR exec_dir[MAX_PATH];
00536 _tcsncpy(path, MB_TO_WIDE_BUFFER(exe, path, lengthof(path)), lengthof(path));
00537 if (!GetFullPathName(path, lengthof(exec_dir), exec_dir, NULL)) {
00538 DEBUG(misc, 0, "GetFullPathName failed (%lu)\n", GetLastError());
00539 _searchpaths[SP_BINARY_DIR] = NULL;
00540 } else {
00541 strecpy(tmp, WIDE_TO_MB_BUFFER(exec_dir, tmp, lengthof(tmp)), lastof(tmp));
00542 char *s = strrchr(tmp, PATHSEPCHAR);
00543 *(s + 1) = '\0';
00544 _searchpaths[SP_BINARY_DIR] = strdup(tmp);
00545 }
00546 }
00547
00548 _searchpaths[SP_INSTALLATION_DIR] = NULL;
00549 _searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
00550 }
00551
00552
00553 bool GetClipboardContents(char *buffer, size_t buff_len)
00554 {
00555 HGLOBAL cbuf;
00556 const char *ptr;
00557
00558 if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
00559 OpenClipboard(NULL);
00560 cbuf = GetClipboardData(CF_UNICODETEXT);
00561
00562 ptr = (const char*)GlobalLock(cbuf);
00563 const char *ret = convert_from_fs((const wchar_t*)ptr, buffer, buff_len);
00564 GlobalUnlock(cbuf);
00565 CloseClipboard();
00566
00567 if (*ret == '\0') return false;
00568 #if !defined(UNICODE)
00569 } else if (IsClipboardFormatAvailable(CF_TEXT)) {
00570 OpenClipboard(NULL);
00571 cbuf = GetClipboardData(CF_TEXT);
00572
00573 ptr = (const char*)GlobalLock(cbuf);
00574 ttd_strlcpy(buffer, FS2OTTD(ptr), buff_len);
00575
00576 GlobalUnlock(cbuf);
00577 CloseClipboard();
00578 #endif
00579 } else {
00580 return false;
00581 }
00582
00583 return true;
00584 }
00585
00586
00587 void CSleep(int milliseconds)
00588 {
00589 Sleep(milliseconds);
00590 }
00591
00592
00606 const char *FS2OTTD(const TCHAR *name)
00607 {
00608 static char utf8_buf[512];
00609 #if defined(UNICODE)
00610 return convert_from_fs(name, utf8_buf, lengthof(utf8_buf));
00611 #else
00612 char *s = utf8_buf;
00613
00614 for (; *name != '\0'; name++) {
00615 wchar_t w;
00616 int len = MultiByteToWideChar(_codepage, 0, name, 1, &w, 1);
00617 if (len != 1) {
00618 DEBUG(misc, 0, "[utf8] M2W error converting '%c'. Errno %lu", *name, GetLastError());
00619 continue;
00620 }
00621
00622 if (s + Utf8CharLen(w) >= lastof(utf8_buf)) break;
00623 s += Utf8Encode(s, w);
00624 }
00625
00626 *s = '\0';
00627 return utf8_buf;
00628 #endif
00629 }
00630
00644 const TCHAR *OTTD2FS(const char *name)
00645 {
00646 static TCHAR system_buf[512];
00647 #if defined(UNICODE)
00648 return convert_to_fs(name, system_buf, lengthof(system_buf));
00649 #else
00650 char *s = system_buf;
00651
00652 for (WChar c; (c = Utf8Consume(&name)) != '\0';) {
00653 if (s >= lastof(system_buf)) break;
00654
00655 char mb;
00656 int len = WideCharToMultiByte(_codepage, 0, (wchar_t*)&c, 1, &mb, 1, NULL, NULL);
00657 if (len != 1) {
00658 DEBUG(misc, 0, "[utf8] W2M error converting '0x%X'. Errno %lu", c, GetLastError());
00659 continue;
00660 }
00661
00662 *s++ = mb;
00663 }
00664
00665 *s = '\0';
00666 return system_buf;
00667 #endif
00668 }
00669
00670
00679 char *convert_from_fs(const wchar_t *name, char *utf8_buf, size_t buflen)
00680 {
00681 int len = WideCharToMultiByte(CP_UTF8, 0, name, -1, utf8_buf, (int)buflen, NULL, NULL);
00682 if (len == 0) {
00683 DEBUG(misc, 0, "[utf8] W2M error converting wide-string. Errno %lu", GetLastError());
00684 utf8_buf[0] = '\0';
00685 }
00686
00687 return utf8_buf;
00688 }
00689
00690
00700 wchar_t *convert_to_fs(const char *name, wchar_t *utf16_buf, size_t buflen)
00701 {
00702 int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, utf16_buf, (int)buflen);
00703 if (len == 0) {
00704 DEBUG(misc, 0, "[utf8] M2W error converting '%s'. Errno %lu", name, GetLastError());
00705 utf16_buf[0] = '\0';
00706 }
00707
00708 return utf16_buf;
00709 }
00710
00717 HRESULT OTTDSHGetFolderPath(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)
00718 {
00719 static HRESULT (WINAPI *SHGetFolderPath)(HWND, int, HANDLE, DWORD, LPTSTR) = NULL;
00720 static bool first_time = true;
00721
00722
00723 if (first_time) {
00724 #if defined(UNICODE)
00725 # define W(x) x "W"
00726 #else
00727 # define W(x) x "A"
00728 #endif
00729 if (!LoadLibraryList((Function*)&SHGetFolderPath, "SHFolder.dll\0" W("SHGetFolderPath") "\0\0")) {
00730 DEBUG(misc, 0, "Unable to load " W("SHGetFolderPath") "from SHFolder.dll");
00731 }
00732 #undef W
00733 first_time = false;
00734 }
00735
00736 if (SHGetFolderPath != NULL) return SHGetFolderPath(hwnd, csidl, hToken, dwFlags, pszPath);
00737
00738
00739
00740
00741
00742
00743
00744
00745 {
00746 DWORD ret;
00747 switch (csidl) {
00748 case CSIDL_FONTS:
00749 ret = GetEnvironmentVariable(_T("WINDIR"), pszPath, MAX_PATH);
00750 if (ret == 0) break;
00751 _tcsncat(pszPath, _T("\\Fonts"), MAX_PATH);
00752
00753 return (HRESULT)0;
00754
00755
00756 }
00757 }
00758
00759 return E_INVALIDARG;
00760 }
00761
00763 const char *GetCurrentLocale(const char *)
00764 {
00765 char lang[9], country[9];
00766 if (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, lang, lengthof(lang)) == 0 ||
00767 GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, country, lengthof(country)) == 0) {
00768
00769 return NULL;
00770 }
00771
00772 static char retbuf[6] = {lang[0], lang[1], '_', country[0], country[1], 0};
00773 return retbuf;
00774 }
00775
00776 uint GetCPUCoreCount()
00777 {
00778 SYSTEM_INFO info;
00779
00780 GetSystemInfo(&info);
00781 return info.dwNumberOfProcessors;
00782 }