1 /* RetroArch - A frontend for libretro.
2  * Copyright (C) 2011-2017 - Daniel De Matteis
3  * Copyright (C) 2016-2019 - Brad Parker
4  * Copyright (C) 2018-2019 - Andrés Suárez
5  *
6  * RetroArch is free software: you can redistribute it and/or modify it under the terms
7  * of the GNU General Public License as published by the Free Software Found-
8  * ation, either version 3 of the License, or (at your option) any later version.
9  *
10  * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12  * PURPOSE. See the GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along with RetroArch.
15  * If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <stdint.h>
19 #include <stddef.h>
20 #include <string.h>
21 
22 #include <retro_miscellaneous.h>
23 #include <windows.h>
24 #if defined(_WIN32) && !defined(_XBOX)
25 #include <process.h>
26 #include <errno.h>
27 #endif
28 
29 #include <boolean.h>
30 #include <compat/strl.h>
31 #include <dynamic/dylib.h>
32 #include <lists/file_list.h>
33 #include <file/file_path.h>
34 #include <string/stdstring.h>
35 #include <encodings/utf.h>
36 #include <features/features_cpu.h>
37 
38 #ifdef HAVE_CONFIG_H
39 #include "../../config.h"
40 #endif
41 
42 #ifdef HAVE_MENU
43 #include "../../menu/menu_driver.h"
44 #endif
45 
46 #include "../frontend_driver.h"
47 #include "../../configuration.h"
48 #include "../../defaults.h"
49 #include "../../verbosity.h"
50 #include "../../ui/drivers/ui_win32.h"
51 #include "../../paths.h"
52 #include "../../msg_hash.h"
53 #include "platform_win32.h"
54 
55 #include "../../verbosity.h"
56 
57 /*
58 #ifdef HAVE_NVDA
59 #include "../../nvda_controller.h"
60 #endif
61 */
62 
63 #ifdef HAVE_SAPI
64 #define COBJMACROS
65 #include <sapi.h>
66 #include <ole2.h>
67 #endif
68 
69 #ifdef HAVE_SAPI
70 static ISpVoice* pVoice        = NULL;
71 #endif
72 #ifdef HAVE_NVDA
73 static bool USE_POWERSHELL     = false;
74 static bool USE_NVDA           = true;
75 #else
76 static bool USE_POWERSHELL     = true;
77 static bool USE_NVDA           = false;
78 #endif
79 static bool USE_NVDA_BRAILLE   = false;
80 
81 #ifndef SM_SERVERR2
82 #define SM_SERVERR2 89
83 #endif
84 
85 /* static public global variable */
86 VOID (WINAPI *DragAcceptFiles_func)(HWND, BOOL);
87 
88 /* TODO/FIXME - static global variables */
89 static bool dwm_composition_disabled = false;
90 static bool console_needs_free       = false;
91 static char win32_cpu_model_name[64] = {0};
92 static bool pi_set                   = false;
93 #ifdef HAVE_DYNAMIC
94 /* We only load this library once, so we let it be
95  * unloaded at application shutdown, since unloading
96  * it early seems to cause issues on some systems.
97  */
98 static dylib_t dwmlib;
99 static dylib_t shell32lib;
100 static dylib_t nvdalib;
101 #endif
102 
103 /* Dynamic loading for Non-Visual Desktop Access support */
104 unsigned long (__stdcall *nvdaController_testIfRunning_func)(void);
105 unsigned long (__stdcall *nvdaController_cancelSpeech_func)(void);
106 unsigned long (__stdcall *nvdaController_brailleMessage_func)(wchar_t*);
107 unsigned long (__stdcall *nvdaController_speakText_func)(wchar_t*);
108 
109 #if defined(HAVE_LANGEXTRA) && !defined(_XBOX)
110 #if (defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500) || !defined(_MSC_VER)
111 struct win32_lang_pair
112 {
113    unsigned short lang_ident;
114    enum retro_language lang;
115 };
116 
117 /* https://docs.microsoft.com/en-us/windows/desktop/Intl/language-identifier-constants-and-strings */
118 const struct win32_lang_pair win32_lang_pairs[] =
119 {
120    /* array order MUST be kept, always largest ID first */
121    {0x7c04, RETRO_LANGUAGE_CHINESE_TRADITIONAL}, /* neutral */
122    {0x1404, RETRO_LANGUAGE_CHINESE_TRADITIONAL}, /* MO */
123    {0x1004, RETRO_LANGUAGE_CHINESE_SIMPLIFIED}, /* SG */
124    {0xC04, RETRO_LANGUAGE_CHINESE_TRADITIONAL}, /* HK/PRC */
125    {0x816, RETRO_LANGUAGE_PORTUGUESE_PORTUGAL},
126    {0x416, RETRO_LANGUAGE_PORTUGUESE_BRAZIL},
127    {0x2a, RETRO_LANGUAGE_VIETNAMESE},
128    {0x19, RETRO_LANGUAGE_RUSSIAN},
129    {0x16, RETRO_LANGUAGE_PORTUGUESE_PORTUGAL},
130    {0x15, RETRO_LANGUAGE_POLISH},
131    {0x13, RETRO_LANGUAGE_DUTCH},
132    {0x12, RETRO_LANGUAGE_KOREAN},
133    {0x11, RETRO_LANGUAGE_JAPANESE},
134    {0x10, RETRO_LANGUAGE_ITALIAN},
135    {0xc, RETRO_LANGUAGE_FRENCH},
136    {0xa, RETRO_LANGUAGE_SPANISH},
137    {0x9, RETRO_LANGUAGE_ENGLISH},
138    {0x8, RETRO_LANGUAGE_GREEK},
139    {0x7, RETRO_LANGUAGE_GERMAN},
140    {0x4, RETRO_LANGUAGE_CHINESE_SIMPLIFIED}, /* neutral */
141    {0x1, RETRO_LANGUAGE_ARABIC},
142    /* MS does not support Esperanto */
143    /*{0x0, RETRO_LANGUAGE_ESPERANTO},*/
144 };
145 
win32_get_langid_from_retro_lang(enum retro_language lang)146 unsigned short win32_get_langid_from_retro_lang(enum retro_language lang)
147 {
148    unsigned i;
149 
150    for (i = 0; i < sizeof(win32_lang_pairs) / sizeof(win32_lang_pairs[0]); i++)
151    {
152       if (win32_lang_pairs[i].lang == lang)
153          return win32_lang_pairs[i].lang_ident;
154    }
155 
156    return 0x409; /* fallback to US English */
157 }
158 
win32_get_retro_lang_from_langid(unsigned short langid)159 enum retro_language win32_get_retro_lang_from_langid(unsigned short langid)
160 {
161    unsigned i;
162 
163    for (i = 0; i < sizeof(win32_lang_pairs) / sizeof(win32_lang_pairs[0]); i++)
164    {
165       if (win32_lang_pairs[i].lang_ident > 0x3ff)
166       {
167          if (langid == win32_lang_pairs[i].lang_ident)
168             return win32_lang_pairs[i].lang;
169       }
170       else
171       {
172          if ((langid & 0x3ff) == win32_lang_pairs[i].lang_ident)
173             return win32_lang_pairs[i].lang;
174       }
175    }
176 
177    return RETRO_LANGUAGE_ENGLISH;
178 }
179 #endif
180 #else
win32_get_langid_from_retro_lang(enum retro_language lang)181 unsigned short win32_get_langid_from_retro_lang(enum retro_language lang)
182 {
183    return 0x409; /* fallback to US English */
184 }
185 
win32_get_retro_lang_from_langid(unsigned short langid)186 enum retro_language win32_get_retro_lang_from_langid(unsigned short langid)
187 {
188    return RETRO_LANGUAGE_ENGLISH;
189 }
190 #endif
191 
gfx_dwm_shutdown(void)192 static void gfx_dwm_shutdown(void)
193 {
194 #ifdef HAVE_DYNAMIC
195    if (dwmlib)
196       dylib_close(dwmlib);
197    if (shell32lib)
198       dylib_close(shell32lib);
199    dwmlib     = NULL;
200    shell32lib = NULL;
201 #endif
202 }
203 
gfx_init_dwm(void)204 static bool gfx_init_dwm(void)
205 {
206    HRESULT (WINAPI *mmcss)(BOOL);
207    static bool inited = false;
208 
209    if (inited)
210       return true;
211 
212    atexit(gfx_dwm_shutdown);
213 
214 #ifdef HAVE_DYNAMIC
215    shell32lib = dylib_load("shell32.dll");
216    if (!shell32lib)
217    {
218       RARCH_WARN("Did not find shell32.dll.\n");
219    }
220 
221    dwmlib = dylib_load("dwmapi.dll");
222    if (!dwmlib)
223    {
224       RARCH_WARN("Did not find dwmapi.dll.\n");
225       return false;
226    }
227 
228    DragAcceptFiles_func =
229       (VOID (WINAPI*)(HWND, BOOL))dylib_proc(shell32lib, "DragAcceptFiles");
230 
231    mmcss =
232       (HRESULT(WINAPI*)(BOOL))dylib_proc(dwmlib, "DwmEnableMMCSS");
233 #else
234    DragAcceptFiles_func = DragAcceptFiles;
235 #if 0
236    mmcss                = DwmEnableMMCSS;
237 #endif
238 #endif
239 
240    if (mmcss)
241       mmcss(TRUE);
242 
243    inited = true;
244    return true;
245 }
246 
gfx_set_dwm(void)247 static void gfx_set_dwm(void)
248 {
249    HRESULT ret;
250    HRESULT (WINAPI *composition_enable)(UINT);
251    settings_t *settings     = config_get_ptr();
252    bool disable_composition = settings->bools.video_disable_composition;
253 
254    if (!gfx_init_dwm())
255       return;
256 
257    if (disable_composition == dwm_composition_disabled)
258       return;
259 
260 #ifdef HAVE_DYNAMIC
261    composition_enable =
262       (HRESULT (WINAPI*)(UINT))dylib_proc(dwmlib, "DwmEnableComposition");
263 #endif
264 
265    if (!composition_enable)
266    {
267       RARCH_ERR("Did not find DwmEnableComposition ...\n");
268       return;
269    }
270 
271    ret = composition_enable(!disable_composition);
272    if (FAILED(ret))
273       RARCH_ERR("Failed to set composition state ...\n");
274    dwm_composition_disabled = disable_composition;
275 }
276 
frontend_win32_get_os(char * s,size_t len,int * major,int * minor)277 static void frontend_win32_get_os(char *s, size_t len, int *major, int *minor)
278 {
279    char buildStr[11]      = {0};
280    bool server            = false;
281    const char *arch       = "";
282 
283 #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500
284    /* Windows 2000 and later */
285    SYSTEM_INFO si         = {{0}};
286    OSVERSIONINFOEX vi     = {0};
287    vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
288 
289    GetSystemInfo(&si);
290 
291    /* Available from NT 3.5 and Win95 */
292    GetVersionEx((OSVERSIONINFO*)&vi);
293 
294    server = vi.wProductType != VER_NT_WORKSTATION;
295 
296    switch (si.wProcessorArchitecture)
297    {
298       case PROCESSOR_ARCHITECTURE_AMD64:
299          arch = "x64";
300          break;
301       case PROCESSOR_ARCHITECTURE_INTEL:
302          arch = "x86";
303          break;
304       case PROCESSOR_ARCHITECTURE_ARM:
305          arch = "ARM";
306          break;
307       default:
308          break;
309    }
310 #else
311    OSVERSIONINFO vi = {0};
312    vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
313 
314    /* Available from NT 3.5 and Win95 */
315    GetVersionEx(&vi);
316 #endif
317 
318    if (major)
319       *major = vi.dwMajorVersion;
320 
321    if (minor)
322       *minor = vi.dwMinorVersion;
323 
324    if (vi.dwMajorVersion == 4 && vi.dwMinorVersion == 0)
325       snprintf(buildStr, sizeof(buildStr), "%lu", (DWORD)(LOWORD(vi.dwBuildNumber))); /* Windows 95 build number is in the low-order word only */
326    else
327       snprintf(buildStr, sizeof(buildStr), "%lu", vi.dwBuildNumber);
328 
329    switch (vi.dwMajorVersion)
330    {
331       case 10:
332          if (server)
333             strcpy_literal(s, "Windows Server 2016");
334          else
335             strcpy_literal(s, "Windows 10");
336          break;
337       case 6:
338          switch (vi.dwMinorVersion)
339          {
340             case 3:
341                if (server)
342                   strcpy_literal(s, "Windows Server 2012 R2");
343                else
344                   strcpy_literal(s, "Windows 8.1");
345                break;
346             case 2:
347                if (server)
348                   strcpy_literal(s, "Windows Server 2012");
349                else
350                   strcpy_literal(s, "Windows 8");
351                break;
352             case 1:
353                if (server)
354                   strcpy_literal(s, "Windows Server 2008 R2");
355                else
356                   strcpy_literal(s, "Windows 7");
357                break;
358             case 0:
359                if (server)
360                   strcpy_literal(s, "Windows Server 2008");
361                else
362                   strcpy_literal(s, "Windows Vista");
363                break;
364             default:
365                break;
366          }
367          break;
368       case 5:
369          switch (vi.dwMinorVersion)
370          {
371             case 2:
372                if (server)
373                {
374                   strcpy_literal(s, "Windows Server 2003");
375                   if (GetSystemMetrics(SM_SERVERR2))
376                      strlcat(s, " R2", len);
377                }
378                else
379                {
380                   /* Yes, XP Pro x64 is a higher version number than XP x86 */
381                   if (string_is_equal(arch, "x64"))
382                      strcpy_literal(s, "Windows XP");
383                }
384                break;
385             case 1:
386                strcpy_literal(s, "Windows XP");
387                break;
388             case 0:
389                strcpy_literal(s, "Windows 2000");
390                break;
391          }
392          break;
393       case 4:
394          switch (vi.dwMinorVersion)
395          {
396             case 0:
397                if (vi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
398                   strcpy_literal(s, "Windows 95");
399                else if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT)
400                   strcpy_literal(s, "Windows NT 4.0");
401                else
402                   strcpy_literal(s, "Unknown");
403                break;
404             case 90:
405                strcpy_literal(s, "Windows ME");
406                break;
407             case 10:
408                strcpy_literal(s, "Windows 98");
409                break;
410          }
411          break;
412       default:
413          snprintf(s, len, "Windows %i.%i", *major, *minor);
414          break;
415    }
416 
417    if (!string_is_empty(arch))
418    {
419       strlcat(s, " ", len);
420       strlcat(s, arch, len);
421    }
422 
423    strlcat(s, " Build ", len);
424    strlcat(s, buildStr, len);
425 
426    if (!string_is_empty(vi.szCSDVersion))
427    {
428       strlcat(s, " ", len);
429       strlcat(s, vi.szCSDVersion, len);
430    }
431 
432 }
433 
frontend_win32_init(void * data)434 static void frontend_win32_init(void *data)
435 {
436    typedef BOOL (WINAPI *isProcessDPIAwareProc)();
437    typedef BOOL (WINAPI *setProcessDPIAwareProc)();
438 #ifdef HAVE_DYNAMIC
439    HMODULE handle                         =
440       GetModuleHandle("User32.dll");
441    isProcessDPIAwareProc  isDPIAwareProc  =
442       (isProcessDPIAwareProc)dylib_proc(handle, "IsProcessDPIAware");
443    setProcessDPIAwareProc setDPIAwareProc =
444       (setProcessDPIAwareProc)dylib_proc(handle, "SetProcessDPIAware");
445 #else
446    isProcessDPIAwareProc  isDPIAwareProc  = IsProcessDPIAware;
447    setProcessDPIAwareProc setDPIAwareProc = SetProcessDPIAware;
448 #endif
449 
450    if (isDPIAwareProc)
451       if (!isDPIAwareProc())
452          if (setDPIAwareProc)
453             setDPIAwareProc();
454 }
455 
456 
457 #ifdef HAVE_NVDA
init_nvda(void)458 static void init_nvda(void)
459 {
460 #ifdef HAVE_DYNAMIC
461    if (USE_NVDA && !nvdalib)
462    {
463       nvdalib = dylib_load("nvdaControllerClient64.dll");
464       if (!nvdalib)
465       {
466          USE_NVDA = false;
467          USE_POWERSHELL = true;
468       }
469       else
470       {
471          nvdaController_testIfRunning_func = ( unsigned long (__stdcall*)(void))dylib_proc(nvdalib, "nvdaController_testIfRunning");
472          nvdaController_cancelSpeech_func = (unsigned long(__stdcall *)(void))dylib_proc(nvdalib, "nvdaController_cancelSpeech");
473          nvdaController_brailleMessage_func = (unsigned long(__stdcall *)(wchar_t*))dylib_proc(nvdalib, "nvdaController_brailleMessage");
474          nvdaController_speakText_func = (unsigned long(__stdcall *)(wchar_t*))dylib_proc(nvdalib, "nvdaController_speakText");
475 
476       }
477    }
478 #else
479    USE_NVDA = false;
480    USE_POWERSHELL = true;
481 #endif
482 }
483 #endif
484 
frontend_win32_get_powerstate(int * seconds,int * percent)485 enum frontend_powerstate frontend_win32_get_powerstate(int *seconds, int *percent)
486 {
487    SYSTEM_POWER_STATUS status;
488    enum frontend_powerstate ret = FRONTEND_POWERSTATE_NONE;
489 
490    if (!GetSystemPowerStatus(&status))
491       return ret;
492 
493    if (status.BatteryFlag == 0xFF)
494       ret = FRONTEND_POWERSTATE_NONE;
495    else if (status.BatteryFlag & (1 << 7))
496       ret = FRONTEND_POWERSTATE_NO_SOURCE;
497    else if (status.BatteryFlag & (1 << 3))
498       ret = FRONTEND_POWERSTATE_CHARGING;
499    else if (status.ACLineStatus == 1)
500       ret = FRONTEND_POWERSTATE_CHARGED;
501    else
502       ret = FRONTEND_POWERSTATE_ON_POWER_SOURCE;
503 
504    *percent  = (int)status.BatteryLifePercent;
505    *seconds  = (int)status.BatteryLifeTime;
506 
507 #ifdef _WIN32
508    if (*percent == 255)
509       *percent = 0;
510 #endif
511 
512    return ret;
513 }
514 
frontend_win32_get_arch(void)515 enum frontend_architecture frontend_win32_get_arch(void)
516 {
517 #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500
518    /* Windows 2000 and later */
519    SYSTEM_INFO si = {{0}};
520 
521    GetSystemInfo(&si);
522 
523    switch (si.wProcessorArchitecture)
524    {
525       case PROCESSOR_ARCHITECTURE_AMD64:
526          return FRONTEND_ARCH_X86_64;
527          break;
528       case PROCESSOR_ARCHITECTURE_INTEL:
529          return FRONTEND_ARCH_X86;
530          break;
531       case PROCESSOR_ARCHITECTURE_ARM:
532          return FRONTEND_ARCH_ARM;
533          break;
534       default:
535          break;
536    }
537 #endif
538 
539    return FRONTEND_ARCH_NONE;
540 }
541 
frontend_win32_parse_drive_list(void * data,bool load_content)542 static int frontend_win32_parse_drive_list(void *data, bool load_content)
543 {
544 #ifdef HAVE_MENU
545    file_list_t *list = (file_list_t*)data;
546    enum msg_hash_enums enum_idx = load_content ?
547          MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR :
548          MENU_ENUM_LABEL_FILE_BROWSER_DIRECTORY;
549    size_t i          = 0;
550    unsigned drives   = GetLogicalDrives();
551    char    drive[]   = " :\\";
552 
553    for (i = 0; i < 32; i++)
554    {
555       drive[0] = 'A' + i;
556       if (drives & (1 << i))
557          menu_entries_append_enum(list,
558                drive,
559                msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR),
560                enum_idx,
561                FILE_TYPE_DIRECTORY, 0, 0);
562    }
563 #endif
564 
565    return 0;
566 }
567 
frontend_win32_env_get(int * argc,char * argv[],void * args,void * params_data)568 static void frontend_win32_env_get(int *argc, char *argv[],
569       void *args, void *params_data)
570 {
571    const char *tmp_dir = getenv("TMP");
572    if (!string_is_empty(tmp_dir))
573       fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_CACHE],
574          tmp_dir, sizeof(g_defaults.dirs[DEFAULT_DIR_CACHE]));
575 
576    gfx_set_dwm();
577 
578    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_ASSETS],
579       ":\\assets", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS]));
580    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER],
581       ":\\filters\\audio", sizeof(g_defaults.dirs[DEFAULT_DIR_AUDIO_FILTER]));
582    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER],
583       ":\\filters\\video", sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER]));
584    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_CHEATS],
585       ":\\cheats", sizeof(g_defaults.dirs[DEFAULT_DIR_CHEATS]));
586    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_DATABASE],
587       ":\\database\\rdb", sizeof(g_defaults.dirs[DEFAULT_DIR_DATABASE]));
588    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_CURSOR],
589       ":\\database\\cursors", sizeof(g_defaults.dirs[DEFAULT_DIR_CURSOR]));
590    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_PLAYLIST],
591       ":\\playlists", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS]));
592    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_RECORD_CONFIG],
593       ":\\config\\record", sizeof(g_defaults.dirs[DEFAULT_DIR_RECORD_CONFIG]));
594    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_RECORD_OUTPUT],
595       ":\\recordings", sizeof(g_defaults.dirs[DEFAULT_DIR_RECORD_OUTPUT]));
596    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG],
597       ":\\config", sizeof(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG]));
598    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_REMAP],
599       ":\\config\\remaps", sizeof(g_defaults.dirs[DEFAULT_DIR_REMAP]));
600    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_WALLPAPERS],
601       ":\\assets\\wallpapers", sizeof(g_defaults.dirs[DEFAULT_DIR_WALLPAPERS]));
602    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_THUMBNAILS],
603       ":\\thumbnails", sizeof(g_defaults.dirs[DEFAULT_DIR_THUMBNAILS]));
604    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_OVERLAY],
605       ":\\overlays", sizeof(g_defaults.dirs[DEFAULT_DIR_OVERLAY]));
606 #ifdef HAVE_VIDEO_LAYOUT
607    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_VIDEO_LAYOUT],
608       ":\\layouts", sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_LAYOUT]));
609 #endif
610    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_CORE],
611       ":\\cores", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE]));
612    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_CORE_INFO],
613       ":\\info", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO]));
614    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_AUTOCONFIG],
615       ":\\autoconfig", sizeof(g_defaults.dirs[DEFAULT_DIR_AUTOCONFIG]));
616    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_SHADER],
617       ":\\shaders", sizeof(g_defaults.dirs[DEFAULT_DIR_SHADER]));
618    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS],
619       ":\\downloads", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS]));
620    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_SCREENSHOT],
621       ":\\screenshots", sizeof(g_defaults.dirs[DEFAULT_DIR_SCREENSHOT]));
622    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_SRAM],
623       ":\\saves", sizeof(g_defaults.dirs[DEFAULT_DIR_SRAM]));
624    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_SAVESTATE],
625       ":\\states", sizeof(g_defaults.dirs[DEFAULT_DIR_SAVESTATE]));
626    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_SYSTEM],
627       ":\\system", sizeof(g_defaults.dirs[DEFAULT_DIR_SYSTEM]));
628    fill_pathname_expand_special(g_defaults.dirs[DEFAULT_DIR_LOGS],
629       ":\\logs", sizeof(g_defaults.dirs[DEFAULT_DIR_LOGS]));
630 
631 #ifndef IS_SALAMANDER
632    dir_check_defaults("custom.ini");
633 #endif
634 }
635 
frontend_win32_get_total_mem(void)636 static uint64_t frontend_win32_get_total_mem(void)
637 {
638    /* OSes below 2000 don't have the Ex version,
639     * and non-Ex cannot work with >4GB RAM */
640 #if _WIN32_WINNT >= 0x0500
641    MEMORYSTATUSEX mem_info;
642    mem_info.dwLength = sizeof(MEMORYSTATUSEX);
643    GlobalMemoryStatusEx(&mem_info);
644    return mem_info.ullTotalPhys;
645 #else
646    MEMORYSTATUS mem_info;
647    mem_info.dwLength = sizeof(MEMORYSTATUS);
648    GlobalMemoryStatus(&mem_info);
649    return mem_info.dwTotalPhys;
650 #endif
651 }
652 
frontend_win32_get_free_mem(void)653 static uint64_t frontend_win32_get_free_mem(void)
654 {
655    /* OSes below 2000 don't have the Ex version,
656     * and non-Ex cannot work with >4GB RAM */
657 #if _WIN32_WINNT >= 0x0500
658    MEMORYSTATUSEX mem_info;
659    mem_info.dwLength = sizeof(MEMORYSTATUSEX);
660    GlobalMemoryStatusEx(&mem_info);
661    return mem_info.ullAvailPhys;
662 #else
663    MEMORYSTATUS mem_info;
664    mem_info.dwLength = sizeof(MEMORYSTATUS);
665    GlobalMemoryStatus(&mem_info);
666    return mem_info.dwAvailPhys;
667 #endif
668 }
669 
frontend_win32_attach_console(void)670 static void frontend_win32_attach_console(void)
671 {
672 #ifdef _WIN32
673 #ifdef _WIN32_WINNT_WINXP
674    /* msys will start the process with FILE_TYPE_PIPE connected.
675     *   cmd will start the process with FILE_TYPE_UNKNOWN connected
676     *   (since this is subsystem windows application
677     * ... UNLESS stdout/stderr were redirected (then FILE_TYPE_DISK
678     * will be connected most likely)
679     * explorer will start the process with NOTHING connected.
680     *
681     * Now, let's not reconnect anything that's already connected.
682     * If any are disconnected, open a console, and connect to them.
683     * In case we're launched from msys or cmd, try attaching to the
684     * parent process console first.
685     *
686     * Take care to leave a record of what we did, so we can
687     * undo it precisely.
688     */
689 
690    bool need_stdout = (GetFileType(GetStdHandle(STD_OUTPUT_HANDLE))
691          == FILE_TYPE_UNKNOWN);
692    bool need_stderr = (GetFileType(GetStdHandle(STD_ERROR_HANDLE))
693          == FILE_TYPE_UNKNOWN);
694 
695    if (need_stdout || need_stderr)
696    {
697       if (!AttachConsole( ATTACH_PARENT_PROCESS))
698          AllocConsole();
699 
700       SetConsoleTitle("Log Console");
701 
702       if (need_stdout)
703          freopen( "CONOUT$", "w", stdout );
704       if (need_stderr)
705          freopen( "CONOUT$", "w", stderr );
706 
707       console_needs_free = true;
708    }
709 
710 #endif
711 #endif
712 }
713 
frontend_win32_detach_console(void)714 static void frontend_win32_detach_console(void)
715 {
716 #if defined(_WIN32) && !defined(_XBOX)
717 #ifdef _WIN32_WINNT_WINXP
718    if (console_needs_free)
719    {
720       /* we don't reconnect stdout/stderr to anything here,
721        * because by definition, they weren't connected to
722        * anything in the first place. */
723       FreeConsole();
724       console_needs_free = false;
725    }
726 #endif
727 #endif
728 }
729 
frontend_win32_get_cpu_model_name(void)730 static const char* frontend_win32_get_cpu_model_name(void)
731 {
732 #ifdef ANDROID
733    return NULL;
734 #else
735    cpu_features_get_model_name(win32_cpu_model_name, sizeof(win32_cpu_model_name));
736    return win32_cpu_model_name;
737 #endif
738 }
739 
frontend_win32_get_user_language(void)740 enum retro_language frontend_win32_get_user_language(void)
741 {
742    enum retro_language lang = RETRO_LANGUAGE_ENGLISH;
743 #if defined(HAVE_LANGEXTRA) && !defined(_XBOX)
744 #if (defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500) || !defined(_MSC_VER)
745    LANGID langid = GetUserDefaultUILanguage();
746    lang = win32_get_retro_lang_from_langid(langid);
747 #endif
748 #endif
749    return lang;
750 }
751 
752 #if defined(_WIN32) && !defined(_XBOX)
753 enum frontend_fork win32_fork_mode;
754 
frontend_win32_respawn(char * s,size_t len,char * args)755 static void frontend_win32_respawn(char *s, size_t len, char *args)
756 {
757    STARTUPINFO si;
758    PROCESS_INFORMATION pi;
759    char executable_path[PATH_MAX_LENGTH] = {0};
760 
761    if (win32_fork_mode != FRONTEND_FORK_RESTART)
762       return;
763 
764    fill_pathname_application_path(executable_path,
765          sizeof(executable_path));
766    path_set(RARCH_PATH_CORE, executable_path);
767    RARCH_LOG("Restarting RetroArch with commandline: %s and %s\n",
768       executable_path, args);
769 
770    memset(&si, 0, sizeof(si));
771    si.cb = sizeof(si);
772    memset(&pi, 0, sizeof(pi));
773 
774    if (!CreateProcess( executable_path, args,
775       NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
776    {
777       RARCH_LOG("Failed to restart RetroArch\n");
778    }
779 }
780 
frontend_win32_set_fork(enum frontend_fork fork_mode)781 static bool frontend_win32_set_fork(enum frontend_fork fork_mode)
782 {
783    switch (fork_mode)
784    {
785       case FRONTEND_FORK_CORE:
786          break;
787       case FRONTEND_FORK_CORE_WITH_ARGS:
788          break;
789       case FRONTEND_FORK_RESTART:
790          command_event(CMD_EVENT_QUIT, NULL);
791          break;
792       case FRONTEND_FORK_NONE:
793       default:
794          break;
795    }
796    win32_fork_mode = fork_mode;
797    return true;
798 }
799 #endif
800 
801 #if defined(_WIN32) && !defined(_XBOX)
accessibility_win_language_id(const char * language)802 static const char *accessibility_win_language_id(const char* language)
803 {
804    if (string_is_equal(language,"en"))
805       return "409";
806    else if (string_is_equal(language,"it"))
807       return "410";
808    else if (string_is_equal(language,"sv"))
809       return "041d";
810    else if (string_is_equal(language,"fr"))
811       return "040c";
812    else if (string_is_equal(language,"de"))
813       return "407";
814    else if (string_is_equal(language,"he"))
815       return "040d";
816    else if (string_is_equal(language,"id"))
817       return "421";
818    else if (string_is_equal(language,"es"))
819       return "040a";
820    else if (string_is_equal(language,"nl"))
821       return "413";
822    else if (string_is_equal(language,"ro"))
823       return "418";
824    else if (string_is_equal(language,"pt_pt"))
825       return "816";
826    else if (string_is_equal(language,"pt_bt") || string_is_equal(language,"pt"))
827       return "416";
828    else if (string_is_equal(language,"th"))
829       return "041e";
830    else if (string_is_equal(language,"ja"))
831       return "411";
832    else if (string_is_equal(language,"sk"))
833       return "041b";
834    else if (string_is_equal(language,"hi"))
835       return "439";
836    else if (string_is_equal(language,"ar"))
837       return "401";
838    else if (string_is_equal(language,"hu"))
839       return "040e";
840    else if (string_is_equal(language,"zh_tw") || string_is_equal(language,"zh"))
841       return "804";
842    else if (string_is_equal(language,"el"))
843       return "408";
844    else if (string_is_equal(language,"ru"))
845       return "419";
846    else if (string_is_equal(language,"nb"))
847       return "414";
848    else if (string_is_equal(language,"da"))
849       return "406";
850    else if (string_is_equal(language,"fi"))
851       return "040b";
852    else if (string_is_equal(language,"zh_hk"))
853       return "0c04";
854    else if (string_is_equal(language,"zh_cn"))
855       return "804";
856    else if (string_is_equal(language,"tr"))
857       return "041f";
858    else if (string_is_equal(language,"ko"))
859       return "412";
860    else if (string_is_equal(language,"pl"))
861       return "415";
862    else if (string_is_equal(language,"cs"))
863       return "405";
864    else
865       return "";
866 
867 
868 }
869 
accessibility_win_language_code(const char * language)870 static const char *accessibility_win_language_code(const char* language)
871 {
872    if (string_is_equal(language,"en"))
873       return "Microsoft David Desktop";
874    else if (string_is_equal(language,"it"))
875       return "Microsoft Cosimo Desktop";
876    else if (string_is_equal(language,"sv"))
877       return "Microsoft Bengt Desktop";
878    else if (string_is_equal(language,"fr"))
879       return "Microsoft Paul Desktop";
880    else if (string_is_equal(language,"de"))
881       return "Microsoft Stefan Desktop";
882    else if (string_is_equal(language,"he"))
883       return "Microsoft Asaf Desktop";
884    else if (string_is_equal(language,"id"))
885       return "Microsoft Andika Desktop";
886    else if (string_is_equal(language,"es"))
887       return "Microsoft Pablo Desktop";
888    else if (string_is_equal(language,"nl"))
889       return "Microsoft Frank Desktop";
890    else if (string_is_equal(language,"ro"))
891       return "Microsoft Andrei Desktop";
892    else if (string_is_equal(language,"pt_pt"))
893       return "Microsoft Helia Desktop";
894    else if (string_is_equal(language,"pt_bt") || string_is_equal(language,"pt"))
895       return "Microsoft Daniel Desktop";
896    else if (string_is_equal(language,"th"))
897       return "Microsoft Pattara Desktop";
898    else if (string_is_equal(language,"ja"))
899       return "Microsoft Ichiro Desktop";
900    else if (string_is_equal(language,"sk"))
901       return "Microsoft Filip Desktop";
902    else if (string_is_equal(language,"hi"))
903       return "Microsoft Hemant Desktop";
904    else if (string_is_equal(language,"ar"))
905       return "Microsoft Naayf Desktop";
906    else if (string_is_equal(language,"hu"))
907       return "Microsoft Szabolcs Desktop";
908    else if (string_is_equal(language,"zh_tw") || string_is_equal(language,"zh"))
909       return "Microsoft Zhiwei Desktop";
910    else if (string_is_equal(language,"el"))
911       return "Microsoft Stefanos Desktop";
912    else if (string_is_equal(language,"ru"))
913       return "Microsoft Pavel Desktop";
914    else if (string_is_equal(language,"nb"))
915       return "Microsoft Jon Desktop";
916    else if (string_is_equal(language,"da"))
917       return "Microsoft Helle Desktop";
918    else if (string_is_equal(language,"fi"))
919       return "Microsoft Heidi Desktop";
920    else if (string_is_equal(language,"zh_hk"))
921       return "Microsoft Danny Desktop";
922    else if (string_is_equal(language,"zh_cn"))
923       return "Microsoft Kangkang Desktop";
924    else if (string_is_equal(language,"tr"))
925       return "Microsoft Tolga Desktop";
926    else if (string_is_equal(language,"ko"))
927       return "Microsoft Heami Desktop";
928    else if (string_is_equal(language,"pl"))
929       return "Microsoft Adam Desktop";
930    else if (string_is_equal(language,"cs"))
931       return "Microsoft Jakub Desktop";
932    else
933       return "";
934 }
935 
terminate_win32_process(PROCESS_INFORMATION pi)936 static bool terminate_win32_process(PROCESS_INFORMATION pi)
937 {
938    TerminateProcess(pi.hProcess,0);
939    CloseHandle(pi.hProcess);
940    CloseHandle(pi.hThread);
941    return true;
942 }
943 
944 static PROCESS_INFORMATION g_pi;
945 
create_win32_process(char * cmd,const char * input)946 static bool create_win32_process(char* cmd, const char * input)
947 {
948    STARTUPINFO si;
949    HANDLE rd = NULL;
950    bool ret;
951    memset(&si, 0, sizeof(si));
952    si.cb = sizeof(si);
953    memset(&g_pi, 0, sizeof(g_pi));
954 
955    if (input)
956    {
957       DWORD dummy;
958       HANDLE wr;
959       if (!CreatePipe(&rd, &wr, NULL, strlen(input))) return false;
960 
961       SetHandleInformation(rd, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
962 
963       WriteFile(wr, input, strlen(input), &dummy, NULL);
964       CloseHandle(wr);
965 
966       si.dwFlags |= STARTF_USESTDHANDLES;
967       si.hStdInput = rd;
968       si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
969       si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
970    }
971 
972    ret = CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW,
973                       NULL, NULL, &si, &g_pi);
974    if (rd) CloseHandle(rd);
975    return ret;
976 }
977 
is_narrator_running_windows(void)978 static bool is_narrator_running_windows(void)
979 {
980    DWORD status = 0;
981 #ifdef HAVE_NVDA
982    init_nvda();
983 #endif
984 
985    if (USE_POWERSHELL)
986    {
987       if (pi_set == false)
988          return false;
989       if (GetExitCodeProcess(g_pi.hProcess, &status))
990       {
991          if (status == STILL_ACTIVE)
992             return true;
993       }
994       return false;
995    }
996 #ifdef HAVE_NVDA
997    else if (USE_NVDA)
998    {
999       long res;
1000       res = nvdaController_testIfRunning_func();
1001 
1002       if (res != 0)
1003       {
1004          /* The running nvda service wasn't found, so revert
1005             back to the powershell method
1006          */
1007          RARCH_LOG("Error communicating with NVDA\n");
1008          USE_POWERSHELL = true;
1009          USE_NVDA       = false;
1010 	 return false;
1011       }
1012       return false;
1013    }
1014 #endif
1015 #ifdef HAVE_SAPI
1016    else
1017    {
1018       SPVOICESTATUS pStatus;
1019       if (pVoice)
1020       {
1021          ISpVoice_GetStatus(pVoice, &pStatus, NULL);
1022          if (pStatus.dwRunningState == SPRS_IS_SPEAKING)
1023             return true;
1024       }
1025    }
1026 #endif
1027    return false;
1028 }
1029 
accessibility_speak_windows(int speed,const char * speak_text,int priority)1030 static bool accessibility_speak_windows(int speed,
1031       const char* speak_text, int priority)
1032 {
1033    char cmd[512];
1034    const char *voice      = get_user_language_iso639_1(true);
1035    const char *language   = accessibility_win_language_code(voice);
1036    const char *langid     = accessibility_win_language_id(voice);
1037    bool res               = false;
1038    const char* speeds[10] = {"-10", "-7.5", "-5", "-2.5", "0", "2", "4", "6", "8", "10"};
1039    size_t nbytes_cmd = 0;
1040    if (speed < 1)
1041       speed = 1;
1042    else if (speed > 10)
1043       speed = 10;
1044 
1045    if (priority < 10)
1046    {
1047       if (is_narrator_running_windows())
1048          return true;
1049 
1050    }
1051 #ifdef HAVE_NVDA
1052    init_nvda();
1053 #endif
1054 
1055    if (USE_POWERSHELL)
1056    {
1057       const char * template_lang = "powershell.exe -NoProfile -WindowStyle Hidden -Command \"Add-Type -AssemblyName System.Speech; $synth = New-Object System.Speech.Synthesis.SpeechSynthesizer; $synth.SelectVoice(\\\"%s\\\"); $synth.Rate = %s; $synth.Speak($input);\"";
1058       const char * template_nolang = "powershell.exe -NoProfile -WindowStyle Hidden -Command \"Add-Type -AssemblyName System.Speech; $synth = New-Object System.Speech.Synthesis.SpeechSynthesizer; $synth.Rate = %s; $synth.Speak($input);\"";
1059       if (strlen(language) > 0)
1060          snprintf(cmd, sizeof(cmd), template_lang, language, speeds[speed-1]);
1061       else
1062          snprintf(cmd, sizeof(cmd), template_nolang, speeds[speed-1]);
1063 
1064       if (pi_set)
1065          terminate_win32_process(g_pi);
1066       pi_set = create_win32_process(cmd, speak_text);
1067    }
1068 #ifdef HAVE_NVDA
1069    else if (USE_NVDA)
1070    {
1071       wchar_t        *wc = utf8_to_utf16_string_alloc(speak_text);
1072       long res           = nvdaController_testIfRunning_func();
1073 
1074       if (!wc || res != 0)
1075       {
1076          RARCH_LOG("Error communicating with NVDA\n");
1077          if (wc)
1078             free(wc);
1079          return false;
1080       }
1081 
1082       nvdaController_cancelSpeech_func();
1083 
1084       if (USE_NVDA_BRAILLE)
1085          nvdaController_brailleMessage_func(wc);
1086       else
1087          nvdaController_speakText_func(wc);
1088       free(wc);
1089    }
1090 #endif
1091 #ifdef HAVE_SAPI
1092    else
1093    {
1094       HRESULT hr;
1095       /* stop the old voice if running */
1096       if (pVoice)
1097       {
1098          CoUninitialize();
1099          ISpVoice_Release(pVoice);
1100       }
1101       pVoice = NULL;
1102 
1103       /* Play the new voice */
1104       if (FAILED(CoInitialize(NULL)))
1105          return NULL;
1106 
1107       hr = CoCreateInstance(&CLSID_SpVoice, NULL,
1108             CLSCTX_ALL, &IID_ISpVoice, (void **)&pVoice);
1109 
1110       if (SUCCEEDED(hr))
1111       {
1112          wchar_t        *wc = utf8_to_utf16_string_alloc(speak_text);
1113          if (!wc)
1114             return false;
1115          hr = ISpVoice_Speak(pVoice, wc, SPF_ASYNC /*SVSFlagsAsync*/, NULL);
1116          free(wc);
1117       }
1118    }
1119 #endif
1120 
1121    return true;
1122 }
1123 #endif
1124 
1125 frontend_ctx_driver_t frontend_ctx_win32 = {
1126    frontend_win32_env_get,         /* env_get   */
1127    frontend_win32_init,            /* init      */
1128    NULL,                           /* deinit    */
1129 #if defined(_WIN32) && !defined(_XBOX)
1130    frontend_win32_respawn,         /* exitspawn */
1131 #else
1132    NULL,                           /* exitspawn */
1133 #endif
1134    NULL,                           /* process_args */
1135    NULL,                           /* exec */
1136 #if defined(_WIN32) && !defined(_XBOX)
1137    frontend_win32_set_fork,        /* set_fork */
1138 #else
1139    NULL,                           /* set_fork */
1140 #endif
1141    NULL,                           /* shutdown                  */
1142    NULL,                           /* get_name                  */
1143    frontend_win32_get_os,
1144    NULL,                           /* get_rating                */
1145    NULL,                           /* content_loaded            */
1146    frontend_win32_get_arch,        /* get_architecture          */
1147    frontend_win32_get_powerstate,
1148    frontend_win32_parse_drive_list,
1149    frontend_win32_get_total_mem,
1150    frontend_win32_get_free_mem,
1151    NULL,                            /* install_signal_handler   */
1152    NULL,                            /* get_sighandler_state     */
1153    NULL,                            /* set_sighandler_state     */
1154    NULL,                            /* destroy_sighandler_state */
1155    frontend_win32_attach_console,   /* attach_console           */
1156    frontend_win32_detach_console,   /* detach_console           */
1157    NULL,                            /* get_lakka_version        */
1158    NULL,                            /* set_screen_brightness    */
1159    NULL,                            /* watch_path_for_changes   */
1160    NULL,                            /* check_for_path_changes   */
1161    NULL,                            /* set_sustained_performance_mode */
1162    frontend_win32_get_cpu_model_name,
1163    frontend_win32_get_user_language,
1164 #if defined(_WIN32) && !defined(_XBOX)
1165    is_narrator_running_windows,     /* is_narrator_running */
1166    accessibility_speak_windows,     /* accessibility_speak */
1167 #else
1168    NULL,                            /* is_narrator_running */
1169    NULL,                            /* accessibility_speak */
1170 #endif
1171    "win32",                         /* ident               */
1172    NULL                             /* get_video_driver    */
1173 };
1174