2012-11-01 16:19:01 +01: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 23:01:49 +01:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 16:19:01 +01: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 22:54:12 +01:00
# include "stdafx.h"
2015-12-25 19:14:05 -08:00
# include <algorithm>
2016-12-05 16:51:28 +01:00
# include <cmath>
2020-03-08 18:59:17 -07:00
# include <functional>
2014-02-13 15:57:36 +01:00
2013-07-28 21:01:49 -07:00
# include "Common/CommonWindows.h"
2020-10-04 20:48:47 +02:00
# include "Common/File/FileUtil.h"
2017-02-14 10:33:42 +01:00
# include "Common/OSVersion.h"
2020-10-04 23:24:14 +02:00
# include "Common/GPU/Vulkan/VulkanLoader.h"
2019-05-10 23:25:57 +02:00
# include "ppsspp_config.h"
2017-02-14 10:33:42 +01:00
2016-12-05 16:51:28 +01:00
# include <mmsystem.h>
2023-08-24 13:04:41 +02:00
# include <shellapi.h>
# include <Wbemidl.h>
# include <ShlObj.h>
2012-11-01 16:19:01 +01:00
2020-10-04 10:10:55 +02:00
# include "Common/System/Display.h"
2020-10-04 10:30:18 +02:00
# include "Common/System/NativeApp.h"
# include "Common/System/System.h"
2023-03-22 12:26:14 +01:00
# include "Common/System/Request.h"
2022-09-19 08:30:57 -07:00
# include "Common/File/FileUtil.h"
2020-10-04 00:25:21 +02:00
# include "Common/File/VFS/VFS.h"
2023-03-06 15:30:39 +01:00
# include "Common/File/VFS/DirectoryReader.h"
2020-10-01 13:05:04 +02:00
# include "Common/Data/Text/I18n.h"
2020-10-04 10:04:01 +02:00
# include "Common/Profiler/Profiler.h"
2020-10-01 09:27:25 +02:00
# include "Common/Thread/ThreadUtil.h"
2020-10-01 13:05:04 +02:00
# include "Common/Data/Encoding/Utf8.h"
2020-10-04 20:48:47 +02:00
# include "Common/Net/Resolve.h"
2023-08-10 13:21:36 +02:00
# include "Common/TimeUtil.h"
2020-05-04 17:33:39 +02:00
# include "W32Util/DarkMode.h"
2023-03-22 13:43:44 +01:00
# include "W32Util/ShellUtil.h"
2012-11-01 16:19:01 +01:00
2013-07-28 21:01:49 -07:00
# include "Core/Config.h"
2018-06-16 18:42:31 -07:00
# include "Core/ConfigValues.h"
2013-07-28 21:01:49 -07:00
# include "Core/SaveState.h"
2023-03-21 11:21:19 +01:00
# include "Core/Instance.h"
2013-07-28 21:01:49 -07:00
# include "Windows/EmuThread.h"
2017-06-27 11:46:10 +02:00
# include "Windows/WindowsAudio.h"
2013-01-08 17:03:17 +01:00
# include "ext/disarm.h"
2012-11-01 16:19:01 +01:00
2024-07-21 12:20:23 +02:00
# include "Common/Log/LogManager.h"
# include "Common/Log/ConsoleListener.h"
2020-09-29 12:19:22 +02:00
# include "Common/StringUtils.h"
2012-11-01 16:19:01 +01:00
# include "Commctrl.h"
2015-10-04 12:24:59 +02:00
# include "UI/GameInfoCache.h"
2012-11-01 16:19:01 +01:00
# include "Windows/resource.h"
2015-09-19 13:14:05 +02:00
# include "Windows/MainWindow.h"
2013-09-22 10:27:09 -07:00
# include "Windows/Debugger/Debugger_Disasm.h"
2012-11-01 16:19:01 +01:00
# include "Windows/Debugger/Debugger_MemoryDlg.h"
# include "Windows/Debugger/Debugger_VFPUDlg.h"
2019-05-10 23:25:57 +02:00
# if PPSSPP_API(ANY_GL)
2013-09-22 11:03:29 -07:00
# include "Windows/GEDebugger/GEDebugger.h"
2019-05-04 06:06:50 +08:00
# endif
2021-10-07 19:53:40 -07:00
# include "Windows/W32Util/ContextMenu.h"
2012-11-01 16:19:01 +01:00
# include "Windows/W32Util/DialogManager.h"
2019-02-17 08:03:10 -08:00
# include "Windows/W32Util/ShellUtil.h"
2012-11-01 16:19:01 +01:00
# include "Windows/Debugger/CtrlDisAsmView.h"
# include "Windows/Debugger/CtrlMemView.h"
# include "Windows/Debugger/CtrlRegisterList.h"
2023-03-21 11:10:09 +01:00
# include "Windows/Debugger/DebuggerShared.h"
2013-12-08 20:16:18 +10:00
# include "Windows/InputBox.h"
2012-11-01 16:19:01 +01:00
# include "Windows/WindowsHost.h"
# include "Windows/main.h"
2016-04-21 23:03:08 -04:00
2017-08-28 13:45:04 +02:00
// Nvidia OpenGL drivers >= v302 will check if the application exports a global
2014-03-03 11:26:27 -05: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 08:41:02 +02:00
// Also on AMD PowerExpress: https://community.amd.com/thread/169965
extern " C " {
__declspec ( dllexport ) int AmdPowerXpressRequestHighPerformance = 1 ;
}
2019-05-10 23:25:57 +02:00
# if PPSSPP_API(ANY_GL)
2020-09-24 00:17:31 +02:00
CGEDebugger * geDebuggerWindow = nullptr ;
2019-05-04 06:06:50 +08:00
# endif
2018-04-15 08:41:02 +02:00
2020-09-24 00:17:31 +02:00
CDisasm * disasmWindow = nullptr ;
CMemoryDlg * memoryWindow = nullptr ;
2021-11-13 22:24:30 +01:00
CVFPUDlg * vfpudlg = nullptr ;
2012-11-01 16:19:01 +01:00
2013-09-04 12:07:42 +02:00
static std : : string langRegion ;
2013-10-09 17:17:28 -04:00
static std : : string osName ;
2024-09-03 15:58:35 +04:00
static std : : string osVersion ;
2014-07-31 01:21:37 -04:00
static std : : string gpuDriverVersion ;
2013-09-04 12:07:42 +02:00
2020-05-09 13:52:04 -07:00
static std : : string restartArgs ;
2017-08-31 17:13:18 +02:00
int g_activeWindow = 0 ;
2023-03-24 19:57:24 +01:00
WindowsInputManager g_inputManager ;
2023-03-21 11:21:19 +01:00
int g_lastNumInstances = 0 ;
2021-02-18 07:27:28 -08:00
2023-08-10 13:21:36 +02: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.
2024-10-25 15:19:44 +02:00
static constexpr double ACTIVITY_IDLE_TIMEOUT = 2.0 * 3600.0 ;
2023-08-10 13:21:36 +02:00
2023-03-21 10:42:23 +01:00
void System_LaunchUrl ( LaunchUrlType urlType , const char * url ) {
2013-08-26 19:00:16 +02:00
ShellExecute ( NULL , L " open " , ConvertUTF8ToWString ( url ) . c_str ( ) , NULL , NULL , SW_SHOWNORMAL ) ;
2013-08-18 00:41:19 +02:00
}
2023-03-21 10:42:23 +01:00
void System_Vibrate ( int length_ms ) {
2013-10-10 08:00:15 -07:00
// Ignore on PC
}
2022-10-09 21:42:04 -07:00
static void AddDebugRestartArgs ( ) {
2024-12-03 21:00:08 +01:00
if ( g_logManager . GetConsoleListener ( ) - > IsOpen ( ) )
2022-10-09 21:42:04 -07:00
restartArgs + = " -l " ;
}
2014-07-31 01:21:37 -04: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 04:00:48 -04:00
std : : string GetVideoCardDriverVersion ( ) {
2014-07-31 01:21:37 -04:00
std : : string retvalue = " " ;
HRESULT hr ;
hr = CoInitializeEx ( NULL , COINIT_MULTITHREADED ) ;
2014-08-02 12:49:09 +02:00
if ( FAILED ( hr ) ) {
return retvalue ;
}
2014-07-31 01:21:37 -04:00
IWbemLocator * pIWbemLocator = NULL ;
2020-01-06 01:04:07 +08:00
hr = CoCreateInstance ( __uuidof ( WbemLocator ) , NULL , CLSCTX_INPROC_SERVER ,
2014-07-31 01:21:37 -04:00
__uuidof ( IWbemLocator ) , ( LPVOID * ) & pIWbemLocator ) ;
2014-08-02 12:49:09 +02:00
if ( FAILED ( hr ) ) {
CoUninitialize ( ) ;
return retvalue ;
}
2014-07-31 01:21:37 -04:00
BSTR bstrServer = SysAllocString ( L " \\ \\ . \\ root \\ cimv2 " ) ;
IWbemServices * pIWbemServices ;
hr = pIWbemLocator - > ConnectServer ( bstrServer , NULL , NULL , 0L , 0L , NULL , NULL , & pIWbemServices ) ;
2014-08-02 12:49:09 +02:00
if ( FAILED ( hr ) ) {
pIWbemLocator - > Release ( ) ;
2015-01-17 14:00:54 -08:00
SysFreeString ( bstrServer ) ;
2014-08-02 12:49:09 +02:00
CoUninitialize ( ) ;
return retvalue ;
}
2014-07-31 01:21:37 -04:00
2020-01-06 01:04:07 +08:00
hr = CoSetProxyBlanket ( pIWbemServices , RPC_C_AUTHN_WINNT , RPC_C_AUTHZ_NONE ,
2014-07-31 01:21:37 -04:00
NULL , RPC_C_AUTHN_LEVEL_CALL , RPC_C_IMP_LEVEL_IMPERSONATE , NULL , EOAC_DEFAULT ) ;
2020-01-06 01:04:07 +08:00
2014-07-31 01:21:37 -04: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-10 21:09:50 -08:00
ULONG uReturned = 0 ;
VARIANT var { } ;
2014-08-02 12:49:09 +02:00
IWbemClassObject * pObj = NULL ;
if ( ! FAILED ( hr ) ) {
hr = pEnum - > Next ( WBEM_INFINITE , 1 , & pObj , & uReturned ) ;
}
2014-07-31 01:21:37 -04:00
2015-04-08 11:35:40 -07:00
if ( ! FAILED ( hr ) & & uReturned ) {
2014-07-31 01:21:37 -04: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 12:49:09 +02:00
pIWbemLocator - > Release ( ) ;
2014-07-31 01:21:37 -04:00
SysFreeString ( bstrServer ) ;
CoUninitialize ( ) ;
return retvalue ;
}
2013-09-04 11:29:38 +02:00
std : : string System_GetProperty ( SystemProperty prop ) {
2014-08-02 12:49:09 +02:00
static bool hasCheckedGPUDriverVersion = false ;
2013-09-04 11:29:38 +02:00
switch ( prop ) {
case SYSPROP_NAME :
2013-10-09 17:17:28 -04:00
return osName ;
2024-09-03 20:42:34 +04:00
case SYSPROP_SYSTEMBUILD :
2024-09-03 15:58:35 +04:00
return osVersion ;
2013-09-04 11:29:38 +02:00
case SYSPROP_LANGREGION :
2013-09-04 12:07:42 +02:00
return langRegion ;
2014-07-21 11:59:47 +02: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 20:54:17 +08:00
if ( wstr )
retval = ConvertWStringToUTF8 ( wstr ) ;
else
2022-09-30 12:26:30 +03:00
retval . clear ( ) ;
2014-07-21 11:59:47 +02:00
GlobalUnlock ( handle ) ;
CloseClipboard ( ) ;
}
return retval ;
}
2014-07-31 01:21:37 -04:00
case SYSPROP_GPUDRIVER_VERSION :
2014-08-02 12:49:09 +02:00
if ( ! hasCheckedGPUDriverVersion ) {
hasCheckedGPUDriverVersion = true ;
gpuDriverVersion = GetVideoCardDriverVersion ( ) ;
}
2014-07-31 01:21:37 -04:00
return gpuDriverVersion ;
2023-07-20 09:56:51 +02:00
case SYSPROP_BUILD_VERSION :
return PPSSPP_GIT_VERSION ;
2023-08-18 15:04:20 +02:00
case SYSPROP_USER_DOCUMENTS_DIR :
return Path ( W32Util : : UserDocumentsPath ( ) ) . ToString ( ) ; // this'll reverse the slashes.
2013-09-04 11:29:38 +02:00
default :
return " " ;
}
2013-08-18 00:41:19 +02:00
}
2021-01-06 16:37:04 +01:00
std : : vector < std : : string > System_GetPropertyStringVec ( SystemProperty prop ) {
2021-01-09 14:45:49 -08:00
std : : vector < std : : string > result ;
2021-01-06 16:37:04 +01:00
switch ( prop ) {
2021-01-09 14:45:49 -08: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 20:33:17 +01:00
if ( getenv ( " TMP " ) & & strlen ( getenv ( " TMP " ) ) ! = 0 )
2021-01-09 14:45:49 -08:00
result . push_back ( getenv ( " TMP " ) ) ;
2021-01-10 20:33:17 +01:00
if ( getenv ( " TEMP " ) & & strlen ( getenv ( " TEMP " ) ) ! = 0 )
2021-01-09 14:45:49 -08:00
result . push_back ( getenv ( " TEMP " ) ) ;
return result ;
}
2021-01-06 16:37:04 +01:00
default :
2021-01-09 14:45:49 -08:00
return result ;
2021-01-06 16:37:04 +01:00
}
}
2015-01-11 21:00:56 +01:00
// Ugly!
extern WindowsAudioBackend * winAudioBackend ;
2017-03-17 13:22:00 +01:00
# ifdef _WIN32
# if PPSSPP_PLATFORM(UWP)
2024-05-03 17:00:36 +02:00
static float ScreenDPI ( ) {
return 96.0f ; // TODO UWP
2017-03-17 13:22:00 +01:00
}
# else
2024-05-03 17:00:36 +02:00
static float ScreenDPI ( ) {
2017-03-17 13:22:00 +01:00
HDC screenDC = GetDC ( nullptr ) ;
int dotsPerInch = GetDeviceCaps ( screenDC , LOGPIXELSY ) ;
ReleaseDC ( nullptr , screenDC ) ;
2024-05-03 17:00:36 +02:00
return dotsPerInch ? ( float ) dotsPerInch : 96.0f ;
2017-03-17 13:22:00 +01:00
}
# endif
# endif
2024-05-03 17:00:36 +02:00
static float ScreenRefreshRateHz ( ) {
static float rate = 0.0f ;
2023-10-03 01:06:07 +02:00
static double lastCheck = 0.0 ;
2024-05-03 17:00:36 +02:00
const double now = time_now_d ( ) ;
2023-10-03 01:06:07 +02:00
if ( ! rate | | lastCheck < now - 10.0 ) {
lastCheck = now ;
DEVMODE lpDevMode { } ;
lpDevMode . dmSize = sizeof ( DEVMODE ) ;
lpDevMode . dmDriverExtra = 0 ;
2023-01-04 16:33:00 +01:00
2023-10-03 01:06:07 +02:00
// TODO: Use QueryDisplayConfig instead (Win7+) so we can get fractional refresh rates correctly.
2023-08-15 15:20:01 +02:00
2023-10-03 01:06:07 +02:00
if ( EnumDisplaySettings ( NULL , ENUM_CURRENT_SETTINGS , & lpDevMode ) = = 0 ) {
2024-05-03 17:00:36 +02:00
rate = 60.0f ; // default value
2023-01-05 13:07:36 +01:00
} else {
2023-10-03 01:06:07 +02:00
if ( lpDevMode . dmFields & DM_DISPLAYFREQUENCY ) {
2024-05-03 17:00:36 +02:00
rate = ( float ) ( lpDevMode . dmDisplayFrequency > 60 ? lpDevMode . dmDisplayFrequency : 60 ) ;
2023-10-03 01:06:07 +02:00
} else {
2024-05-03 17:00:36 +02:00
rate = 60.0f ;
2023-10-03 01:06:07 +02:00
}
2023-01-05 13:07:36 +01:00
}
2023-01-04 16:33:00 +01:00
}
2023-10-03 01:06:07 +02:00
return rate ;
2023-01-04 16:33:00 +01:00
}
2024-04-05 11:07:57 +02:00
int64_t System_GetPropertyInt ( SystemProperty prop ) {
2015-01-11 14:18:40 +01:00
switch ( prop ) {
2024-04-05 11:07:57 +02:00
case SYSPROP_MAIN_WINDOW_HANDLE :
return ( int64_t ) MainWindow : : GetHWND ( ) ;
2015-01-11 14:18:40 +01:00
case SYSPROP_AUDIO_SAMPLE_RATE :
2015-01-11 21:00:56 +01:00
return winAudioBackend ? winAudioBackend - > GetSampleRate ( ) : - 1 ;
2015-10-06 19:17:29 +02:00
case SYSPROP_DEVICE_TYPE :
return DEVICE_TYPE_DESKTOP ;
2017-07-30 08:33:02 -07: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 ;
}
}
2015-01-11 14:18:40 +01:00
default :
return - 1 ;
}
2014-07-20 12:11:50 +02:00
}
2020-01-06 01:04:07 +08:00
float System_GetPropertyFloat ( SystemProperty prop ) {
switch ( prop ) {
case SYSPROP_DISPLAY_REFRESH_RATE :
2024-05-03 17:00:36 +02:00
return ScreenRefreshRateHz ( ) ;
2020-01-06 01:04:07 +08:00
case SYSPROP_DISPLAY_DPI :
2024-05-03 17:00:36 +02:00
return ScreenDPI ( ) ;
2020-04-05 17:06:36 -07: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-06 01:04:07 +08:00
default :
return - 1 ;
}
}
2017-04-29 17:35:12 -07:00
bool System_GetPropertyBool ( SystemProperty prop ) {
switch ( prop ) {
2024-09-02 22:48:47 +02:00
case SYSPROP_HAS_TEXT_CLIPBOARD :
2023-03-24 18:08:31 +01:00
case SYSPROP_HAS_DEBUGGER :
2017-04-29 17:35:12 -07:00
case SYSPROP_HAS_FILE_BROWSER :
2020-12-20 01:28:43 +01:00
case SYSPROP_HAS_FOLDER_BROWSER :
2023-02-02 14:54:50 +01:00
case SYSPROP_HAS_OPEN_DIRECTORY :
2023-06-22 00:00:08 +02:00
case SYSPROP_HAS_TEXT_INPUT_DIALOG :
2023-08-18 10:42:50 +02:00
case SYSPROP_CAN_CREATE_SHORTCUT :
2023-08-24 13:04:41 +02:00
case SYSPROP_CAN_SHOW_FILE :
2017-04-29 17:35:12 -07:00
return true ;
case SYSPROP_HAS_IMAGE_BROWSER :
return true ;
case SYSPROP_HAS_BACK_BUTTON :
return true ;
2023-06-19 23:22:44 +02:00
case SYSPROP_HAS_LOGIN_DIALOG :
return true ;
2017-04-29 17:35:12 -07:00
case SYSPROP_APP_GOLD :
# ifdef GOLD
return true ;
# else
return false ;
# endif
2021-02-21 22:02:11 +01:00
case SYSPROP_CAN_JIT :
return true ;
2021-04-25 22:54:28 -07:00
case SYSPROP_HAS_KEYBOARD :
return true ;
2022-07-10 22:34:44 +02:00
case SYSPROP_SUPPORTS_OPEN_FILE_IN_EDITOR :
return true ; // FileUtil.cpp: OpenFileInEditor
2023-07-20 11:25:27 +02:00
case SYSPROP_SUPPORTS_HTTPS :
2023-08-24 21:28:30 +02:00
return ! g_Config . bDisableHTTPS ;
2023-10-11 23:40:07 +02:00
case SYSPROP_DEBUGGER_PRESENT :
return IsDebuggerPresent ( ) ;
2023-12-05 14:17:14 +01:00
case SYSPROP_OK_BUTTON_LEFT :
return true ;
2017-04-29 17:35:12 -07:00
default :
return false ;
}
}
2023-03-21 11:10:09 +01:00
static BOOL PostDialogMessage ( Dialog * dialog , UINT message , WPARAM wParam = 0 , LPARAM lParam = 0 ) {
return PostMessage ( dialog - > GetDlgHandle ( ) , message , wParam , lParam ) ;
}
2023-03-22 12:26:14 +01:00
// This can come from any thread, so this mostly uses PostMessage. Can't access most data directly.
2023-03-21 11:10:09 +01:00
void System_Notify ( SystemNotification notification ) {
switch ( notification ) {
case SystemNotification : : BOOT_DONE :
2023-03-21 11:21:19 +01:00
{
2023-03-21 11:10:09 +01:00
if ( g_symbolMap )
2023-03-22 12:26:14 +01:00
g_symbolMap - > SortSymbols ( ) ; // internal locking is performed here
2023-03-21 11:10:09 +01:00
PostMessage ( MainWindow : : GetHWND ( ) , WM_USER + 1 , 0 , 0 ) ;
if ( disasmWindow )
2023-03-21 11:40:48 +01:00
PostDialogMessage ( disasmWindow , WM_DEB_SETDEBUGLPARAM , 0 , ( LPARAM ) Core_IsStepping ( ) ) ;
2023-03-21 11:10:09 +01:00
break ;
}
2023-03-21 11:21:19 +01: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 11:28:50 +01:00
2024-11-03 17:29:13 +01:00
case SystemNotification : : DISASSEMBLY_AFTERSTEP :
if ( disasmWindow )
PostDialogMessage ( disasmWindow , WM_DEB_AFTERSTEP ) ;
break ;
2023-03-21 11:28:50 +01:00
case SystemNotification : : SYMBOL_MAP_UPDATED :
if ( g_symbolMap )
2023-03-22 12:26:14 +01:00
g_symbolMap - > SortSymbols ( ) ; // internal locking is performed here
2023-03-21 11:28:50 +01:00
PostMessage ( MainWindow : : GetHWND ( ) , WM_USER + 1 , 0 , 0 ) ;
break ;
2023-03-21 11:31:52 +01:00
case SystemNotification : : SWITCH_UMD_UPDATED :
PostMessage ( MainWindow : : GetHWND ( ) , MainWindow : : WM_USER_SWITCHUMD_UPDATED , 0 , 0 ) ;
break ;
2023-03-21 11:40:48 +01:00
case SystemNotification : : DEBUG_MODE_CHANGE :
if ( disasmWindow )
PostDialogMessage ( disasmWindow , WM_DEB_SETDEBUGLPARAM , 0 , ( LPARAM ) Core_IsStepping ( ) ) ;
break ;
2023-03-24 21:43:45 +01:00
2023-03-24 19:57:24 +01:00
case SystemNotification : : POLL_CONTROLLERS :
g_inputManager . PollControllers ( ) ;
break ;
2023-03-24 21:43:45 +01:00
case SystemNotification : : TOGGLE_DEBUG_CONSOLE :
MainWindow : : ToggleDebugConsoleVisibility ( ) ;
break ;
2023-08-10 13:21:36 +02: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 11:21:19 +01:00
}
2023-03-21 11:10:09 +01:00
}
2024-11-25 23:03:15 +01:00
static std : : wstring FinalizeFilter ( std : : wstring filter ) {
2023-03-22 13:43:44 +01:00
for ( size_t i = 0 ; i < filter . length ( ) ; i + + ) {
if ( filter [ i ] = = ' | ' )
filter [ i ] = ' \0 ' ;
}
return filter ;
}
2024-11-25 23:03:15 +01:00
static std : : wstring MakeWindowsFilter ( BrowseFileType type ) {
switch ( type ) {
case BrowseFileType : : BOOTABLE :
return FinalizeFilter ( 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 ( * . * ) | * . * | | " );
case BrowseFileType : : INI :
return FinalizeFilter ( L " Ini files (*.ini) | * . ini | All files ( * . * ) | * . * | | " ) ;
case BrowseFileType : : ZIP :
return FinalizeFilter ( L " ZIP files (*.zip) | * . zip | All files ( * . * ) | * . * | | " ) ;
case BrowseFileType : : DB :
return FinalizeFilter ( L " Cheat db files (*.db) | * . db | All files ( * . * ) | * . * | | " ) ;
case BrowseFileType : : SOUND_EFFECT :
return FinalizeFilter ( L " Sound effect files (*.wav *.mp3) | * . wav ; * . mp3 | All files ( * . * ) | * . * | | " );
case BrowseFileType : : SYMBOL_MAP :
return FinalizeFilter ( L " Symbol map files (*.ppmap) | * . ppmap | All files ( * . * ) | * . * | | " ) ;
case BrowseFileType : : ANY :
return FinalizeFilter ( L " All files (*.*) | * . * | | " ) ;
default :
return std : : wstring ( ) ;
}
}
2024-04-06 12:04:45 +02: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 12:26:14 +01:00
switch ( type ) {
2023-03-22 22:07:29 +01:00
case SystemRequestType : : EXIT_APP :
if ( ! NativeIsRestarting ( ) ) {
PostMessage ( MainWindow : : GetHWND ( ) , WM_CLOSE , 0 , 0 ) ;
}
return true ;
2023-03-22 22:17:53 +01:00
case SystemRequestType : : RESTART_APP :
{
restartArgs = param1 ;
if ( ! restartArgs . empty ( ) )
AddDebugRestartArgs ( ) ;
2023-10-11 23:40:07 +02:00
if ( System_GetPropertyBool ( SYSPROP_DEBUGGER_PRESENT ) ) {
2023-03-22 22:17:53 +01: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 22:07:29 +01:00
case SystemRequestType : : COPY_TO_CLIPBOARD :
{
std : : wstring data = ConvertUTF8ToWString ( param1 ) ;
W32Util : : CopyTextToClipboard ( MainWindow : : GetDisplayHWND ( ) , data ) ;
return true ;
}
2023-03-24 17:40:03 +01:00
case SystemRequestType : : SET_WINDOW_TITLE :
{
2023-04-19 14:54:58 +02:00
const char * name = System_GetPropertyBool ( SYSPROP_APP_GOLD ) ? " PPSSPP Gold " : " PPSSPP " ;
2023-03-24 17:40:03 +01: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 14:03:54 +02:00
case SystemRequestType : : SET_KEEP_SCREEN_BRIGHT :
{
MainWindow : : SetKeepScreenBright ( param3 ! = 0 ) ;
return true ;
}
2023-03-22 12:26:14 +01:00
case SystemRequestType : : INPUT_TEXT_MODAL :
2023-03-25 16:19:26 -07:00
std : : thread ( [ = ] {
2023-03-22 12:26:14 +01:00
std : : string out ;
2024-09-26 10:48:31 +02:00
InputBoxFlags flags { } ;
if ( param3 ) {
flags | = InputBoxFlags : : PasswordMasking ;
}
if ( ! param2 . empty ( ) ) {
flags | = InputBoxFlags : : Selected ;
}
if ( InputBox_GetString ( MainWindow : : GetHInstance ( ) , MainWindow : : GetHWND ( ) , ConvertUTF8ToWString ( param1 ) . c_str ( ) , param2 , out , flags ) ) {
2023-03-22 12:26:14 +01:00
g_requestManager . PostSystemSuccess ( requestId , out . c_str ( ) ) ;
} else {
g_requestManager . PostSystemFailure ( requestId ) ;
}
2023-03-25 16:19:26 -07:00
} ) . detach ( ) ;
2023-03-22 12:26:14 +01:00
return true ;
2023-06-19 23:22:44 +02: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 13:43:44 +01:00
case SystemRequestType : : BROWSE_FOR_IMAGE :
2023-03-25 16:19:26 -07:00
std : : thread ( [ = ] {
2023-03-22 13:43:44 +01:00
std : : string out ;
if ( W32Util : : BrowseForFileName ( true , MainWindow : : GetHWND ( ) , ConvertUTF8ToWString ( param1 ) . c_str ( ) , nullptr ,
2024-11-25 23:03:15 +01:00
FinalizeFilter ( L " All supported images (*.jpg *.jpeg *.png)|*.jpg;*.jpeg;*.png|All files (*.*)|*.*|| " ) . c_str ( ) , L " jpg " , out ) ) {
2023-03-22 13:43:44 +01:00
g_requestManager . PostSystemSuccess ( requestId , out . c_str ( ) ) ;
} else {
g_requestManager . PostSystemFailure ( requestId ) ;
}
2023-03-25 16:19:26 -07:00
} ) . detach ( ) ;
2023-03-22 13:43:44 +01:00
return true ;
2023-03-22 15:21:03 +01:00
case SystemRequestType : : BROWSE_FOR_FILE :
2024-11-25 23:03:15 +01:00
case SystemRequestType : : BROWSE_FOR_FILE_SAVE :
2023-03-22 15:21:03 +01:00
{
2024-11-25 23:03:15 +01:00
const BrowseFileType browseType = ( BrowseFileType ) param3 ;
std : : wstring filter = MakeWindowsFilter ( browseType ) ;
std : : wstring initialFilename = ConvertUTF8ToWString ( param2 ) ; // TODO: Plumb through
if ( filter . empty ( ) ) {
// Unsupported.
2023-03-22 15:21:03 +01:00
return false ;
}
2024-11-25 23:03:15 +01:00
bool load = type = = SystemRequestType : : BROWSE_FOR_FILE ;
2023-03-25 16:19:26 -07:00
std : : thread ( [ = ] {
2023-03-22 15:21:03 +01:00
std : : string out ;
2024-11-25 23:03:15 +01:00
if ( W32Util : : BrowseForFileName ( load , MainWindow : : GetHWND ( ) , ConvertUTF8ToWString ( param1 ) . c_str ( ) , nullptr , filter . c_str ( ) , L " " , out ) ) {
2023-03-22 15:21:03 +01:00
g_requestManager . PostSystemSuccess ( requestId , out . c_str ( ) ) ;
} else {
g_requestManager . PostSystemFailure ( requestId ) ;
}
2023-03-25 16:19:26 -07:00
} ) . detach ( ) ;
2023-03-22 15:21:03 +01:00
return true ;
}
2023-03-22 17:39:45 +01:00
case SystemRequestType : : BROWSE_FOR_FOLDER :
{
2023-03-25 16:19:26 -07:00
std : : thread ( [ = ] {
2024-01-25 12:47:37 +01:00
std : : string folder = W32Util : : BrowseForFolder ( MainWindow : : GetHWND ( ) , param1 , param2 ) ;
2023-03-24 21:32:20 +01:00
if ( folder . size ( ) ) {
g_requestManager . PostSystemSuccess ( requestId , folder . c_str ( ) ) ;
} else {
g_requestManager . PostSystemFailure ( requestId ) ;
}
2023-03-25 16:19:26 -07:00
} ) . detach ( ) ;
2023-03-22 17:39:45 +01:00
return true ;
}
2023-08-24 13:04:41 +02:00
case SystemRequestType : : SHOW_FILE_IN_FOLDER :
W32Util : : ShowFileInFolder ( param1 ) ;
2023-08-24 21:28:30 +02:00
return true ;
2023-08-24 13:04:41 +02:00
2023-03-22 22:17:53 +01:00
case SystemRequestType : : TOGGLE_FULLSCREEN_STATE :
{
bool flag = ! MainWindow : : IsFullscreen ( ) ;
if ( param1 = = " 0 " ) {
flag = false ;
} else if ( param1 = = " 1 " ) {
flag = true ;
}
MainWindow : : SendToggleFullscreen ( flag ) ;
return true ;
}
case SystemRequestType : : GRAPHICS_BACKEND_FAILED_ALERT :
{
2023-04-06 00:34:50 +02:00
auto err = GetI18NCategory ( I18NCat : : ERRORS ) ;
2024-01-19 13:44:49 +01: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 22:17:53 +01: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 20:05:48 +01:00
case SystemRequestType : : CREATE_GAME_SHORTCUT :
2024-05-13 01:37:53 +02: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 14:42:59 +02:00
ERROR_LOG ( Log : : System , " ICO creation failed " ) ;
2024-05-13 01:37:53 +02:00
icoPath . clear ( ) ;
}
}
}
return W32Util : : CreateDesktopShortcut ( param1 , param2 , icoPath ) ;
}
2024-04-06 12:14:29 +02: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 12:26:14 +01:00
default :
return false ;
}
}
2015-12-17 22:41:50 +01:00
void System_AskForPermission ( SystemPermission permission ) { }
PermissionStatus System_GetPermissionStatus ( SystemPermission permission ) { return PERMISSION_STATUS_GRANTED ; }
2024-05-03 17:00:36 +02:00
// Don't swallow exceptions.
static void EnableCrashingOnCrashes ( ) {
2015-01-17 12:32:58 -08:00
typedef BOOL ( WINAPI * tGetPolicy ) ( LPDWORD lpFlags ) ;
typedef BOOL ( WINAPI * tSetPolicy ) ( DWORD dwFlags ) ;
const DWORD EXCEPTION_SWALLOWING = 0x1 ;
2013-11-13 02:50:35 +10:00
2015-01-17 12:32:58 -08:00
HMODULE kernel32 = LoadLibrary ( L " kernel32.dll " ) ;
2022-12-10 21:09:50 -08:00
if ( ! kernel32 )
return ;
2015-01-17 12:32:58 -08: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-13 02:50:35 +10:00
}
2024-01-19 13:44:49 +01:00
void System_Toast ( std : : string_view text ) {
2022-07-10 22:34:44 +02: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-25 19:14:05 -08: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 03:30:38 -05:00
2019-02-16 10:14:54 -08:00
static const int EXIT_CODE_VULKAN_WORKS = 42 ;
2021-02-15 09:28:07 -08:00
# ifndef _DEBUG
2019-02-16 10:14:54 -08:00
static bool DetectVulkanInExternalProcess ( ) {
2020-01-04 09:02:10 -08:00
std : : wstring workingDirectory ;
std : : wstring moduleFilename ;
W32Util : : GetSelfExecuteParams ( workingDirectory , moduleFilename ) ;
2019-02-16 10:14:54 -08:00
const wchar_t * cmdline = L " --vulkan-available-check " ;
DWORD exitCode = 0 ;
2023-10-03 12:00:59 +02:00
if ( W32Util : : ExecuteAndGetReturnCode ( moduleFilename . c_str ( ) , cmdline , workingDirectory . c_str ( ) , & exitCode ) ) {
return exitCode = = EXIT_CODE_VULKAN_WORKS ;
} else {
2024-07-14 14:42:59 +02:00
ERROR_LOG ( Log : : G3D , " Failed to detect Vulkan in external process somehow " ) ;
2019-02-16 10:14:54 -08:00
return false ;
}
}
2021-02-15 09:28:07 -08:00
# endif
2019-02-16 10:14:54 -08:00
2014-08-31 01:17:25 -04:00
std : : vector < std : : wstring > GetWideCmdLine ( ) {
wchar_t * * wargv ;
int wargc = - 1 ;
2020-05-09 13:52:04 -07: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 01:17:25 -04:00
std : : vector < std : : wstring > wideArgs ( wargv , wargv + wargc ) ;
2018-01-17 19:25:01 +09:00
LocalFree ( wargv ) ;
2014-08-31 01:17:25 -04:00
return wideArgs ;
}
2023-08-18 12:48:57 +02: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 14:42:59 +02:00
INFO_LOG ( Log : : Common , " Memstick directory not present, creating at '%s' " , g_Config . memStickDirectory . c_str ( ) ) ;
2023-08-18 12:48:57 +02: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.
2024-11-28 11:40:52 +01:00
File : : Delete ( testFile ) ;
2023-08-18 12:48:57 +02:00
}
2019-02-16 10:14:54 -08:00
static void WinMainInit ( ) {
2015-01-15 22:32:25 +01:00
CoInitializeEx ( NULL , COINIT_MULTITHREADED ) ;
2017-03-12 17:24:46 +01: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 10:51:28 +01: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 12:24:59 +02:00
# ifdef _DEBUG
2016-02-10 15:22:28 +01:00
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ) ;
2015-10-04 12:24:59 +02:00
# endif
2015-05-12 19:45:14 +02:00
PROFILE_INIT ( ) ;
2014-02-17 03:30:38 -05:00
2021-03-02 21:49:21 -08:00
# if PPSSPP_ARCH(AMD64) && defined(_MSC_VER) && _MSC_VER < 1900
2014-02-13 15:57:36 +01: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 16:02:10 +01:00
# endif
2020-05-04 17:33:39 +02:00
InitDarkMode ( ) ;
2019-02-16 10:14:54 -08:00
}
static void WinMainCleanup ( ) {
2023-03-25 16:19:26 -07:00
// This will ensure no further callbacks are called, which may prevent crashing.
g_requestManager . Clear ( ) ;
2019-02-16 10:14:54 -08:00
net : : Shutdown ( ) ;
CoUninitialize ( ) ;
2020-05-09 13:52:04 -07:00
if ( g_Config . bRestartRequired ) {
2023-09-20 14:37:01 +02: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 13:52:04 -07:00
W32Util : : ExitAndRestart ( ! restartArgs . empty ( ) , restartArgs ) ;
}
2019-02-16 10:14:54 -08:00
}
int WINAPI WinMain ( HINSTANCE _hInstance , HINSTANCE hPrevInstance , LPSTR szCmdLine , int iCmdShow ) {
2023-10-03 12:06:43 +02: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 ( ) ;
2024-12-03 21:00:08 +01:00
g_logManager . Shutdown ( ) ;
2023-10-03 12:06:43 +02:00
WinMainCleanup ( ) ;
return result ? EXIT_CODE_VULKAN_WORKS : EXIT_FAILURE ;
}
}
}
2020-12-01 00:46:26 +01:00
SetCurrentThreadName ( " Main " ) ;
2019-02-16 10:14:54 -08:00
2024-07-26 11:16:41 +02:00
TimeInit ( ) ;
2019-02-16 10:14:54 -08:00
WinMainInit ( ) ;
2014-02-13 15:57:36 +01:00
2013-10-13 12:27:05 -07:00
# ifndef _DEBUG
2013-10-15 22:27:03 -07:00
bool showLog = false ;
2013-10-13 12:27:05 -07:00
# else
2015-12-25 19:14:05 -08:00
bool showLog = true ;
2013-10-13 12:27:05 -07:00
# endif
2021-05-15 09:59:25 -07:00
const Path & exePath = File : : GetExeDirectory ( ) ;
2023-03-06 15:30:39 +01:00
g_VFS . Register ( " " , new DirectoryReader ( exePath / " assets " ) ) ;
g_VFS . Register ( " " , new DirectoryReader ( exePath ) ) ;
2013-09-15 11:02:13 +02:00
2015-12-25 19:14:05 -08:00
langRegion = GetDefaultLangRegion ( ) ;
2024-09-03 20:07:57 +04:00
osName = GetWindowsVersion ( ) + " " + GetWindowsSystemArchitecture ( ) ;
2024-09-03 15:58:35 +04:00
2024-09-03 20:07:57 +04:00
// OS Build
2024-09-03 15:58:35 +04:00
uint32_t outMajor = 0 , outMinor = 0 , outBuild = 0 ;
2024-09-03 20:07:57 +04: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 15:58:35 +04:00
}
2013-10-09 17:17:28 -04:00
2018-01-18 00:36:27 +09:00
std : : string configFilename = " " ;
2014-08-31 01:17:25 -04:00
const std : : wstring configOption = L " --config= " ;
2013-09-15 12:50:42 -04:00
2018-01-18 00:36:27 +09:00
std : : string controlsConfigFilename = " " ;
2014-08-31 01:17:25 -04:00
const std : : wstring controlsOption = L " --controlconfig= " ;
2013-09-15 12:50:42 -04:00
2014-09-12 16:58:05 -04:00
for ( size_t i = 1 ; i < wideArgs . size ( ) ; + + i ) {
2014-08-31 01:17:25 -04:00
if ( wideArgs [ i ] [ 0 ] = = L ' \0 ' )
2013-09-15 12:50:42 -04:00
continue ;
2014-08-31 01:17:25 -04: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-18 00:36:27 +09:00
configFilename = ConvertWStringToUTF8 ( tempWide ) ;
2013-09-15 12:50:42 -04:00
}
2014-08-31 01:17:25 -04:00
if ( wideArgs [ i ] . find ( controlsOption ) ! = std : : wstring : : npos & & wideArgs [ i ] . size ( ) > controlsOption . size ( ) ) {
2014-11-02 20:38:00 +01:00
const std : : wstring tempWide = wideArgs [ i ] . substr ( controlsOption . size ( ) ) ;
2018-01-18 00:36:27 +09:00
controlsConfigFilename = ConvertWStringToUTF8 ( tempWide ) ;
2013-09-15 12:50:42 -04:00
}
}
}
2024-11-27 16:02:56 +01:00
// This will be overridden by the actual config. But we do want to log during startup.
g_Config . bEnableLogging = true ;
2024-12-03 21:00:08 +01:00
g_logManager . Init ( & g_Config . bEnableLogging ) ;
2017-03-06 11:44:35 +01:00
2020-01-06 01:04:07 +08:00
// On Win32 it makes more sense to initialize the system directories here
2013-10-15 02:28:14 -04:00
// because the next place it was called was in the EmuThread, and it's too late by then.
2021-05-15 09:32:41 -07:00
g_Config . internalDataDirectory = Path ( W32Util : : UserDocumentsPath ( ) ) ;
2023-08-18 12:48:57 +02:00
InitMemstickDirectory ( ) ;
2023-08-18 12:15:12 +02:00
CreateSysDirectories ( ) ;
2013-09-15 12:50:42 -04:00
2013-09-12 16:56:18 -04:00
// Load config up here, because those changes below would be overwritten
// if it's not loaded here first.
2021-05-30 12:45:12 +02:00
g_Config . SetSearchPath ( GetSysDirectory ( DIRECTORY_SYSTEM ) ) ;
2018-01-18 00:36:27 +09:00
g_Config . Load ( configFilename . c_str ( ) , controlsConfigFilename . c_str ( ) ) ;
2013-09-12 16:56:18 -04:00
2014-03-31 22:44:05 -04:00
bool debugLogLevel = false ;
2014-09-13 01:33:45 -04:00
const std : : wstring gpuBackend = L " --graphics= " ;
2014-09-12 16:16:13 -04:00
2013-03-31 18:22:27 -07:00
// The rest is handled in NativeInit().
2014-09-12 16:58:05 -04:00
for ( size_t i = 1 ; i < wideArgs . size ( ) ; + + i ) {
2014-08-31 01:17:25 -04:00
if ( wideArgs [ i ] [ 0 ] = = L ' \0 ' )
2012-12-22 09:21:23 -08:00
continue ;
2012-11-01 16:19:01 +01:00
2014-08-31 01:17:25 -04:00
if ( wideArgs [ i ] [ 0 ] = = L ' - ' ) {
switch ( wideArgs [ i ] [ 1 ] ) {
case L ' l ' :
2013-10-15 22:27:03 -07:00
showLog = true ;
2013-10-13 14:25:59 -04:00
g_Config . bEnableLogging = true ;
2012-12-22 09:21:23 -08:00
break ;
2014-08-31 01:17:25 -04:00
case L ' s ' :
2013-01-04 10:26:14 +01:00
g_Config . bAutoRun = false ;
g_Config . bSaveSettings = false ;
2012-12-22 09:21:23 -08:00
break ;
2014-08-31 01:17:25 -04:00
case L ' d ' :
2014-03-31 22:44:05 -04:00
debugLogLevel = true ;
break ;
2012-12-22 09:21:23 -08:00
}
2013-09-12 16:56:18 -04:00
2014-09-12 16:58:05 -04:00
if ( wideArgs [ i ] . find ( gpuBackend ) ! = std : : wstring : : npos & & wideArgs [ i ] . size ( ) > gpuBackend . size ( ) ) {
2014-09-12 16:16:13 -04:00
const std : : wstring restOfOption = wideArgs [ i ] . substr ( gpuBackend . size ( ) ) ;
2014-09-13 01:33:45 -04: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 15:55:24 -08:00
g_Config . iGPUBackend = ( int ) GPUBackend : : DIRECT3D9 ;
2014-09-13 01:33:45 -04:00
g_Config . bSoftwareRendering = false ;
2017-02-16 11:30:58 +01:00
} else if ( restOfOption = = L " directx11 " ) {
2017-12-26 15:55:24 -08:00
g_Config . iGPUBackend = ( int ) GPUBackend : : DIRECT3D11 ;
2017-02-16 11:30:58 +01:00
g_Config . bSoftwareRendering = false ;
2016-02-13 12:22:06 -08:00
} else if ( restOfOption = = L " gles " ) {
2017-12-26 15:55:24 -08:00
g_Config . iGPUBackend = ( int ) GPUBackend : : OPENGL ;
2014-09-13 01:33:45 -04:00
g_Config . bSoftwareRendering = false ;
2017-02-16 11:30:58 +01:00
} else if ( restOfOption = = L " vulkan " ) {
2017-12-26 15:55:24 -08:00
g_Config . iGPUBackend = ( int ) GPUBackend : : VULKAN ;
2017-02-16 11:30:58 +01:00
g_Config . bSoftwareRendering = false ;
2016-02-13 12:22:06 -08:00
} else if ( restOfOption = = L " software " ) {
2017-12-26 15:55:24 -08:00
g_Config . iGPUBackend = ( int ) GPUBackend : : OPENGL ;
2014-09-13 01:33:45 -04:00
g_Config . bSoftwareRendering = true ;
}
2014-09-12 16:16:13 -04:00
}
2012-12-22 09:21:23 -08:00
}
2012-11-01 16:19:01 +01:00
}
2013-10-13 14:25:59 -04:00
# ifdef _DEBUG
g_Config . bEnableLogging = true ;
# endif
2012-11-01 16:19:01 +01:00
2019-02-16 10:14:54 -08:00
# ifndef _DEBUG
// See #11719 - too many Vulkan drivers crash on basic init.
2024-05-26 17:26:11 +02:00
if ( g_Config . IsBackendEnabled ( GPUBackend : : VULKAN ) ) {
2019-06-22 11:48:36 -07:00
VulkanSetAvailable ( DetectVulkanInExternalProcess ( ) ) ;
}
2019-02-16 10:14:54 -08:00
# endif
2015-12-25 19:18:32 -08:00
if ( iCmdShow = = SW_MAXIMIZE ) {
// Consider this to mean --fullscreen.
2022-05-28 15:47:12 -07:00
g_Config . iForceFullScreen = 1 ;
2015-12-25 19:18:32 -08:00
}
2013-10-13 12:27:05 -07: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-12-03 21:00:08 +01:00
g_logManager . GetConsoleListener ( ) - > Init ( showLog , 150 , 120 ) ;
2020-01-06 01:04:07 +08:00
2020-08-15 19:01:16 +02:00
if ( debugLogLevel ) {
2024-12-03 21:00:08 +01:00
g_logManager . SetAllLogLevels ( LogLevel : : LDEBUG ) ;
2020-08-15 19:01:16 +02:00
}
2013-06-08 08:32:07 +08:00
2021-10-07 19:53:40 -07:00
ContextMenuInit ( _hInstance ) ;
2012-11-01 16:19:01 +01:00
MainWindow : : Init ( _hInstance ) ;
2014-07-17 00:06:52 -04:00
MainWindow : : Show ( _hInstance ) ;
2012-11-01 16:19:01 +01:00
HWND hwndMain = MainWindow : : GetHWND ( ) ;
2020-01-06 01:04:07 +08:00
2012-11-01 16:19:01 +01:00
//initialize custom controls
CtrlDisAsmView : : init ( ) ;
CtrlMemView : : init ( ) ;
CtrlRegisterList : : init ( ) ;
2019-05-10 23:25:57 +02:00
# if PPSSPP_API(ANY_GL)
2013-09-27 22:41:44 -07:00
CGEDebugger : : Init ( ) ;
2019-05-04 06:06:50 +08:00
# endif
2012-11-01 16:19:01 +01:00
2021-11-13 22:10:37 +01:00
if ( g_Config . bShowDebuggerOnLoad ) {
MainWindow : : CreateDisasmWindow ( ) ;
disasmWindow - > Show ( g_Config . bShowDebuggerOnLoad , false ) ;
}
2013-08-26 14:19:46 +02:00
2015-12-25 19:18:32 -08:00
const bool minimized = iCmdShow = = SW_MINIMIZE | | iCmdShow = = SW_SHOWMINIMIZED | | iCmdShow = = SW_SHOWMINNOACTIVE ;
if ( minimized ) {
MainWindow : : Minimize ( ) ;
}
2023-03-24 19:57:24 +01:00
g_inputManager . Init ( ) ;
2017-12-15 12:40:38 +01:00
// Emu thread (and render thread, if any) is always running!
2018-01-16 14:16:56 +01: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 15:52:19 +01:00
MainThread_Start ( g_Config . iGPUBackend = = ( int ) GPUBackend : : OPENGL ) ;
2014-03-23 22:18:54 -07:00
InputDevice : : BeginPolling ( ) ;
2013-03-29 18:50:08 +01:00
2013-06-08 08:32:07 +08:00
HACCEL hAccelTable = LoadAccelerators ( _hInstance , ( LPCTSTR ) IDR_ACCELS ) ;
2013-08-14 23:30:50 +02:00
HACCEL hDebugAccelTable = LoadAccelerators ( _hInstance , ( LPCTSTR ) IDR_DEBUGACCELS ) ;
2012-12-22 09:21:23 -08:00
2012-11-01 16:19:01 +01:00
//so.. we're at the message pump of the GUI thread
2013-06-08 08:32:07 +08:00
for ( MSG msg ; GetMessage ( & msg , NULL , 0 , 0 ) ; ) // for no quit
2012-11-01 16:19:01 +01:00
{
2013-05-13 16:08:10 +08:00
if ( msg . message = = WM_KEYDOWN )
{
//hack to enable/disable menu command accelerate keys
MainWindow : : UpdateCommands ( ) ;
2020-01-06 01:04:07 +08:00
2013-05-13 16:08:10 +08: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 16:19:01 +01:00
//Translate accelerators and dialog messages...
2013-08-14 23:46:59 +02:00
HWND wnd ;
HACCEL accel ;
2013-09-28 14:34:08 +02:00
switch ( g_activeWindow )
{
case WINDOW_MAINWINDOW :
2013-08-14 23:46:59 +02:00
wnd = hwndMain ;
2021-02-27 14:40:02 -08:00
accel = g_Config . bSystemControls ? hAccelTable : NULL ;
2013-09-28 14:34:08 +02:00
break ;
case WINDOW_CPUDEBUGGER :
2021-02-27 14:40:02 -08:00
wnd = disasmWindow ? disasmWindow - > GetDlgHandle ( ) : NULL ;
accel = g_Config . bSystemControls ? hDebugAccelTable : NULL ;
2013-09-28 14:34:08 +02:00
break ;
case WINDOW_GEDEBUGGER :
default :
2021-02-27 14:40:02 -08:00
wnd = NULL ;
accel = NULL ;
2013-09-28 14:34:08 +02:00
break ;
2013-08-14 23:46:59 +02:00
}
2022-12-10 21:09:50 -08:00
if ( ! wnd | | ! accel | | ! TranslateAccelerator ( wnd , accel , & msg ) ) {
2018-02-10 09:06:43 +01:00
if ( ! DialogManager : : IsDialogMessage ( & msg ) ) {
2012-11-01 16:19:01 +01:00
//and finally translate and dispatch
TranslateMessage ( & msg ) ;
DispatchMessage ( & msg ) ;
}
}
}
2023-03-06 14:23:56 +01:00
g_VFS . Clear ( ) ;
2013-02-08 10:35:05 -08:00
2014-02-14 21:17:36 -08:00
MainWindow : : DestroyDebugWindows ( ) ;
2012-11-01 16:19:01 +01:00
DialogManager : : DestroyAll ( ) ;
2012-12-03 16:44:37 +08:00
timeEndPeriod ( 1 ) ;
2014-09-14 06:59:27 -04:00
2024-12-03 21:00:08 +01:00
g_logManager . Shutdown ( ) ;
2019-02-16 10:14:54 -08:00
WinMainCleanup ( ) ;
2015-10-04 12:24:59 +02:00
2012-11-01 16:19:01 +01:00
return 0 ;
}