2012-11-01 15:19:01 +00:00
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
2012-11-04 22:01:49 +00:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 15:19:01 +00:00
// This program 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. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
2018-03-23 21:54:12 +00:00
# include "stdafx.h"
2015-12-26 03:14:05 +00:00
# include <algorithm>
2016-12-05 15:51:28 +00:00
# include <cmath>
2020-03-09 01:59:17 +00:00
# include <functional>
2014-02-13 14:57:36 +00:00
2013-07-29 04:01:49 +00:00
# include "Common/CommonWindows.h"
2020-10-04 18:48:47 +00:00
# include "Common/File/FileUtil.h"
2017-02-14 09:33:42 +00:00
# include "Common/OSVersion.h"
2020-10-04 21:24:14 +00:00
# include "Common/GPU/Vulkan/VulkanLoader.h"
2019-05-10 21:25:57 +00:00
# include "ppsspp_config.h"
2017-02-14 09:33:42 +00:00
2016-12-05 15:51:28 +00:00
# include <Wbemidl.h>
# include <shellapi.h>
2019-05-21 21:22:56 +00:00
# include <ShlObj.h>
2016-12-05 15:51:28 +00:00
# include <mmsystem.h>
2012-11-01 15:19:01 +00:00
2020-10-04 08:10:55 +00:00
# include "Common/System/Display.h"
2020-10-04 08:30:18 +00:00
# include "Common/System/NativeApp.h"
# include "Common/System/System.h"
2020-10-03 22:25:21 +00:00
# include "Common/File/VFS/VFS.h"
# include "Common/File/VFS/AssetReader.h"
2020-10-01 11:05:04 +00:00
# include "Common/Data/Text/I18n.h"
2020-10-04 08:04:01 +00:00
# include "Common/Profiler/Profiler.h"
2020-10-01 07:27:25 +00:00
# include "Common/Thread/ThreadUtil.h"
2020-10-01 11:05:04 +00:00
# include "Common/Data/Encoding/Utf8.h"
2020-10-04 18:48:47 +00:00
# include "Common/Net/Resolve.h"
2012-11-01 15:19:01 +00:00
2013-07-29 04:01:49 +00:00
# include "Core/Config.h"
2018-06-17 01:42:31 +00:00
# include "Core/ConfigValues.h"
2013-07-29 04:01:49 +00:00
# include "Core/SaveState.h"
# include "Windows/EmuThread.h"
2017-06-27 09:46:10 +00:00
# include "Windows/WindowsAudio.h"
2013-01-08 16:03:17 +00:00
# include "ext/disarm.h"
2012-11-01 15:19:01 +00:00
2013-07-29 04:01:49 +00:00
# include "Common/LogManager.h"
# include "Common/ConsoleListener.h"
2020-09-29 10:19:22 +00:00
# include "Common/StringUtils.h"
2012-11-01 15:19:01 +00:00
# include "Commctrl.h"
2015-10-04 10:24:59 +00:00
# include "UI/GameInfoCache.h"
2012-11-01 15:19:01 +00:00
# include "Windows/resource.h"
2015-09-19 11:14:05 +00:00
# include "Windows/MainWindow.h"
2013-09-22 17:27:09 +00:00
# include "Windows/Debugger/Debugger_Disasm.h"
2012-11-01 15:19:01 +00:00
# include "Windows/Debugger/Debugger_MemoryDlg.h"
# include "Windows/Debugger/Debugger_VFPUDlg.h"
2019-05-10 21:25:57 +00:00
# if PPSSPP_API(ANY_GL)
2013-09-22 18:03:29 +00:00
# include "Windows/GEDebugger/GEDebugger.h"
2019-05-03 22:06:50 +00:00
# endif
2021-10-08 02:53:40 +00:00
# include "Windows/W32Util/ContextMenu.h"
2012-11-01 15:19:01 +00:00
# include "Windows/W32Util/DialogManager.h"
2019-02-17 16:03:10 +00:00
# include "Windows/W32Util/ShellUtil.h"
2012-11-01 15:19:01 +00:00
# include "Windows/Debugger/CtrlDisAsmView.h"
# include "Windows/Debugger/CtrlMemView.h"
# include "Windows/Debugger/CtrlRegisterList.h"
2013-12-08 10:16:18 +00:00
# include "Windows/InputBox.h"
2012-11-01 15:19:01 +00:00
# include "Windows/WindowsHost.h"
# include "Windows/main.h"
2016-04-22 03:03:08 +00:00
2017-08-28 11:45:04 +00:00
// Nvidia OpenGL drivers >= v302 will check if the application exports a global
2014-03-03 16:26:27 +00:00
// variable named NvOptimusEnablement to know if it should run the app in high
// performance graphics mode or using the IGP.
extern " C " {
__declspec ( dllexport ) DWORD NvOptimusEnablement = 1 ;
}
2018-04-15 06:41:02 +00:00
// Also on AMD PowerExpress: https://community.amd.com/thread/169965
extern " C " {
__declspec ( dllexport ) int AmdPowerXpressRequestHighPerformance = 1 ;
}
2019-05-10 21:25:57 +00:00
# if PPSSPP_API(ANY_GL)
2020-09-23 22:17:31 +00:00
CGEDebugger * geDebuggerWindow = nullptr ;
2019-05-03 22:06:50 +00:00
# endif
2018-04-15 06:41:02 +00:00
2020-09-23 22:17:31 +00:00
CDisasm * disasmWindow = nullptr ;
CMemoryDlg * memoryWindow = nullptr ;
2021-11-13 21:24:30 +00:00
CVFPUDlg * vfpudlg = nullptr ;
2012-11-01 15:19:01 +00:00
2013-09-04 10:07:42 +00:00
static std : : string langRegion ;
2013-10-09 21:17:28 +00:00
static std : : string osName ;
2014-07-31 05:21:37 +00:00
static std : : string gpuDriverVersion ;
2013-09-04 10:07:42 +00:00
2020-05-09 20:52:04 +00:00
static std : : string restartArgs ;
2017-08-31 15:13:18 +00:00
int g_activeWindow = 0 ;
2021-02-18 15:27:28 +00:00
static std : : thread inputBoxThread ;
static bool inputBoxRunning = false ;
2019-05-21 21:22:56 +00:00
void OpenDirectory ( const char * path ) {
2021-10-09 22:58:49 +00:00
SFGAOF flags ;
PIDLIST_ABSOLUTE pidl = nullptr ;
HRESULT hr = SHParseDisplayName ( ConvertUTF8ToWString ( ReplaceAll ( path , " / " , " \\ " ) ) . c_str ( ) , nullptr , & pidl , 0 , & flags ) ;
2019-05-21 21:22:56 +00:00
if ( pidl ) {
2021-10-09 22:58:49 +00:00
if ( SUCCEEDED ( hr ) )
SHOpenFolderAndSelectItems ( pidl , 0 , nullptr , 0 ) ;
CoTaskMemFree ( pidl ) ;
2019-05-21 21:22:56 +00:00
}
}
2013-08-26 17:00:16 +00:00
void LaunchBrowser ( const char * url ) {
ShellExecute ( NULL , L " open " , ConvertUTF8ToWString ( url ) . c_str ( ) , NULL , NULL , SW_SHOWNORMAL ) ;
2013-08-17 22:41:19 +00:00
}
2013-10-10 15:00:15 +00:00
void Vibrate ( int length_ms ) {
// Ignore on PC
}
2014-07-31 05:21:37 +00:00
// Adapted mostly as-is from http://www.gamedev.net/topic/495075-how-to-retrieve-info-about-videocard/?view=findpost&p=4229170
// so credit goes to that post's author, and in turn, the author of the site mentioned in that post (which seems to be down?).
2014-07-31 08:00:48 +00:00
std : : string GetVideoCardDriverVersion ( ) {
2014-07-31 05:21:37 +00:00
std : : string retvalue = " " ;
HRESULT hr ;
hr = CoInitializeEx ( NULL , COINIT_MULTITHREADED ) ;
2014-08-02 10:49:09 +00:00
if ( FAILED ( hr ) ) {
return retvalue ;
}
2014-07-31 05:21:37 +00:00
IWbemLocator * pIWbemLocator = NULL ;
2020-01-05 17:04:07 +00:00
hr = CoCreateInstance ( __uuidof ( WbemLocator ) , NULL , CLSCTX_INPROC_SERVER ,
2014-07-31 05:21:37 +00:00
__uuidof ( IWbemLocator ) , ( LPVOID * ) & pIWbemLocator ) ;
2014-08-02 10:49:09 +00:00
if ( FAILED ( hr ) ) {
CoUninitialize ( ) ;
return retvalue ;
}
2014-07-31 05:21:37 +00:00
BSTR bstrServer = SysAllocString ( L " \\ \\ . \\ root \\ cimv2 " ) ;
IWbemServices * pIWbemServices ;
hr = pIWbemLocator - > ConnectServer ( bstrServer , NULL , NULL , 0L , 0L , NULL , NULL , & pIWbemServices ) ;
2014-08-02 10:49:09 +00:00
if ( FAILED ( hr ) ) {
pIWbemLocator - > Release ( ) ;
2015-01-17 22:00:54 +00:00
SysFreeString ( bstrServer ) ;
2014-08-02 10:49:09 +00:00
CoUninitialize ( ) ;
return retvalue ;
}
2014-07-31 05:21:37 +00:00
2020-01-05 17:04:07 +00:00
hr = CoSetProxyBlanket ( pIWbemServices , RPC_C_AUTHN_WINNT , RPC_C_AUTHZ_NONE ,
2014-07-31 05:21:37 +00:00
NULL , RPC_C_AUTHN_LEVEL_CALL , RPC_C_IMP_LEVEL_IMPERSONATE , NULL , EOAC_DEFAULT ) ;
2020-01-05 17:04:07 +00:00
2014-07-31 05:21:37 +00:00
BSTR bstrWQL = SysAllocString ( L " WQL " ) ;
BSTR bstrPath = SysAllocString ( L " select * from Win32_VideoController " ) ;
IEnumWbemClassObject * pEnum ;
hr = pIWbemServices - > ExecQuery ( bstrWQL , bstrPath , WBEM_FLAG_FORWARD_ONLY , NULL , & pEnum ) ;
ULONG uReturned ;
VARIANT var ;
2014-08-02 10:49:09 +00:00
IWbemClassObject * pObj = NULL ;
if ( ! FAILED ( hr ) ) {
hr = pEnum - > Next ( WBEM_INFINITE , 1 , & pObj , & uReturned ) ;
}
2014-07-31 05:21:37 +00:00
2015-04-08 18:35:40 +00:00
if ( ! FAILED ( hr ) & & uReturned ) {
2014-07-31 05:21:37 +00:00
hr = pObj - > Get ( L " DriverVersion " , 0 , & var , NULL , NULL ) ;
if ( SUCCEEDED ( hr ) ) {
char str [ MAX_PATH ] ;
WideCharToMultiByte ( CP_ACP , 0 , var . bstrVal , - 1 , str , sizeof ( str ) , NULL , NULL ) ;
retvalue = str ;
}
}
pEnum - > Release ( ) ;
SysFreeString ( bstrPath ) ;
SysFreeString ( bstrWQL ) ;
pIWbemServices - > Release ( ) ;
2014-08-02 10:49:09 +00:00
pIWbemLocator - > Release ( ) ;
2014-07-31 05:21:37 +00:00
SysFreeString ( bstrServer ) ;
CoUninitialize ( ) ;
return retvalue ;
}
2013-09-04 09:29:38 +00:00
std : : string System_GetProperty ( SystemProperty prop ) {
2014-08-02 10:49:09 +00:00
static bool hasCheckedGPUDriverVersion = false ;
2013-09-04 09:29:38 +00:00
switch ( prop ) {
case SYSPROP_NAME :
2013-10-09 21:17:28 +00:00
return osName ;
2013-09-04 09:29:38 +00:00
case SYSPROP_LANGREGION :
2013-09-04 10:07:42 +00:00
return langRegion ;
2014-07-21 09:59:47 +00:00
case SYSPROP_CLIPBOARD_TEXT :
{
std : : string retval ;
if ( OpenClipboard ( MainWindow : : GetDisplayHWND ( ) ) ) {
HANDLE handle = GetClipboardData ( CF_UNICODETEXT ) ;
const wchar_t * wstr = ( const wchar_t * ) GlobalLock ( handle ) ;
2014-07-21 12:54:17 +00:00
if ( wstr )
retval = ConvertWStringToUTF8 ( wstr ) ;
else
retval = " " ;
2014-07-21 09:59:47 +00:00
GlobalUnlock ( handle ) ;
CloseClipboard ( ) ;
}
return retval ;
}
2014-07-31 05:21:37 +00:00
case SYSPROP_GPUDRIVER_VERSION :
2014-08-02 10:49:09 +00:00
if ( ! hasCheckedGPUDriverVersion ) {
hasCheckedGPUDriverVersion = true ;
gpuDriverVersion = GetVideoCardDriverVersion ( ) ;
}
2014-07-31 05:21:37 +00:00
return gpuDriverVersion ;
2013-09-04 09:29:38 +00:00
default :
return " " ;
}
2013-08-17 22:41:19 +00:00
}
2021-01-06 15:37:04 +00:00
std : : vector < std : : string > System_GetPropertyStringVec ( SystemProperty prop ) {
2021-01-09 22:45:49 +00:00
std : : vector < std : : string > result ;
2021-01-06 15:37:04 +00:00
switch ( prop ) {
2021-01-09 22:45:49 +00:00
case SYSPROP_TEMP_DIRS :
{
std : : wstring tempPath ( MAX_PATH , ' \0 ' ) ;
size_t sz = GetTempPath ( ( DWORD ) tempPath . size ( ) , & tempPath [ 0 ] ) ;
if ( sz > = tempPath . size ( ) ) {
tempPath . resize ( sz ) ;
sz = GetTempPath ( ( DWORD ) tempPath . size ( ) , & tempPath [ 0 ] ) ;
}
// Need to resize off the null terminator either way.
tempPath . resize ( sz ) ;
result . push_back ( ConvertWStringToUTF8 ( tempPath ) ) ;
if ( getenv ( " TMPDIR " ) & & strlen ( getenv ( " TMPDIR " ) ) ! = 0 )
result . push_back ( getenv ( " TMPDIR " ) ) ;
2021-01-10 19:33:17 +00:00
if ( getenv ( " TMP " ) & & strlen ( getenv ( " TMP " ) ) ! = 0 )
2021-01-09 22:45:49 +00:00
result . push_back ( getenv ( " TMP " ) ) ;
2021-01-10 19:33:17 +00:00
if ( getenv ( " TEMP " ) & & strlen ( getenv ( " TEMP " ) ) ! = 0 )
2021-01-09 22:45:49 +00:00
result . push_back ( getenv ( " TEMP " ) ) ;
return result ;
}
2021-01-06 15:37:04 +00:00
default :
2021-01-09 22:45:49 +00:00
return result ;
2021-01-06 15:37:04 +00:00
}
}
2015-01-11 20:00:56 +00:00
// Ugly!
extern WindowsAudioBackend * winAudioBackend ;
2017-03-17 12:22:00 +00:00
# ifdef _WIN32
# if PPSSPP_PLATFORM(UWP)
static int ScreenDPI ( ) {
return 96 ; // TODO UWP
}
# else
static int ScreenDPI ( ) {
HDC screenDC = GetDC ( nullptr ) ;
int dotsPerInch = GetDeviceCaps ( screenDC , LOGPIXELSY ) ;
ReleaseDC ( nullptr , screenDC ) ;
2017-04-30 06:54:34 +00:00
return dotsPerInch ? dotsPerInch : 96 ;
2017-03-17 12:22:00 +00:00
}
# endif
# endif
2014-07-20 10:11:50 +00:00
int System_GetPropertyInt ( SystemProperty prop ) {
2015-01-11 13:18:40 +00:00
switch ( prop ) {
case SYSPROP_AUDIO_SAMPLE_RATE :
2015-01-11 20:00:56 +00:00
return winAudioBackend ? winAudioBackend - > GetSampleRate ( ) : - 1 ;
2015-10-06 17:17:29 +00:00
case SYSPROP_DEVICE_TYPE :
return DEVICE_TYPE_DESKTOP ;
2017-07-30 15:33:02 +00:00
case SYSPROP_DISPLAY_COUNT :
return GetSystemMetrics ( SM_CMONITORS ) ;
2022-01-10 00:11:08 +00:00
case SYSPROP_KEYBOARD_LAYOUT :
{
HKL localeId = GetKeyboardLayout ( 0 ) ;
// TODO: Is this list complete enough?
switch ( ( int ) ( intptr_t ) localeId & 0xFFFF ) {
case 0x407 :
return KEYBOARD_LAYOUT_QWERTZ ;
case 0x040c :
case 0x080c :
case 0x1009 :
return KEYBOARD_LAYOUT_AZERTY ;
default :
return KEYBOARD_LAYOUT_QWERTY ;
}
}
2017-04-30 00:35:12 +00:00
default :
return - 1 ;
}
}
2020-01-05 17:04:07 +00:00
float System_GetPropertyFloat ( SystemProperty prop ) {
switch ( prop ) {
case SYSPROP_DISPLAY_REFRESH_RATE :
return 60.f ;
case SYSPROP_DISPLAY_DPI :
return ( float ) ScreenDPI ( ) ;
2020-04-06 00:06:36 +00:00
case SYSPROP_DISPLAY_SAFE_INSET_LEFT :
case SYSPROP_DISPLAY_SAFE_INSET_RIGHT :
case SYSPROP_DISPLAY_SAFE_INSET_TOP :
case SYSPROP_DISPLAY_SAFE_INSET_BOTTOM :
return 0.0f ;
2020-01-05 17:04:07 +00:00
default :
return - 1 ;
}
}
2017-04-30 00:35:12 +00:00
bool System_GetPropertyBool ( SystemProperty prop ) {
switch ( prop ) {
2017-02-27 10:32:40 +00:00
case SYSPROP_HAS_FILE_BROWSER :
2020-12-20 00:28:43 +00:00
case SYSPROP_HAS_FOLDER_BROWSER :
2017-04-30 00:35:12 +00:00
return true ;
2017-04-02 17:50:09 +00:00
case SYSPROP_HAS_IMAGE_BROWSER :
2017-04-30 00:35:12 +00:00
return true ;
2017-03-07 09:33:53 +00:00
case SYSPROP_HAS_BACK_BUTTON :
2017-04-30 00:35:12 +00:00
return true ;
2017-04-05 14:21:08 +00:00
case SYSPROP_APP_GOLD :
# ifdef GOLD
2017-04-30 00:35:12 +00:00
return true ;
2017-04-05 14:21:08 +00:00
# else
2017-04-30 00:35:12 +00:00
return false ;
2017-04-05 14:21:08 +00:00
# endif
2021-02-21 21:02:11 +00:00
case SYSPROP_CAN_JIT :
return true ;
2021-04-26 05:54:28 +00:00
case SYSPROP_HAS_KEYBOARD :
return true ;
2022-07-10 20:34:44 +00:00
case SYSPROP_SUPPORTS_OPEN_FILE_IN_EDITOR :
return true ; // FileUtil.cpp: OpenFileInEditor
2015-01-11 13:18:40 +00:00
default :
2017-04-30 00:35:12 +00:00
return false ;
2015-01-11 13:18:40 +00:00
}
2014-07-20 10:11:50 +00:00
}
2014-04-26 05:00:45 +00:00
void System_SendMessage ( const char * command , const char * parameter ) {
if ( ! strcmp ( command , " finish " ) ) {
2017-04-15 23:33:30 +00:00
if ( ! NativeIsRestarting ( ) ) {
PostMessage ( MainWindow : : GetHWND ( ) , WM_CLOSE , 0 , 0 ) ;
}
} else if ( ! strcmp ( command , " graphics_restart " ) ) {
2020-05-09 20:52:04 +00:00
restartArgs = parameter = = nullptr ? " " : parameter ;
2017-05-21 15:24:40 +00:00
if ( IsDebuggerPresent ( ) ) {
PostMessage ( MainWindow : : GetHWND ( ) , MainWindow : : WM_USER_RESTART_EMUTHREAD , 0 , 0 ) ;
} else {
g_Config . bRestartRequired = true ;
PostMessage ( MainWindow : : GetHWND ( ) , WM_CLOSE , 0 , 0 ) ;
}
2020-03-28 13:19:11 +00:00
} else if ( ! strcmp ( command , " graphics_failedBackend " ) ) {
auto err = GetI18NCategory ( " Error " ) ;
2020-05-09 06:20:37 +00:00
const char * backendSwitchError = err - > T ( " GenericBackendSwitchCrash " , " PPSSPP crashed while starting. This usually means a graphics driver problem. Try upgrading your graphics drivers. \n \n Graphics backend has been switched: " ) ;
2020-03-28 13:19:11 +00:00
std : : wstring full_error = ConvertUTF8ToWString ( StringFromFormat ( " %s %s " , backendSwitchError , parameter ) ) ;
std : : wstring title = ConvertUTF8ToWString ( err - > T ( " GenericGraphicsError " , " Graphics Error " ) ) ;
MessageBox ( MainWindow : : GetHWND ( ) , full_error . c_str ( ) , title . c_str ( ) , MB_OK ) ;
2014-07-21 15:36:25 +00:00
} else if ( ! strcmp ( command , " setclipboardtext " ) ) {
if ( OpenClipboard ( MainWindow : : GetDisplayHWND ( ) ) ) {
std : : wstring data = ConvertUTF8ToWString ( parameter ) ;
HANDLE handle = GlobalAlloc ( GMEM_MOVEABLE , ( data . size ( ) + 1 ) * sizeof ( wchar_t ) ) ;
wchar_t * wstr = ( wchar_t * ) GlobalLock ( handle ) ;
memcpy ( wstr , data . c_str ( ) , ( data . size ( ) + 1 ) * sizeof ( wchar_t ) ) ;
GlobalUnlock ( wstr ) ;
SetClipboardData ( CF_UNICODETEXT , handle ) ;
GlobalFree ( handle ) ;
CloseClipboard ( ) ;
}
2017-04-02 17:50:09 +00:00
} else if ( ! strcmp ( command , " browse_file " ) ) {
MainWindow : : BrowseAndBoot ( " " ) ;
2019-02-17 16:03:10 +00:00
} else if ( ! strcmp ( command , " browse_folder " ) ) {
2020-01-26 18:43:18 +00:00
auto mm = GetI18NCategory ( " MainMenu " ) ;
2019-02-17 16:03:10 +00:00
std : : string folder = W32Util : : BrowseForFolder ( MainWindow : : GetHWND ( ) , mm - > T ( " Choose folder " ) ) ;
if ( folder . size ( ) )
NativeMessageReceived ( " browse_folderSelect " , folder . c_str ( ) ) ;
2017-04-02 17:50:09 +00:00
} else if ( ! strcmp ( command , " bgImage_browse " ) ) {
MainWindow : : BrowseBackground ( ) ;
2017-07-30 14:51:53 +00:00
} else if ( ! strcmp ( command , " toggle_fullscreen " ) ) {
2021-01-09 22:17:41 +00:00
bool flag = ! MainWindow : : IsFullscreen ( ) ;
2017-07-30 14:51:53 +00:00
if ( strcmp ( parameter , " 0 " ) = = 0 ) {
flag = false ;
} else if ( strcmp ( parameter , " 1 " ) = = 0 ) {
flag = true ;
}
MainWindow : : SendToggleFullscreen ( flag ) ;
2014-04-26 05:00:45 +00:00
}
}
2013-12-04 16:41:59 +00:00
2015-12-17 21:41:50 +00:00
void System_AskForPermission ( SystemPermission permission ) { }
PermissionStatus System_GetPermissionStatus ( SystemPermission permission ) { return PERMISSION_STATUS_GRANTED ; }
2015-01-17 20:32:58 +00:00
void EnableCrashingOnCrashes ( ) {
typedef BOOL ( WINAPI * tGetPolicy ) ( LPDWORD lpFlags ) ;
typedef BOOL ( WINAPI * tSetPolicy ) ( DWORD dwFlags ) ;
const DWORD EXCEPTION_SWALLOWING = 0x1 ;
HMODULE kernel32 = LoadLibrary ( L " kernel32.dll " ) ;
tGetPolicy pGetPolicy = ( tGetPolicy ) GetProcAddress ( kernel32 ,
" GetProcessUserModeExceptionPolicy " ) ;
tSetPolicy pSetPolicy = ( tSetPolicy ) GetProcAddress ( kernel32 ,
" SetProcessUserModeExceptionPolicy " ) ;
if ( pGetPolicy & & pSetPolicy ) {
DWORD dwFlags ;
if ( pGetPolicy ( & dwFlags ) ) {
// Turn off the filter.
pSetPolicy ( dwFlags & ~ EXCEPTION_SWALLOWING ) ;
}
}
FreeLibrary ( kernel32 ) ;
2013-11-12 16:50:35 +00:00
}
2020-03-09 01:59:17 +00:00
void System_InputBoxGetString ( const std : : string & title , const std : : string & defaultValue , std : : function < void ( bool , const std : : string & ) > cb ) {
2021-02-18 15:27:28 +00:00
if ( inputBoxRunning ) {
inputBoxThread . join ( ) ;
2013-12-08 10:16:18 +00:00
}
2021-02-18 15:27:28 +00:00
inputBoxRunning = true ;
inputBoxThread = std : : thread ( [ = ] {
std : : string out ;
if ( InputBox_GetString ( MainWindow : : GetHInstance ( ) , MainWindow : : GetHWND ( ) , ConvertUTF8ToWString ( title ) . c_str ( ) , defaultValue , out ) ) {
NativeInputBoxReceived ( cb , true , out ) ;
} else {
NativeInputBoxReceived ( cb , false , " " ) ;
}
} ) ;
2013-12-08 10:16:18 +00:00
}
2022-07-10 20:34:44 +00:00
void System_Toast ( const char * text ) {
// Not-very-good implementation. Will normally not be used on Windows anyway.
std : : wstring str = ConvertUTF8ToWString ( text ) ;
MessageBox ( 0 , str . c_str ( ) , L " Toast! " , MB_ICONINFORMATION ) ;
}
2015-12-26 03:14:05 +00:00
static std : : string GetDefaultLangRegion ( ) {
wchar_t lcLangName [ 256 ] = { } ;
// LOCALE_SNAME is only available in WinVista+
if ( 0 ! = GetLocaleInfo ( LOCALE_NAME_USER_DEFAULT , LOCALE_SNAME , lcLangName , ARRAY_SIZE ( lcLangName ) ) ) {
std : : string result = ConvertWStringToUTF8 ( lcLangName ) ;
std : : replace ( result . begin ( ) , result . end ( ) , ' - ' , ' _ ' ) ;
return result ;
} else {
// This should work on XP, but we may get numbers for some countries.
if ( 0 ! = GetLocaleInfo ( LOCALE_USER_DEFAULT , LOCALE_SISO639LANGNAME , lcLangName , ARRAY_SIZE ( lcLangName ) ) ) {
wchar_t lcRegion [ 256 ] = { } ;
if ( 0 ! = GetLocaleInfo ( LOCALE_USER_DEFAULT , LOCALE_SISO3166CTRYNAME , lcRegion , ARRAY_SIZE ( lcRegion ) ) ) {
return ConvertWStringToUTF8 ( lcLangName ) + " _ " + ConvertWStringToUTF8 ( lcRegion ) ;
}
}
// Unfortunate default. We tried.
return " en_US " ;
}
}
2014-02-17 08:30:38 +00:00
2019-02-16 18:14:54 +00:00
static const int EXIT_CODE_VULKAN_WORKS = 42 ;
2021-02-15 17:28:07 +00:00
# ifndef _DEBUG
2019-02-16 18:14:54 +00:00
static bool DetectVulkanInExternalProcess ( ) {
2020-01-04 17:02:10 +00:00
std : : wstring workingDirectory ;
std : : wstring moduleFilename ;
W32Util : : GetSelfExecuteParams ( workingDirectory , moduleFilename ) ;
2019-02-16 18:14:54 +00:00
const wchar_t * cmdline = L " --vulkan-available-check " ;
SHELLEXECUTEINFO info { sizeof ( SHELLEXECUTEINFO ) } ;
info . fMask = SEE_MASK_NOCLOSEPROCESS ;
2020-01-04 17:02:10 +00:00
info . lpFile = moduleFilename . c_str ( ) ;
2019-02-16 18:14:54 +00:00
info . lpParameters = cmdline ;
2020-01-04 17:02:10 +00:00
info . lpDirectory = workingDirectory . c_str ( ) ;
2019-02-16 18:14:54 +00:00
info . nShow = SW_HIDE ;
if ( ShellExecuteEx ( & info ) ! = TRUE ) {
return false ;
}
if ( info . hProcess = = nullptr ) {
return false ;
}
DWORD result = WaitForSingleObject ( info . hProcess , 10000 ) ;
DWORD exitCode = 0 ;
if ( result = = WAIT_FAILED | | GetExitCodeProcess ( info . hProcess , & exitCode ) = = 0 ) {
CloseHandle ( info . hProcess ) ;
return false ;
}
CloseHandle ( info . hProcess ) ;
return exitCode = = EXIT_CODE_VULKAN_WORKS ;
}
2021-02-15 17:28:07 +00:00
# endif
2019-02-16 18:14:54 +00:00
2014-08-31 05:17:25 +00:00
std : : vector < std : : wstring > GetWideCmdLine ( ) {
wchar_t * * wargv ;
int wargc = - 1 ;
2020-05-09 20:52:04 +00:00
// This is used for the WM_USER_RESTART_EMUTHREAD path.
if ( ! restartArgs . empty ( ) ) {
std : : wstring wargs = ConvertUTF8ToWString ( " PPSSPP " + restartArgs ) ;
wargv = CommandLineToArgvW ( wargs . c_str ( ) , & wargc ) ;
restartArgs . clear ( ) ;
} else {
wargv = CommandLineToArgvW ( GetCommandLineW ( ) , & wargc ) ;
}
2014-08-31 05:17:25 +00:00
std : : vector < std : : wstring > wideArgs ( wargv , wargv + wargc ) ;
2018-01-17 10:25:01 +00:00
LocalFree ( wargv ) ;
2014-08-31 05:17:25 +00:00
return wideArgs ;
}
2019-02-16 18:14:54 +00:00
static void WinMainInit ( ) {
2015-01-15 21:32:25 +00:00
CoInitializeEx ( NULL , COINIT_MULTITHREADED ) ;
2017-03-12 16:24:46 +00:00
net : : Init ( ) ; // This needs to happen before we load the config. So on Windows we also run it in Main. It's fine to call multiple times.
2017-03-06 09:51:28 +00:00
// Windows, API init stuff
INITCOMMONCONTROLSEX comm ;
comm . dwSize = sizeof ( comm ) ;
comm . dwICC = ICC_BAR_CLASSES | ICC_LISTVIEW_CLASSES | ICC_TAB_CLASSES ;
InitCommonControlsEx ( & comm ) ;
EnableCrashingOnCrashes ( ) ;
2015-10-04 10:24:59 +00:00
# ifdef _DEBUG
2016-02-10 14:22:28 +00:00
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ) ;
2015-10-04 10:24:59 +00:00
# endif
2015-05-12 17:45:14 +00:00
PROFILE_INIT ( ) ;
2014-02-17 08:30:38 +00:00
2021-03-03 05:49:21 +00:00
# if PPSSPP_ARCH(AMD64) && defined(_MSC_VER) && _MSC_VER < 1900
2014-02-13 14:57:36 +00:00
// FMA3 support in the 2013 CRT is broken on Vista and Windows 7 RTM (fixed in SP1). Just disable it.
_set_FMA3_enable ( 0 ) ;
2014-02-13 15:02:10 +00:00
# endif
2019-02-16 18:14:54 +00:00
}
static void WinMainCleanup ( ) {
2021-02-18 15:27:28 +00:00
if ( inputBoxRunning ) {
inputBoxThread . join ( ) ;
inputBoxRunning = false ;
}
2019-02-16 18:14:54 +00:00
net : : Shutdown ( ) ;
CoUninitialize ( ) ;
2020-05-09 20:52:04 +00:00
if ( g_Config . bRestartRequired ) {
W32Util : : ExitAndRestart ( ! restartArgs . empty ( ) , restartArgs ) ;
}
2019-02-16 18:14:54 +00:00
}
int WINAPI WinMain ( HINSTANCE _hInstance , HINSTANCE hPrevInstance , LPSTR szCmdLine , int iCmdShow ) {
2020-11-30 23:46:26 +00:00
SetCurrentThreadName ( " Main " ) ;
2019-02-16 18:14:54 +00:00
WinMainInit ( ) ;
2014-02-13 14:57:36 +00:00
2013-10-13 19:27:05 +00:00
# ifndef _DEBUG
2013-10-16 05:27:03 +00:00
bool showLog = false ;
2013-10-13 19:27:05 +00:00
# else
2015-12-26 03:14:05 +00:00
bool showLog = true ;
2013-10-13 19:27:05 +00:00
# endif
2021-05-15 16:59:25 +00:00
const Path & exePath = File : : GetExeDirectory ( ) ;
2021-05-15 16:54:28 +00:00
VFSRegister ( " " , new DirectoryAssetReader ( exePath / " assets " ) ) ;
VFSRegister ( " " , new DirectoryAssetReader ( exePath ) ) ;
2013-09-15 09:02:13 +00:00
2015-12-26 03:14:05 +00:00
langRegion = GetDefaultLangRegion ( ) ;
2013-10-09 21:17:28 +00:00
osName = GetWindowsVersion ( ) + " " + GetWindowsSystemArchitecture ( ) ;
2018-01-17 15:36:27 +00:00
std : : string configFilename = " " ;
2014-08-31 05:17:25 +00:00
const std : : wstring configOption = L " --config= " ;
2013-09-15 16:50:42 +00:00
2018-01-17 15:36:27 +00:00
std : : string controlsConfigFilename = " " ;
2014-08-31 05:17:25 +00:00
const std : : wstring controlsOption = L " --controlconfig= " ;
2013-09-15 16:50:42 +00:00
2014-08-31 05:17:25 +00:00
std : : vector < std : : wstring > wideArgs = GetWideCmdLine ( ) ;
2014-09-12 20:58:05 +00:00
for ( size_t i = 1 ; i < wideArgs . size ( ) ; + + i ) {
2014-08-31 05:17:25 +00:00
if ( wideArgs [ i ] [ 0 ] = = L ' \0 ' )
2013-09-15 16:50:42 +00:00
continue ;
2014-08-31 05:17:25 +00:00
if ( wideArgs [ i ] [ 0 ] = = L ' - ' ) {
if ( wideArgs [ i ] . find ( configOption ) ! = std : : wstring : : npos & & wideArgs [ i ] . size ( ) > configOption . size ( ) ) {
const std : : wstring tempWide = wideArgs [ i ] . substr ( configOption . size ( ) ) ;
2018-01-17 15:36:27 +00:00
configFilename = ConvertWStringToUTF8 ( tempWide ) ;
2013-09-15 16:50:42 +00:00
}
2014-08-31 05:17:25 +00:00
if ( wideArgs [ i ] . find ( controlsOption ) ! = std : : wstring : : npos & & wideArgs [ i ] . size ( ) > controlsOption . size ( ) ) {
2014-11-02 19:38:00 +00:00
const std : : wstring tempWide = wideArgs [ i ] . substr ( controlsOption . size ( ) ) ;
2018-01-17 15:36:27 +00:00
controlsConfigFilename = ConvertWStringToUTF8 ( tempWide ) ;
2013-09-15 16:50:42 +00:00
}
}
}
2020-08-15 16:40:50 +00:00
LogManager : : Init ( & g_Config . bEnableLogging ) ;
2017-03-06 10:44:35 +00:00
2020-01-05 17:04:07 +00:00
// On Win32 it makes more sense to initialize the system directories here
2013-10-15 06:28:14 +00:00
// because the next place it was called was in the EmuThread, and it's too late by then.
2021-05-15 16:32:41 +00:00
g_Config . internalDataDirectory = Path ( W32Util : : UserDocumentsPath ( ) ) ;
2013-10-15 06:03:39 +00:00
InitSysDirectories ( ) ;
2013-09-15 16:50:42 +00:00
2020-08-18 07:47:22 +00:00
// Check for the Vulkan workaround before any serious init.
for ( size_t i = 1 ; i < wideArgs . size ( ) ; + + i ) {
if ( wideArgs [ i ] [ 0 ] = = L ' - ' ) {
// This should only be called by DetectVulkanInExternalProcess().
if ( wideArgs [ i ] = = L " --vulkan-available-check " ) {
// Just call it, this way it will crash here if it doesn't work.
// (this is an external process.)
bool result = VulkanMayBeAvailable ( ) ;
LogManager : : Shutdown ( ) ;
WinMainCleanup ( ) ;
return result ? EXIT_CODE_VULKAN_WORKS : EXIT_FAILURE ;
}
}
}
2013-09-12 20:56:18 +00:00
// Load config up here, because those changes below would be overwritten
// if it's not loaded here first.
2021-05-30 10:45:12 +00:00
g_Config . SetSearchPath ( GetSysDirectory ( DIRECTORY_SYSTEM ) ) ;
2018-01-17 15:36:27 +00:00
g_Config . Load ( configFilename . c_str ( ) , controlsConfigFilename . c_str ( ) ) ;
2013-09-12 20:56:18 +00:00
2014-04-01 02:44:05 +00:00
bool debugLogLevel = false ;
2014-09-13 05:33:45 +00:00
const std : : wstring gpuBackend = L " --graphics= " ;
2014-09-12 20:16:13 +00:00
2013-04-01 01:22:27 +00:00
// The rest is handled in NativeInit().
2014-09-12 20:58:05 +00:00
for ( size_t i = 1 ; i < wideArgs . size ( ) ; + + i ) {
2014-08-31 05:17:25 +00:00
if ( wideArgs [ i ] [ 0 ] = = L ' \0 ' )
2012-12-22 17:21:23 +00:00
continue ;
2014-08-31 05:17:25 +00:00
if ( wideArgs [ i ] [ 0 ] = = L ' - ' ) {
switch ( wideArgs [ i ] [ 1 ] ) {
case L ' l ' :
2013-10-16 05:27:03 +00:00
showLog = true ;
2013-10-13 18:25:59 +00:00
g_Config . bEnableLogging = true ;
2012-12-22 17:21:23 +00:00
break ;
2014-08-31 05:17:25 +00:00
case L ' s ' :
2013-01-04 09:26:14 +00:00
g_Config . bAutoRun = false ;
g_Config . bSaveSettings = false ;
2012-12-22 17:21:23 +00:00
break ;
2014-08-31 05:17:25 +00:00
case L ' d ' :
2014-04-01 02:44:05 +00:00
debugLogLevel = true ;
break ;
2012-12-22 17:21:23 +00:00
}
2013-09-12 20:56:18 +00:00
2014-09-12 20:58:05 +00:00
if ( wideArgs [ i ] . find ( gpuBackend ) ! = std : : wstring : : npos & & wideArgs [ i ] . size ( ) > gpuBackend . size ( ) ) {
2014-09-12 20:16:13 +00:00
const std : : wstring restOfOption = wideArgs [ i ] . substr ( gpuBackend . size ( ) ) ;
2014-09-13 05:33:45 +00:00
// Force software rendering off, as picking directx9 or gles implies HW acceleration.
// Once software rendering supports Direct3D9/11, we can add more options for software,
// such as "software-gles", "software-d3d9", and "software-d3d11", or something similar.
// For now, software rendering force-activates OpenGL.
if ( restOfOption = = L " directx9 " ) {
2017-12-26 23:55:24 +00:00
g_Config . iGPUBackend = ( int ) GPUBackend : : DIRECT3D9 ;
2014-09-13 05:33:45 +00:00
g_Config . bSoftwareRendering = false ;
2017-02-16 10:30:58 +00:00
} else if ( restOfOption = = L " directx11 " ) {
2017-12-26 23:55:24 +00:00
g_Config . iGPUBackend = ( int ) GPUBackend : : DIRECT3D11 ;
2017-02-16 10:30:58 +00:00
g_Config . bSoftwareRendering = false ;
2016-02-13 20:22:06 +00:00
} else if ( restOfOption = = L " gles " ) {
2017-12-26 23:55:24 +00:00
g_Config . iGPUBackend = ( int ) GPUBackend : : OPENGL ;
2014-09-13 05:33:45 +00:00
g_Config . bSoftwareRendering = false ;
2017-02-16 10:30:58 +00:00
} else if ( restOfOption = = L " vulkan " ) {
2017-12-26 23:55:24 +00:00
g_Config . iGPUBackend = ( int ) GPUBackend : : VULKAN ;
2017-02-16 10:30:58 +00:00
g_Config . bSoftwareRendering = false ;
2016-02-13 20:22:06 +00:00
} else if ( restOfOption = = L " software " ) {
2017-12-26 23:55:24 +00:00
g_Config . iGPUBackend = ( int ) GPUBackend : : OPENGL ;
2014-09-13 05:33:45 +00:00
g_Config . bSoftwareRendering = true ;
}
2014-09-12 20:16:13 +00:00
}
2012-12-22 17:21:23 +00:00
}
2012-11-01 15:19:01 +00:00
}
2013-10-13 18:25:59 +00:00
# ifdef _DEBUG
g_Config . bEnableLogging = true ;
# endif
2012-11-01 15:19:01 +00:00
2019-02-16 18:14:54 +00:00
# ifndef _DEBUG
// See #11719 - too many Vulkan drivers crash on basic init.
2019-07-01 06:45:14 +00:00
if ( g_Config . IsBackendEnabled ( GPUBackend : : VULKAN , false ) ) {
2019-06-22 18:48:36 +00:00
VulkanSetAvailable ( DetectVulkanInExternalProcess ( ) ) ;
}
2019-02-16 18:14:54 +00:00
# endif
2015-12-26 03:18:32 +00:00
if ( iCmdShow = = SW_MAXIMIZE ) {
// Consider this to mean --fullscreen.
2022-05-28 22:47:12 +00:00
g_Config . iForceFullScreen = 1 ;
2015-12-26 03:18:32 +00:00
}
2013-10-13 19:27:05 +00:00
// Consider at least the following cases before changing this code:
// - By default in Release, the console should be hidden by default even if logging is enabled.
// - By default in Debug, the console should be shown by default.
// - The -l switch is expected to show the log console, REGARDLESS of config settings.
// - It should be possible to log to a file without showing the console.
2013-10-16 05:27:03 +00:00
LogManager : : GetInstance ( ) - > GetConsoleListener ( ) - > Init ( showLog , 150 , 120 , " PPSSPP Debug Console " ) ;
2020-01-05 17:04:07 +00:00
2020-08-15 17:01:16 +00:00
if ( debugLogLevel ) {
2014-04-01 02:44:05 +00:00
LogManager : : GetInstance ( ) - > SetAllLogLevels ( LogTypes : : LDEBUG ) ;
2020-08-15 17:01:16 +00:00
}
2013-06-08 00:32:07 +00:00
2022-01-30 16:23:14 +00:00
// This still seems to improve performance noticeably.
timeBeginPeriod ( 1 ) ;
2017-03-06 09:51:28 +00:00
2021-10-08 02:53:40 +00:00
ContextMenuInit ( _hInstance ) ;
2012-11-01 15:19:01 +00:00
MainWindow : : Init ( _hInstance ) ;
2014-07-17 04:06:52 +00:00
MainWindow : : Show ( _hInstance ) ;
2012-11-01 15:19:01 +00:00
HWND hwndMain = MainWindow : : GetHWND ( ) ;
2020-01-05 17:04:07 +00:00
2012-11-01 15:19:01 +00:00
//initialize custom controls
CtrlDisAsmView : : init ( ) ;
CtrlMemView : : init ( ) ;
CtrlRegisterList : : init ( ) ;
2019-05-10 21:25:57 +00:00
# if PPSSPP_API(ANY_GL)
2013-09-28 05:41:44 +00:00
CGEDebugger : : Init ( ) ;
2019-05-03 22:06:50 +00:00
# endif
2012-11-01 15:19:01 +00:00
2021-11-13 21:10:37 +00:00
if ( g_Config . bShowDebuggerOnLoad ) {
MainWindow : : CreateDisasmWindow ( ) ;
disasmWindow - > Show ( g_Config . bShowDebuggerOnLoad , false ) ;
}
2013-08-26 12:19:46 +00:00
2015-12-26 03:18:32 +00:00
const bool minimized = iCmdShow = = SW_MINIMIZE | | iCmdShow = = SW_SHOWMINIMIZED | | iCmdShow = = SW_SHOWMINNOACTIVE ;
if ( minimized ) {
MainWindow : : Minimize ( ) ;
}
2017-12-15 11:40:38 +00:00
// Emu thread (and render thread, if any) is always running!
2018-01-16 13:16:56 +00:00
// Only OpenGL uses an externally managed render thread (due to GL's single-threaded context design). Vulkan
// manages its own render thread.
2018-02-07 14:52:19 +00:00
MainThread_Start ( g_Config . iGPUBackend = = ( int ) GPUBackend : : OPENGL ) ;
2014-03-24 05:18:54 +00:00
InputDevice : : BeginPolling ( ) ;
2013-03-29 17:50:08 +00:00
2013-06-08 00:32:07 +00:00
HACCEL hAccelTable = LoadAccelerators ( _hInstance , ( LPCTSTR ) IDR_ACCELS ) ;
2013-08-14 21:30:50 +00:00
HACCEL hDebugAccelTable = LoadAccelerators ( _hInstance , ( LPCTSTR ) IDR_DEBUGACCELS ) ;
2012-12-22 17:21:23 +00:00
2012-11-01 15:19:01 +00:00
//so.. we're at the message pump of the GUI thread
2013-06-08 00:32:07 +00:00
for ( MSG msg ; GetMessage ( & msg , NULL , 0 , 0 ) ; ) // for no quit
2012-11-01 15:19:01 +00:00
{
2013-05-13 08:08:10 +00:00
if ( msg . message = = WM_KEYDOWN )
{
//hack to enable/disable menu command accelerate keys
MainWindow : : UpdateCommands ( ) ;
2020-01-05 17:04:07 +00:00
2013-05-13 08:08:10 +00:00
//hack to make it possible to get to main window from floating windows with Esc
if ( msg . hwnd ! = hwndMain & & msg . wParam = = VK_ESCAPE )
BringWindowToTop ( hwndMain ) ;
}
2012-11-01 15:19:01 +00:00
//Translate accelerators and dialog messages...
2013-08-14 21:46:59 +00:00
HWND wnd ;
HACCEL accel ;
2013-09-28 12:34:08 +00:00
switch ( g_activeWindow )
{
case WINDOW_MAINWINDOW :
2013-08-14 21:46:59 +00:00
wnd = hwndMain ;
2021-02-27 22:40:02 +00:00
accel = g_Config . bSystemControls ? hAccelTable : NULL ;
2013-09-28 12:34:08 +00:00
break ;
case WINDOW_CPUDEBUGGER :
2021-02-27 22:40:02 +00:00
wnd = disasmWindow ? disasmWindow - > GetDlgHandle ( ) : NULL ;
accel = g_Config . bSystemControls ? hDebugAccelTable : NULL ;
2013-09-28 12:34:08 +00:00
break ;
case WINDOW_GEDEBUGGER :
default :
2021-02-27 22:40:02 +00:00
wnd = NULL ;
accel = NULL ;
2013-09-28 12:34:08 +00:00
break ;
2013-08-14 21:46:59 +00:00
}
2018-02-10 08:06:43 +00:00
if ( ! TranslateAccelerator ( wnd , accel , & msg ) ) {
if ( ! DialogManager : : IsDialogMessage ( & msg ) ) {
2012-11-01 15:19:01 +00:00
//and finally translate and dispatch
TranslateMessage ( & msg ) ;
DispatchMessage ( & msg ) ;
}
}
}
2018-02-10 08:06:43 +00:00
MainThread_Stop ( ) ;
2013-02-08 18:35:05 +00:00
VFSShutdown ( ) ;
2014-02-15 05:17:36 +00:00
MainWindow : : DestroyDebugWindows ( ) ;
2012-11-01 15:19:01 +00:00
DialogManager : : DestroyAll ( ) ;
2012-12-03 08:44:37 +00:00
timeEndPeriod ( 1 ) ;
2014-09-14 10:59:27 +00:00
2013-06-26 07:31:52 +00:00
LogManager : : Shutdown ( ) ;
2019-02-16 18:14:54 +00:00
WinMainCleanup ( ) ;
2015-10-04 10:24:59 +00:00
2012-11-01 15:19:01 +00:00
return 0 ;
}