1
// Copyright (c) 2012- PPSSPP Project.
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
23
#include "Common/CommonWindows.h"
26
#include "file/zip_read.h"
27
#include "base/NativeApp.h"
28
#include "profiler/profiler.h"
29
#include "thread/threadutil.h"
30
#include "util/text/utf8.h"
32
#include "Core/Config.h"
33
#include "Core/SaveState.h"
34
#include "Windows/EmuThread.h"
35
#include "Windows/DSoundStream.h"
36
#include "ext/disarm.h"
38
#include "Common/LogManager.h"
39
#include "Common/ConsoleListener.h"
43
#include "UI/GameInfoCache.h"
44
#include "Windows/resource.h"
46
#include "Windows/MainWindow.h"
47
#include "Windows/Debugger/Debugger_Disasm.h"
48
#include "Windows/Debugger/Debugger_MemoryDlg.h"
49
#include "Windows/Debugger/Debugger_VFPUDlg.h"
50
#include "Windows/GEDebugger/GEDebugger.h"
52
#include "Windows/W32Util/DialogManager.h"
54
#include "Windows/Debugger/CtrlDisAsmView.h"
55
#include "Windows/Debugger/CtrlMemView.h"
56
#include "Windows/Debugger/CtrlRegisterList.h"
57
#include "Windows/InputBox.h"
59
#include "Windows/WindowsHost.h"
60
#include "Windows/main.h"
63
// Nvidia drivers >= v302 will check if the application exports a global
64
// variable named NvOptimusEnablement to know if it should run the app in high
65
// performance graphics mode or using the IGP.
67
__declspec(dllexport) DWORD NvOptimusEnablement = 1;
70
CDisasm *disasmWindow[MAX_CPUCOUNT] = {0};
71
CGEDebugger *geDebuggerWindow = 0;
72
CMemoryDlg *memoryWindow[MAX_CPUCOUNT] = {0};
74
static std::string langRegion;
75
static std::string osName;
76
static std::string gpuDriverVersion;
78
void LaunchBrowser(const char *url) {
79
ShellExecute(NULL, L"open", ConvertUTF8ToWString(url).c_str(), NULL, NULL, SW_SHOWNORMAL);
82
void Vibrate(int length_ms) {
86
bool DoesVersionMatchWindows(const u32 major, const u32 minor, const u32 spMajor = 0, const u32 spMinor = 0) {
87
u64 conditionMask = 0;
89
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
91
osvi.dwOSVersionInfoSize = sizeof(osvi);
92
osvi.dwMajorVersion = major;
93
osvi.dwMinorVersion = minor;
94
osvi.wServicePackMajor = spMajor;
95
osvi.wServicePackMinor = spMinor;
98
VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, op);
99
VER_SET_CONDITION(conditionMask, VER_MINORVERSION, op);
100
VER_SET_CONDITION(conditionMask, VER_SERVICEPACKMAJOR, op);
101
VER_SET_CONDITION(conditionMask, VER_SERVICEPACKMINOR, op);
103
const u32 typeMask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR;
105
return VerifyVersionInfo(&osvi, typeMask, conditionMask) != FALSE;
108
std::string GetWindowsVersion() {
109
const bool IsWindowsXPSP2 = DoesVersionMatchWindows(5, 1, 2, 0);
110
const bool IsWindowsXPSP3 = DoesVersionMatchWindows(5, 1, 3, 0);
111
const bool IsWindowsVista = DoesVersionMatchWindows(6, 0);
112
const bool IsWindowsVistaSP1 = DoesVersionMatchWindows(6, 0, 1, 0);
113
const bool IsWindowsVistaSP2 = DoesVersionMatchWindows(6, 0, 2, 0);
114
const bool IsWindows7 = DoesVersionMatchWindows(6, 1);
115
const bool IsWindows7SP1 = DoesVersionMatchWindows(6, 1, 1, 0);
116
const bool IsWindows8 = DoesVersionMatchWindows(6, 2);
117
const bool IsWindows8_1 = DoesVersionMatchWindows(6, 3);
121
return "Microsoft Windows XP, Service Pack 2";
124
return "Microsoft Windows XP, Service Pack 3";
127
return "Microsoft Windows Vista";
129
if (IsWindowsVistaSP1)
130
return "Microsoft Windows Vista, Service Pack 1";
132
if (IsWindowsVistaSP2)
133
return "Microsoft Windows Vista, Service Pack 2";
136
return "Microsoft Windows 7";
139
return "Microsoft Windows 7, Service Pack 1";
142
return "Microsoft Windows 8 or greater"; // "Applications not manifested for Windows 10 will return the Windows 8 OS version value (6.2)."
145
return "Microsoft Windows 8.1";
147
return "Unsupported version of Microsoft Windows.";
150
std::string GetWindowsSystemArchitecture() {
152
ZeroMemory(&sysinfo, sizeof(SYSTEM_INFO));
153
GetNativeSystemInfo(&sysinfo);
155
if (sysinfo.wProcessorArchitecture & PROCESSOR_ARCHITECTURE_AMD64)
157
// Need to check for equality here, since ANDing with 0 is always 0.
158
else if (sysinfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
160
else if (sysinfo.wProcessorArchitecture & PROCESSOR_ARCHITECTURE_ARM)
166
// Adapted mostly as-is from http://www.gamedev.net/topic/495075-how-to-retrieve-info-about-videocard/?view=findpost&p=4229170
167
// 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?).
168
std::string GetVideoCardDriverVersion() {
169
std::string retvalue = "";
172
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
177
IWbemLocator *pIWbemLocator = NULL;
178
hr = CoCreateInstance(__uuidof(WbemLocator), NULL, CLSCTX_INPROC_SERVER,
179
__uuidof(IWbemLocator), (LPVOID *)&pIWbemLocator);
185
BSTR bstrServer = SysAllocString(L"\\\\.\\root\\cimv2");
186
IWbemServices *pIWbemServices;
187
hr = pIWbemLocator->ConnectServer(bstrServer, NULL, NULL, 0L, 0L, NULL, NULL, &pIWbemServices);
189
pIWbemLocator->Release();
190
SysFreeString(bstrServer);
195
hr = CoSetProxyBlanket(pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE,
196
NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL,EOAC_DEFAULT);
198
BSTR bstrWQL = SysAllocString(L"WQL");
199
BSTR bstrPath = SysAllocString(L"select * from Win32_VideoController");
200
IEnumWbemClassObject* pEnum;
201
hr = pIWbemServices->ExecQuery(bstrWQL, bstrPath, WBEM_FLAG_FORWARD_ONLY, NULL, &pEnum);
205
IWbemClassObject* pObj = NULL;
207
hr = pEnum->Next(WBEM_INFINITE, 1, &pObj, &uReturned);
210
if (!FAILED(hr) && uReturned) {
211
hr = pObj->Get(L"DriverVersion", 0, &var, NULL, NULL);
214
WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, str, sizeof(str), NULL, NULL);
220
SysFreeString(bstrPath);
221
SysFreeString(bstrWQL);
222
pIWbemServices->Release();
223
pIWbemLocator->Release();
224
SysFreeString(bstrServer);
229
std::string System_GetProperty(SystemProperty prop) {
230
static bool hasCheckedGPUDriverVersion = false;
234
case SYSPROP_LANGREGION:
236
case SYSPROP_CLIPBOARD_TEXT:
239
if (OpenClipboard(MainWindow::GetDisplayHWND())) {
240
HANDLE handle = GetClipboardData(CF_UNICODETEXT);
241
const wchar_t *wstr = (const wchar_t*)GlobalLock(handle);
243
retval = ConvertWStringToUTF8(wstr);
246
GlobalUnlock(handle);
251
case SYSPROP_GPUDRIVER_VERSION:
252
if (!hasCheckedGPUDriverVersion) {
253
hasCheckedGPUDriverVersion = true;
254
gpuDriverVersion = GetVideoCardDriverVersion();
256
return gpuDriverVersion;
263
extern WindowsAudioBackend *winAudioBackend;
265
int System_GetPropertyInt(SystemProperty prop) {
267
case SYSPROP_AUDIO_SAMPLE_RATE:
268
return winAudioBackend ? winAudioBackend->GetSampleRate() : -1;
269
case SYSPROP_DISPLAY_REFRESH_RATE:
271
case SYSPROP_DEVICE_TYPE:
272
return DEVICE_TYPE_DESKTOP;
278
void System_SendMessage(const char *command, const char *parameter) {
279
if (!strcmp(command, "finish")) {
280
PostMessage(MainWindow::GetHWND(), WM_CLOSE, 0, 0);
281
} else if (!strcmp(command, "setclipboardtext")) {
282
if (OpenClipboard(MainWindow::GetDisplayHWND())) {
283
std::wstring data = ConvertUTF8ToWString(parameter);
284
HANDLE handle = GlobalAlloc(GMEM_MOVEABLE, (data.size() + 1) * sizeof(wchar_t));
285
wchar_t *wstr = (wchar_t *)GlobalLock(handle);
286
memcpy(wstr, data.c_str(), (data.size() + 1) * sizeof(wchar_t));
288
SetClipboardData(CF_UNICODETEXT, handle);
295
void System_AskForPermission(SystemPermission permission) {}
296
PermissionStatus System_GetPermissionStatus(SystemPermission permission) { return PERMISSION_STATUS_GRANTED; }
298
void EnableCrashingOnCrashes() {
299
typedef BOOL (WINAPI *tGetPolicy)(LPDWORD lpFlags);
300
typedef BOOL (WINAPI *tSetPolicy)(DWORD dwFlags);
301
const DWORD EXCEPTION_SWALLOWING = 0x1;
303
HMODULE kernel32 = LoadLibrary(L"kernel32.dll");
304
tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32,
305
"GetProcessUserModeExceptionPolicy");
306
tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32,
307
"SetProcessUserModeExceptionPolicy");
308
if (pGetPolicy && pSetPolicy) {
310
if (pGetPolicy(&dwFlags)) {
311
// Turn off the filter.
312
pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING);
315
FreeLibrary(kernel32);
318
bool System_InputBoxGetString(const char *title, const char *defaultValue, char *outValue, size_t outLength)
321
if (InputBox_GetString(MainWindow::GetHInstance(), MainWindow::GetHWND(), ConvertUTF8ToWString(title).c_str(), defaultValue, out)) {
322
strcpy(outValue, out.c_str());
329
bool System_InputBoxGetWString(const wchar_t *title, const std::wstring &defaultvalue, std::wstring &outvalue)
331
if (InputBox_GetWString(MainWindow::GetHInstance(), MainWindow::GetHWND(), title, defaultvalue, outvalue)) {
338
static std::string GetDefaultLangRegion() {
339
wchar_t lcLangName[256] = {};
341
// LOCALE_SNAME is only available in WinVista+
342
if (0 != GetLocaleInfo(LOCALE_NAME_USER_DEFAULT, LOCALE_SNAME, lcLangName, ARRAY_SIZE(lcLangName))) {
343
std::string result = ConvertWStringToUTF8(lcLangName);
344
std::replace(result.begin(), result.end(), '-', '_');
347
// This should work on XP, but we may get numbers for some countries.
348
if (0 != GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, lcLangName, ARRAY_SIZE(lcLangName))) {
349
wchar_t lcRegion[256] = {};
350
if (0 != GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, lcRegion, ARRAY_SIZE(lcRegion))) {
351
return ConvertWStringToUTF8(lcLangName) + "_" + ConvertWStringToUTF8(lcRegion);
354
// Unfortunate default. We tried.
359
std::vector<std::wstring> GetWideCmdLine() {
362
wargv = CommandLineToArgvW(GetCommandLineW(), &wargc);
364
std::vector<std::wstring> wideArgs(wargv, wargv + wargc);
369
int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow)
371
setCurrentThreadName("Main");
373
CoInitializeEx(NULL, COINIT_MULTITHREADED);
376
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
380
#if defined(_M_X64) && defined(_MSC_VER) && _MSC_VER < 1900
381
// FMA3 support in the 2013 CRT is broken on Vista and Windows 7 RTM (fixed in SP1). Just disable it.
385
EnableCrashingOnCrashes();
388
bool showLog = false;
393
const std::string &exePath = File::GetExeDirectory();
394
VFSRegister("", new DirectoryAssetReader((exePath + "/assets/").c_str()));
395
VFSRegister("", new DirectoryAssetReader(exePath.c_str()));
397
langRegion = GetDefaultLangRegion();
398
osName = GetWindowsVersion() + " " + GetWindowsSystemArchitecture();
400
char configFilename[MAX_PATH] = { 0 };
401
const std::wstring configOption = L"--config=";
403
char controlsConfigFilename[MAX_PATH] = { 0 };
404
const std::wstring controlsOption = L"--controlconfig=";
406
std::vector<std::wstring> wideArgs = GetWideCmdLine();
408
for (size_t i = 1; i < wideArgs.size(); ++i) {
409
if (wideArgs[i][0] == L'\0')
411
if (wideArgs[i][0] == L'-') {
412
if (wideArgs[i].find(configOption) != std::wstring::npos && wideArgs[i].size() > configOption.size()) {
413
const std::wstring tempWide = wideArgs[i].substr(configOption.size());
414
const std::string tempStr = ConvertWStringToUTF8(tempWide);
415
std::strncpy(configFilename, tempStr.c_str(), MAX_PATH);
418
if (wideArgs[i].find(controlsOption) != std::wstring::npos && wideArgs[i].size() > controlsOption.size()) {
419
const std::wstring tempWide = wideArgs[i].substr(controlsOption.size());
420
const std::string tempStr = ConvertWStringToUTF8(tempWide);
421
std::strncpy(controlsConfigFilename, tempStr.c_str(), MAX_PATH);
426
// On Win32 it makes more sense to initialize the system directories here
427
// because the next place it was called was in the EmuThread, and it's too late by then.
428
InitSysDirectories();
430
// Load config up here, because those changes below would be overwritten
431
// if it's not loaded here first.
432
g_Config.AddSearchPath("");
433
g_Config.AddSearchPath(GetSysDirectory(DIRECTORY_SYSTEM));
434
g_Config.SetDefaultPath(GetSysDirectory(DIRECTORY_SYSTEM));
435
g_Config.Load(configFilename, controlsConfigFilename);
437
bool debugLogLevel = false;
439
const std::wstring gpuBackend = L"--graphics=";
441
// The rest is handled in NativeInit().
442
for (size_t i = 1; i < wideArgs.size(); ++i) {
443
if (wideArgs[i][0] == L'\0')
446
if (wideArgs[i][0] == L'-') {
447
switch (wideArgs[i][1]) {
450
g_Config.bEnableLogging = true;
453
g_Config.bAutoRun = false;
454
g_Config.bSaveSettings = false;
457
debugLogLevel = true;
461
if (wideArgs[i] == L"--fullscreen")
462
g_Config.bFullScreen = true;
464
if (wideArgs[i] == L"--windowed")
465
g_Config.bFullScreen = false;
467
if (wideArgs[i].find(gpuBackend) != std::wstring::npos && wideArgs[i].size() > gpuBackend.size()) {
468
const std::wstring restOfOption = wideArgs[i].substr(gpuBackend.size());
470
// Force software rendering off, as picking directx9 or gles implies HW acceleration.
471
// Once software rendering supports Direct3D9/11, we can add more options for software,
472
// such as "software-gles", "software-d3d9", and "software-d3d11", or something similar.
473
// For now, software rendering force-activates OpenGL.
474
if (restOfOption == L"directx9") {
475
g_Config.iGPUBackend = GPU_BACKEND_DIRECT3D9;
476
g_Config.bSoftwareRendering = false;
477
} else if (restOfOption == L"gles") {
478
g_Config.iGPUBackend = GPU_BACKEND_OPENGL;
479
g_Config.bSoftwareRendering = false;
480
} else if (restOfOption == L"software") {
481
g_Config.iGPUBackend = GPU_BACKEND_OPENGL;
482
g_Config.bSoftwareRendering = true;
488
g_Config.bEnableLogging = true;
491
if (iCmdShow == SW_MAXIMIZE) {
492
// Consider this to mean --fullscreen.
493
g_Config.bFullScreen = true;
497
// Consider at least the following cases before changing this code:
498
// - By default in Release, the console should be hidden by default even if logging is enabled.
499
// - By default in Debug, the console should be shown by default.
500
// - The -l switch is expected to show the log console, REGARDLESS of config settings.
501
// - It should be possible to log to a file without showing the console.
502
LogManager::GetInstance()->GetConsoleListener()->Init(showLog, 150, 120, "PPSSPP Debug Console");
505
LogManager::GetInstance()->SetAllLogLevels(LogTypes::LDEBUG);
507
//Windows, API init stuff
508
INITCOMMONCONTROLSEX comm;
509
comm.dwSize = sizeof(comm);
510
comm.dwICC = ICC_BAR_CLASSES | ICC_LISTVIEW_CLASSES | ICC_TAB_CLASSES;
511
InitCommonControlsEx(&comm);
513
MainWindow::Init(_hInstance);
515
g_hPopupMenus = LoadMenu(_hInstance, (LPCWSTR)IDR_POPUPMENUS);
517
MainWindow::Show(_hInstance);
519
HWND hwndMain = MainWindow::GetHWND();
520
HWND hwndDisplay = MainWindow::GetDisplayHWND();
522
//initialize custom controls
523
CtrlDisAsmView::init();
525
CtrlRegisterList::init();
528
DialogManager::AddDlg(vfpudlg = new CVFPUDlg(_hInstance, hwndMain, currentDebugMIPS));
530
host = new WindowsHost(_hInstance, hwndMain, hwndDisplay);
531
host->SetWindowTitle(0);
533
MainWindow::CreateDebugWindows();
535
const bool minimized = iCmdShow == SW_MINIMIZE || iCmdShow == SW_SHOWMINIMIZED || iCmdShow == SW_SHOWMINNOACTIVE;
537
MainWindow::Minimize();
540
// Emu thread is always running!
542
InputDevice::BeginPolling();
544
HACCEL hAccelTable = LoadAccelerators(_hInstance, (LPCTSTR)IDR_ACCELS);
545
HACCEL hDebugAccelTable = LoadAccelerators(_hInstance, (LPCTSTR)IDR_DEBUGACCELS);
547
//so.. we're at the message pump of the GUI thread
548
for (MSG msg; GetMessage(&msg, NULL, 0, 0); ) // for no quit
550
if (msg.message == WM_KEYDOWN)
552
//hack to enable/disable menu command accelerate keys
553
MainWindow::UpdateCommands();
555
//hack to make it possible to get to main window from floating windows with Esc
556
if (msg.hwnd != hwndMain && msg.wParam == VK_ESCAPE)
557
BringWindowToTop(hwndMain);
560
//Translate accelerators and dialog messages...
563
switch (g_activeWindow)
565
case WINDOW_MAINWINDOW:
569
case WINDOW_CPUDEBUGGER:
570
wnd = disasmWindow[0] ? disasmWindow[0]->GetDlgHandle() : 0;
571
accel = hDebugAccelTable;
573
case WINDOW_GEDEBUGGER:
580
if (!TranslateAccelerator(wnd, accel, &msg))
582
if (!DialogManager::IsDialogMessage(&msg))
584
//and finally translate and dispatch
585
TranslateMessage(&msg);
586
DispatchMessage(&msg);
593
InputDevice::StopPolling();
596
MainWindow::DestroyDebugWindows();
597
DialogManager::DestroyAll();
602
LogManager::Shutdown();
604
if (g_Config.bRestartRequired) {
605
W32Util::ExitAndRestart();