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