Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Windows/MainWindowMenu.cpp
3185 views
1
#include "ppsspp_config.h"
2
#include <algorithm>
3
#include <string>
4
#include <sstream>
5
#include <unordered_map>
6
7
#include "CommonWindows.h"
8
#include <shellapi.h>
9
10
#include "resource.h"
11
12
#include "Common/GPU/OpenGL/GLFeatures.h"
13
14
#include "Common/Data/Text/I18n.h"
15
#include "Common/Data/Encoding/Utf8.h"
16
#include "Common/System/System.h"
17
#include "Common/System/OSD.h"
18
#include "Common/System/NativeApp.h"
19
#include "Common/System/Request.h"
20
#include "Common/File/FileUtil.h"
21
#include "Common/Log.h"
22
#include "Common/Log/LogManager.h"
23
#include "Common/Log/ConsoleListener.h"
24
#include "Common/OSVersion.h"
25
#include "Common/GPU/Vulkan/VulkanLoader.h"
26
#include "Common/StringUtils.h"
27
#if PPSSPP_API(ANY_GL)
28
#include "GPU/GLES/TextureCacheGLES.h"
29
#include "GPU/GLES/FramebufferManagerGLES.h"
30
#endif
31
#include "UI/OnScreenDisplay.h"
32
#include "GPU/Common/PostShader.h"
33
#include "GPU/Common/FramebufferManagerCommon.h"
34
#include "GPU/Common/TextureDecoder.h"
35
#include "GPU/Common/TextureScalerCommon.h"
36
37
#include "Core/Config.h"
38
#include "Core/ConfigValues.h"
39
#include "Core/FileSystems/MetaFileSystem.h"
40
#include "Core/KeyMap.h"
41
#include "UI/OnScreenDisplay.h"
42
#include "Windows/MainWindowMenu.h"
43
#include "Windows/MainWindow.h"
44
#include "Windows/W32Util/DialogManager.h"
45
#include "Windows/W32Util/ShellUtil.h"
46
#include "Windows/W32Util/Misc.h"
47
#include "Windows/InputBox.h"
48
#include "Windows/main.h"
49
#include "Windows/W32Util/DarkMode.h"
50
51
#include "Core/HLE/sceUmd.h"
52
#include "Core/HLE/sceNet.h"
53
#include "Core/SaveState.h"
54
#include "Core/Core.h"
55
#include "Core/RetroAchievements.h"
56
57
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
58
#include "ext/rcheevos/include/rc_client_raintegration.h"
59
#endif
60
61
extern bool g_TakeScreenshot;
62
63
namespace MainWindow {
64
extern HINSTANCE hInst;
65
extern bool noFocusPause;
66
static bool browsePauseAfter;
67
68
static std::unordered_map<int, std::string> initialMenuKeys;
69
static std::vector<std::string> availableShaders;
70
static std::string menuLanguageID = "";
71
static int menuKeymapGeneration = -1;
72
static bool menuShaderInfoLoaded = false;
73
std::vector<ShaderInfo> menuShaderInfo;
74
75
LRESULT CALLBACK AboutDlgProc(HWND, UINT, WPARAM, LPARAM);
76
77
void SetIngameMenuItemStates(HMENU menu, const GlobalUIState state) {
78
bool menuEnableBool = state == UISTATE_INGAME || state == UISTATE_EXCEPTION;
79
80
bool loadStateEnableBool = menuEnableBool;
81
bool saveStateEnableBool = menuEnableBool;
82
if (Achievements::HardcoreModeActive()) {
83
loadStateEnableBool = false;
84
if (!g_Config.bAchievementsSaveStateInHardcoreMode) {
85
saveStateEnableBool = false;
86
}
87
}
88
89
if (!NetworkAllowSaveState()) {
90
loadStateEnableBool = false;
91
saveStateEnableBool = false;
92
}
93
94
const UINT menuEnable = menuEnableBool ? MF_ENABLED : MF_GRAYED;
95
const UINT loadStateEnable = loadStateEnableBool ? MF_ENABLED : MF_GRAYED;
96
const UINT saveStateEnable = saveStateEnableBool ? MF_ENABLED : MF_GRAYED;
97
const UINT menuInGameEnable = state == UISTATE_INGAME ? MF_ENABLED : MF_GRAYED;
98
const UINT umdSwitchEnable = state == UISTATE_INGAME && getUMDReplacePermit() ? MF_ENABLED : MF_GRAYED;
99
const UINT debugEnable = !Achievements::HardcoreModeActive() ? MF_ENABLED : MF_GRAYED;
100
const UINT debugIngameEnable = (state == UISTATE_INGAME && !Achievements::HardcoreModeActive()) ? MF_ENABLED : MF_GRAYED;
101
102
EnableMenuItem(menu, ID_FILE_SAVESTATE_SLOT_MENU, saveStateEnable);
103
EnableMenuItem(menu, ID_FILE_SAVESTATEFILE, saveStateEnable);
104
EnableMenuItem(menu, ID_FILE_LOADSTATEFILE, loadStateEnable);
105
EnableMenuItem(menu, ID_FILE_QUICKSAVESTATE, saveStateEnable);
106
EnableMenuItem(menu, ID_FILE_QUICKLOADSTATE, loadStateEnable);
107
EnableMenuItem(menu, ID_EMULATION_PAUSE, menuEnable);
108
EnableMenuItem(menu, ID_EMULATION_STOP, menuEnable);
109
EnableMenuItem(menu, ID_EMULATION_RESET, menuEnable);
110
EnableMenuItem(menu, ID_EMULATION_SWITCH_UMD, umdSwitchEnable);
111
EnableMenuItem(menu, ID_EMULATION_CHAT, g_Config.bEnableNetworkChat ? menuInGameEnable : MF_GRAYED);
112
EnableMenuItem(menu, ID_TOGGLE_BREAK, debugIngameEnable);
113
EnableMenuItem(menu, ID_DEBUG_LOADMAPFILE, debugIngameEnable);
114
EnableMenuItem(menu, ID_DEBUG_SAVEMAPFILE, debugIngameEnable);
115
EnableMenuItem(menu, ID_DEBUG_LOADSYMFILE, debugIngameEnable);
116
EnableMenuItem(menu, ID_DEBUG_SAVESYMFILE, debugIngameEnable);
117
EnableMenuItem(menu, ID_DEBUG_RESETSYMBOLTABLE, debugIngameEnable);
118
EnableMenuItem(menu, ID_DEBUG_SHOWDEBUGSTATISTICS, debugEnable);
119
EnableMenuItem(menu, ID_DEBUG_EXTRACTFILE, menuEnable);
120
EnableMenuItem(menu, ID_DEBUG_MEMORYBASE, menuInGameEnable);
121
EnableMenuItem(menu, ID_DEBUG_DISASSEMBLY, debugEnable);
122
EnableMenuItem(menu, ID_DEBUG_MEMORYVIEW, debugEnable);
123
EnableMenuItem(menu, ID_DEBUG_GEDEBUGGER, debugEnable);
124
125
// While playing, this pop up doesn't work - and probably doesn't make sense.
126
EnableMenuItem(menu, ID_OPTIONS_LANGUAGE, state == UISTATE_INGAME ? MF_GRAYED : MF_ENABLED);
127
}
128
129
static HMENU GetSubmenuById(HMENU menu, int menuID) {
130
MENUITEMINFO menuInfo{ sizeof(MENUITEMINFO), MIIM_SUBMENU };
131
if (GetMenuItemInfo(menu, menuID, MF_BYCOMMAND, &menuInfo) != FALSE) {
132
return menuInfo.hSubMenu;
133
}
134
return nullptr;
135
}
136
137
static void EmptySubMenu(HMENU menu) {
138
int c = GetMenuItemCount(menu);
139
for (int i = 0; i < c; ++i) {
140
RemoveMenu(menu, 0, MF_BYPOSITION);
141
}
142
}
143
144
static std::string GetMenuItemText(HMENU menu, int menuID) {
145
MENUITEMINFO menuInfo{ sizeof(menuInfo), MIIM_STRING };
146
std::string retVal;
147
if (GetMenuItemInfo(menu, menuID, MF_BYCOMMAND, &menuInfo) != FALSE) {
148
wchar_t *buffer = new wchar_t[++menuInfo.cch];
149
menuInfo.dwTypeData = buffer;
150
GetMenuItemInfo(menu, menuID, MF_BYCOMMAND, &menuInfo);
151
retVal = ConvertWStringToUTF8(menuInfo.dwTypeData);
152
delete[] buffer;
153
}
154
155
return retVal;
156
}
157
158
const std::string &GetMenuItemInitialText(HMENU menu, const int menuID) {
159
if (initialMenuKeys.find(menuID) == initialMenuKeys.end()) {
160
initialMenuKeys[menuID] = GetMenuItemText(menu, menuID);
161
}
162
return initialMenuKeys[menuID];
163
}
164
165
// TODO: Why isn't this just defined in the resoucre
166
void CreateHelpMenu(HMENU menu) {
167
auto des = GetI18NCategory(I18NCat::DESKTOPUI);
168
169
const std::wstring visitMainWebsite = ConvertUTF8ToWString(des->T("www.ppsspp.org"));
170
const std::wstring visitForum = ConvertUTF8ToWString(des->T("PPSSPP Forums"));
171
const std::wstring buyGold = ConvertUTF8ToWString(des->T("Buy Gold"));
172
const std::wstring gitHub = ConvertUTF8ToWString(des->T("GitHub"));
173
const std::wstring discord = ConvertUTF8ToWString(des->T("Discord"));
174
const std::wstring aboutPPSSPP = ConvertUTF8ToWString(des->T("About PPSSPP..."));
175
176
HMENU helpMenu = GetSubmenuById(menu, ID_HELP_MENU);
177
EmptySubMenu(helpMenu);
178
179
AppendMenu(helpMenu, MF_STRING | MF_BYCOMMAND, ID_HELP_OPENWEBSITE, visitMainWebsite.c_str());
180
AppendMenu(helpMenu, MF_STRING | MF_BYCOMMAND, ID_HELP_OPENFORUM, visitForum.c_str());
181
// Repeat the process for other languages, if necessary.
182
if (!System_GetPropertyBool(SYSPROP_APP_GOLD)) {
183
AppendMenu(helpMenu, MF_STRING | MF_BYCOMMAND, ID_HELP_BUYGOLD, buyGold.c_str());
184
}
185
AppendMenu(helpMenu, MF_STRING | MF_BYCOMMAND, ID_HELP_GITHUB, gitHub.c_str());
186
AppendMenu(helpMenu, MF_STRING | MF_BYCOMMAND, ID_HELP_DISCORD, discord.c_str());
187
AppendMenu(helpMenu, MF_SEPARATOR, 0, 0);
188
AppendMenu(helpMenu, MF_STRING | MF_BYCOMMAND, ID_HELP_ABOUT, aboutPPSSPP.c_str());
189
}
190
191
static void TranslateMenuItem(const HMENU hMenu, const int menuID, const std::wstring& accelerator = L"", const char *key = nullptr) {
192
auto des = GetI18NCategory(I18NCat::DESKTOPUI);
193
194
std::wstring translated;
195
if (key == nullptr || !strcmp(key, "")) {
196
translated = ConvertUTF8ToWString(des->T(GetMenuItemInitialText(hMenu, menuID)));
197
} else {
198
translated = ConvertUTF8ToWString(des->T(key));
199
}
200
translated.append(accelerator);
201
202
ModifyMenu(hMenu, menuID, MF_STRING | MF_BYCOMMAND, menuID, translated.c_str());
203
}
204
205
void DoTranslateMenus(HWND hWnd, HMENU menu) {
206
auto useDefHotkey = [](int virtKey) {
207
return !KeyMap::PspButtonHasMappings(virtKey);
208
};
209
210
TranslateMenuItem(menu, ID_FILE_MENU);
211
TranslateMenuItem(menu, ID_EMULATION_MENU);
212
TranslateMenuItem(menu, ID_DEBUG_MENU);
213
TranslateMenuItem(menu, ID_OPTIONS_MENU);
214
TranslateMenuItem(menu, ID_HELP_MENU);
215
216
// File menu
217
TranslateMenuItem(menu, ID_FILE_LOAD);
218
TranslateMenuItem(menu, ID_FILE_LOAD_DIR);
219
TranslateMenuItem(menu, ID_FILE_LOAD_MEMSTICK);
220
TranslateMenuItem(menu, ID_FILE_OPEN_NEW_INSTANCE);
221
TranslateMenuItem(menu, ID_FILE_MEMSTICK);
222
TranslateMenuItem(menu, ID_FILE_SAVESTATE_SLOT_MENU, useDefHotkey(VIRTKEY_NEXT_SLOT) ? L"\tF3" : L"");
223
TranslateMenuItem(menu, ID_FILE_QUICKLOADSTATE, useDefHotkey(VIRTKEY_LOAD_STATE) ? L"\tF4" : L"");
224
TranslateMenuItem(menu, ID_FILE_QUICKSAVESTATE, useDefHotkey(VIRTKEY_SAVE_STATE) ? L"\tF2" : L"");
225
TranslateMenuItem(menu, ID_FILE_LOADSTATEFILE);
226
TranslateMenuItem(menu, ID_FILE_SAVESTATEFILE);
227
TranslateMenuItem(menu, ID_FILE_RECORD_MENU);
228
TranslateMenuItem(menu, ID_FILE_EXIT, L"\tAlt+F4");
229
230
// Emulation menu
231
TranslateMenuItem(menu, ID_EMULATION_PAUSE);
232
TranslateMenuItem(menu, ID_EMULATION_STOP, g_Config.bSystemControls ? L"\tCtrl+W" : L"");
233
TranslateMenuItem(menu, ID_EMULATION_RESET, g_Config.bSystemControls ? L"\tCtrl+B" : L"");
234
TranslateMenuItem(menu, ID_EMULATION_SWITCH_UMD, g_Config.bSystemControls ? L"\tCtrl+U" : L"");
235
TranslateMenuItem(menu, ID_EMULATION_ROTATION_MENU);
236
TranslateMenuItem(menu, ID_EMULATION_ROTATION_H);
237
TranslateMenuItem(menu, ID_EMULATION_ROTATION_V);
238
TranslateMenuItem(menu, ID_EMULATION_ROTATION_H_R);
239
TranslateMenuItem(menu, ID_EMULATION_ROTATION_V_R);
240
241
// Debug menu
242
TranslateMenuItem(menu, ID_TOGGLE_BREAK, g_Config.bSystemControls ? L"\tF8" : L"", "Break");
243
TranslateMenuItem(menu, ID_DEBUG_BREAKONLOAD);
244
TranslateMenuItem(menu, ID_DEBUG_IGNOREILLEGALREADS);
245
TranslateMenuItem(menu, ID_DEBUG_LOADMAPFILE);
246
TranslateMenuItem(menu, ID_DEBUG_SAVEMAPFILE);
247
TranslateMenuItem(menu, ID_DEBUG_LOADSYMFILE);
248
TranslateMenuItem(menu, ID_DEBUG_SAVESYMFILE);
249
TranslateMenuItem(menu, ID_DEBUG_RESETSYMBOLTABLE);
250
TranslateMenuItem(menu, ID_DEBUG_TAKESCREENSHOT, g_Config.bSystemControls ? L"\tF12" : L"");
251
TranslateMenuItem(menu, ID_DEBUG_SAVEFRAMEDUMP);
252
TranslateMenuItem(menu, ID_DEBUG_SHOWDEBUGSTATISTICS);
253
TranslateMenuItem(menu, ID_DEBUG_RESTARTGRAPHICS);
254
TranslateMenuItem(menu, ID_DEBUG_DISASSEMBLY, g_Config.bSystemControls ? L"\tCtrl+D" : L"");
255
TranslateMenuItem(menu, ID_DEBUG_GEDEBUGGER, g_Config.bSystemControls ? L"\tCtrl+G" : L"");
256
TranslateMenuItem(menu, ID_DEBUG_EXTRACTFILE);
257
TranslateMenuItem(menu, ID_DEBUG_LOG, g_Config.bSystemControls ? L"\tCtrl+L" : L"");
258
TranslateMenuItem(menu, ID_DEBUG_MEMORYBASE);
259
TranslateMenuItem(menu, ID_DEBUG_MEMORYVIEW, g_Config.bSystemControls ? L"\tCtrl+M" : L"");
260
261
// Options menu
262
TranslateMenuItem(menu, ID_OPTIONS_LANGUAGE);
263
TranslateMenuItem(menu, ID_OPTIONS_TOPMOST);
264
TranslateMenuItem(menu, ID_OPTIONS_PAUSE_FOCUS);
265
TranslateMenuItem(menu, ID_OPTIONS_IGNOREWINKEY);
266
TranslateMenuItem(menu, ID_OPTIONS_MORE_SETTINGS);
267
TranslateMenuItem(menu, ID_OPTIONS_CONTROLS);
268
TranslateMenuItem(menu, ID_OPTIONS_DISPLAY_LAYOUT);
269
270
// Movie menu
271
TranslateMenuItem(menu, ID_FILE_DUMPFRAMES);
272
TranslateMenuItem(menu, ID_FILE_USEFFV1);
273
TranslateMenuItem(menu, ID_FILE_DUMP_VIDEO_OUTPUT);
274
TranslateMenuItem(menu, ID_FILE_DUMPAUDIO);
275
276
// Skip display multipliers x1-x10
277
TranslateMenuItem(menu, ID_OPTIONS_FULLSCREEN, g_Config.bSystemControls ? L"\tAlt+Return, F11" : L"");
278
TranslateMenuItem(menu, ID_OPTIONS_VSYNC);
279
TranslateMenuItem(menu, ID_OPTIONS_SCREEN_MENU, g_Config.bSystemControls ? L"\tCtrl+1" : L"");
280
TranslateMenuItem(menu, ID_OPTIONS_SCREENAUTO);
281
// Skip rendering resolution 2x-5x..
282
TranslateMenuItem(menu, ID_OPTIONS_WINDOW_MENU);
283
// Skip window size 1x-4x..
284
TranslateMenuItem(menu, ID_OPTIONS_BACKEND_MENU);
285
TranslateMenuItem(menu, ID_OPTIONS_DIRECT3D11);
286
TranslateMenuItem(menu, ID_OPTIONS_OPENGL);
287
TranslateMenuItem(menu, ID_OPTIONS_VULKAN);
288
289
TranslateMenuItem(menu, ID_OPTIONS_RENDERMODE_MENU);
290
TranslateMenuItem(menu, ID_OPTIONS_SKIP_BUFFER_EFFECTS);
291
TranslateMenuItem(menu, ID_OPTIONS_FRAMESKIP_MENU, g_Config.bSystemControls ? L"\tF7" : L"");
292
TranslateMenuItem(menu, ID_OPTIONS_FRAMESKIP_AUTO);
293
TranslateMenuItem(menu, ID_OPTIONS_FRAMESKIP_0);
294
TranslateMenuItem(menu, ID_OPTIONS_FRAMESKIPTYPE_MENU);
295
TranslateMenuItem(menu, ID_OPTIONS_FRAMESKIPTYPE_COUNT);
296
TranslateMenuItem(menu, ID_OPTIONS_FRAMESKIPTYPE_PRCNT);
297
// Skip frameskipping 1-8..
298
TranslateMenuItem(menu, ID_OPTIONS_TEXTUREFILTERING_MENU);
299
TranslateMenuItem(menu, ID_OPTIONS_TEXTUREFILTERING_AUTO);
300
TranslateMenuItem(menu, ID_OPTIONS_NEARESTFILTERING);
301
TranslateMenuItem(menu, ID_OPTIONS_LINEARFILTERING);
302
TranslateMenuItem(menu, ID_OPTIONS_AUTOMAXQUALITYFILTERING);
303
TranslateMenuItem(menu, ID_OPTIONS_SMART2DTEXTUREFILTERING);
304
TranslateMenuItem(menu, ID_OPTIONS_SCREENFILTER_MENU);
305
TranslateMenuItem(menu, ID_OPTIONS_BUFLINEARFILTER);
306
TranslateMenuItem(menu, ID_OPTIONS_BUFNEARESTFILTER);
307
TranslateMenuItem(menu, ID_OPTIONS_TEXTURESCALING_MENU);
308
TranslateMenuItem(menu, ID_TEXTURESCALING_OFF);
309
// Skip texture scaling 2x-5x...
310
TranslateMenuItem(menu, ID_TEXTURESCALING_XBRZ);
311
TranslateMenuItem(menu, ID_TEXTURESCALING_HYBRID);
312
TranslateMenuItem(menu, ID_TEXTURESCALING_BICUBIC);
313
TranslateMenuItem(menu, ID_TEXTURESCALING_HYBRID_BICUBIC);
314
TranslateMenuItem(menu, ID_TEXTURESCALING_DEPOSTERIZE);
315
TranslateMenuItem(menu, ID_OPTIONS_HARDWARETRANSFORM);
316
TranslateMenuItem(menu, ID_EMULATION_SOUND);
317
TranslateMenuItem(menu, ID_EMULATION_CHEATS, g_Config.bSystemControls ? L"\tCtrl+T" : L"");
318
TranslateMenuItem(menu, ID_EMULATION_CHAT, g_Config.bSystemControls ? L"\tCtrl+C" : L"");
319
320
// Help menu: it's translated in CreateHelpMenu.
321
CreateHelpMenu(menu);
322
}
323
324
void TranslateMenus(HWND hWnd, HMENU menu) {
325
bool changed = false;
326
327
const std::string curLanguageID = g_i18nrepo.LanguageID();
328
if (curLanguageID != menuLanguageID || KeyMap::HasChanged(menuKeymapGeneration)) {
329
DoTranslateMenus(hWnd, menu);
330
menuLanguageID = curLanguageID;
331
changed = true;
332
}
333
334
if (changed) {
335
DrawMenuBar(hWnd);
336
}
337
}
338
339
void BrowseAndBootDone(std::string filename);
340
341
void BrowseAndBoot(RequesterToken token, std::string defaultPath, bool browseDirectory) {
342
browsePauseAfter = false;
343
if (GetUIState() == UISTATE_INGAME) {
344
browsePauseAfter = Core_IsStepping();
345
if (!browsePauseAfter)
346
Core_Break(BreakReason::BreakOnBoot, 0);
347
}
348
auto mm = GetI18NCategory(I18NCat::MAINMENU);
349
350
W32Util::MakeTopMost(GetHWND(), false);
351
352
if (browseDirectory) {
353
System_BrowseForFolder(token, mm->T("Load"), Path(), [](const std::string &value, int) {
354
BrowseAndBootDone(value);
355
});
356
} else {
357
System_BrowseForFile(token, mm->T("Load"), BrowseFileType::BOOTABLE, [](const std::string &value, int) {
358
BrowseAndBootDone(value);
359
});
360
}
361
}
362
363
void BrowseAndBootDone(std::string filename) {
364
if (GetUIState() == UISTATE_INGAME || GetUIState() == UISTATE_EXCEPTION || GetUIState() == UISTATE_PAUSEMENU) {
365
Core_Resume();
366
}
367
filename = ReplaceAll(filename, "\\", "/");
368
System_PostUIMessage(UIMessage::REQUEST_GAME_BOOT, filename);
369
W32Util::MakeTopMost(GetHWND(), g_Config.bTopMost);
370
}
371
372
static void UmdSwitchAction(RequesterToken token) {
373
auto mm = GetI18NCategory(I18NCat::MAINMENU);
374
System_BrowseForFile(token, mm->T("Switch UMD"), BrowseFileType::BOOTABLE, [](const std::string &value, int) {
375
// This is safe because the callback runs on the emu thread.
376
__UmdReplace(Path(value));
377
});
378
}
379
380
static void SaveStateActionFinished(SaveState::Status status, std::string_view message) {
381
if (!message.empty() && (!g_Config.bDumpFrames || !g_Config.bDumpVideoOutput)) {
382
g_OSD.Show(status == SaveState::Status::SUCCESS ? OSDType::MESSAGE_SUCCESS : OSDType::MESSAGE_ERROR, message, status == SaveState::Status::SUCCESS ? 2.0 : 5.0);
383
}
384
PostMessage(MainWindow::GetHWND(), WM_USER_SAVESTATE_FINISH, 0, 0);
385
}
386
387
// not static
388
void setTexScalingMultiplier(int level) {
389
System_RunOnMainThread([level]() {
390
g_Config.iTexScalingLevel = level;
391
});
392
System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);
393
}
394
395
static void setTexScalingType(int type) {
396
System_RunOnMainThread([type]() {
397
g_Config.iTexScalingType = type;
398
});
399
System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);
400
}
401
402
static void setSkipBufferEffects(bool skip) {
403
System_RunOnMainThread([skip]() {
404
g_Config.bSkipBufferEffects = skip;
405
});
406
System_PostUIMessage(UIMessage::GPU_RENDER_RESIZED);
407
System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);
408
}
409
410
static void setFrameSkipping(int framesToSkip = -1) {
411
if (framesToSkip >= FRAMESKIP_OFF)
412
g_Config.iFrameSkip = framesToSkip;
413
else {
414
if (++g_Config.iFrameSkip > FRAMESKIP_MAX)
415
g_Config.iFrameSkip = FRAMESKIP_OFF;
416
}
417
418
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
419
420
std::ostringstream messageStream;
421
messageStream << gr->T("Frame Skipping") << ":" << " ";
422
423
if (g_Config.iFrameSkip == FRAMESKIP_OFF)
424
messageStream << gr->T("Off");
425
else
426
messageStream << g_Config.iFrameSkip;
427
428
g_OSD.Show(OSDType::MESSAGE_INFO, messageStream.str());
429
}
430
431
static void setFrameSkippingType(int fskipType = -1) {
432
if (fskipType >= 0 && fskipType <= 1) {
433
g_Config.iFrameSkipType = fskipType;
434
} else {
435
g_Config.iFrameSkipType = 0;
436
}
437
438
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
439
440
std::ostringstream messageStream;
441
messageStream << gr->T("Frame Skipping Type") << ":" << " ";
442
443
if (g_Config.iFrameSkipType == 0)
444
messageStream << gr->T("Number of Frames");
445
else
446
messageStream << gr->T("Percent of FPS");
447
448
g_OSD.Show(OSDType::MESSAGE_INFO, messageStream.str());
449
}
450
451
static void RestartApp() {
452
if (System_GetPropertyBool(SYSPROP_DEBUGGER_PRESENT)) {
453
PostMessage(MainWindow::GetHWND(), WM_USER_RESTART_EMUTHREAD, 0, 0);
454
} else {
455
g_Config.bRestartRequired = true;
456
PostMessage(MainWindow::GetHWND(), WM_USER_DESTROY, 0, 0);
457
}
458
}
459
460
void MainWindowMenu_Process(HWND hWnd, WPARAM wParam) {
461
std::string fn;
462
463
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
464
465
int wmId = LOWORD(wParam);
466
// Parse the menu selections:
467
switch (wmId) {
468
case ID_FILE_LOAD:
469
BrowseAndBoot(NON_EPHEMERAL_TOKEN, "", false);
470
break;
471
472
case ID_FILE_LOAD_DIR:
473
BrowseAndBoot(NON_EPHEMERAL_TOKEN, "", true);
474
break;
475
476
case ID_FILE_LOAD_MEMSTICK:
477
BrowseAndBoot(NON_EPHEMERAL_TOKEN, GetSysDirectory(DIRECTORY_GAME).ToString());
478
break;
479
480
case ID_FILE_OPEN_NEW_INSTANCE:
481
W32Util::SpawnNewInstance(false);
482
break;
483
484
case ID_FILE_MEMSTICK:
485
ShellExecute(NULL, L"open", g_Config.memStickDirectory.ToWString().c_str(), 0, 0, SW_SHOWNORMAL);
486
break;
487
488
case ID_TOGGLE_BREAK:
489
if (GetUIState() == UISTATE_PAUSEMENU) {
490
// Causes hang (outdated comment?)
491
// System_PostUIMessage(UIMessage::REQUEST_GAME_RUN, "");
492
493
if (disasmWindow)
494
SendMessage(disasmWindow->GetDlgHandle(), WM_COMMAND, IDC_STOPGO, 0);
495
} else if (Core_IsStepping()) { // It is paused, then continue to run.
496
if (disasmWindow)
497
SendMessage(disasmWindow->GetDlgHandle(), WM_COMMAND, IDC_STOPGO, 0);
498
else
499
Core_Resume();
500
} else {
501
if (disasmWindow)
502
SendMessage(disasmWindow->GetDlgHandle(), WM_COMMAND, IDC_STOPGO, 0);
503
else
504
Core_Break(BreakReason::DebugBreak, 0);
505
}
506
noFocusPause = !noFocusPause; // If we pause, override pause on lost focus
507
break;
508
509
case ID_EMULATION_PAUSE:
510
System_PostUIMessage(UIMessage::REQUEST_GAME_PAUSE);
511
break;
512
513
case ID_EMULATION_STOP:
514
System_PostUIMessage(UIMessage::REQUEST_GAME_STOP);
515
break;
516
517
case ID_EMULATION_RESET:
518
if (MainWindow::ConfirmAction(hWnd, true)) {
519
System_PostUIMessage(UIMessage::REQUEST_GAME_RESET);
520
}
521
break;
522
523
case ID_EMULATION_SWITCH_UMD:
524
UmdSwitchAction(NON_EPHEMERAL_TOKEN);
525
break;
526
527
case ID_EMULATION_ROTATION_H: g_Config.iInternalScreenRotation = ROTATION_LOCKED_HORIZONTAL; break;
528
case ID_EMULATION_ROTATION_V: g_Config.iInternalScreenRotation = ROTATION_LOCKED_VERTICAL; break;
529
case ID_EMULATION_ROTATION_H_R: g_Config.iInternalScreenRotation = ROTATION_LOCKED_HORIZONTAL180; break;
530
case ID_EMULATION_ROTATION_V_R: g_Config.iInternalScreenRotation = ROTATION_LOCKED_VERTICAL180; break;
531
532
case ID_EMULATION_CHEATS:
533
g_Config.bEnableCheats = !g_Config.bEnableCheats;
534
g_OSD.ShowOnOff(gr->T("Cheats"), g_Config.bEnableCheats);
535
break;
536
537
case ID_EMULATION_CHAT:
538
if (GetUIState() == UISTATE_INGAME) {
539
System_PostUIMessage(UIMessage::SHOW_CHAT_SCREEN);
540
}
541
break;
542
543
case ID_FILE_LOADSTATEFILE:
544
if (!Achievements::WarnUserIfHardcoreModeActive(false) && !NetworkWarnUserIfOnlineAndCantSavestate()) {
545
if (W32Util::BrowseForFileName(true, hWnd, L"Load state", 0, L"Save States (*.ppst)\0*.ppst\0All files\0*.*\0\0", L"ppst", fn)) {
546
SetCursor(LoadCursor(0, IDC_WAIT));
547
SaveState::Load(Path(fn), -1, SaveStateActionFinished);
548
}
549
}
550
break;
551
case ID_FILE_SAVESTATEFILE:
552
if (!Achievements::WarnUserIfHardcoreModeActive(true) && !NetworkWarnUserIfOnlineAndCantSavestate()) {
553
if (W32Util::BrowseForFileName(false, hWnd, L"Save state", 0, L"Save States (*.ppst)\0*.ppst\0All files\0*.*\0\0", L"ppst", fn)) {
554
SetCursor(LoadCursor(0, IDC_WAIT));
555
SaveState::Save(Path(fn), -1, SaveStateActionFinished);
556
}
557
}
558
break;
559
560
case ID_FILE_SAVESTATE_NEXT_SLOT:
561
{
562
if (!Achievements::WarnUserIfHardcoreModeActive(true) && !NetworkWarnUserIfOnlineAndCantSavestate()) {
563
SaveState::NextSlot();
564
System_PostUIMessage(UIMessage::SAVESTATE_DISPLAY_SLOT);
565
}
566
break;
567
}
568
569
case ID_FILE_SAVESTATE_NEXT_SLOT_HC:
570
{
571
if (!Achievements::WarnUserIfHardcoreModeActive(true) && !NetworkWarnUserIfOnlineAndCantSavestate()) {
572
// We let F3 (search next) in the imdebugger take priority, if active.
573
if (!KeyMap::PspButtonHasMappings(VIRTKEY_NEXT_SLOT) && !g_Config.bShowImDebugger) {
574
SaveState::NextSlot();
575
System_PostUIMessage(UIMessage::SAVESTATE_DISPLAY_SLOT);
576
}
577
}
578
break;
579
}
580
581
case ID_FILE_SAVESTATE_SLOT_1:
582
case ID_FILE_SAVESTATE_SLOT_2:
583
case ID_FILE_SAVESTATE_SLOT_3:
584
case ID_FILE_SAVESTATE_SLOT_4:
585
case ID_FILE_SAVESTATE_SLOT_5:
586
if (!Achievements::WarnUserIfHardcoreModeActive(true) && !NetworkWarnUserIfOnlineAndCantSavestate()) {
587
g_Config.iCurrentStateSlot = wmId - ID_FILE_SAVESTATE_SLOT_1;
588
}
589
break;
590
591
case ID_FILE_QUICKLOADSTATE:
592
if (!Achievements::WarnUserIfHardcoreModeActive(false) && !NetworkWarnUserIfOnlineAndCantSavestate()) {
593
SetCursor(LoadCursor(0, IDC_WAIT));
594
SaveState::LoadSlot(PSP_CoreParameter().fileToStart, g_Config.iCurrentStateSlot, SaveStateActionFinished);
595
}
596
break;
597
598
case ID_FILE_QUICKLOADSTATE_HC:
599
{
600
if (!Achievements::WarnUserIfHardcoreModeActive(false) && !NetworkWarnUserIfOnlineAndCantSavestate()) {
601
if (!KeyMap::PspButtonHasMappings(VIRTKEY_LOAD_STATE)) {
602
SetCursor(LoadCursor(0, IDC_WAIT));
603
SaveState::LoadSlot(PSP_CoreParameter().fileToStart, g_Config.iCurrentStateSlot, SaveStateActionFinished);
604
}
605
}
606
break;
607
}
608
case ID_FILE_QUICKSAVESTATE:
609
{
610
if (!Achievements::WarnUserIfHardcoreModeActive(true) && !NetworkWarnUserIfOnlineAndCantSavestate()) {
611
SetCursor(LoadCursor(0, IDC_WAIT));
612
SaveState::SaveSlot(PSP_CoreParameter().fileToStart, g_Config.iCurrentStateSlot, SaveStateActionFinished);
613
}
614
break;
615
}
616
617
case ID_FILE_QUICKSAVESTATE_HC:
618
{
619
if (!Achievements::WarnUserIfHardcoreModeActive(true) && !NetworkWarnUserIfOnlineAndCantSavestate()) {
620
if (!KeyMap::PspButtonHasMappings(VIRTKEY_SAVE_STATE))
621
{
622
SetCursor(LoadCursor(0, IDC_WAIT));
623
SaveState::SaveSlot(PSP_CoreParameter().fileToStart, g_Config.iCurrentStateSlot, SaveStateActionFinished);
624
break;
625
}
626
}
627
break;
628
}
629
630
case ID_OPTIONS_LANGUAGE:
631
System_PostUIMessage(UIMessage::SHOW_LANGUAGE_SCREEN);
632
break;
633
634
case ID_OPTIONS_IGNOREWINKEY:
635
g_Config.bIgnoreWindowsKey = !g_Config.bIgnoreWindowsKey;
636
break;
637
638
case ID_OPTIONS_SCREENAUTO: SetInternalResolution(RESOLUTION_AUTO); break;
639
case ID_OPTIONS_SCREEN1X: SetInternalResolution(RESOLUTION_NATIVE); break;
640
case ID_OPTIONS_SCREEN2X: SetInternalResolution(RESOLUTION_2X); break;
641
case ID_OPTIONS_SCREEN3X: SetInternalResolution(RESOLUTION_3X); break;
642
case ID_OPTIONS_SCREEN4X: SetInternalResolution(RESOLUTION_4X); break;
643
case ID_OPTIONS_SCREEN5X: SetInternalResolution(RESOLUTION_5X); break;
644
case ID_OPTIONS_SCREEN6X: SetInternalResolution(RESOLUTION_6X); break;
645
case ID_OPTIONS_SCREEN7X: SetInternalResolution(RESOLUTION_7X); break;
646
case ID_OPTIONS_SCREEN8X: SetInternalResolution(RESOLUTION_8X); break;
647
case ID_OPTIONS_SCREEN9X: SetInternalResolution(RESOLUTION_9X); break;
648
case ID_OPTIONS_SCREEN10X: SetInternalResolution(RESOLUTION_MAX); break;
649
650
case ID_OPTIONS_WINDOW1X: SetWindowSize(1); break;
651
case ID_OPTIONS_WINDOW2X: SetWindowSize(2); break;
652
case ID_OPTIONS_WINDOW3X: SetWindowSize(3); break;
653
case ID_OPTIONS_WINDOW4X: SetWindowSize(4); break;
654
case ID_OPTIONS_WINDOW5X: SetWindowSize(5); break;
655
case ID_OPTIONS_WINDOW6X: SetWindowSize(6); break;
656
case ID_OPTIONS_WINDOW7X: SetWindowSize(7); break;
657
case ID_OPTIONS_WINDOW8X: SetWindowSize(8); break;
658
case ID_OPTIONS_WINDOW9X: SetWindowSize(9); break;
659
case ID_OPTIONS_WINDOW10X: SetWindowSize(10); break;
660
661
case ID_OPTIONS_RESOLUTIONDUMMY:
662
{
663
SetInternalResolution();
664
break;
665
}
666
667
case ID_OPTIONS_VSYNC:
668
g_Config.bVSync = !g_Config.bVSync;
669
System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);
670
break;
671
672
case ID_OPTIONS_FRAMESKIP_AUTO:
673
g_Config.bAutoFrameSkip = !g_Config.bAutoFrameSkip;
674
if (g_Config.bAutoFrameSkip && g_Config.bSkipBufferEffects) {
675
setSkipBufferEffects(false);
676
}
677
break;
678
679
case ID_TEXTURESCALING_OFF: setTexScalingMultiplier(TEXSCALING_OFF); break;
680
case ID_TEXTURESCALING_2X: setTexScalingMultiplier(TEXSCALING_2X); break;
681
case ID_TEXTURESCALING_3X: setTexScalingMultiplier(TEXSCALING_3X); break;
682
case ID_TEXTURESCALING_4X: setTexScalingMultiplier(TEXSCALING_4X); break;
683
case ID_TEXTURESCALING_5X: setTexScalingMultiplier(TEXSCALING_MAX); break;
684
685
case ID_TEXTURESCALING_XBRZ: setTexScalingType(TextureScalerCommon::XBRZ); break;
686
case ID_TEXTURESCALING_HYBRID: setTexScalingType(TextureScalerCommon::HYBRID); break;
687
case ID_TEXTURESCALING_BICUBIC: setTexScalingType(TextureScalerCommon::BICUBIC); break;
688
case ID_TEXTURESCALING_HYBRID_BICUBIC: setTexScalingType(TextureScalerCommon::HYBRID_BICUBIC); break;
689
690
case ID_TEXTURESCALING_DEPOSTERIZE:
691
g_Config.bTexDeposterize = !g_Config.bTexDeposterize;
692
System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);
693
break;
694
695
case ID_OPTIONS_DIRECT3D11:
696
g_Config.iGPUBackend = (int)GPUBackend::DIRECT3D11;
697
g_Config.Save("gpu_choice");
698
RestartApp();
699
break;
700
701
case ID_OPTIONS_OPENGL:
702
g_Config.iGPUBackend = (int)GPUBackend::OPENGL;
703
g_Config.Save("gpu_choice");
704
RestartApp();
705
break;
706
707
case ID_OPTIONS_VULKAN:
708
g_Config.iGPUBackend = (int)GPUBackend::VULKAN;
709
g_Config.Save("gpu_choice");
710
RestartApp();
711
break;
712
713
case ID_OPTIONS_SKIP_BUFFER_EFFECTS:
714
setSkipBufferEffects(!g_Config.bSkipBufferEffects);
715
g_OSD.ShowOnOff(gr->T("Skip Buffer Effects"), g_Config.bSkipBufferEffects);
716
break;
717
718
case ID_DEBUG_SHOWDEBUGSTATISTICS:
719
// This is still useful as a shortcut to tell users to use.
720
// So let's fake the enum.
721
if ((DebugOverlay)g_Config.iDebugOverlay == DebugOverlay::DEBUG_STATS) {
722
g_Config.iDebugOverlay = (int)DebugOverlay::OFF;
723
} else {
724
g_Config.iDebugOverlay = (int)DebugOverlay::DEBUG_STATS;
725
}
726
System_PostUIMessage(UIMessage::REQUEST_CLEAR_JIT);
727
break;
728
729
case ID_OPTIONS_HARDWARETRANSFORM:
730
System_RunOnMainThread([]() {
731
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
732
g_Config.bHardwareTransform = !g_Config.bHardwareTransform;
733
System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);
734
g_OSD.ShowOnOff(gr->T("Hardware Transform"), g_Config.bHardwareTransform);
735
});
736
break;
737
738
case ID_OPTIONS_DISPLAY_LAYOUT:
739
System_PostUIMessage(UIMessage::SHOW_DISPLAY_LAYOUT_EDITOR);
740
break;
741
742
743
case ID_OPTIONS_FRAMESKIP_0: setFrameSkipping(FRAMESKIP_OFF); break;
744
case ID_OPTIONS_FRAMESKIP_1: setFrameSkipping(FRAMESKIP_1); break;
745
case ID_OPTIONS_FRAMESKIP_2: setFrameSkipping(FRAMESKIP_2); break;
746
case ID_OPTIONS_FRAMESKIP_3: setFrameSkipping(FRAMESKIP_3); break;
747
case ID_OPTIONS_FRAMESKIP_4: setFrameSkipping(FRAMESKIP_4); break;
748
case ID_OPTIONS_FRAMESKIP_5: setFrameSkipping(FRAMESKIP_5); break;
749
case ID_OPTIONS_FRAMESKIP_6: setFrameSkipping(FRAMESKIP_6); break;
750
case ID_OPTIONS_FRAMESKIP_7: setFrameSkipping(FRAMESKIP_7); break;
751
case ID_OPTIONS_FRAMESKIP_8: setFrameSkipping(FRAMESKIP_MAX); break;
752
753
case ID_OPTIONS_FRAMESKIPTYPE_COUNT: setFrameSkippingType(FRAMESKIPTYPE_COUNT); break;
754
case ID_OPTIONS_FRAMESKIPTYPE_PRCNT: setFrameSkippingType(FRAMESKIPTYPE_PRCNT); break;
755
756
case ID_FILE_EXIT:
757
if (MainWindow::ConfirmAction(hWnd, false)) {
758
DestroyWindow(hWnd);
759
}
760
break;
761
762
case ID_DEBUG_BREAKONLOAD:
763
g_Config.bAutoRun = !g_Config.bAutoRun;
764
break;
765
766
case ID_DEBUG_SAVEFRAMEDUMP:
767
{
768
System_PostUIMessage(UIMessage::SAVE_FRAME_DUMP);
769
break;
770
}
771
772
case ID_DEBUG_LOADMAPFILE:
773
if (W32Util::BrowseForFileName(true, hWnd, L"Load .ppmap", 0, L"Maps\0*.ppmap\0All files\0*.*\0\0", L"ppmap", fn)) {
774
g_symbolMap->LoadSymbolMap(Path(fn));
775
NotifyDebuggerMapLoaded();
776
}
777
break;
778
779
case ID_DEBUG_SAVEMAPFILE:
780
if (W32Util::BrowseForFileName(false, hWnd, L"Save .ppmap", 0, L"Maps\0*.ppmap\0All files\0*.*\0\0", L"ppmap", fn))
781
g_symbolMap->SaveSymbolMap(Path(fn));
782
break;
783
784
case ID_DEBUG_LOADSYMFILE:
785
if (W32Util::BrowseForFileName(true, hWnd, L"Load .sym", 0, L"Symbols\0*.sym\0All files\0*.*\0\0", L"sym", fn)) {
786
g_symbolMap->LoadNocashSym(Path(fn));
787
NotifyDebuggerMapLoaded();
788
}
789
break;
790
791
case ID_DEBUG_SAVESYMFILE:
792
if (W32Util::BrowseForFileName(false, hWnd, L"Save .sym", 0, L"Symbols\0*.sym\0All files\0*.*\0\0", L"sym", fn))
793
g_symbolMap->SaveNocashSym(Path(fn));
794
break;
795
796
case ID_DEBUG_RESETSYMBOLTABLE:
797
g_symbolMap->Clear();
798
NotifyDebuggerMapLoaded();
799
break;
800
801
case ID_DEBUG_DISASSEMBLY:
802
CreateDisasmWindow();
803
if (disasmWindow)
804
disasmWindow->Show(true);
805
break;
806
807
case ID_DEBUG_GEDEBUGGER:
808
#if PPSSPP_API(ANY_GL)
809
CreateGeDebuggerWindow();
810
if (geDebuggerWindow)
811
geDebuggerWindow->Show(true);
812
#endif
813
break;
814
815
case ID_DEBUG_MEMORYVIEW:
816
CreateMemoryWindow();
817
if (memoryWindow)
818
memoryWindow->Show(true);
819
break;
820
821
case ID_DEBUG_MEMORYBASE:
822
{
823
System_CopyStringToClipboard(StringFromFormat("%016llx", (uint64_t)(uintptr_t)Memory::base));
824
break;
825
}
826
827
case ID_DEBUG_EXTRACTFILE:
828
{
829
std::string filename;
830
if (!InputBox_GetString(hInst, hWnd, L"Disc filename", filename, filename)) {
831
break;
832
}
833
const char *lastSlash = strrchr(filename.c_str(), '/');
834
if (lastSlash) {
835
fn = lastSlash + 1;
836
} else {
837
fn.clear();
838
}
839
840
PSPFileInfo info = pspFileSystem.GetFileInfo(filename);
841
if (!info.exists) {
842
MessageBox(hWnd, L"File does not exist.", L"Sorry", 0);
843
} else if (info.type == FILETYPE_DIRECTORY) {
844
MessageBox(hWnd, L"Cannot extract directories.", L"Sorry", 0);
845
} else if (W32Util::BrowseForFileName(false, hWnd, L"Save file as...", 0, L"All files\0*.*\0\0", L"", fn)) {
846
u32 handle = pspFileSystem.OpenFile(filename, FILEACCESS_READ, "");
847
// Note: len may be in blocks.
848
size_t len = pspFileSystem.SeekFile(handle, 0, FILEMOVE_END);
849
bool isBlockMode = pspFileSystem.DevType(handle) & PSPDevType::BLOCK;
850
851
FILE *fp = File::OpenCFile(Path(fn), "wb");
852
pspFileSystem.SeekFile(handle, 0, FILEMOVE_BEGIN);
853
u8 buffer[4096];
854
size_t bufferSize = isBlockMode ? sizeof(buffer) / 2048 : sizeof(buffer);
855
while (len > 0) {
856
// This is all in blocks, not bytes, if isBlockMode.
857
size_t remain = std::min(len, bufferSize);
858
size_t readSize = pspFileSystem.ReadFile(handle, buffer, remain);
859
if (readSize == 0)
860
break;
861
size_t bytes = isBlockMode ? readSize * 2048 : readSize;
862
fwrite(buffer, 1, bytes, fp);
863
len -= readSize;
864
}
865
pspFileSystem.CloseFile(handle);
866
fclose(fp);
867
}
868
}
869
break;
870
871
case ID_DEBUG_LOG:
872
g_logManager.GetConsoleListener()->Show(g_logManager.GetConsoleListener()->Hidden());
873
break;
874
875
case ID_DEBUG_IGNOREILLEGALREADS:
876
g_Config.bIgnoreBadMemAccess = !g_Config.bIgnoreBadMemAccess;
877
break;
878
879
case ID_OPTIONS_FULLSCREEN:
880
if (!g_Config.bShowImDebugger) {
881
SendToggleFullscreen(!g_Config.UseFullScreen());
882
}
883
break;
884
885
case ID_OPTIONS_TEXTUREFILTERING_AUTO: g_Config.iTexFiltering = TEX_FILTER_AUTO; break;
886
case ID_OPTIONS_NEARESTFILTERING: g_Config.iTexFiltering = TEX_FILTER_FORCE_NEAREST; break;
887
case ID_OPTIONS_LINEARFILTERING: g_Config.iTexFiltering = TEX_FILTER_FORCE_LINEAR; break;
888
case ID_OPTIONS_AUTOMAXQUALITYFILTERING: g_Config.iTexFiltering = TEX_FILTER_AUTO_MAX_QUALITY; break;
889
890
case ID_OPTIONS_SMART2DTEXTUREFILTERING: g_Config.bSmart2DTexFiltering = !g_Config.bSmart2DTexFiltering; break;
891
892
case ID_OPTIONS_BUFLINEARFILTER: g_Config.iDisplayFilter = SCALE_LINEAR; break;
893
case ID_OPTIONS_BUFNEARESTFILTER: g_Config.iDisplayFilter = SCALE_NEAREST; break;
894
895
case ID_OPTIONS_TOPMOST:
896
g_Config.bTopMost = !g_Config.bTopMost;
897
W32Util::MakeTopMost(hWnd, g_Config.bTopMost);
898
break;
899
900
case ID_OPTIONS_PAUSE_FOCUS:
901
g_Config.bPauseOnLostFocus = !g_Config.bPauseOnLostFocus;
902
break;
903
904
case ID_OPTIONS_CONTROLS:
905
System_PostUIMessage(UIMessage::SHOW_CONTROL_MAPPING);
906
break;
907
908
case ID_OPTIONS_MORE_SETTINGS:
909
System_PostUIMessage(UIMessage::SHOW_SETTINGS);
910
break;
911
912
case ID_EMULATION_SOUND:
913
g_Config.bEnableSound = !g_Config.bEnableSound;
914
break;
915
916
case ID_HELP_OPENWEBSITE:
917
ShellExecute(NULL, L"open", L"https://www.ppsspp.org/", NULL, NULL, SW_SHOWNORMAL);
918
break;
919
920
case ID_HELP_BUYGOLD:
921
ShellExecute(NULL, L"open", L"https://www.ppsspp.org/buygold", NULL, NULL, SW_SHOWNORMAL);
922
break;
923
924
case ID_HELP_OPENFORUM:
925
ShellExecute(NULL, L"open", L"https://forums.ppsspp.org/", NULL, NULL, SW_SHOWNORMAL);
926
break;
927
928
case ID_HELP_GITHUB:
929
ShellExecute(NULL, L"open", L"https://github.com/hrydgard/ppsspp/", NULL, NULL, SW_SHOWNORMAL);
930
break;
931
932
case ID_HELP_DISCORD:
933
ShellExecute(NULL, L"open", L"https://discord.gg/5NJB6dD", NULL, NULL, SW_SHOWNORMAL);
934
break;
935
936
case ID_HELP_ABOUT:
937
DialogManager::EnableAll(FALSE);
938
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)AboutDlgProc);
939
DialogManager::EnableAll(TRUE);
940
break;
941
942
case ID_DEBUG_TAKESCREENSHOT:
943
g_TakeScreenshot = true;
944
break;
945
946
case ID_DEBUG_RESTARTGRAPHICS:
947
System_PostUIMessage(UIMessage::RESTART_GRAPHICS);
948
break;
949
950
case ID_FILE_DUMPFRAMES:
951
g_Config.bDumpFrames = !g_Config.bDumpFrames;
952
break;
953
954
case ID_FILE_USEFFV1:
955
g_Config.bUseFFV1 = !g_Config.bUseFFV1;
956
break;
957
958
case ID_FILE_DUMP_VIDEO_OUTPUT:
959
g_Config.bDumpVideoOutput = !g_Config.bDumpVideoOutput;
960
break;
961
962
case ID_FILE_DUMPAUDIO:
963
g_Config.bDumpAudio = !g_Config.bDumpAudio;
964
break;
965
966
default:
967
#ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION
968
if (rc_client_raintegration_activate_menu_item(Achievements::GetClient(), LOWORD(wParam))) {
969
break;
970
}
971
#endif
972
MessageBox(hWnd, L"Unhandled menu item", L"Sorry", 0);
973
break;
974
}
975
}
976
977
void HideDebugWindows() {
978
if (disasmWindow)
979
disasmWindow->Show(false);
980
#if PPSSPP_API(ANY_GL)
981
if (geDebuggerWindow)
982
geDebuggerWindow->Show(false);
983
#endif
984
if (memoryWindow)
985
memoryWindow->Show(false);
986
if (vfpudlg)
987
vfpudlg->Show(false);
988
}
989
990
void UpdateMenus(bool isMenuSelect) {
991
if (isMenuSelect) {
992
menuShaderInfoLoaded = false;
993
}
994
995
HMENU menu = GetMenu(GetHWND());
996
#define CHECKITEM(item,value) CheckMenuItem(menu,item,MF_BYCOMMAND | ((value) ? MF_CHECKED : MF_UNCHECKED));
997
CHECKITEM(ID_DEBUG_IGNOREILLEGALREADS, g_Config.bIgnoreBadMemAccess);
998
CHECKITEM(ID_DEBUG_SHOWDEBUGSTATISTICS, (DebugOverlay)g_Config.iDebugOverlay == DebugOverlay::DEBUG_STATS);
999
CHECKITEM(ID_OPTIONS_HARDWARETRANSFORM, g_Config.bHardwareTransform);
1000
CHECKITEM(ID_DEBUG_BREAKONLOAD, !g_Config.bAutoRun);
1001
CHECKITEM(ID_OPTIONS_FRAMESKIP_AUTO, g_Config.bAutoFrameSkip);
1002
CHECKITEM(ID_OPTIONS_FRAMESKIP, g_Config.iFrameSkip != FRAMESKIP_OFF);
1003
CHECKITEM(ID_OPTIONS_FRAMESKIPTYPE_COUNT, g_Config.iFrameSkipType == FRAMESKIPTYPE_COUNT);
1004
CHECKITEM(ID_OPTIONS_FRAMESKIPTYPE_PRCNT, g_Config.iFrameSkipType == FRAMESKIPTYPE_PRCNT);
1005
CHECKITEM(ID_OPTIONS_VSYNC, g_Config.bVSync);
1006
CHECKITEM(ID_OPTIONS_TOPMOST, g_Config.bTopMost);
1007
CHECKITEM(ID_OPTIONS_PAUSE_FOCUS, g_Config.bPauseOnLostFocus);
1008
CHECKITEM(ID_OPTIONS_SMART2DTEXTUREFILTERING, g_Config.bSmart2DTexFiltering);
1009
CHECKITEM(ID_EMULATION_SOUND, g_Config.bEnableSound);
1010
CHECKITEM(ID_TEXTURESCALING_DEPOSTERIZE, g_Config.bTexDeposterize);
1011
CHECKITEM(ID_EMULATION_CHEATS, g_Config.bEnableCheats);
1012
CHECKITEM(ID_OPTIONS_IGNOREWINKEY, g_Config.bIgnoreWindowsKey);
1013
CHECKITEM(ID_FILE_DUMPFRAMES, g_Config.bDumpFrames);
1014
CHECKITEM(ID_FILE_USEFFV1, g_Config.bUseFFV1);
1015
CHECKITEM(ID_FILE_DUMP_VIDEO_OUTPUT, g_Config.bDumpVideoOutput);
1016
CHECKITEM(ID_FILE_DUMPAUDIO, g_Config.bDumpAudio);
1017
CHECKITEM(ID_OPTIONS_SKIP_BUFFER_EFFECTS, g_Config.bSkipBufferEffects);
1018
1019
static const int displayrotationitems[] = {
1020
ID_EMULATION_ROTATION_H,
1021
ID_EMULATION_ROTATION_V,
1022
ID_EMULATION_ROTATION_H_R,
1023
ID_EMULATION_ROTATION_V_R
1024
};
1025
if (g_Config.iInternalScreenRotation < ROTATION_LOCKED_HORIZONTAL)
1026
g_Config.iInternalScreenRotation = ROTATION_LOCKED_HORIZONTAL;
1027
1028
else if (g_Config.iInternalScreenRotation > ROTATION_LOCKED_VERTICAL180)
1029
g_Config.iInternalScreenRotation = ROTATION_LOCKED_VERTICAL180;
1030
1031
for (int i = 0; i < ARRAY_SIZE(displayrotationitems); i++) {
1032
CheckMenuItem(menu, displayrotationitems[i], MF_BYCOMMAND | ((i + 1) == g_Config.iInternalScreenRotation ? MF_CHECKED : MF_UNCHECKED));
1033
}
1034
1035
static const int zoomitems[11] = {
1036
ID_OPTIONS_SCREENAUTO,
1037
ID_OPTIONS_SCREEN1X,
1038
ID_OPTIONS_SCREEN2X,
1039
ID_OPTIONS_SCREEN3X,
1040
ID_OPTIONS_SCREEN4X,
1041
ID_OPTIONS_SCREEN5X,
1042
ID_OPTIONS_SCREEN6X,
1043
ID_OPTIONS_SCREEN7X,
1044
ID_OPTIONS_SCREEN8X,
1045
ID_OPTIONS_SCREEN9X,
1046
ID_OPTIONS_SCREEN10X,
1047
};
1048
if (g_Config.iInternalResolution < RESOLUTION_AUTO)
1049
g_Config.iInternalResolution = RESOLUTION_AUTO;
1050
1051
else if (g_Config.iInternalResolution > RESOLUTION_MAX)
1052
g_Config.iInternalResolution = RESOLUTION_MAX;
1053
1054
for (int i = 0; i < ARRAY_SIZE(zoomitems); i++) {
1055
CheckMenuItem(menu, zoomitems[i], MF_BYCOMMAND | ((i == g_Config.iInternalResolution) ? MF_CHECKED : MF_UNCHECKED));
1056
}
1057
1058
static const int windowSizeItems[10] = {
1059
ID_OPTIONS_WINDOW1X,
1060
ID_OPTIONS_WINDOW2X,
1061
ID_OPTIONS_WINDOW3X,
1062
ID_OPTIONS_WINDOW4X,
1063
ID_OPTIONS_WINDOW5X,
1064
ID_OPTIONS_WINDOW6X,
1065
ID_OPTIONS_WINDOW7X,
1066
ID_OPTIONS_WINDOW8X,
1067
ID_OPTIONS_WINDOW9X,
1068
ID_OPTIONS_WINDOW10X,
1069
};
1070
1071
RECT rc;
1072
GetClientRect(GetHWND(), &rc);
1073
1074
int checkW = g_Config.IsPortrait() ? 272 : 480;
1075
int checkH = g_Config.IsPortrait() ? 480 : 272;
1076
1077
for (int i = 0; i < ARRAY_SIZE(windowSizeItems); i++) {
1078
bool check = (i + 1) * checkW == rc.right - rc.left || (i + 1) * checkH == rc.bottom - rc.top;
1079
CheckMenuItem(menu, windowSizeItems[i], MF_BYCOMMAND | (check ? MF_CHECKED : MF_UNCHECKED));
1080
}
1081
1082
static const int texscalingitems[] = {
1083
ID_TEXTURESCALING_OFF,
1084
ID_TEXTURESCALING_2X,
1085
ID_TEXTURESCALING_3X,
1086
ID_TEXTURESCALING_4X,
1087
ID_TEXTURESCALING_5X,
1088
};
1089
if (g_Config.iTexScalingLevel < TEXSCALING_OFF)
1090
g_Config.iTexScalingLevel = TEXSCALING_OFF;
1091
1092
else if (g_Config.iTexScalingLevel > TEXSCALING_MAX)
1093
g_Config.iTexScalingLevel = TEXSCALING_MAX;
1094
1095
for (int i = 0; i < ARRAY_SIZE(texscalingitems); i++) {
1096
// OFF is 1, skip 0.
1097
bool selected = i + 1 == g_Config.iTexScalingLevel;
1098
CheckMenuItem(menu, texscalingitems[i], MF_BYCOMMAND | (selected ? MF_CHECKED : MF_UNCHECKED));
1099
}
1100
1101
if (g_Config.iGPUBackend == (int)GPUBackend::OPENGL && !gl_extensions.OES_texture_npot) {
1102
EnableMenuItem(menu, ID_TEXTURESCALING_3X, MF_GRAYED);
1103
EnableMenuItem(menu, ID_TEXTURESCALING_5X, MF_GRAYED);
1104
} else {
1105
EnableMenuItem(menu, ID_TEXTURESCALING_3X, MF_ENABLED);
1106
EnableMenuItem(menu, ID_TEXTURESCALING_5X, MF_ENABLED);
1107
}
1108
1109
static const int texscalingtypeitems[] = {
1110
ID_TEXTURESCALING_XBRZ,
1111
ID_TEXTURESCALING_HYBRID,
1112
ID_TEXTURESCALING_BICUBIC,
1113
ID_TEXTURESCALING_HYBRID_BICUBIC,
1114
};
1115
if (g_Config.iTexScalingType < TextureScalerCommon::XBRZ)
1116
g_Config.iTexScalingType = TextureScalerCommon::XBRZ;
1117
1118
else if (g_Config.iTexScalingType > TextureScalerCommon::HYBRID_BICUBIC)
1119
g_Config.iTexScalingType = TextureScalerCommon::HYBRID_BICUBIC;
1120
1121
for (int i = 0; i < ARRAY_SIZE(texscalingtypeitems); i++) {
1122
CheckMenuItem(menu, texscalingtypeitems[i], MF_BYCOMMAND | ((i == g_Config.iTexScalingType) ? MF_CHECKED : MF_UNCHECKED));
1123
}
1124
1125
static const int texfilteringitems[] = {
1126
ID_OPTIONS_TEXTUREFILTERING_AUTO,
1127
ID_OPTIONS_NEARESTFILTERING,
1128
ID_OPTIONS_LINEARFILTERING,
1129
ID_OPTIONS_AUTOMAXQUALITYFILTERING,
1130
};
1131
if (g_Config.iTexFiltering < TEX_FILTER_AUTO)
1132
g_Config.iTexFiltering = TEX_FILTER_AUTO;
1133
else if (g_Config.iTexFiltering > TEX_FILTER_AUTO_MAX_QUALITY)
1134
g_Config.iTexFiltering = TEX_FILTER_AUTO_MAX_QUALITY;
1135
1136
for (int i = 0; i < ARRAY_SIZE(texfilteringitems); i++) {
1137
CheckMenuItem(menu, texfilteringitems[i], MF_BYCOMMAND | ((i + 1) == g_Config.iTexFiltering ? MF_CHECKED : MF_UNCHECKED));
1138
}
1139
1140
static const int bufferfilteritems[] = {
1141
ID_OPTIONS_BUFLINEARFILTER,
1142
ID_OPTIONS_BUFNEARESTFILTER,
1143
};
1144
if (g_Config.iDisplayFilter < SCALE_LINEAR)
1145
g_Config.iDisplayFilter = SCALE_LINEAR;
1146
1147
else if (g_Config.iDisplayFilter > SCALE_NEAREST)
1148
g_Config.iDisplayFilter = SCALE_NEAREST;
1149
1150
for (int i = 0; i < ARRAY_SIZE(bufferfilteritems); i++) {
1151
CheckMenuItem(menu, bufferfilteritems[i], MF_BYCOMMAND | ((i + 1) == g_Config.iDisplayFilter ? MF_CHECKED : MF_UNCHECKED));
1152
}
1153
1154
static const int frameskipping[] = {
1155
ID_OPTIONS_FRAMESKIP_0,
1156
ID_OPTIONS_FRAMESKIP_1,
1157
ID_OPTIONS_FRAMESKIP_2,
1158
ID_OPTIONS_FRAMESKIP_3,
1159
ID_OPTIONS_FRAMESKIP_4,
1160
ID_OPTIONS_FRAMESKIP_5,
1161
ID_OPTIONS_FRAMESKIP_6,
1162
ID_OPTIONS_FRAMESKIP_7,
1163
ID_OPTIONS_FRAMESKIP_8,
1164
};
1165
1166
static const int frameskippingType[] = {
1167
ID_OPTIONS_FRAMESKIPTYPE_COUNT,
1168
ID_OPTIONS_FRAMESKIPTYPE_PRCNT,
1169
};
1170
1171
if (g_Config.iFrameSkip < FRAMESKIP_OFF)
1172
g_Config.iFrameSkip = FRAMESKIP_OFF;
1173
1174
else if (g_Config.iFrameSkip > FRAMESKIP_MAX)
1175
g_Config.iFrameSkip = FRAMESKIP_MAX;
1176
1177
for (int i = 0; i < ARRAY_SIZE(frameskipping); i++) {
1178
CheckMenuItem(menu, frameskipping[i], MF_BYCOMMAND | ((i == g_Config.iFrameSkip) ? MF_CHECKED : MF_UNCHECKED));
1179
}
1180
1181
for (int i = 0; i < ARRAY_SIZE(frameskippingType); i++) {
1182
CheckMenuItem(menu, frameskippingType[i], MF_BYCOMMAND | ((i == g_Config.iFrameSkipType) ? MF_CHECKED : MF_UNCHECKED));
1183
}
1184
1185
static const int savestateSlot[] = {
1186
ID_FILE_SAVESTATE_SLOT_1,
1187
ID_FILE_SAVESTATE_SLOT_2,
1188
ID_FILE_SAVESTATE_SLOT_3,
1189
ID_FILE_SAVESTATE_SLOT_4,
1190
ID_FILE_SAVESTATE_SLOT_5,
1191
};
1192
1193
if (g_Config.iCurrentStateSlot < 0)
1194
g_Config.iCurrentStateSlot = 0;
1195
1196
else if (g_Config.iCurrentStateSlot >= SaveState::NUM_SLOTS)
1197
g_Config.iCurrentStateSlot = SaveState::NUM_SLOTS - 1;
1198
1199
for (int i = 0; i < ARRAY_SIZE(savestateSlot); i++) {
1200
CheckMenuItem(menu, savestateSlot[i], MF_BYCOMMAND | ((i == g_Config.iCurrentStateSlot) ? MF_CHECKED : MF_UNCHECKED));
1201
}
1202
1203
bool allowD3D11 = g_Config.IsBackendEnabled(GPUBackend::DIRECT3D11);
1204
bool allowOpenGL = g_Config.IsBackendEnabled(GPUBackend::OPENGL);
1205
bool allowVulkan = g_Config.IsBackendEnabled(GPUBackend::VULKAN);
1206
1207
switch (GetGPUBackend()) {
1208
case GPUBackend::OPENGL:
1209
EnableMenuItem(menu, ID_OPTIONS_DIRECT3D11, allowD3D11 ? MF_ENABLED : MF_GRAYED);
1210
EnableMenuItem(menu, ID_OPTIONS_OPENGL, MF_GRAYED);
1211
EnableMenuItem(menu, ID_OPTIONS_VULKAN, allowVulkan ? MF_ENABLED : MF_GRAYED);
1212
CheckMenuItem(menu, ID_OPTIONS_DIRECT3D11, MF_UNCHECKED);
1213
CheckMenuItem(menu, ID_OPTIONS_OPENGL, MF_CHECKED);
1214
CheckMenuItem(menu, ID_OPTIONS_VULKAN, MF_UNCHECKED);
1215
break;
1216
case GPUBackend::VULKAN:
1217
EnableMenuItem(menu, ID_OPTIONS_DIRECT3D11, allowD3D11 ? MF_ENABLED : MF_GRAYED);
1218
EnableMenuItem(menu, ID_OPTIONS_OPENGL, allowOpenGL ? MF_ENABLED : MF_GRAYED);
1219
EnableMenuItem(menu, ID_OPTIONS_VULKAN, MF_GRAYED);
1220
CheckMenuItem(menu, ID_OPTIONS_DIRECT3D11, MF_UNCHECKED);
1221
CheckMenuItem(menu, ID_OPTIONS_OPENGL, MF_UNCHECKED);
1222
CheckMenuItem(menu, ID_OPTIONS_VULKAN, MF_CHECKED);
1223
break;
1224
case GPUBackend::DIRECT3D11:
1225
EnableMenuItem(menu, ID_OPTIONS_DIRECT3D11, MF_GRAYED);
1226
EnableMenuItem(menu, ID_OPTIONS_OPENGL, allowOpenGL ? MF_ENABLED : MF_GRAYED);
1227
EnableMenuItem(menu, ID_OPTIONS_VULKAN, allowVulkan ? MF_ENABLED : MF_GRAYED);
1228
CheckMenuItem(menu, ID_OPTIONS_DIRECT3D11, MF_CHECKED);
1229
CheckMenuItem(menu, ID_OPTIONS_OPENGL, MF_UNCHECKED);
1230
CheckMenuItem(menu, ID_OPTIONS_VULKAN, MF_UNCHECKED);
1231
break;
1232
}
1233
1234
#if !PPSSPP_API(ANY_GL)
1235
EnableMenuItem(menu, ID_DEBUG_GEDEBUGGER, MF_GRAYED);
1236
#endif
1237
1238
UpdateCommands();
1239
}
1240
1241
void UpdateCommands() {
1242
static GlobalUIState lastGlobalUIState = UISTATE_PAUSEMENU;
1243
static CoreState lastCoreState = CORE_POWERDOWN;
1244
1245
HMENU menu = GetMenu(GetHWND());
1246
EnableMenuItem(menu, ID_DEBUG_LOG, g_Config.bEnableLogging ? MF_ENABLED : MF_GRAYED);
1247
SetIngameMenuItemStates(menu, GetUIState());
1248
1249
if (lastGlobalUIState == GetUIState() && lastCoreState == coreState)
1250
return;
1251
1252
lastCoreState = coreState;
1253
lastGlobalUIState = GetUIState();
1254
1255
bool isPaused = Core_IsStepping() && GetUIState() == UISTATE_INGAME;
1256
TranslateMenuItem(menu, ID_TOGGLE_BREAK, L"\tF8", isPaused ? "Run" : "Break");
1257
}
1258
1259
void UpdateSwitchUMD() {
1260
HMENU menu = GetMenu(GetHWND());
1261
GlobalUIState state = GetUIState();
1262
UINT umdSwitchEnable = state == UISTATE_INGAME && getUMDReplacePermit() ? MF_ENABLED : MF_GRAYED;
1263
EnableMenuItem(menu, ID_EMULATION_SWITCH_UMD, umdSwitchEnable);
1264
}
1265
1266
// Message handler for about box.
1267
LRESULT CALLBACK AboutDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
1268
switch (message) {
1269
case WM_INITDIALOG:
1270
{
1271
W32Util::CenterWindow(hDlg);
1272
HWND versionBox = GetDlgItem(hDlg, IDC_VERSION);
1273
std::string windowText = System_GetPropertyBool(SYSPROP_APP_GOLD) ? "PPSSPP Gold " : "PPSSPP ";
1274
windowText.append(PPSSPP_GIT_VERSION);
1275
SetWindowText(versionBox, ConvertUTF8ToWString(windowText).c_str());
1276
DarkModeInitDialog(hDlg);
1277
return TRUE;
1278
}
1279
1280
case WM_COMMAND:
1281
{
1282
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
1283
EndDialog(hDlg, LOWORD(wParam));
1284
return TRUE;
1285
}
1286
break;
1287
return FALSE;
1288
}
1289
1290
default:
1291
return DarkModeDlgProc(hDlg, message, wParam, lParam);
1292
}
1293
return FALSE;
1294
}
1295
}
1296
1297