OpenTTD

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files

- File List
- Globals

win32.cpp
Go to the documentation of this file.
1 /* $Id: win32.cpp 26606 2014-05-22 19:03:14Z frosch $ */
2
3 /*
4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 */
9
12 #include "../../stdafx.h"
13 #include "../../debug.h"
14 #include "../../gfx_func.h"
15 #include "../../textbuf_gui.h"
16 #include "../../fileio_func.h"
17 #include "../../fios.h"
18 #include <windows.h>
19 #include <fcntl.h>
20 #include <regstr.h>
21 #include <shlobj.h> /* SHGetFolderPath */
22 #include <shellapi.h>
23 #include "win32.h"
24 #include "../../core/alloc_func.hpp"
25 #include "../../openttd.h"
26 #include "../../core/random_func.hpp"
27 #include "../../string_func.h"
28 #include "../../crashlog.h"
29 #include <errno.h>
30 #include <sys/stat.h>
31
32 /* Due to TCHAR, strncat and strncpy have to remain (for a while). */
33 #include "../../safeguards.h"
34 #undef strncat
35 #undef strncpy
36
37 static bool _has_console;
38 static bool _cursor_disable = true;
39 static bool _cursor_visible = true;
40
41 bool MyShowCursor(bool show, bool toggle)
42 {
43 if (toggle) _cursor_disable = !_cursor_disable;
44 if (_cursor_disable) return show;
45 if (_cursor_visible == show) return show;
46
47 _cursor_visible = show;
48 ShowCursor(show);
49
50 return !show;
51 }
52
58 bool LoadLibraryList(Function proc[], const char *dll)
59 {
60 while (*dll != '\0') {
61 HMODULE lib;
62 lib = LoadLibrary(MB_TO_WIDE(dll));
63
64 if (lib == NULL) return false;
65 for (;;) {
66 FARPROC p;
67
68 while (*dll++ != '\0') { /* Nothing */ }
69 if (*dll == '\0') break;
70 #if defined(WINCE)
71 p = GetProcAddress(lib, MB_TO_WIDE(dll));
72 #else
73 p = GetProcAddress(lib, dll);
74 #endif
75 if (p == NULL) return false;
76 *proc++ = (Function)p;
77 }
78 dll++;
79 }
80 return true;
81 }
82
83 void ShowOSErrorBox(const char *buf, bool system)
84 {
85 MyShowCursor(true);
86 MessageBox(GetActiveWindow(), OTTD2FS(buf), _T("Error!"), MB_ICONSTOP);
87 }
88
89 void OSOpenBrowser(const char *url)
90 {
91 ShellExecute(GetActiveWindow(), _T("open"), OTTD2FS(url), NULL, NULL, SW_SHOWNORMAL);
92 }
93
94 /* Code below for windows version of opendir/readdir/closedir copied and
95 * modified from Jan Wassenberg's GPL implementation posted over at
96 * http://www.gamedev.net/community/forums/topic.asp?topic_id=364584&whichpage=1&#2398903 */
97
98 struct DIR {
99 HANDLE hFind;
100 /* the dirent returned by readdir.
101 * note: having only one global instance is not possible because
102 * multiple independent opendir/readdir sequences must be supported. */
103 dirent ent;
104 WIN32_FIND_DATA fd;
105 /* since opendir calls FindFirstFile, we need a means of telling the
106 * first call to readdir that we already have a file.
107 * that's the case iff this is true */
108 bool at_first_entry;
109 };
110
111 /* suballocator - satisfies most requests with a reusable static instance.
112 * this avoids hundreds of alloc/free which would fragment the heap.
113 * To guarantee concurrency, we fall back to malloc if the instance is
114 * already in use (it's important to avoid surprises since this is such a
115 * low-level routine). */
116 static DIR _global_dir;
117 static LONG _global_dir_is_in_use = false;
118
119 static inline DIR *dir_calloc()
120 {
121 DIR *d;
122
123 if (InterlockedExchange(&_global_dir_is_in_use, true) == (LONG)true) {
124 d = CallocT<DIR>(1);
125 } else {
126 d = &_global_dir;
127 memset(d, 0, sizeof(*d));
128 }
129 return d;
130 }
131
132 static inline void dir_free(DIR *d)
133 {
134 if (d == &_global_dir) {
135 _global_dir_is_in_use = (LONG)false;
136 } else {
137 free(d);
138 }
139 }
140
141 DIR *opendir(const TCHAR *path)
142 {
143 DIR *d;
144 UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS); // disable 'no-disk' message box
145 DWORD fa = GetFileAttributes(path);
146
147 if ((fa != INVALID_FILE_ATTRIBUTES) && (fa & FILE_ATTRIBUTE_DIRECTORY)) {
148 d = dir_calloc();
149 if (d != NULL) {
150 TCHAR search_path[MAX_PATH];
151 bool slash = path[_tcslen(path) - 1] == '\\';
152
153 /* build search path for FindFirstFile, try not to append additional slashes
154 * as it throws Win9x off its groove for root directories */
155 _sntprintf(search_path, lengthof(search_path), _T("%s%s*"), path, slash ? _T("") : _T("\\"));
156 *lastof(search_path) = '\0';
157 d->hFind = FindFirstFile(search_path, &d->fd);
158
159 if (d->hFind != INVALID_HANDLE_VALUE ||
160 GetLastError() == ERROR_NO_MORE_FILES) { // the directory is empty
161 d->ent.dir = d;
162 d->at_first_entry = true;
163 } else {
164 dir_free(d);
165 d = NULL;
166 }
167 } else {
168 errno = ENOMEM;
169 }
170 } else {
171 /* path not found or not a directory */
172 d = NULL;
173 errno = ENOENT;
174 }
175
176 SetErrorMode(sem); // restore previous setting
177 return d;
178 }
179
180 struct dirent *readdir(DIR *d)
181 {
182 DWORD prev_err = GetLastError(); // avoid polluting last error
183
184 if (d->at_first_entry) {
185 /* the directory was empty when opened */
186 if (d->hFind == INVALID_HANDLE_VALUE) return NULL;
187 d->at_first_entry = false;
188 } else if (!FindNextFile(d->hFind, &d->fd)) { // determine cause and bail
189 if (GetLastError() == ERROR_NO_MORE_FILES) SetLastError(prev_err);
190 return NULL;
191 }
192
193 /* This entry has passed all checks; return information about it.
194 * (note: d_name is a pointer; see struct dirent definition) */
195 d->ent.d_name = d->fd.cFileName;
196 return &d->ent;
197 }
198
199 int closedir(DIR *d)
200 {
201 FindClose(d->hFind);
202 dir_free(d);
203 return 0;
204 }
205
206 bool FiosIsRoot(const char *file)
207 {
208 return file[3] == '\0'; // C:\...
209 }
210
211 void FiosGetDrives()
212 {
213 #if defined(WINCE)
214 /* WinCE only knows one drive: / */
215 FiosItem *fios = _fios_items.Append();
216 fios->type = FIOS_TYPE_DRIVE;
217 fios->mtime = 0;
218 seprintf(fios->name, lastof(fios->name), PATHSEP "");
219 strecpy(fios->title, fios->name, lastof(fios->title));
220 #else
221 TCHAR drives[256];
222 const TCHAR *s;
223
224 GetLogicalDriveStrings(lengthof(drives), drives);
225 for (s = drives; *s != '\0';) {
226 FiosItem *fios = _fios_items.Append();
227 fios->type = FIOS_TYPE_DRIVE;
228 fios->mtime = 0;
229 seprintf(fios->name, lastof(fios->name), "%c:", s[0] & 0xFF);
230 strecpy(fios->title, fios->name, lastof(fios->title));
231 while (*s++ != '\0') { /* Nothing */ }
232 }
233 #endif
234 }
235
236 bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb)
237 {
238 /* hectonanoseconds between Windows and POSIX epoch */
239 static const int64 posix_epoch_hns = 0x019DB1DED53E8000LL;
240 const WIN32_FIND_DATA *fd = &ent->dir->fd;
241
242 sb->st_size = ((uint64) fd->nFileSizeHigh << 32) + fd->nFileSizeLow;
243 /* UTC FILETIME to seconds-since-1970 UTC
244 * we just have to subtract POSIX epoch and scale down to units of seconds.
245 * http://www.gamedev.net/community/forums/topic.asp?topic_id=294070&whichpage=1&#1860504
246 * XXX - not entirely correct, since filetimes on FAT aren't UTC but local,
247 * this won't entirely be correct, but we use the time only for comparison. */
248 sb->st_mtime = (time_t)((*(const uint64*)&fd->ftLastWriteTime - posix_epoch_hns) / 1E7);
249 sb->st_mode = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)? S_IFDIR : S_IFREG;
250
251 return true;
252 }
253
254 bool FiosIsHiddenFile(const struct dirent *ent)
255 {
256 return (ent->dir->fd.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != 0;
257 }
258
259 bool FiosGetDiskFreeSpace(const char *path, uint64 *tot)
260 {
261 UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS); // disable 'no-disk' message box
262 bool retval = false;
263 TCHAR root[4];
264 DWORD spc, bps, nfc, tnc;
265
266 _sntprintf(root, lengthof(root), _T("%c:") _T(PATHSEP), path[0]);
267 if (tot != NULL && GetDiskFreeSpace(root, &spc, &bps, &nfc, &tnc)) {
268 *tot = ((spc * bps) * (uint64)nfc);
269 retval = true;
270 }
271
272 SetErrorMode(sem); // reset previous setting
273 return retval;
274 }
275
276 static int ParseCommandLine(char *line, char **argv, int max_argc)
277 {
278 int n = 0;
279
280 do {
281 /* skip whitespace */
282 while (*line == ' ' || *line == '\t') line++;
283
284 /* end? */
285 if (*line == '\0') break;
286
287 /* special handling when quoted */
288 if (*line == '"') {
289 argv[n++] = ++line;
290 while (*line != '"') {
291 if (*line == '\0') return n;
292 line++;
293 }
294 } else {
295 argv[n++] = line;
296 while (*line != ' ' && *line != '\t') {
297 if (*line == '\0') return n;
298 line++;
299 }
300 }
301 *line++ = '\0';
302 } while (n != max_argc);
303
304 return n;
305 }
306
307 void CreateConsole()
308 {
309 #if defined(WINCE)
310 /* WinCE doesn't support console stuff */
311 #else
312 HANDLE hand;
313 CONSOLE_SCREEN_BUFFER_INFO coninfo;
314
315 if (_has_console) return;
316 _has_console = true;
317
318 AllocConsole();
319
320 hand = GetStdHandle(STD_OUTPUT_HANDLE);
321 GetConsoleScreenBufferInfo(hand, &coninfo);
322 coninfo.dwSize.Y = 500;
323 SetConsoleScreenBufferSize(hand, coninfo.dwSize);
324
325 /* redirect unbuffered STDIN, STDOUT, STDERR to the console */
326 #if !defined(__CYGWIN__)
327
328 /* Check if we can open a handle to STDOUT. */
329 int fd = _open_osfhandle((intptr_t)hand, _O_TEXT);
330 if (fd == -1) {
331 /* Free everything related to the console. */
332 FreeConsole();
333 _has_console = false;
334 _close(fd);
335 CloseHandle(hand);
336
337 ShowInfo("Unable to open an output handle to the console. Check known-bugs.txt for details.");
338 return;
339 }
340
341 *stdout = *_fdopen(fd, "w");
342 *stdin = *_fdopen(_open_osfhandle((intptr_t)GetStdHandle(STD_INPUT_HANDLE), _O_TEXT), "r" );
343 *stderr = *_fdopen(_open_osfhandle((intptr_t)GetStdHandle(STD_ERROR_HANDLE), _O_TEXT), "w" );
344 #else
345 /* open_osfhandle is not in cygwin */
346 *stdout = *fdopen(1, "w" );
347 *stdin = *fdopen(0, "r" );
348 *stderr = *fdopen(2, "w" );
349 #endif
350
351 setvbuf(stdin, NULL, _IONBF, 0);
352 setvbuf(stdout, NULL, _IONBF, 0);
353 setvbuf(stderr, NULL, _IONBF, 0);
354 #endif
355 }
356
358 static const char *_help_msg;
359
361 static INT_PTR CALLBACK HelpDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
362 {
363 switch (msg) {
364 case WM_INITDIALOG: {
365 char help_msg[8192];
366 const char *p = _help_msg;
367 char *q = help_msg;
368 while (q != lastof(help_msg) && *p != '\0') {
369 if (*p == '\n') {
370 *q++ = '\r';
371 if (q == lastof(help_msg)) {
372 q[-1] = '\0';
373 break;
374 }
375 }
376 *q++ = *p++;
377 }
378 *q = '\0';
379 /* We need to put the text in a separate buffer because the default
380 * buffer in OTTD2FS might not be large enough (512 chars). */
381 TCHAR help_msg_buf[8192];
382 SetDlgItemText(wnd, 11, convert_to_fs(help_msg, help_msg_buf, lengthof(help_msg_buf)));
383 SendDlgItemMessage(wnd, 11, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), FALSE);
384 } return TRUE;
385
386 case WM_COMMAND:
387 if (wParam == 12) ExitProcess(0);
388 return TRUE;
389 case WM_CLOSE:
390 ExitProcess(0);
391 }
392
393 return FALSE;
394 }
395
396 void ShowInfo(const char *str)
397 {
398 if (_has_console) {
399 fprintf(stderr, "%s\n", str);
400 } else {
401 bool old;
402 ReleaseCapture();
403 _left_button_clicked = _left_button_down = false;
404
405 old = MyShowCursor(true);
406 if (strlen(str) > 2048) {
407 /* The minimum length of the help message is 2048. Other messages sent via
408 * ShowInfo are much shorter, or so long they need this way of displaying
409 * them anyway. */
410 _help_msg = str;
411 DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(101), NULL, HelpDialogFunc);
412 } else {
413 /* We need to put the text in a separate buffer because the default
414 * buffer in OTTD2FS might not be large enough (512 chars). */
415 TCHAR help_msg_buf[8192];
416 MessageBox(GetActiveWindow(), convert_to_fs(str, help_msg_buf, lengthof(help_msg_buf)), _T("OpenTTD"), MB_ICONINFORMATION | MB_OK);
417 }
418 MyShowCursor(old);
419 }
420 }
421
422 #if defined(WINCE)
423 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
424 #else
425 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
426 #endif
427 {
428 int argc;
429 char *argv[64]; // max 64 command line arguments
430
431 CrashLog::InitialiseCrashLog();
432
433 #if defined(UNICODE) && !defined(WINCE)
434 /* Check if a win9x user started the win32 version */
435 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.");
436 #endif
437
438 /* Convert the command line to UTF-8. We need a dedicated buffer
439 * for this because argv[] points into this buffer and this needs to
440 * be available between subsequent calls to FS2OTTD(). */
441 char *cmdline = stredup(FS2OTTD(GetCommandLine()));
442
443 #if defined(_DEBUG)
444 CreateConsole();
445 #endif
446
447 #if !defined(WINCE)
448 _set_error_mode(_OUT_TO_MSGBOX); // force assertion output to messagebox
449 #endif
450
451 /* setup random seed to something quite random */
452 SetRandomSeed(GetTickCount());
453
454 argc = ParseCommandLine(cmdline, argv, lengthof(argv));
455
456 openttd_main(argc, argv);
457 free(cmdline);
458 return 0;
459 }
460
461 #if defined(WINCE)
462 void GetCurrentDirectoryW(int length, wchar_t *path)
463 {
464 /* Get the name of this module */
465 GetModuleFileName(NULL, path, length);
466
467 /* Remove the executable name, this we call CurrentDir */
468 wchar_t *pDest = wcsrchr(path, '\\');
469 if (pDest != NULL) {
470 int result = pDest - path + 1;
471 path[result] = '\0';
472 }
473 }
474 #endif
475
476 char *getcwd(char *buf, size_t size)
477 {
478 #if defined(WINCE)
479 TCHAR path[MAX_PATH];
480 GetModuleFileName(NULL, path, MAX_PATH);
481 convert_from_fs(path, buf, size);
482 /* GetModuleFileName returns dir with file, so remove everything behind latest '\\' */
483 char *p = strrchr(buf, '\\');
484 if (p != NULL) *p = '\0';
485 #else
486 TCHAR path[MAX_PATH];
487 GetCurrentDirectory(MAX_PATH - 1, path);
488 convert_from_fs(path, buf, size);
489 #endif
490 return buf;
491 }
492
493
494 void DetermineBasePaths(const char *exe)
495 {
496 char tmp[MAX_PATH];
497 TCHAR path[MAX_PATH];
498 #ifdef WITH_PERSONAL_DIR
499 if (SUCCEEDED(OTTDSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, path))) {
500 strecpy(tmp, FS2OTTD(path), lastof(tmp));
501 AppendPathSeparator(tmp, lastof(tmp));
502 strecat(tmp, PERSONAL_DIR, lastof(tmp));
503 AppendPathSeparator(tmp, lastof(tmp));
504 _searchpaths[SP_PERSONAL_DIR] = stredup(tmp);
505 } else {
506 _searchpaths[SP_PERSONAL_DIR] = NULL;
507 }
508
509 if (SUCCEEDED(OTTDSHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, SHGFP_TYPE_CURRENT, path))) {
510 strecpy(tmp, FS2OTTD(path), lastof(tmp));
511 AppendPathSeparator(tmp, lastof(tmp));
512 strecat(tmp, PERSONAL_DIR, lastof(tmp));
513 AppendPathSeparator(tmp, lastof(tmp));
514 _searchpaths[SP_SHARED_DIR] = stredup(tmp);
515 } else {
516 _searchpaths[SP_SHARED_DIR] = NULL;
517 }
518 #else
519 _searchpaths[SP_PERSONAL_DIR] = NULL;
520 _searchpaths[SP_SHARED_DIR] = NULL;
521 #endif
522
523 /* Get the path to working directory of OpenTTD */
524 getcwd(tmp, lengthof(tmp));
525 AppendPathSeparator(tmp, lastof(tmp));
526 _searchpaths[SP_WORKING_DIR] = stredup(tmp);
527
528 if (!GetModuleFileName(NULL, path, lengthof(path))) {
529 DEBUG(misc, 0, "GetModuleFileName failed (%lu)\n", GetLastError());
530 _searchpaths[SP_BINARY_DIR] = NULL;
531 } else {
532 TCHAR exec_dir[MAX_PATH];
533 _tcsncpy(path, convert_to_fs(exe, path, lengthof(path)), lengthof(path));
534 if (!GetFullPathName(path, lengthof(exec_dir), exec_dir, NULL)) {
535 DEBUG(misc, 0, "GetFullPathName failed (%lu)\n", GetLastError());
536 _searchpaths[SP_BINARY_DIR] = NULL;
537 } else {
538 strecpy(tmp, convert_from_fs(exec_dir, tmp, lengthof(tmp)), lastof(tmp));
539 char *s = strrchr(tmp, PATHSEPCHAR);
540 *(s + 1) = '\0';
541 _searchpaths[SP_BINARY_DIR] = stredup(tmp);
542 }
543 }
544
545 _searchpaths[SP_INSTALLATION_DIR] = NULL;
546 _searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
547 }
548
549
550 bool GetClipboardContents(char *buffer, const char *last)
551 {
552 HGLOBAL cbuf;
553 const char *ptr;
554
555 if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
556 OpenClipboard(NULL);
557 cbuf = GetClipboardData(CF_UNICODETEXT);
558
559 ptr = (const char*)GlobalLock(cbuf);
560 int out_len = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)ptr, -1, buffer, (last - buffer) + 1, NULL, NULL);
561 GlobalUnlock(cbuf);
562 CloseClipboard();
563
564 if (out_len == 0) return false;
565 #if !defined(UNICODE)
566 } else if (IsClipboardFormatAvailable(CF_TEXT)) {
567 OpenClipboard(NULL);
568 cbuf = GetClipboardData(CF_TEXT);
569
570 ptr = (const char*)GlobalLock(cbuf);
571 strecpy(buffer, FS2OTTD(ptr), last);
572
573 GlobalUnlock(cbuf);
574 CloseClipboard();
575 #endif /* UNICODE */
576 } else {
577 return false;
578 }
579
580 return true;
581 }
582
583
584 void CSleep(int milliseconds)
585 {
586 Sleep(milliseconds);
587 }
588
589
603 const char *FS2OTTD(const TCHAR *name)
604 {
605 static char utf8_buf[512];
606 return convert_from_fs(name, utf8_buf, lengthof(utf8_buf));
607 }
608
621 const TCHAR *OTTD2FS(const char *name, bool console_cp)
622 {
623 static TCHAR system_buf[512];
624 return convert_to_fs(name, system_buf, lengthof(system_buf), console_cp);
625 }
626
627
636 char *convert_from_fs(const TCHAR *name, char *utf8_buf, size_t buflen)
637 {
638 #if defined(UNICODE)
639 const WCHAR *wide_buf = name;
640 #else
641 /* Convert string from the local codepage to UTF-16. */
642 int wide_len = MultiByteToWideChar(CP_ACP, 0, name, -1, NULL, 0);
643 if (wide_len == 0) {
644 utf8_buf[0] = '\0';
645 return utf8_buf;
646 }
647
648 WCHAR *wide_buf = AllocaM(WCHAR, wide_len);
649 MultiByteToWideChar(CP_ACP, 0, name, -1, wide_buf, wide_len);
650 #endif
651
652 /* Convert UTF-16 string to UTF-8. */
653 int len = WideCharToMultiByte(CP_UTF8, 0, wide_buf, -1, utf8_buf, (int)buflen, NULL, NULL);
654 if (len == 0) utf8_buf[0] = '\0';
655
656 return utf8_buf;
657 }
658
659
670 TCHAR *convert_to_fs(const char *name, TCHAR *system_buf, size_t buflen, bool console_cp)
671 {
672 #if defined(UNICODE)
673 int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, system_buf, (int)buflen);
674 if (len == 0) system_buf[0] = '\0';
675 #else
676 int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0);
677 if (len == 0) {
678 system_buf[0] = '\0';
679 return system_buf;
680 }
681
682 WCHAR *wide_buf = AllocaM(WCHAR, len);
683 MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_buf, len);
684
685 len = WideCharToMultiByte(console_cp ? CP_OEMCP : CP_ACP, 0, wide_buf, len, system_buf, (int)buflen, NULL, NULL);
686 if (len == 0) system_buf[0] = '\0';
687 #endif
688
689 return system_buf;
690 }
691
698 HRESULT OTTDSHGetFolderPath(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)
699 {
700 static HRESULT (WINAPI *SHGetFolderPath)(HWND, int, HANDLE, DWORD, LPTSTR) = NULL;
701 static bool first_time = true;
702
703 /* We only try to load the library one time; if it fails, it fails */
704 if (first_time) {
705 #if defined(UNICODE)
706 # define W(x) x "W"
707 #else
708 # define W(x) x "A"
709 #endif
710 /* The function lives in shell32.dll for all current Windows versions, but it first started to appear in SHFolder.dll. */
711 if (!LoadLibraryList((Function*)&SHGetFolderPath, "shell32.dll\0" W("SHGetFolderPath") "\0\0")) {
712 if (!LoadLibraryList((Function*)&SHGetFolderPath, "SHFolder.dll\0" W("SHGetFolderPath") "\0\0")) {
713 DEBUG(misc, 0, "Unable to load " W("SHGetFolderPath") "from either shell32.dll or SHFolder.dll");
714 }
715 }
716 #undef W
717 first_time = false;
718 }
719
720 if (SHGetFolderPath != NULL) return SHGetFolderPath(hwnd, csidl, hToken, dwFlags, pszPath);
721
722 /* SHGetFolderPath doesn't exist, try a more conservative approach,
723 * eg environment variables. This is only included for legacy modes
724 * MSDN says: that 'pszPath' is a "Pointer to a null-terminated string of
725 * length MAX_PATH which will receive the path" so let's assume that
726 * Windows 95 with Internet Explorer 5.0, Windows 98 with Internet Explorer 5.0,
727 * Windows 98 Second Edition (SE), Windows NT 4.0 with Internet Explorer 5.0,
728 * Windows NT 4.0 with Service Pack 4 (SP4) */
729 {
730 DWORD ret;
731 switch (csidl) {
732 case CSIDL_FONTS: // Get the system font path, eg %WINDIR%\Fonts
733 ret = GetEnvironmentVariable(_T("WINDIR"), pszPath, MAX_PATH);
734 if (ret == 0) break;
735 _tcsncat(pszPath, _T("\\Fonts"), MAX_PATH);
736
737 return (HRESULT)0;
738
739 case CSIDL_PERSONAL:
740 case CSIDL_COMMON_DOCUMENTS: {
741 HKEY key;
742 if (RegOpenKeyEx(csidl == CSIDL_PERSONAL ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, REGSTR_PATH_SPECIAL_FOLDERS, 0, KEY_READ, &key) != ERROR_SUCCESS) break;
743 DWORD len = MAX_PATH;
744 ret = RegQueryValueEx(key, csidl == CSIDL_PERSONAL ? _T("Personal") : _T("Common Documents"), NULL, NULL, (LPBYTE)pszPath, &len);
745 RegCloseKey(key);
746 if (ret == ERROR_SUCCESS) return (HRESULT)0;
747 break;
748 }
749
750 /* XXX - other types to go here when needed... */
751 }
752 }
753
754 return E_INVALIDARG;
755 }
756
758 const char *GetCurrentLocale(const char *)
759 {
760 char lang[9], country[9];
761 if (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, lang, lengthof(lang)) == 0 ||
762 GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, country, lengthof(country)) == 0) {
763 /* Unable to retrieve the locale. */
764 return NULL;
765 }
766 /* Format it as 'en_us'. */
767 static char retbuf[6] = {lang[0], lang[1], '_', country[0], country[1], 0};
768 return retbuf;
769 }
770
771 uint GetCPUCoreCount()
772 {
773 SYSTEM_INFO info;
774
775 GetSystemInfo(&info);
776 return info.dwNumberOfProcessors;
777 }