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
2023-08-24 11:04:41 +00:00
# include <mmsystem.h>
2016-12-05 15:51:28 +00:00
# include <shellapi.h>
2023-08-24 11:04:41 +00:00
# include <Wbemidl.h>
2019-05-21 21:22:56 +00:00
# include <ShlObj.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"
2023-03-22 11:26:14 +00:00
# include "Common/System/Request.h"
2022-09-19 15:30:57 +00:00
# include "Common/File/FileUtil.h"
2020-10-03 22:25:21 +00:00
# include "Common/File/VFS/VFS.h"
2023-03-06 14:30:39 +00:00
# include "Common/File/VFS/DirectoryReader.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"
2023-08-10 11:21:36 +00:00
# include "Common/TimeUtil.h"
2020-05-04 15:33:39 +00:00
# include "W32Util/DarkMode.h"
2023-03-22 12:43:44 +00:00
# include "W32Util/ShellUtil.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"
2023-03-21 10:21:19 +00:00
# include "Core/Instance.h"
2013-07-29 04:01:49 +00:00
# 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
2024-07-21 10:20:23 +00:00
# include "Common/Log/LogManager.h"
# include "Common/Log/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"
2023-03-21 10:10:09 +00:00
# include "Windows/Debugger/DebuggerShared.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 ;
2024-09-03 11:58:35 +00:00
static std : : string osVersion ;
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 ;
2023-03-24 18:57:24 +00:00
WindowsInputManager g_inputManager ;
2023-03-21 10:21:19 +00:00
int g_lastNumInstances = 0 ;
2021-02-18 15:27:28 +00:00
2023-08-10 11:21:36 +00:00
static double g_lastActivity = 0.0 ;
static double g_lastKeepAwake = 0.0 ;
// Time until we stop considering the core active without user input.
// Should this be configurable? 2 hours currently.
static const double ACTIVITY_IDLE_TIMEOUT = 2.0 * 3600.0 ;
2023-03-21 09:42:23 +00:00
void System_LaunchUrl ( LaunchUrlType urlType , const char * url ) {
2013-08-26 17:00:16 +00:00
ShellExecute ( NULL , L " open " , ConvertUTF8ToWString ( url ) . c_str ( ) , NULL , NULL , SW_SHOWNORMAL ) ;
2013-08-17 22:41:19 +00:00
}
2023-03-21 09:42:23 +00:00
void System_Vibrate ( int length_ms ) {
2013-10-10 15:00:15 +00:00
// Ignore on PC
}
2022-10-10 04:42:04 +00:00
static void AddDebugRestartArgs ( ) {
if ( LogManager : : GetInstance ( ) - > GetConsoleListener ( ) - > IsOpen ( ) )
restartArgs + = " -l " ;
}
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 ) ;
2022-12-11 05:09:50 +00:00
ULONG uReturned = 0 ;
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 ;
2024-09-03 16:42:34 +00:00
case SYSPROP_SYSTEMBUILD :
2024-09-03 11:58:35 +00:00
return osVersion ;
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
2022-09-30 09:26:30 +00:00
retval . clear ( ) ;
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 ;
2023-07-20 07:56:51 +00:00
case SYSPROP_BUILD_VERSION :
return PPSSPP_GIT_VERSION ;
2023-08-18 13:04:20 +00:00
case SYSPROP_USER_DOCUMENTS_DIR :
return Path ( W32Util : : UserDocumentsPath ( ) ) . ToString ( ) ; // this'll reverse the slashes.
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)
2024-05-03 15:00:36 +00:00
static float ScreenDPI ( ) {
return 96.0f ; // TODO UWP
2017-03-17 12:22:00 +00:00
}
# else
2024-05-03 15:00:36 +00:00
static float ScreenDPI ( ) {
2017-03-17 12:22:00 +00:00
HDC screenDC = GetDC ( nullptr ) ;
int dotsPerInch = GetDeviceCaps ( screenDC , LOGPIXELSY ) ;
ReleaseDC ( nullptr , screenDC ) ;
2024-05-03 15:00:36 +00:00
return dotsPerInch ? ( float ) dotsPerInch : 96.0f ;
2017-03-17 12:22:00 +00:00
}
# endif
# endif
2024-05-03 15:00:36 +00:00
static float ScreenRefreshRateHz ( ) {
static float rate = 0.0f ;
2023-10-02 23:06:07 +00:00
static double lastCheck = 0.0 ;
2024-05-03 15:00:36 +00:00
const double now = time_now_d ( ) ;
2023-10-02 23:06:07 +00:00
if ( ! rate | | lastCheck < now - 10.0 ) {
lastCheck = now ;
DEVMODE lpDevMode { } ;
lpDevMode . dmSize = sizeof ( DEVMODE ) ;
lpDevMode . dmDriverExtra = 0 ;
// TODO: Use QueryDisplayConfig instead (Win7+) so we can get fractional refresh rates correctly.
if ( EnumDisplaySettings ( NULL , ENUM_CURRENT_SETTINGS , & lpDevMode ) = = 0 ) {
2024-05-03 15:00:36 +00:00
rate = 60.0f ; // default value
2023-01-05 12:07:36 +00:00
} else {
2023-10-02 23:06:07 +00:00
if ( lpDevMode . dmFields & DM_DISPLAYFREQUENCY ) {
2024-05-03 15:00:36 +00:00
rate = ( float ) ( lpDevMode . dmDisplayFrequency > 60 ? lpDevMode . dmDisplayFrequency : 60 ) ;
2023-10-02 23:06:07 +00:00
} else {
2024-05-03 15:00:36 +00:00
rate = 60.0f ;
2023-10-02 23:06:07 +00:00
}
2023-01-05 12:07:36 +00:00
}
2023-01-04 15:33:00 +00:00
}
2023-10-02 23:06:07 +00:00
return rate ;
2023-01-04 15:33:00 +00:00
}
2024-04-05 09:07:57 +00:00
int64_t System_GetPropertyInt ( SystemProperty prop ) {
2015-01-11 13:18:40 +00:00
switch ( prop ) {
2024-04-05 09:07:57 +00:00
case SYSPROP_MAIN_WINDOW_HANDLE :
return ( int64_t ) MainWindow : : GetHWND ( ) ;
2015-01-11 13:18:40 +00:00
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 :
2024-05-03 15:00:36 +00:00
return ScreenRefreshRateHz ( ) ;
2020-01-05 17:04:07 +00:00
case SYSPROP_DISPLAY_DPI :
2024-05-03 15:00:36 +00:00
return 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 ) {
2024-09-02 20:48:47 +00:00
case SYSPROP_HAS_TEXT_CLIPBOARD :
2023-03-24 17:08:31 +00:00
case SYSPROP_HAS_DEBUGGER :
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 :
2023-02-02 13:54:50 +00:00
case SYSPROP_HAS_OPEN_DIRECTORY :
2023-06-21 22:00:08 +00:00
case SYSPROP_HAS_TEXT_INPUT_DIALOG :
2023-08-18 08:42:50 +00:00
case SYSPROP_CAN_CREATE_SHORTCUT :
2023-08-24 11:04:41 +00:00
case SYSPROP_CAN_SHOW_FILE :
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 ;
2023-06-19 21:22:44 +00:00
case SYSPROP_HAS_LOGIN_DIALOG :
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
2023-07-20 09:25:27 +00:00
case SYSPROP_SUPPORTS_HTTPS :
2023-08-24 19:28:30 +00:00
return ! g_Config . bDisableHTTPS ;
2023-10-11 21:40:07 +00:00
case SYSPROP_DEBUGGER_PRESENT :
return IsDebuggerPresent ( ) ;
2023-12-05 13:17:14 +00:00
case SYSPROP_OK_BUTTON_LEFT :
return true ;
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
}
2023-03-21 10:10:09 +00:00
static BOOL PostDialogMessage ( Dialog * dialog , UINT message , WPARAM wParam = 0 , LPARAM lParam = 0 ) {
return PostMessage ( dialog - > GetDlgHandle ( ) , message , wParam , lParam ) ;
}
2023-03-22 11:26:14 +00:00
// This can come from any thread, so this mostly uses PostMessage. Can't access most data directly.
2023-03-21 10:10:09 +00:00
void System_Notify ( SystemNotification notification ) {
switch ( notification ) {
case SystemNotification : : BOOT_DONE :
2023-03-21 10:21:19 +00:00
{
2023-03-21 10:10:09 +00:00
if ( g_symbolMap )
2023-03-22 11:26:14 +00:00
g_symbolMap - > SortSymbols ( ) ; // internal locking is performed here
2023-03-21 10:10:09 +00:00
PostMessage ( MainWindow : : GetHWND ( ) , WM_USER + 1 , 0 , 0 ) ;
if ( disasmWindow )
2023-03-21 10:40:48 +00:00
PostDialogMessage ( disasmWindow , WM_DEB_SETDEBUGLPARAM , 0 , ( LPARAM ) Core_IsStepping ( ) ) ;
2023-03-21 10:10:09 +00:00
break ;
}
2023-03-21 10:21:19 +00:00
case SystemNotification : : UI :
{
PostMessage ( MainWindow : : GetHWND ( ) , MainWindow : : WM_USER_UPDATE_UI , 0 , 0 ) ;
int peers = GetInstancePeerCount ( ) ;
if ( PPSSPP_ID > = 1 & & peers ! = g_lastNumInstances ) {
g_lastNumInstances = peers ;
PostMessage ( MainWindow : : GetHWND ( ) , MainWindow : : WM_USER_WINDOW_TITLE_CHANGED , 0 , 0 ) ;
}
break ;
}
case SystemNotification : : MEM_VIEW :
if ( memoryWindow )
PostDialogMessage ( memoryWindow , WM_DEB_UPDATE ) ;
break ;
case SystemNotification : : DISASSEMBLY :
if ( disasmWindow )
PostDialogMessage ( disasmWindow , WM_DEB_UPDATE ) ;
break ;
2023-03-21 10:28:50 +00:00
case SystemNotification : : SYMBOL_MAP_UPDATED :
if ( g_symbolMap )
2023-03-22 11:26:14 +00:00
g_symbolMap - > SortSymbols ( ) ; // internal locking is performed here
2023-03-21 10:28:50 +00:00
PostMessage ( MainWindow : : GetHWND ( ) , WM_USER + 1 , 0 , 0 ) ;
break ;
2023-03-21 10:31:52 +00:00
case SystemNotification : : SWITCH_UMD_UPDATED :
PostMessage ( MainWindow : : GetHWND ( ) , MainWindow : : WM_USER_SWITCHUMD_UPDATED , 0 , 0 ) ;
break ;
2023-03-21 10:40:48 +00:00
case SystemNotification : : DEBUG_MODE_CHANGE :
if ( disasmWindow )
PostDialogMessage ( disasmWindow , WM_DEB_SETDEBUGLPARAM , 0 , ( LPARAM ) Core_IsStepping ( ) ) ;
break ;
2023-03-24 20:43:45 +00:00
2023-03-24 18:57:24 +00:00
case SystemNotification : : POLL_CONTROLLERS :
g_inputManager . PollControllers ( ) ;
break ;
2023-03-24 20:43:45 +00:00
case SystemNotification : : TOGGLE_DEBUG_CONSOLE :
MainWindow : : ToggleDebugConsoleVisibility ( ) ;
break ;
2023-08-10 11:21:36 +00:00
case SystemNotification : : ACTIVITY :
g_lastActivity = time_now_d ( ) ;
break ;
case SystemNotification : : KEEP_SCREEN_AWAKE :
{
// Keep the system awake for longer than normal for cutscenes and the like.
const double now = time_now_d ( ) ;
if ( now < g_lastActivity + ACTIVITY_IDLE_TIMEOUT ) {
// Only resetting it ever prime number seconds in case the call is expensive.
// Using a prime number to ensure there's no interaction with other periodic events.
if ( now - g_lastKeepAwake > 89.0 | | now < g_lastKeepAwake ) {
// Note that this needs to be called periodically.
// It's also possible to set ES_CONTINUOUS but let's not, for simplicity.
# if defined(_WIN32) && !PPSSPP_PLATFORM(UWP)
SetThreadExecutionState ( ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED ) ;
# endif
g_lastKeepAwake = now ;
}
}
break ;
}
2023-03-21 10:21:19 +00:00
}
2023-03-21 10:10:09 +00:00
}
2024-05-03 15:00:36 +00:00
static std : : wstring MakeFilter ( std : : wstring filter ) {
2023-03-22 12:43:44 +00:00
for ( size_t i = 0 ; i < filter . length ( ) ; i + + ) {
if ( filter [ i ] = = ' | ' )
filter [ i ] = ' \0 ' ;
}
return filter ;
}
2024-04-06 10:04:45 +00:00
bool System_MakeRequest ( SystemRequestType type , int requestId , const std : : string & param1 , const std : : string & param2 , int64_t param3 , int64_t param4 ) {
2023-03-22 11:26:14 +00:00
switch ( type ) {
2023-03-22 21:07:29 +00:00
case SystemRequestType : : EXIT_APP :
if ( ! NativeIsRestarting ( ) ) {
PostMessage ( MainWindow : : GetHWND ( ) , WM_CLOSE , 0 , 0 ) ;
}
return true ;
2023-03-22 21:17:53 +00:00
case SystemRequestType : : RESTART_APP :
{
restartArgs = param1 ;
if ( ! restartArgs . empty ( ) )
AddDebugRestartArgs ( ) ;
2023-10-11 21:40:07 +00:00
if ( System_GetPropertyBool ( SYSPROP_DEBUGGER_PRESENT ) ) {
2023-03-22 21:17:53 +00:00
PostMessage ( MainWindow : : GetHWND ( ) , MainWindow : : WM_USER_RESTART_EMUTHREAD , 0 , 0 ) ;
} else {
g_Config . bRestartRequired = true ;
PostMessage ( MainWindow : : GetHWND ( ) , WM_CLOSE , 0 , 0 ) ;
}
return true ;
}
2023-03-22 21:07:29 +00:00
case SystemRequestType : : COPY_TO_CLIPBOARD :
{
std : : wstring data = ConvertUTF8ToWString ( param1 ) ;
W32Util : : CopyTextToClipboard ( MainWindow : : GetDisplayHWND ( ) , data ) ;
return true ;
}
2023-03-24 16:40:03 +00:00
case SystemRequestType : : SET_WINDOW_TITLE :
{
2023-04-19 12:54:58 +00:00
const char * name = System_GetPropertyBool ( SYSPROP_APP_GOLD ) ? " PPSSPP Gold " : " PPSSPP " ;
2023-03-24 16:40:03 +00:00
std : : wstring winTitle = ConvertUTF8ToWString ( std : : string ( name ) + PPSSPP_GIT_VERSION ) ;
if ( ! param1 . empty ( ) ) {
winTitle . append ( ConvertUTF8ToWString ( " - " + param1 ) ) ;
}
# ifdef _DEBUG
winTitle . append ( L " (debug) " ) ;
# endif
MainWindow : : SetWindowTitle ( winTitle . c_str ( ) ) ;
PostMessage ( MainWindow : : GetHWND ( ) , MainWindow : : WM_USER_WINDOW_TITLE_CHANGED , 0 , 0 ) ;
return true ;
}
2024-05-20 12:03:54 +00:00
case SystemRequestType : : SET_KEEP_SCREEN_BRIGHT :
{
MainWindow : : SetKeepScreenBright ( param3 ! = 0 ) ;
return true ;
}
2023-03-22 11:26:14 +00:00
case SystemRequestType : : INPUT_TEXT_MODAL :
2023-03-25 23:19:26 +00:00
std : : thread ( [ = ] {
2023-03-22 11:26:14 +00:00
std : : string out ;
if ( InputBox_GetString ( MainWindow : : GetHInstance ( ) , MainWindow : : GetHWND ( ) , ConvertUTF8ToWString ( param1 ) . c_str ( ) , param2 , out ) ) {
g_requestManager . PostSystemSuccess ( requestId , out . c_str ( ) ) ;
} else {
g_requestManager . PostSystemFailure ( requestId ) ;
}
2023-03-25 23:19:26 +00:00
} ) . detach ( ) ;
2023-03-22 11:26:14 +00:00
return true ;
2023-06-19 21:22:44 +00:00
case SystemRequestType : : ASK_USERNAME_PASSWORD :
std : : thread ( [ = ] {
std : : string username ;
std : : string password ;
if ( UserPasswordBox_GetStrings ( MainWindow : : GetHInstance ( ) , MainWindow : : GetHWND ( ) , ConvertUTF8ToWString ( param1 ) . c_str ( ) , & username , & password ) ) {
g_requestManager . PostSystemSuccess ( requestId , ( username + ' \n ' + password ) . c_str ( ) ) ;
} else {
g_requestManager . PostSystemFailure ( requestId ) ;
}
} ) . detach ( ) ;
return true ;
2023-03-22 12:43:44 +00:00
case SystemRequestType : : BROWSE_FOR_IMAGE :
2023-03-25 23:19:26 +00:00
std : : thread ( [ = ] {
2023-03-22 12:43:44 +00:00
std : : string out ;
if ( W32Util : : BrowseForFileName ( true , MainWindow : : GetHWND ( ) , ConvertUTF8ToWString ( param1 ) . c_str ( ) , nullptr ,
MakeFilter ( L " All supported images (*.jpg *.jpeg *.png)|*.jpg;*.jpeg;*.png|All files (*.*)|*.*|| " ) . c_str ( ) , L " jpg " , out ) ) {
g_requestManager . PostSystemSuccess ( requestId , out . c_str ( ) ) ;
} else {
g_requestManager . PostSystemFailure ( requestId ) ;
}
2023-03-25 23:19:26 +00:00
} ) . detach ( ) ;
2023-03-22 12:43:44 +00:00
return true ;
2023-03-22 14:21:03 +00:00
case SystemRequestType : : BROWSE_FOR_FILE :
{
BrowseFileType type = ( BrowseFileType ) param3 ;
std : : wstring filter ;
switch ( type ) {
case BrowseFileType : : BOOTABLE :
2023-08-09 18:34:43 +00:00
filter = MakeFilter ( L " All supported file types (*.iso *.cso *.chd *.pbp *.elf *.prx *.zip *.ppdmp)|*.pbp;*.elf;*.iso;*.cso;*.chd;*.prx;*.zip;*.ppdmp|PSP ROMs (*.iso *.cso *.chd *.pbp *.elf *.prx)|*.pbp;*.elf;*.iso;*.cso;*.chd;*.prx|Homebrew/Demos installers (*.zip)|*.zip|All files (*.*)|*.*|| " ) ;
2023-03-22 14:21:03 +00:00
break ;
case BrowseFileType : : INI :
filter = MakeFilter ( L " Ini files (*.ini)|*.ini|All files (*.*)|*.*|| " ) ;
break ;
2024-01-15 10:46:41 +00:00
case BrowseFileType : : ZIP :
filter = MakeFilter ( L " ZIP files (*.zip)|*.zip|All files (*.*)|*.*|| " ) ;
break ;
2023-03-23 00:16:12 +00:00
case BrowseFileType : : DB :
filter = MakeFilter ( L " Cheat db files (*.db)|*.db|All files (*.*)|*.*|| " ) ;
break ;
2023-07-16 10:14:40 +00:00
case BrowseFileType : : SOUND_EFFECT :
2024-04-09 15:29:57 +00:00
filter = MakeFilter ( L " Sound effect files (*.wav *.mp3)|*.wav;*.mp3|All files (*.*)|*.*|| " ) ;
2023-07-16 10:14:40 +00:00
break ;
2023-03-22 14:21:03 +00:00
case BrowseFileType : : ANY :
filter = MakeFilter ( L " All files (*.*)|*.*|| " ) ;
break ;
default :
return false ;
}
2023-03-25 23:19:26 +00:00
std : : thread ( [ = ] {
2023-03-22 14:21:03 +00:00
std : : string out ;
if ( W32Util : : BrowseForFileName ( true , MainWindow : : GetHWND ( ) , ConvertUTF8ToWString ( param1 ) . c_str ( ) , nullptr , filter . c_str ( ) , L " " , out ) ) {
g_requestManager . PostSystemSuccess ( requestId , out . c_str ( ) ) ;
} else {
g_requestManager . PostSystemFailure ( requestId ) ;
}
2023-03-25 23:19:26 +00:00
} ) . detach ( ) ;
2023-03-22 14:21:03 +00:00
return true ;
}
2023-03-22 16:39:45 +00:00
case SystemRequestType : : BROWSE_FOR_FOLDER :
{
2023-03-25 23:19:26 +00:00
std : : thread ( [ = ] {
2024-01-25 11:47:37 +00:00
std : : string folder = W32Util : : BrowseForFolder ( MainWindow : : GetHWND ( ) , param1 , param2 ) ;
2023-03-24 20:32:20 +00:00
if ( folder . size ( ) ) {
g_requestManager . PostSystemSuccess ( requestId , folder . c_str ( ) ) ;
} else {
g_requestManager . PostSystemFailure ( requestId ) ;
}
2023-03-25 23:19:26 +00:00
} ) . detach ( ) ;
2023-03-22 16:39:45 +00:00
return true ;
}
2023-08-24 11:04:41 +00:00
case SystemRequestType : : SHOW_FILE_IN_FOLDER :
W32Util : : ShowFileInFolder ( param1 ) ;
2023-08-24 19:28:30 +00:00
return true ;
2023-08-24 11:04:41 +00:00
2023-03-22 21:17:53 +00:00
case SystemRequestType : : TOGGLE_FULLSCREEN_STATE :
{
2021-01-09 22:17:41 +00:00
bool flag = ! MainWindow : : IsFullscreen ( ) ;
2023-03-22 21:17:53 +00:00
if ( param1 = = " 0 " ) {
2017-07-30 14:51:53 +00:00
flag = false ;
2023-03-22 21:17:53 +00:00
} else if ( param1 = = " 1 " ) {
2017-07-30 14:51:53 +00:00
flag = true ;
}
MainWindow : : SendToggleFullscreen ( flag ) ;
2023-03-22 21:17:53 +00:00
return true ;
}
case SystemRequestType : : GRAPHICS_BACKEND_FAILED_ALERT :
{
2023-04-05 22:34:50 +00:00
auto err = GetI18NCategory ( I18NCat : : ERRORS ) ;
2024-01-19 12:44:49 +00:00
std : : string_view 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: " ) ;
2023-03-22 21:17:53 +00:00
std : : wstring full_error = ConvertUTF8ToWString ( StringFromFormat ( " %s %s " , backendSwitchError , param1 . c_str ( ) ) ) ;
std : : wstring title = ConvertUTF8ToWString ( err - > T ( " GenericGraphicsError " , " Graphics Error " ) ) ;
MessageBox ( MainWindow : : GetHWND ( ) , full_error . c_str ( ) , title . c_str ( ) , MB_OK ) ;
return true ;
}
2023-03-24 19:05:48 +00:00
case SystemRequestType : : CREATE_GAME_SHORTCUT :
2024-05-12 23:37:53 +00:00
{
// Get the game info to get our hands on the icon png
Path gamePath ( param1 ) ;
std : : shared_ptr < GameInfo > info = g_gameInfoCache - > GetInfo ( nullptr , gamePath , GameInfoFlags : : ICON ) ;
Path icoPath ;
if ( info - > icon . dataLoaded ) {
// Write the icon png out as a .ICO file so the shortcut can point to it
// Savestate seems like a good enough place to put ico files.
Path iconFolder = GetSysDirectory ( PSPDirectories : : DIRECTORY_SAVESTATE ) ;
icoPath = iconFolder / ( info - > id + " .ico " ) ;
if ( ! File : : Exists ( icoPath ) ) {
if ( ! W32Util : : CreateICOFromPNGData ( ( const uint8_t * ) info - > icon . data . data ( ) , info - > icon . data . size ( ) , icoPath ) ) {
2024-07-14 12:42:59 +00:00
ERROR_LOG ( Log : : System , " ICO creation failed " ) ;
2024-05-12 23:37:53 +00:00
icoPath . clear ( ) ;
}
}
}
return W32Util : : CreateDesktopShortcut ( param1 , param2 , icoPath ) ;
}
2024-04-06 10:14:29 +00:00
case SystemRequestType : : RUN_CALLBACK_IN_WNDPROC :
{
auto func = reinterpret_cast < void ( * ) ( void * window , void * userdata ) > ( param3 ) ;
void * userdata = reinterpret_cast < void * > ( param4 ) ;
MainWindow : : RunCallbackInWndProc ( func , userdata ) ;
return true ;
}
2023-03-22 21:17:53 +00:00
default :
return false ;
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 ; }
2024-05-03 15:00:36 +00:00
// Don't swallow exceptions.
static void EnableCrashingOnCrashes ( ) {
2015-01-17 20:32:58 +00:00
typedef BOOL ( WINAPI * tGetPolicy ) ( LPDWORD lpFlags ) ;
typedef BOOL ( WINAPI * tSetPolicy ) ( DWORD dwFlags ) ;
const DWORD EXCEPTION_SWALLOWING = 0x1 ;
HMODULE kernel32 = LoadLibrary ( L " kernel32.dll " ) ;
2022-12-11 05:09:50 +00:00
if ( ! kernel32 )
return ;
2015-01-17 20:32:58 +00:00
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
}
2024-01-19 12:44:49 +00:00
void System_Toast ( std : : string_view text ) {
2022-07-10 20:34:44 +00:00
// 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 " ;
DWORD exitCode = 0 ;
2023-10-03 10:00:59 +00:00
if ( W32Util : : ExecuteAndGetReturnCode ( moduleFilename . c_str ( ) , cmdline , workingDirectory . c_str ( ) , & exitCode ) ) {
return exitCode = = EXIT_CODE_VULKAN_WORKS ;
} else {
2024-07-14 12:42:59 +00:00
ERROR_LOG ( Log : : G3D , " Failed to detect Vulkan in external process somehow " ) ;
2019-02-16 18:14:54 +00:00
return false ;
}
}
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 ;
}
2023-08-18 10:48:57 +00:00
static void InitMemstickDirectory ( ) {
if ( ! g_Config . memStickDirectory . empty ( ) & & ! g_Config . flash0Directory . empty ( ) )
return ;
const Path & exePath = File : : GetExeDirectory ( ) ;
// Mount a filesystem
g_Config . flash0Directory = exePath / " assets/flash0 " ;
// Caller sets this to the Documents folder.
const Path rootMyDocsPath = g_Config . internalDataDirectory ;
const Path myDocsPath = rootMyDocsPath / " PPSSPP " ;
const Path installedFile = exePath / " installed.txt " ;
const bool installed = File : : Exists ( installedFile ) ;
// If installed.txt exists(and we can determine the Documents directory)
if ( installed & & ! rootMyDocsPath . empty ( ) ) {
FILE * fp = File : : OpenCFile ( installedFile , " rt " ) ;
if ( fp ) {
char temp [ 2048 ] ;
char * tempStr = fgets ( temp , sizeof ( temp ) , fp ) ;
// Skip UTF-8 encoding bytes if there are any. There are 3 of them.
if ( tempStr & & strncmp ( tempStr , " \xEF \xBB \xBF " , 3 ) = = 0 ) {
tempStr + = 3 ;
}
std : : string tempString = tempStr ? tempStr : " " ;
if ( ! tempString . empty ( ) & & tempString . back ( ) = = ' \n ' )
tempString . resize ( tempString . size ( ) - 1 ) ;
g_Config . memStickDirectory = Path ( tempString ) ;
fclose ( fp ) ;
}
// Check if the file is empty first, before appending the slash.
if ( g_Config . memStickDirectory . empty ( ) )
g_Config . memStickDirectory = myDocsPath ;
} else {
g_Config . memStickDirectory = exePath / " memstick " ;
}
// Create the memstickpath before trying to write to it, and fall back on Documents yet again
// if we can't make it.
if ( ! File : : Exists ( g_Config . memStickDirectory ) ) {
if ( ! File : : CreateDir ( g_Config . memStickDirectory ) )
g_Config . memStickDirectory = myDocsPath ;
2024-07-14 12:42:59 +00:00
INFO_LOG ( Log : : Common , " Memstick directory not present, creating at '%s' " , g_Config . memStickDirectory . c_str ( ) ) ;
2023-08-18 10:48:57 +00:00
}
Path testFile = g_Config . memStickDirectory / " _writable_test.$$$ " ;
// If any directory is read-only, fall back to the Documents directory.
// We're screwed anyway if we can't write to Documents, or can't detect it.
if ( ! File : : CreateEmptyFile ( testFile ) )
g_Config . memStickDirectory = myDocsPath ;
// Clean up our mess.
if ( File : : Exists ( testFile ) )
File : : Delete ( testFile ) ;
}
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
2020-05-04 15:33:39 +00:00
InitDarkMode ( ) ;
2019-02-16 18:14:54 +00:00
}
static void WinMainCleanup ( ) {
2023-03-25 23:19:26 +00:00
// This will ensure no further callbacks are called, which may prevent crashing.
g_requestManager . Clear ( ) ;
2019-02-16 18:14:54 +00:00
net : : Shutdown ( ) ;
CoUninitialize ( ) ;
2020-05-09 20:52:04 +00:00
if ( g_Config . bRestartRequired ) {
2023-09-20 12:37:01 +00:00
// TODO: ExitAndRestart prevents the Config::~Config destructor from running,
// which normally would have done this instance counter update.
// ExitAndRestart calls ExitProcess which really bad, we should do something better that
// allows us to fall out of main() properly.
if ( g_Config . bUpdatedInstanceCounter ) {
ShutdownInstanceCounter ( ) ;
}
2020-05-09 20:52:04 +00:00
W32Util : : ExitAndRestart ( ! restartArgs . empty ( ) , restartArgs ) ;
}
2019-02-16 18:14:54 +00:00
}
int WINAPI WinMain ( HINSTANCE _hInstance , HINSTANCE hPrevInstance , LPSTR szCmdLine , int iCmdShow ) {
2023-10-03 10:06:43 +00:00
std : : vector < std : : wstring > wideArgs = GetWideCmdLine ( ) ;
// 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 ;
}
}
}
2020-11-30 23:46:26 +00:00
SetCurrentThreadName ( " Main " ) ;
2019-02-16 18:14:54 +00:00
2024-07-26 09:16:41 +00:00
TimeInit ( ) ;
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 ( ) ;
2023-03-06 14:30:39 +00:00
g_VFS . Register ( " " , new DirectoryReader ( exePath / " assets " ) ) ;
g_VFS . Register ( " " , new DirectoryReader ( exePath ) ) ;
2013-09-15 09:02:13 +00:00
2015-12-26 03:14:05 +00:00
langRegion = GetDefaultLangRegion ( ) ;
2024-09-03 16:07:57 +00:00
osName = GetWindowsVersion ( ) + " " + GetWindowsSystemArchitecture ( ) ;
2024-09-03 11:58:35 +00:00
2024-09-03 16:07:57 +00:00
// OS Build
2024-09-03 11:58:35 +00:00
uint32_t outMajor = 0 , outMinor = 0 , outBuild = 0 ;
2024-09-03 16:07:57 +00:00
if ( GetVersionFromKernel32 ( outMajor , outMinor , outBuild ) ) {
// Builds with (service pack) don't show OS Build for now
osVersion = std : : to_string ( outMajor ) + " . " + std : : to_string ( outMinor ) + " . " + std : : to_string ( outBuild ) ;
2024-09-03 11:58:35 +00:00
}
2013-10-09 21:17:28 +00:00
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-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 ( ) ) ;
2023-08-18 10:48:57 +00:00
InitMemstickDirectory ( ) ;
2023-08-18 10:15:12 +00:00
CreateSysDirectories ( ) ;
2013-09-15 16:50:42 +00:00
2023-08-18 10:15:12 +00:00
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.
2024-05-26 15:26:11 +00:00
if ( g_Config . IsBackendEnabled ( GPUBackend : : VULKAN ) ) {
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.
2024-07-21 10:20:23 +00:00
LogManager : : GetInstance ( ) - > GetConsoleListener ( ) - > Init ( showLog , 150 , 120 ) ;
2020-01-05 17:04:07 +00:00
2020-08-15 17:01:16 +00:00
if ( debugLogLevel ) {
2023-08-25 09:33:48 +00:00
LogManager : : GetInstance ( ) - > SetAllLogLevels ( LogLevel : : LDEBUG ) ;
2020-08-15 17:01:16 +00:00
}
2013-06-08 00:32:07 +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 ( ) ;
}
2023-03-24 18:57:24 +00:00
g_inputManager . Init ( ) ;
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
}
2022-12-11 05:09:50 +00:00
if ( ! wnd | | ! accel | | ! TranslateAccelerator ( wnd , accel , & msg ) ) {
2018-02-10 08:06:43 +00:00
if ( ! DialogManager : : IsDialogMessage ( & msg ) ) {
2012-11-01 15:19:01 +00:00
//and finally translate and dispatch
TranslateMessage ( & msg ) ;
DispatchMessage ( & msg ) ;
}
}
}
2023-03-06 13:23:56 +00:00
g_VFS . Clear ( ) ;
2013-02-08 18:35:05 +00:00
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 ;
}