ppsspp/Windows/MainWindowMenu.cpp
Henrik Rydgård 0e3a84b4a8 Move most GPU things to Common.
It works after the move, on Windows and Android at least.

Deletes the D3DX9 shader compiler loader, which was not used.
2020-10-04 23:39:02 +02:00

1404 lines
48 KiB
C++

#include <map>
#include <string>
#include <sstream>
#include "CommonWindows.h"
#include <shellapi.h>
#include "resource.h"
#include "Common/GPU/OpenGL/GLFeatures.h"
#include "Common/Data/Text/I18n.h"
#include "Common/Data/Encoding/Utf8.h"
#include "Common/System/System.h"
#include "Common/System/NativeApp.h"
#include "Common/File/FileUtil.h"
#include "Common/Log.h"
#include "Common/LogManager.h"
#include "Common/ConsoleListener.h"
#include "Common/OSVersion.h"
#include "Common/GPU/Vulkan/VulkanLoader.h"
#include "Common/StringUtils.h"
#if PPSSPP_API(ANY_GL)
#include "GPU/GLES/TextureScalerGLES.h"
#include "GPU/GLES/TextureCacheGLES.h"
#include "GPU/GLES/FramebufferManagerGLES.h"
#endif
#include "UI/OnScreenDisplay.h"
#include "GPU/Common/PostShader.h"
#include "GPU/Common/FramebufferManagerCommon.h"
#include "GPU/Common/TextureCacheCommon.h"
#include "GPU/Common/TextureScalerCommon.h"
#include "Core/Config.h"
#include "Core/ConfigValues.h"
#include "Core/FileSystems/MetaFileSystem.h"
#include "Core/KeyMap.h"
#include "UI/OnScreenDisplay.h"
#include "Windows/MainWindowMenu.h"
#include "Windows/MainWindow.h"
#include "Windows/W32Util/DialogManager.h"
#include "Windows/W32Util/ShellUtil.h"
#include "Windows/W32Util/Misc.h"
#include "Windows/InputBox.h"
#include "Windows/main.h"
#include "Core/HLE/sceUmd.h"
#include "Core/SaveState.h"
#include "Core/Core.h"
extern bool g_TakeScreenshot;
extern bool g_ShaderNameListChanged;
namespace MainWindow {
extern HINSTANCE hInst;
extern bool noFocusPause;
static W32Util::AsyncBrowseDialog *browseDialog;
static W32Util::AsyncBrowseDialog *browseImageDialog;
static bool browsePauseAfter;
static std::unordered_map<int, std::string> initialMenuKeys;
static std::vector<std::string> availableShaders;
static std::string menuLanguageID = "";
static int menuKeymapGeneration = -1;
static bool menuShaderInfoLoaded = false;
std::vector<ShaderInfo> menuShaderInfo;
LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
void SetIngameMenuItemStates(HMENU menu, const GlobalUIState state) {
UINT menuEnable = state == UISTATE_INGAME ? MF_ENABLED : MF_GRAYED;
UINT umdSwitchEnable = state == UISTATE_INGAME && getUMDReplacePermit() ? MF_ENABLED : MF_GRAYED;
EnableMenuItem(menu, ID_FILE_SAVESTATEFILE, menuEnable);
EnableMenuItem(menu, ID_FILE_LOADSTATEFILE, menuEnable);
EnableMenuItem(menu, ID_FILE_QUICKSAVESTATE, menuEnable);
EnableMenuItem(menu, ID_FILE_QUICKLOADSTATE, menuEnable);
EnableMenuItem(menu, ID_EMULATION_PAUSE, menuEnable);
EnableMenuItem(menu, ID_EMULATION_STOP, menuEnable);
EnableMenuItem(menu, ID_EMULATION_RESET, menuEnable);
EnableMenuItem(menu, ID_EMULATION_SWITCH_UMD, umdSwitchEnable);
EnableMenuItem(menu, ID_TOGGLE_BREAK, menuEnable);
EnableMenuItem(menu, ID_DEBUG_LOADMAPFILE, menuEnable);
EnableMenuItem(menu, ID_DEBUG_SAVEMAPFILE, menuEnable);
EnableMenuItem(menu, ID_DEBUG_LOADSYMFILE, menuEnable);
EnableMenuItem(menu, ID_DEBUG_SAVESYMFILE, menuEnable);
EnableMenuItem(menu, ID_DEBUG_RESETSYMBOLTABLE, menuEnable);
EnableMenuItem(menu, ID_DEBUG_TAKESCREENSHOT, menuEnable);
EnableMenuItem(menu, ID_DEBUG_SHOWDEBUGSTATISTICS, menuEnable);
EnableMenuItem(menu, ID_DEBUG_EXTRACTFILE, menuEnable);
// While playing, this pop up doesn't work - and probably doesn't make sense.
EnableMenuItem(menu, ID_OPTIONS_LANGUAGE, state == UISTATE_INGAME ? MF_GRAYED : MF_ENABLED);
}
static HMENU GetSubmenuById(HMENU menu, int menuID) {
MENUITEMINFO menuInfo{ sizeof(MENUITEMINFO), MIIM_SUBMENU };
if (GetMenuItemInfo(menu, menuID, MF_BYCOMMAND, &menuInfo) != FALSE) {
return menuInfo.hSubMenu;
}
return nullptr;
}
static void EmptySubMenu(HMENU menu) {
int c = GetMenuItemCount(menu);
for (int i = 0; i < c; ++i) {
RemoveMenu(menu, 0, MF_BYPOSITION);
}
}
static std::string GetMenuItemText(HMENU menu, int menuID) {
MENUITEMINFO menuInfo{ sizeof(menuInfo), MIIM_STRING };
std::string retVal;
if (GetMenuItemInfo(menu, menuID, MF_BYCOMMAND, &menuInfo) != FALSE) {
wchar_t *buffer = new wchar_t[++menuInfo.cch];
menuInfo.dwTypeData = buffer;
GetMenuItemInfo(menu, menuID, MF_BYCOMMAND, &menuInfo);
retVal = ConvertWStringToUTF8(menuInfo.dwTypeData);
delete[] buffer;
}
return retVal;
}
const std::string &GetMenuItemInitialText(HMENU menu, const int menuID) {
if (initialMenuKeys.find(menuID) == initialMenuKeys.end()) {
initialMenuKeys[menuID] = GetMenuItemText(menu, menuID);
}
return initialMenuKeys[menuID];
}
void CreateHelpMenu(HMENU menu) {
auto des = GetI18NCategory("DesktopUI");
const std::wstring visitMainWebsite = ConvertUTF8ToWString(des->T("www.ppsspp.org"));
const std::wstring visitForum = ConvertUTF8ToWString(des->T("PPSSPP Forums"));
const std::wstring buyGold = ConvertUTF8ToWString(des->T("Buy Gold"));
const std::wstring gitHub = ConvertUTF8ToWString(des->T("GitHub"));
const std::wstring discord = ConvertUTF8ToWString(des->T("Discord"));
const std::wstring aboutPPSSPP = ConvertUTF8ToWString(des->T("About PPSSPP..."));
HMENU helpMenu = GetSubmenuById(menu, ID_HELP_MENU);
EmptySubMenu(helpMenu);
AppendMenu(helpMenu, MF_STRING | MF_BYCOMMAND, ID_HELP_OPENWEBSITE, visitMainWebsite.c_str());
AppendMenu(helpMenu, MF_STRING | MF_BYCOMMAND, ID_HELP_OPENFORUM, visitForum.c_str());
// Repeat the process for other languages, if necessary.
AppendMenu(helpMenu, MF_STRING | MF_BYCOMMAND, ID_HELP_BUYGOLD, buyGold.c_str());
AppendMenu(helpMenu, MF_STRING | MF_BYCOMMAND, ID_HELP_GITHUB, gitHub.c_str());
AppendMenu(helpMenu, MF_STRING | MF_BYCOMMAND, ID_HELP_DISCORD, discord.c_str());
AppendMenu(helpMenu, MF_SEPARATOR, 0, 0);
AppendMenu(helpMenu, MF_STRING | MF_BYCOMMAND, ID_HELP_ABOUT, aboutPPSSPP.c_str());
}
void UpdateDynamicMenuCheckmarks(HMENU menu) {
int item = ID_SHADERS_BASE + 1;
for (size_t i = 0; i < availableShaders.size(); i++)
CheckMenuItem(menu, item++, ((g_Config.vPostShaderNames[0] == availableShaders[i] && (g_Config.vPostShaderNames[0] == "Off" || g_Config.vPostShaderNames[1] == "Off")) ? MF_CHECKED : MF_UNCHECKED));
}
bool CreateShadersSubmenu(HMENU menu) {
// NOTE: We do not load this until translations are loaded!
if (!I18NCategoryLoaded("PostShaders"))
return false;
// We only reload this initially and when a menu is actually opened.
if (!menuShaderInfoLoaded) {
ReloadAllPostShaderInfo();
menuShaderInfoLoaded = true;
}
std::vector<ShaderInfo> info = GetAllPostShaderInfo();
if (menuShaderInfo.size() == info.size() && std::equal(info.begin(), info.end(), menuShaderInfo.begin())) {
return false;
}
auto ps = GetI18NCategory("PostShaders");
HMENU shaderMenu = GetSubmenuById(menu, ID_OPTIONS_SHADER_MENU);
EmptySubMenu(shaderMenu);
int item = ID_SHADERS_BASE + 1;
const char *translatedShaderName = nullptr;
availableShaders.clear();
for (auto i = info.begin(); i != info.end(); ++i) {
if (!i->visible)
continue;
int checkedStatus = MF_UNCHECKED;
availableShaders.push_back(i->section);
if (g_Config.vPostShaderNames[0] == i->section && (g_Config.vPostShaderNames[0] == "Off" || g_Config.vPostShaderNames[1] == "Off")) {
checkedStatus = MF_CHECKED;
}
translatedShaderName = ps->T(i->section.c_str(), i->name.c_str());
AppendMenu(shaderMenu, MF_STRING | MF_BYPOSITION | checkedStatus, item++, ConvertUTF8ToWString(translatedShaderName).c_str());
}
menuShaderInfo = info;
return true;
}
static void TranslateMenuItem(const HMENU hMenu, const int menuID, const std::wstring& accelerator = L"", const char *key = nullptr) {
auto des = GetI18NCategory("DesktopUI");
std::wstring translated;
if (key == nullptr || !strcmp(key, "")) {
translated = ConvertUTF8ToWString(des->T(GetMenuItemInitialText(hMenu, menuID)));
} else {
translated = ConvertUTF8ToWString(des->T(key));
}
translated.append(accelerator);
ModifyMenu(hMenu, menuID, MF_STRING | MF_BYCOMMAND, menuID, translated.c_str());
}
void DoTranslateMenus(HWND hWnd, HMENU menu) {
auto useDefHotkey = [](int virtkey) {
return KeyMap::g_controllerMap[virtkey].empty();
};
TranslateMenuItem(menu, ID_FILE_MENU);
TranslateMenuItem(menu, ID_EMULATION_MENU);
TranslateMenuItem(menu, ID_DEBUG_MENU);
TranslateMenuItem(menu, ID_OPTIONS_MENU);
TranslateMenuItem(menu, ID_HELP_MENU);
// File menu
TranslateMenuItem(menu, ID_FILE_LOAD);
TranslateMenuItem(menu, ID_FILE_LOAD_DIR);
TranslateMenuItem(menu, ID_FILE_LOAD_MEMSTICK);
TranslateMenuItem(menu, ID_FILE_OPEN_NEW_INSTANCE);
TranslateMenuItem(menu, ID_FILE_MEMSTICK);
TranslateMenuItem(menu, ID_FILE_SAVESTATE_SLOT_MENU, useDefHotkey(VIRTKEY_NEXT_SLOT) ? L"\tF3" : L"");
TranslateMenuItem(menu, ID_FILE_QUICKLOADSTATE, useDefHotkey(VIRTKEY_LOAD_STATE) ? L"\tF4" : L"");
TranslateMenuItem(menu, ID_FILE_QUICKSAVESTATE, useDefHotkey(VIRTKEY_SAVE_STATE) ? L"\tF2" : L"");
TranslateMenuItem(menu, ID_FILE_LOADSTATEFILE);
TranslateMenuItem(menu, ID_FILE_SAVESTATEFILE);
TranslateMenuItem(menu, ID_FILE_RECORD_MENU);
TranslateMenuItem(menu, ID_FILE_EXIT, L"\tAlt+F4");
// Emulation menu
TranslateMenuItem(menu, ID_EMULATION_PAUSE);
TranslateMenuItem(menu, ID_EMULATION_STOP, L"\tCtrl+W");
TranslateMenuItem(menu, ID_EMULATION_RESET, L"\tCtrl+B");
TranslateMenuItem(menu, ID_EMULATION_SWITCH_UMD, L"\tCtrl+U");
TranslateMenuItem(menu, ID_EMULATION_ROTATION_MENU);
TranslateMenuItem(menu, ID_EMULATION_ROTATION_H);
TranslateMenuItem(menu, ID_EMULATION_ROTATION_V);
TranslateMenuItem(menu, ID_EMULATION_ROTATION_H_R);
TranslateMenuItem(menu, ID_EMULATION_ROTATION_V_R);
// Debug menu
TranslateMenuItem(menu, ID_TOGGLE_BREAK, L"\tF8", "Break");
TranslateMenuItem(menu, ID_DEBUG_BREAKONLOAD);
TranslateMenuItem(menu, ID_DEBUG_IGNOREILLEGALREADS);
TranslateMenuItem(menu, ID_DEBUG_LOADMAPFILE);
TranslateMenuItem(menu, ID_DEBUG_SAVEMAPFILE);
TranslateMenuItem(menu, ID_DEBUG_LOADSYMFILE);
TranslateMenuItem(menu, ID_DEBUG_SAVESYMFILE);
TranslateMenuItem(menu, ID_DEBUG_RESETSYMBOLTABLE);
TranslateMenuItem(menu, ID_DEBUG_TAKESCREENSHOT, L"\tF12");
TranslateMenuItem(menu, ID_DEBUG_DUMPNEXTFRAME);
TranslateMenuItem(menu, ID_DEBUG_SHOWDEBUGSTATISTICS);
TranslateMenuItem(menu, ID_DEBUG_DISASSEMBLY, L"\tCtrl+D");
TranslateMenuItem(menu, ID_DEBUG_GEDEBUGGER, L"\tCtrl+G");
TranslateMenuItem(menu, ID_DEBUG_EXTRACTFILE);
TranslateMenuItem(menu, ID_DEBUG_LOG, L"\tCtrl+L");
TranslateMenuItem(menu, ID_DEBUG_MEMORYVIEW, L"\tCtrl+M");
// Options menu
TranslateMenuItem(menu, ID_OPTIONS_LANGUAGE);
TranslateMenuItem(menu, ID_OPTIONS_TOPMOST);
TranslateMenuItem(menu, ID_OPTIONS_PAUSE_FOCUS);
TranslateMenuItem(menu, ID_OPTIONS_IGNOREWINKEY);
TranslateMenuItem(menu, ID_OPTIONS_MORE_SETTINGS);
TranslateMenuItem(menu, ID_OPTIONS_CONTROLS);
TranslateMenuItem(menu, ID_OPTIONS_DISPLAY_LAYOUT);
// Movie menu
TranslateMenuItem(menu, ID_FILE_DUMPFRAMES);
TranslateMenuItem(menu, ID_FILE_USEFFV1);
TranslateMenuItem(menu, ID_FILE_DUMP_VIDEO_OUTPUT);
TranslateMenuItem(menu, ID_FILE_DUMPAUDIO);
// Skip display multipliers x1-x10
TranslateMenuItem(menu, ID_OPTIONS_FULLSCREEN, L"\tAlt+Return, F11");
TranslateMenuItem(menu, ID_OPTIONS_VSYNC);
TranslateMenuItem(menu, ID_OPTIONS_SHADER_MENU);
TranslateMenuItem(menu, ID_OPTIONS_SCREEN_MENU, L"\tCtrl+1");
TranslateMenuItem(menu, ID_OPTIONS_SCREENAUTO);
// Skip rendering resolution 2x-5x..
TranslateMenuItem(menu, ID_OPTIONS_WINDOW_MENU);
// Skip window size 1x-4x..
TranslateMenuItem(menu, ID_OPTIONS_BACKEND_MENU);
TranslateMenuItem(menu, ID_OPTIONS_DIRECT3D11);
TranslateMenuItem(menu, ID_OPTIONS_DIRECT3D9);
TranslateMenuItem(menu, ID_OPTIONS_OPENGL);
TranslateMenuItem(menu, ID_OPTIONS_VULKAN);
TranslateMenuItem(menu, ID_OPTIONS_RENDERMODE_MENU);
TranslateMenuItem(menu, ID_OPTIONS_NONBUFFEREDRENDERING);
TranslateMenuItem(menu, ID_OPTIONS_BUFFEREDRENDERING);
TranslateMenuItem(menu, ID_OPTIONS_FRAMESKIP_MENU, L"\tF7");
TranslateMenuItem(menu, ID_OPTIONS_FRAMESKIP_AUTO);
TranslateMenuItem(menu, ID_OPTIONS_FRAMESKIP_0);
TranslateMenuItem(menu, ID_OPTIONS_FRAMESKIPTYPE_MENU);
TranslateMenuItem(menu, ID_OPTIONS_FRAMESKIPTYPE_COUNT);
TranslateMenuItem(menu, ID_OPTIONS_FRAMESKIPTYPE_PRCNT);
// Skip frameskipping 1-8..
TranslateMenuItem(menu, ID_OPTIONS_TEXTUREFILTERING_MENU);
TranslateMenuItem(menu, ID_OPTIONS_TEXTUREFILTERING_AUTO);
TranslateMenuItem(menu, ID_OPTIONS_NEARESTFILTERING);
TranslateMenuItem(menu, ID_OPTIONS_LINEARFILTERING);
TranslateMenuItem(menu, ID_OPTIONS_SCREENFILTER_MENU);
TranslateMenuItem(menu, ID_OPTIONS_BUFLINEARFILTER);
TranslateMenuItem(menu, ID_OPTIONS_BUFNEARESTFILTER);
TranslateMenuItem(menu, ID_OPTIONS_TEXTURESCALING_MENU);
TranslateMenuItem(menu, ID_TEXTURESCALING_OFF);
// Skip texture scaling 2x-5x...
TranslateMenuItem(menu, ID_TEXTURESCALING_XBRZ);
TranslateMenuItem(menu, ID_TEXTURESCALING_HYBRID);
TranslateMenuItem(menu, ID_TEXTURESCALING_BICUBIC);
TranslateMenuItem(menu, ID_TEXTURESCALING_HYBRID_BICUBIC);
TranslateMenuItem(menu, ID_TEXTURESCALING_DEPOSTERIZE);
TranslateMenuItem(menu, ID_OPTIONS_HARDWARETRANSFORM);
TranslateMenuItem(menu, ID_OPTIONS_VERTEXCACHE);
TranslateMenuItem(menu, ID_OPTIONS_SHOWFPS);
TranslateMenuItem(menu, ID_EMULATION_SOUND);
TranslateMenuItem(menu, ID_EMULATION_CHEATS, L"\tCtrl+T");
TranslateMenuItem(menu, ID_EMULATION_CHAT, L"\tCtrl+C");
// Help menu: it's translated in CreateHelpMenu.
CreateHelpMenu(menu);
}
void TranslateMenus(HWND hWnd, HMENU menu) {
bool changed = false;
const std::string curLanguageID = i18nrepo.LanguageID();
if (curLanguageID != menuLanguageID || menuKeymapGeneration != KeyMap::g_controllerMapGeneration) {
DoTranslateMenus(hWnd, menu);
menuLanguageID = curLanguageID;
changed = true;
}
if (CreateShadersSubmenu(menu)) {
changed = true;
}
if (changed) {
DrawMenuBar(hWnd);
}
}
void BrowseAndBoot(std::string defaultPath, bool browseDirectory) {
static std::wstring filter = L"All supported file types (*.iso *.cso *.pbp *.elf *.prx *.zip *.ppdmp)|*.pbp;*.elf;*.iso;*.cso;*.prx;*.zip;*.ppdmp|PSP ROMs (*.iso *.cso *.pbp *.elf *.prx)|*.pbp;*.elf;*.iso;*.cso;*.prx|Homebrew/Demos installers (*.zip)|*.zip|All files (*.*)|*.*||";
for (int i = 0; i < (int)filter.length(); i++) {
if (filter[i] == '|')
filter[i] = '\0';
}
browsePauseAfter = false;
if (GetUIState() == UISTATE_INGAME) {
browsePauseAfter = Core_IsStepping();
if (!browsePauseAfter)
Core_EnableStepping(true);
}
W32Util::MakeTopMost(GetHWND(), false);
if (browseDirectory) {
browseDialog = new W32Util::AsyncBrowseDialog(GetHWND(), WM_USER_BROWSE_BOOT_DONE, L"Choose directory");
} else {
browseDialog = new W32Util::AsyncBrowseDialog(W32Util::AsyncBrowseDialog::OPEN, GetHWND(), WM_USER_BROWSE_BOOT_DONE, L"LoadFile", ConvertUTF8ToWString(defaultPath), filter, L"*.pbp;*.elf;*.iso;*.cso;");
}
}
void BrowseAndBootDone() {
std::string filename;
if (!browseDialog->GetResult(filename)) {
if (!browsePauseAfter) {
Core_EnableStepping(false);
}
} else {
if (GetUIState() == UISTATE_INGAME || GetUIState() == UISTATE_PAUSEMENU) {
Core_EnableStepping(false);
}
filename = ReplaceAll(filename, "\\", "/");
NativeMessageReceived("boot", filename.c_str());
}
W32Util::MakeTopMost(GetHWND(), g_Config.bTopMost);
delete browseDialog;
browseDialog = 0;
}
void BrowseBackground() {
static std::wstring filter = L"All supported images (*.jpg *.jpeg *.png)|*.jpg;*.jpeg;*.png|All files (*.*)|*.*||";
for (size_t i = 0; i < filter.length(); i++) {
if (filter[i] == '|')
filter[i] = '\0';
}
W32Util::MakeTopMost(GetHWND(), false);
browseImageDialog = new W32Util::AsyncBrowseDialog(W32Util::AsyncBrowseDialog::OPEN, GetHWND(), WM_USER_BROWSE_BG_DONE, L"LoadFile", L"", filter, L"*.jpg;*.jpeg;*.png;");
}
void BrowseBackgroundDone() {
std::string filename;
if (browseImageDialog->GetResult(filename)) {
std::wstring src = ConvertUTF8ToWString(filename);
std::wstring dest;
if (filename.size() >= 5 && (filename.substr(filename.size() - 4) == ".jpg" || filename.substr(filename.size() - 5) == ".jpeg")) {
dest = ConvertUTF8ToWString(GetSysDirectory(DIRECTORY_SYSTEM) + "background.jpg");
} else {
dest = ConvertUTF8ToWString(GetSysDirectory(DIRECTORY_SYSTEM) + "background.png");
}
CopyFileW(src.c_str(), dest.c_str(), FALSE);
NativeMessageReceived("bgImage_updated", "");
}
W32Util::MakeTopMost(GetHWND(), g_Config.bTopMost);
delete browseImageDialog;
browseImageDialog = nullptr;
}
static void UmdSwitchAction() {
std::string fn;
std::string filter = "PSP ROMs (*.iso *.cso *.pbp *.elf)|*.pbp;*.elf;*.iso;*.cso;*.prx|All files (*.*)|*.*||";
for (int i = 0; i < (int)filter.length(); i++) {
if (filter[i] == '|')
filter[i] = '\0';
}
if (W32Util::BrowseForFileName(true, GetHWND(), L"Switch UMD", 0, ConvertUTF8ToWString(filter).c_str(), L"*.pbp;*.elf;*.iso;*.cso;", fn)) {
fn = ReplaceAll(fn, "\\", "/");
__UmdReplace(fn);
}
}
static void setScreenRotation(int rotation) {
g_Config.iInternalScreenRotation = rotation;
}
static void SaveStateActionFinished(SaveState::Status status, const std::string &message, void *userdata) {
if (!message.empty() && (!g_Config.bDumpFrames || !g_Config.bDumpVideoOutput)) {
osm.Show(message, status == SaveState::Status::SUCCESS ? 2.0 : 5.0);
}
PostMessage(MainWindow::GetHWND(), WM_USER_SAVESTATE_FINISH, 0, 0);
}
// not static
void setTexScalingMultiplier(int level) {
g_Config.iTexScalingLevel = level;
NativeMessageReceived("gpu_clearCache", "");
}
static void setTexFiltering(int type) {
g_Config.iTexFiltering = type;
}
static void setBufFilter(int type) {
g_Config.iBufFilter = type;
}
static void setTexScalingType(int type) {
g_Config.iTexScalingType = type;
NativeMessageReceived("gpu_clearCache", "");
}
static void setRenderingMode(int mode) {
auto gr = GetI18NCategory("Graphics");
g_Config.iRenderingMode = mode;
switch (g_Config.iRenderingMode) {
case FB_NON_BUFFERED_MODE:
osm.Show(gr->T("Non-Buffered Rendering"));
g_Config.bAutoFrameSkip = false;
break;
case FB_BUFFERED_MODE:
osm.Show(gr->T("Buffered Rendering"));
break;
}
NativeMessageReceived("gpu_resized", "");
}
static void setFrameSkipping(int framesToSkip = -1) {
if (framesToSkip >= FRAMESKIP_OFF)
g_Config.iFrameSkip = framesToSkip;
else {
if (++g_Config.iFrameSkip > FRAMESKIP_MAX)
g_Config.iFrameSkip = FRAMESKIP_OFF;
}
auto gr = GetI18NCategory("Graphics");
std::ostringstream messageStream;
messageStream << gr->T("Frame Skipping") << ":" << " ";
if (g_Config.iFrameSkip == FRAMESKIP_OFF)
messageStream << gr->T("Off");
else
messageStream << g_Config.iFrameSkip;
osm.Show(messageStream.str());
}
static void setFrameSkippingType(int fskipType = -1) {
if (fskipType >= 0 && fskipType <= 1) {
g_Config.iFrameSkipType = fskipType;
} else {
g_Config.iFrameSkipType = 0;
}
auto gr = GetI18NCategory("Graphics");
std::ostringstream messageStream;
messageStream << gr->T("Frame Skipping Type") << ":" << " ";
if (g_Config.iFrameSkipType == 0)
messageStream << gr->T("Number of Frames");
else
messageStream << gr->T("Percent of FPS");
osm.Show(messageStream.str());
}
static void enableCheats(bool cheats) {
g_Config.bEnableCheats = cheats;
}
static void setDisplayOptions(int options) {
g_Config.iSmallDisplayZoomType = options;
NativeMessageReceived("gpu_resized", "");
}
static void RestartApp() {
if (IsDebuggerPresent()) {
PostMessage(MainWindow::GetHWND(), WM_USER_RESTART_EMUTHREAD, 0, 0);
} else {
g_Config.bRestartRequired = true;
PostMessage(MainWindow::GetHWND(), WM_CLOSE, 0, 0);
}
}
void MainWindowMenu_Process(HWND hWnd, WPARAM wParam) {
std::string fn;
auto gr = GetI18NCategory("Graphics");
int wmId = LOWORD(wParam);
int wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId) {
case ID_FILE_LOAD:
BrowseAndBoot("");
break;
case ID_FILE_LOAD_DIR:
BrowseAndBoot("", true);
break;
case ID_FILE_LOAD_MEMSTICK:
BrowseAndBoot(GetSysDirectory(DIRECTORY_GAME));
break;
case ID_FILE_OPEN_NEW_INSTANCE:
W32Util::SpawnNewInstance(false);
break;
case ID_FILE_MEMSTICK:
ShellExecute(NULL, L"open", ConvertUTF8ToWString(g_Config.memStickDirectory).c_str(), 0, 0, SW_SHOW);
break;
case ID_TOGGLE_BREAK:
if (GetUIState() == UISTATE_PAUSEMENU) {
// Causes hang
//NativeMessageReceived("run", "");
if (disasmWindow)
SendMessage(disasmWindow->GetDlgHandle(), WM_COMMAND, IDC_STOPGO, 0);
} else if (Core_IsStepping()) { // It is paused, then continue to run.
if (disasmWindow)
SendMessage(disasmWindow->GetDlgHandle(), WM_COMMAND, IDC_STOPGO, 0);
else
Core_EnableStepping(false);
} else {
if (disasmWindow)
SendMessage(disasmWindow->GetDlgHandle(), WM_COMMAND, IDC_STOPGO, 0);
else
Core_EnableStepping(true);
}
noFocusPause = !noFocusPause; // If we pause, override pause on lost focus
break;
case ID_EMULATION_PAUSE:
NativeMessageReceived("pause", "");
Core_EnableStepping(false);
break;
case ID_EMULATION_STOP:
if (Core_IsStepping())
Core_EnableStepping(false);
Core_Stop();
NativeMessageReceived("stop", "");
Core_WaitInactive();
break;
case ID_EMULATION_RESET:
NativeMessageReceived("reset", "");
Core_EnableStepping(false);
break;
case ID_EMULATION_SWITCH_UMD:
UmdSwitchAction();
break;
case ID_EMULATION_ROTATION_H: setScreenRotation(ROTATION_LOCKED_HORIZONTAL); break;
case ID_EMULATION_ROTATION_V: setScreenRotation(ROTATION_LOCKED_VERTICAL); break;
case ID_EMULATION_ROTATION_H_R: setScreenRotation(ROTATION_LOCKED_HORIZONTAL180); break;
case ID_EMULATION_ROTATION_V_R: setScreenRotation(ROTATION_LOCKED_VERTICAL180); break;
case ID_EMULATION_CHEATS:
g_Config.bEnableCheats = !g_Config.bEnableCheats;
osm.ShowOnOff(gr->T("Cheats"), g_Config.bEnableCheats);
break;
case ID_EMULATION_CHAT:
if (GetUIState() == UISTATE_INGAME) {
NativeMessageReceived("chat screen", "");
}
break;
case ID_FILE_LOADSTATEFILE:
if (W32Util::BrowseForFileName(true, hWnd, L"Load state", 0, L"Save States (*.ppst)\0*.ppst\0All files\0*.*\0\0", L"ppst", fn)) {
SetCursor(LoadCursor(0, IDC_WAIT));
SaveState::Load(fn, -1, SaveStateActionFinished);
}
break;
case ID_FILE_SAVESTATEFILE:
if (W32Util::BrowseForFileName(false, hWnd, L"Save state", 0, L"Save States (*.ppst)\0*.ppst\0All files\0*.*\0\0", L"ppst", fn)) {
SetCursor(LoadCursor(0, IDC_WAIT));
SaveState::Save(fn, -1, SaveStateActionFinished);
}
break;
case ID_FILE_SAVESTATE_NEXT_SLOT:
{
SaveState::NextSlot();
NativeMessageReceived("savestate_displayslot", "");
break;
}
case ID_FILE_SAVESTATE_NEXT_SLOT_HC:
{
if (KeyMap::g_controllerMap[VIRTKEY_NEXT_SLOT].empty())
{
SaveState::NextSlot();
NativeMessageReceived("savestate_displayslot", "");
}
break;
}
case ID_FILE_SAVESTATE_SLOT_1: g_Config.iCurrentStateSlot = 0; break;
case ID_FILE_SAVESTATE_SLOT_2: g_Config.iCurrentStateSlot = 1; break;
case ID_FILE_SAVESTATE_SLOT_3: g_Config.iCurrentStateSlot = 2; break;
case ID_FILE_SAVESTATE_SLOT_4: g_Config.iCurrentStateSlot = 3; break;
case ID_FILE_SAVESTATE_SLOT_5: g_Config.iCurrentStateSlot = 4; break;
case ID_FILE_QUICKLOADSTATE:
{
SetCursor(LoadCursor(0, IDC_WAIT));
SaveState::LoadSlot(PSP_CoreParameter().fileToStart, g_Config.iCurrentStateSlot, SaveStateActionFinished);
break;
}
case ID_FILE_QUICKLOADSTATE_HC:
{
if (KeyMap::g_controllerMap[VIRTKEY_LOAD_STATE].empty())
{
SetCursor(LoadCursor(0, IDC_WAIT));
SaveState::LoadSlot(PSP_CoreParameter().fileToStart, g_Config.iCurrentStateSlot, SaveStateActionFinished);
}
break;
}
case ID_FILE_QUICKSAVESTATE:
{
SetCursor(LoadCursor(0, IDC_WAIT));
SaveState::SaveSlot(PSP_CoreParameter().fileToStart, g_Config.iCurrentStateSlot, SaveStateActionFinished);
break;
}
case ID_FILE_QUICKSAVESTATE_HC:
{
if (KeyMap::g_controllerMap[VIRTKEY_SAVE_STATE].empty())
{
SetCursor(LoadCursor(0, IDC_WAIT));
SaveState::SaveSlot(PSP_CoreParameter().fileToStart, g_Config.iCurrentStateSlot, SaveStateActionFinished);
break;
}
break;
}
case ID_OPTIONS_LANGUAGE:
NativeMessageReceived("language screen", "");
break;
case ID_OPTIONS_IGNOREWINKEY:
g_Config.bIgnoreWindowsKey = !g_Config.bIgnoreWindowsKey;
break;
case ID_OPTIONS_SCREENAUTO: SetInternalResolution(RESOLUTION_AUTO); break;
case ID_OPTIONS_SCREEN1X: SetInternalResolution(RESOLUTION_NATIVE); break;
case ID_OPTIONS_SCREEN2X: SetInternalResolution(RESOLUTION_2X); break;
case ID_OPTIONS_SCREEN3X: SetInternalResolution(RESOLUTION_3X); break;
case ID_OPTIONS_SCREEN4X: SetInternalResolution(RESOLUTION_4X); break;
case ID_OPTIONS_SCREEN5X: SetInternalResolution(RESOLUTION_5X); break;
case ID_OPTIONS_SCREEN6X: SetInternalResolution(RESOLUTION_6X); break;
case ID_OPTIONS_SCREEN7X: SetInternalResolution(RESOLUTION_7X); break;
case ID_OPTIONS_SCREEN8X: SetInternalResolution(RESOLUTION_8X); break;
case ID_OPTIONS_SCREEN9X: SetInternalResolution(RESOLUTION_9X); break;
case ID_OPTIONS_SCREEN10X: SetInternalResolution(RESOLUTION_MAX); break;
case ID_OPTIONS_WINDOW1X: SetWindowSize(1); break;
case ID_OPTIONS_WINDOW2X: SetWindowSize(2); break;
case ID_OPTIONS_WINDOW3X: SetWindowSize(3); break;
case ID_OPTIONS_WINDOW4X: SetWindowSize(4); break;
case ID_OPTIONS_WINDOW5X: SetWindowSize(5); break;
case ID_OPTIONS_WINDOW6X: SetWindowSize(6); break;
case ID_OPTIONS_WINDOW7X: SetWindowSize(7); break;
case ID_OPTIONS_WINDOW8X: SetWindowSize(8); break;
case ID_OPTIONS_WINDOW9X: SetWindowSize(9); break;
case ID_OPTIONS_WINDOW10X: SetWindowSize(10); break;
case ID_OPTIONS_RESOLUTIONDUMMY:
{
SetInternalResolution();
break;
}
case ID_OPTIONS_VSYNC:
g_Config.bVSync = !g_Config.bVSync;
NativeResized();
break;
case ID_OPTIONS_FRAMESKIP_AUTO:
g_Config.bAutoFrameSkip = !g_Config.bAutoFrameSkip;
if (g_Config.bAutoFrameSkip && g_Config.iRenderingMode == FB_NON_BUFFERED_MODE) {
g_Config.iRenderingMode = FB_BUFFERED_MODE;
NativeMessageReceived("gpu_resized", "");
}
break;
case ID_TEXTURESCALING_AUTO: setTexScalingMultiplier(TEXSCALING_AUTO); break;
case ID_TEXTURESCALING_OFF: setTexScalingMultiplier(TEXSCALING_OFF); break;
case ID_TEXTURESCALING_2X: setTexScalingMultiplier(TEXSCALING_2X); break;
case ID_TEXTURESCALING_3X: setTexScalingMultiplier(TEXSCALING_3X); break;
case ID_TEXTURESCALING_4X: setTexScalingMultiplier(TEXSCALING_4X); break;
case ID_TEXTURESCALING_5X: setTexScalingMultiplier(TEXSCALING_MAX); break;
case ID_TEXTURESCALING_XBRZ: setTexScalingType(TextureScalerCommon::XBRZ); break;
case ID_TEXTURESCALING_HYBRID: setTexScalingType(TextureScalerCommon::HYBRID); break;
case ID_TEXTURESCALING_BICUBIC: setTexScalingType(TextureScalerCommon::BICUBIC); break;
case ID_TEXTURESCALING_HYBRID_BICUBIC: setTexScalingType(TextureScalerCommon::HYBRID_BICUBIC); break;
case ID_TEXTURESCALING_DEPOSTERIZE:
g_Config.bTexDeposterize = !g_Config.bTexDeposterize;
NativeMessageReceived("gpu_clearCache", "");
break;
case ID_OPTIONS_DIRECT3D9:
g_Config.iGPUBackend = (int)GPUBackend::DIRECT3D9;
g_Config.Save("gpu_choice");
RestartApp();
break;
case ID_OPTIONS_DIRECT3D11:
g_Config.iGPUBackend = (int)GPUBackend::DIRECT3D11;
g_Config.Save("gpu_choice");
RestartApp();
break;
case ID_OPTIONS_OPENGL:
g_Config.iGPUBackend = (int)GPUBackend::OPENGL;
g_Config.Save("gpu_choice");
RestartApp();
break;
case ID_OPTIONS_VULKAN:
g_Config.iGPUBackend = (int)GPUBackend::VULKAN;
g_Config.Save("gpu_choice");
RestartApp();
break;
case ID_OPTIONS_NONBUFFEREDRENDERING: setRenderingMode(FB_NON_BUFFERED_MODE); break;
case ID_OPTIONS_BUFFEREDRENDERING: setRenderingMode(FB_BUFFERED_MODE); break;
case ID_DEBUG_SHOWDEBUGSTATISTICS:
g_Config.bShowDebugStats = !g_Config.bShowDebugStats;
NativeMessageReceived("clear jit", "");
break;
case ID_OPTIONS_HARDWARETRANSFORM:
g_Config.bHardwareTransform = !g_Config.bHardwareTransform;
NativeMessageReceived("gpu_resized", "");
osm.ShowOnOff(gr->T("Hardware Transform"), g_Config.bHardwareTransform);
break;
case ID_OPTIONS_DISPLAY_LAYOUT:
NativeMessageReceived("display layout editor", "");
break;
case ID_OPTIONS_FRAMESKIP_0: setFrameSkipping(FRAMESKIP_OFF); break;
case ID_OPTIONS_FRAMESKIP_1: setFrameSkipping(FRAMESKIP_1); break;
case ID_OPTIONS_FRAMESKIP_2: setFrameSkipping(FRAMESKIP_2); break;
case ID_OPTIONS_FRAMESKIP_3: setFrameSkipping(FRAMESKIP_3); break;
case ID_OPTIONS_FRAMESKIP_4: setFrameSkipping(FRAMESKIP_4); break;
case ID_OPTIONS_FRAMESKIP_5: setFrameSkipping(FRAMESKIP_5); break;
case ID_OPTIONS_FRAMESKIP_6: setFrameSkipping(FRAMESKIP_6); break;
case ID_OPTIONS_FRAMESKIP_7: setFrameSkipping(FRAMESKIP_7); break;
case ID_OPTIONS_FRAMESKIP_8: setFrameSkipping(FRAMESKIP_MAX); break;
case ID_OPTIONS_FRAMESKIPTYPE_COUNT: setFrameSkippingType(FRAMESKIPTYPE_COUNT); break;
case ID_OPTIONS_FRAMESKIPTYPE_PRCNT: setFrameSkippingType(FRAMESKIPTYPE_PRCNT); break;
case ID_OPTIONS_FRAMESKIPDUMMY:
setFrameSkipping();
setFrameSkippingType();
break;
case ID_FILE_EXIT:
PostMessage(hWnd, WM_CLOSE, 0, 0);
break;
case ID_DEBUG_BREAKONLOAD:
g_Config.bAutoRun = !g_Config.bAutoRun;
break;
case ID_DEBUG_DUMPNEXTFRAME:
NativeMessageReceived("gpu dump next frame", "");
break;
case ID_DEBUG_LOADMAPFILE:
if (W32Util::BrowseForFileName(true, hWnd, L"Load .ppmap", 0, L"Maps\0*.ppmap\0All files\0*.*\0\0", L"ppmap", fn)) {
g_symbolMap->LoadSymbolMap(fn.c_str());
if (disasmWindow)
disasmWindow->NotifyMapLoaded();
if (memoryWindow)
memoryWindow->NotifyMapLoaded();
}
break;
case ID_DEBUG_SAVEMAPFILE:
if (W32Util::BrowseForFileName(false, hWnd, L"Save .ppmap", 0, L"Maps\0*.ppmap\0All files\0*.*\0\0", L"ppmap", fn))
g_symbolMap->SaveSymbolMap(fn.c_str());
break;
case ID_DEBUG_LOADSYMFILE:
if (W32Util::BrowseForFileName(true, hWnd, L"Load .sym", 0, L"Symbols\0*.sym\0All files\0*.*\0\0", L"sym", fn)) {
g_symbolMap->LoadNocashSym(fn.c_str());
if (disasmWindow)
disasmWindow->NotifyMapLoaded();
if (memoryWindow)
memoryWindow->NotifyMapLoaded();
}
break;
case ID_DEBUG_SAVESYMFILE:
if (W32Util::BrowseForFileName(false, hWnd, L"Save .sym", 0, L"Symbols\0*.sym\0All files\0*.*\0\0", L"sym", fn))
g_symbolMap->SaveNocashSym(fn.c_str());
break;
case ID_DEBUG_RESETSYMBOLTABLE:
g_symbolMap->Clear();
if (disasmWindow)
disasmWindow->NotifyMapLoaded();
if (memoryWindow)
memoryWindow->NotifyMapLoaded();
break;
case ID_DEBUG_DISASSEMBLY:
if (disasmWindow)
disasmWindow->Show(true);
break;
case ID_DEBUG_GEDEBUGGER:
#if PPSSPP_API(ANY_GL)
if (geDebuggerWindow)
geDebuggerWindow->Show(true);
#endif
break;
case ID_DEBUG_MEMORYVIEW:
if (memoryWindow)
memoryWindow->Show(true);
break;
case ID_DEBUG_EXTRACTFILE:
{
std::string filename;
if (!InputBox_GetString(hInst, hWnd, L"Disc filename", filename, filename)) {
break;
}
const char *lastSlash = strrchr(filename.c_str(), '/');
if (lastSlash) {
fn = lastSlash + 1;
} else {
fn = "";
}
PSPFileInfo info = pspFileSystem.GetFileInfo(filename);
if (!info.exists) {
MessageBox(hWnd, L"File does not exist.", L"Sorry", 0);
} else if (info.type == FILETYPE_DIRECTORY) {
MessageBox(hWnd, L"Cannot extract directories.", L"Sorry", 0);
} else if (W32Util::BrowseForFileName(false, hWnd, L"Save file as...", 0, L"All files\0*.*\0\0", L"", fn)) {
u32 handle = pspFileSystem.OpenFile(filename, FILEACCESS_READ, "");
// Note: len may be in blocks.
size_t len = pspFileSystem.SeekFile(handle, 0, FILEMOVE_END);
bool isBlockMode = pspFileSystem.DevType(handle) & PSPDevType::BLOCK;
FILE *fp = File::OpenCFile(fn, "wb");
pspFileSystem.SeekFile(handle, 0, FILEMOVE_BEGIN);
u8 buffer[4096];
size_t bufferSize = isBlockMode ? sizeof(buffer) / 2048 : sizeof(buffer);
while (len > 0) {
// This is all in blocks, not bytes, if isBlockMode.
size_t remain = std::min(len, bufferSize);
size_t readSize = pspFileSystem.ReadFile(handle, buffer, remain);
if (readSize == 0)
break;
size_t bytes = isBlockMode ? readSize * 2048 : readSize;
fwrite(buffer, 1, bytes, fp);
len -= readSize;
}
pspFileSystem.CloseFile(handle);
fclose(fp);
}
}
break;
case ID_DEBUG_LOG:
LogManager::GetInstance()->GetConsoleListener()->Show(LogManager::GetInstance()->GetConsoleListener()->Hidden());
break;
case ID_DEBUG_IGNOREILLEGALREADS:
g_Config.bIgnoreBadMemAccess = !g_Config.bIgnoreBadMemAccess;
break;
case ID_OPTIONS_FULLSCREEN:
SendToggleFullscreen(!g_Config.bFullScreen);
break;
case ID_OPTIONS_VERTEXCACHE:
g_Config.bVertexCache = !g_Config.bVertexCache;
break;
case ID_OPTIONS_SHOWFPS:
g_Config.iShowFPSCounter = g_Config.iShowFPSCounter ? 0 : 3; // 3 = both speed and FPS
break;
case ID_OPTIONS_TEXTUREFILTERING_AUTO: setTexFiltering(TEX_FILTER_AUTO); break;
case ID_OPTIONS_NEARESTFILTERING: setTexFiltering(TEX_FILTER_FORCE_NEAREST); break;
case ID_OPTIONS_LINEARFILTERING: setTexFiltering(TEX_FILTER_FORCE_LINEAR); break;
case ID_OPTIONS_BUFLINEARFILTER: setBufFilter(SCALE_LINEAR); break;
case ID_OPTIONS_BUFNEARESTFILTER: setBufFilter(SCALE_NEAREST); break;
case ID_OPTIONS_TOPMOST:
g_Config.bTopMost = !g_Config.bTopMost;
W32Util::MakeTopMost(hWnd, g_Config.bTopMost);
break;
case ID_OPTIONS_PAUSE_FOCUS:
g_Config.bPauseOnLostFocus = !g_Config.bPauseOnLostFocus;
break;
case ID_OPTIONS_CONTROLS:
NativeMessageReceived("control mapping", "");
break;
case ID_OPTIONS_MORE_SETTINGS:
NativeMessageReceived("settings", "");
break;
case ID_EMULATION_SOUND:
g_Config.bEnableSound = !g_Config.bEnableSound;
if (g_Config.bEnableSound) {
if (PSP_IsInited() && !IsAudioInitialised())
Audio_Init();
}
break;
case ID_HELP_OPENWEBSITE:
ShellExecute(NULL, L"open", L"https://www.ppsspp.org/", NULL, NULL, SW_SHOWNORMAL);
break;
case ID_HELP_BUYGOLD:
ShellExecute(NULL, L"open", L"https://central.ppsspp.org/buygold", NULL, NULL, SW_SHOWNORMAL);
break;
case ID_HELP_OPENFORUM:
ShellExecute(NULL, L"open", L"https://forums.ppsspp.org/", NULL, NULL, SW_SHOWNORMAL);
break;
case ID_HELP_GITHUB:
ShellExecute(NULL, L"open", L"https://github.com/hrydgard/ppsspp/", NULL, NULL, SW_SHOWNORMAL);
break;
case ID_HELP_DISCORD:
ShellExecute(NULL, L"open", L"https://discord.gg/5NJB6dD", NULL, NULL, SW_SHOWNORMAL);
break;
case ID_HELP_ABOUT:
DialogManager::EnableAll(FALSE);
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
DialogManager::EnableAll(TRUE);
break;
case ID_DEBUG_TAKESCREENSHOT:
g_TakeScreenshot = true;
break;
case ID_FILE_DUMPFRAMES:
g_Config.bDumpFrames = !g_Config.bDumpFrames;
break;
case ID_FILE_USEFFV1:
g_Config.bUseFFV1 = !g_Config.bUseFFV1;
break;
case ID_FILE_DUMP_VIDEO_OUTPUT:
g_Config.bDumpVideoOutput = !g_Config.bDumpVideoOutput;
break;
case ID_FILE_DUMPAUDIO:
g_Config.bDumpAudio = !g_Config.bDumpAudio;
break;
default:
{
// Handle the dynamic shader switching here.
// The Menu ID is contained in wParam, so subtract
// ID_SHADERS_BASE and an additional 1 off it.
u32 index = (wParam - ID_SHADERS_BASE - 1);
if (index < availableShaders.size()) {
g_Config.vPostShaderNames.clear();
if (availableShaders[index] != "Off")
g_Config.vPostShaderNames.push_back(availableShaders[index]);
g_Config.vPostShaderNames.push_back("Off");
g_ShaderNameListChanged = true;
g_Config.bShaderChainRequires60FPS = PostShaderChainRequires60FPS(GetFullPostShadersChain(g_Config.vPostShaderNames));
NativeMessageReceived("gpu_resized", "");
break;
}
MessageBox(hWnd, L"Unimplemented", L"Sorry", 0);
}
break;
}
}
void UpdateMenus(bool isMenuSelect) {
if (isMenuSelect) {
menuShaderInfoLoaded = false;
}
HMENU menu = GetMenu(GetHWND());
#define CHECKITEM(item,value) CheckMenuItem(menu,item,MF_BYCOMMAND | ((value) ? MF_CHECKED : MF_UNCHECKED));
CHECKITEM(ID_DEBUG_IGNOREILLEGALREADS, g_Config.bIgnoreBadMemAccess);
CHECKITEM(ID_DEBUG_SHOWDEBUGSTATISTICS, g_Config.bShowDebugStats);
CHECKITEM(ID_OPTIONS_HARDWARETRANSFORM, g_Config.bHardwareTransform);
CHECKITEM(ID_DEBUG_BREAKONLOAD, !g_Config.bAutoRun);
CHECKITEM(ID_OPTIONS_VERTEXCACHE, g_Config.bVertexCache);
CHECKITEM(ID_OPTIONS_SHOWFPS, g_Config.iShowFPSCounter);
CHECKITEM(ID_OPTIONS_FRAMESKIP_AUTO, g_Config.bAutoFrameSkip);
CHECKITEM(ID_OPTIONS_FRAMESKIP, g_Config.iFrameSkip != FRAMESKIP_OFF);
CHECKITEM(ID_OPTIONS_FRAMESKIPTYPE_COUNT, g_Config.iFrameSkipType == FRAMESKIPTYPE_COUNT);
CHECKITEM(ID_OPTIONS_FRAMESKIPTYPE_PRCNT, g_Config.iFrameSkipType == FRAMESKIPTYPE_PRCNT);
CHECKITEM(ID_OPTIONS_VSYNC, g_Config.bVSync);
CHECKITEM(ID_OPTIONS_TOPMOST, g_Config.bTopMost);
CHECKITEM(ID_OPTIONS_PAUSE_FOCUS, g_Config.bPauseOnLostFocus);
CHECKITEM(ID_EMULATION_SOUND, g_Config.bEnableSound);
CHECKITEM(ID_TEXTURESCALING_DEPOSTERIZE, g_Config.bTexDeposterize);
CHECKITEM(ID_EMULATION_CHEATS, g_Config.bEnableCheats);
CHECKITEM(ID_OPTIONS_IGNOREWINKEY, g_Config.bIgnoreWindowsKey);
CHECKITEM(ID_FILE_DUMPFRAMES, g_Config.bDumpFrames);
CHECKITEM(ID_FILE_USEFFV1, g_Config.bUseFFV1);
CHECKITEM(ID_FILE_DUMP_VIDEO_OUTPUT, g_Config.bDumpVideoOutput);
CHECKITEM(ID_FILE_DUMPAUDIO, g_Config.bDumpAudio);
static const int displayrotationitems[] = {
ID_EMULATION_ROTATION_H,
ID_EMULATION_ROTATION_V,
ID_EMULATION_ROTATION_H_R,
ID_EMULATION_ROTATION_V_R
};
if (g_Config.iInternalScreenRotation < ROTATION_LOCKED_HORIZONTAL)
g_Config.iInternalScreenRotation = ROTATION_LOCKED_HORIZONTAL;
else if (g_Config.iInternalScreenRotation > ROTATION_LOCKED_VERTICAL180)
g_Config.iInternalScreenRotation = ROTATION_LOCKED_VERTICAL180;
for (int i = 0; i < ARRAY_SIZE(displayrotationitems); i++) {
CheckMenuItem(menu, displayrotationitems[i], MF_BYCOMMAND | ((i + 1) == g_Config.iInternalScreenRotation ? MF_CHECKED : MF_UNCHECKED));
}
// Disable Vertex Cache when HW T&L is disabled.
if (!g_Config.bHardwareTransform) {
EnableMenuItem(menu, ID_OPTIONS_VERTEXCACHE, MF_GRAYED);
} else {
EnableMenuItem(menu, ID_OPTIONS_VERTEXCACHE, MF_ENABLED);
}
static const int zoomitems[11] = {
ID_OPTIONS_SCREENAUTO,
ID_OPTIONS_SCREEN1X,
ID_OPTIONS_SCREEN2X,
ID_OPTIONS_SCREEN3X,
ID_OPTIONS_SCREEN4X,
ID_OPTIONS_SCREEN5X,
ID_OPTIONS_SCREEN6X,
ID_OPTIONS_SCREEN7X,
ID_OPTIONS_SCREEN8X,
ID_OPTIONS_SCREEN9X,
ID_OPTIONS_SCREEN10X,
};
if (g_Config.iInternalResolution < RESOLUTION_AUTO)
g_Config.iInternalResolution = RESOLUTION_AUTO;
else if (g_Config.iInternalResolution > RESOLUTION_MAX)
g_Config.iInternalResolution = RESOLUTION_MAX;
for (int i = 0; i < ARRAY_SIZE(zoomitems); i++) {
CheckMenuItem(menu, zoomitems[i], MF_BYCOMMAND | ((i == g_Config.iInternalResolution) ? MF_CHECKED : MF_UNCHECKED));
}
static const int windowSizeItems[10] = {
ID_OPTIONS_WINDOW1X,
ID_OPTIONS_WINDOW2X,
ID_OPTIONS_WINDOW3X,
ID_OPTIONS_WINDOW4X,
ID_OPTIONS_WINDOW5X,
ID_OPTIONS_WINDOW6X,
ID_OPTIONS_WINDOW7X,
ID_OPTIONS_WINDOW8X,
ID_OPTIONS_WINDOW9X,
ID_OPTIONS_WINDOW10X,
};
RECT rc;
GetClientRect(GetHWND(), &rc);
int checkW = g_Config.IsPortrait() ? 272 : 480;
int checkH = g_Config.IsPortrait() ? 480 : 272;
for (int i = 0; i < ARRAY_SIZE(windowSizeItems); i++) {
bool check = (i + 1) * checkW == rc.right - rc.left || (i + 1) * checkH == rc.bottom - rc.top;
CheckMenuItem(menu, windowSizeItems[i], MF_BYCOMMAND | (check ? MF_CHECKED : MF_UNCHECKED));
}
static const int texscalingitems[] = {
ID_TEXTURESCALING_AUTO,
ID_TEXTURESCALING_OFF,
ID_TEXTURESCALING_2X,
ID_TEXTURESCALING_3X,
ID_TEXTURESCALING_4X,
ID_TEXTURESCALING_5X,
};
if (g_Config.iTexScalingLevel < TEXSCALING_AUTO)
g_Config.iTexScalingLevel = TEXSCALING_AUTO;
else if (g_Config.iTexScalingLevel > TEXSCALING_MAX)
g_Config.iTexScalingLevel = TEXSCALING_MAX;
for (int i = 0; i < ARRAY_SIZE(texscalingitems); i++) {
CheckMenuItem(menu, texscalingitems[i], MF_BYCOMMAND | ((i == g_Config.iTexScalingLevel) ? MF_CHECKED : MF_UNCHECKED));
}
if (g_Config.iGPUBackend == (int)GPUBackend::OPENGL && !gl_extensions.OES_texture_npot) {
EnableMenuItem(menu, ID_TEXTURESCALING_3X, MF_GRAYED);
EnableMenuItem(menu, ID_TEXTURESCALING_5X, MF_GRAYED);
} else {
EnableMenuItem(menu, ID_TEXTURESCALING_3X, MF_ENABLED);
EnableMenuItem(menu, ID_TEXTURESCALING_5X, MF_ENABLED);
}
static const int texscalingtypeitems[] = {
ID_TEXTURESCALING_XBRZ,
ID_TEXTURESCALING_HYBRID,
ID_TEXTURESCALING_BICUBIC,
ID_TEXTURESCALING_HYBRID_BICUBIC,
};
if (g_Config.iTexScalingType < TextureScalerCommon::XBRZ)
g_Config.iTexScalingType = TextureScalerCommon::XBRZ;
else if (g_Config.iTexScalingType > TextureScalerCommon::HYBRID_BICUBIC)
g_Config.iTexScalingType = TextureScalerCommon::HYBRID_BICUBIC;
for (int i = 0; i < ARRAY_SIZE(texscalingtypeitems); i++) {
CheckMenuItem(menu, texscalingtypeitems[i], MF_BYCOMMAND | ((i == g_Config.iTexScalingType) ? MF_CHECKED : MF_UNCHECKED));
}
static const int texfilteringitems[] = {
ID_OPTIONS_TEXTUREFILTERING_AUTO,
ID_OPTIONS_NEARESTFILTERING,
ID_OPTIONS_LINEARFILTERING,
};
if (g_Config.iTexFiltering < TEX_FILTER_AUTO)
g_Config.iTexFiltering = TEX_FILTER_AUTO;
else if (g_Config.iTexFiltering > TEX_FILTER_FORCE_LINEAR)
g_Config.iTexFiltering = TEX_FILTER_FORCE_LINEAR;
for (int i = 0; i < ARRAY_SIZE(texfilteringitems); i++) {
CheckMenuItem(menu, texfilteringitems[i], MF_BYCOMMAND | ((i + 1) == g_Config.iTexFiltering ? MF_CHECKED : MF_UNCHECKED));
}
static const int bufferfilteritems[] = {
ID_OPTIONS_BUFLINEARFILTER,
ID_OPTIONS_BUFNEARESTFILTER,
};
if (g_Config.iBufFilter < SCALE_LINEAR)
g_Config.iBufFilter = SCALE_LINEAR;
else if (g_Config.iBufFilter > SCALE_NEAREST)
g_Config.iBufFilter = SCALE_NEAREST;
for (int i = 0; i < ARRAY_SIZE(bufferfilteritems); i++) {
CheckMenuItem(menu, bufferfilteritems[i], MF_BYCOMMAND | ((i + 1) == g_Config.iBufFilter ? MF_CHECKED : MF_UNCHECKED));
}
static const int renderingmode[] = {
ID_OPTIONS_NONBUFFEREDRENDERING,
ID_OPTIONS_BUFFEREDRENDERING,
};
for (int i = 0; i < ARRAY_SIZE(renderingmode); i++) {
CheckMenuItem(menu, renderingmode[i], MF_BYCOMMAND | ((i == g_Config.iRenderingMode) ? MF_CHECKED : MF_UNCHECKED));
}
static const int frameskipping[] = {
ID_OPTIONS_FRAMESKIP_0,
ID_OPTIONS_FRAMESKIP_1,
ID_OPTIONS_FRAMESKIP_2,
ID_OPTIONS_FRAMESKIP_3,
ID_OPTIONS_FRAMESKIP_4,
ID_OPTIONS_FRAMESKIP_5,
ID_OPTIONS_FRAMESKIP_6,
ID_OPTIONS_FRAMESKIP_7,
ID_OPTIONS_FRAMESKIP_8,
};
static const int frameskippingType[] = {
ID_OPTIONS_FRAMESKIPTYPE_COUNT,
ID_OPTIONS_FRAMESKIPTYPE_PRCNT,
};
if (g_Config.iFrameSkip < FRAMESKIP_OFF)
g_Config.iFrameSkip = FRAMESKIP_OFF;
else if (g_Config.iFrameSkip > FRAMESKIP_MAX)
g_Config.iFrameSkip = FRAMESKIP_MAX;
for (int i = 0; i < ARRAY_SIZE(frameskipping); i++) {
CheckMenuItem(menu, frameskipping[i], MF_BYCOMMAND | ((i == g_Config.iFrameSkip) ? MF_CHECKED : MF_UNCHECKED));
}
for (int i = 0; i < ARRAY_SIZE(frameskippingType); i++) {
CheckMenuItem(menu, frameskippingType[i], MF_BYCOMMAND | ((i == g_Config.iFrameSkipType) ? MF_CHECKED : MF_UNCHECKED));
}
static const int savestateSlot[] = {
ID_FILE_SAVESTATE_SLOT_1,
ID_FILE_SAVESTATE_SLOT_2,
ID_FILE_SAVESTATE_SLOT_3,
ID_FILE_SAVESTATE_SLOT_4,
ID_FILE_SAVESTATE_SLOT_5,
};
if (g_Config.iCurrentStateSlot < 0)
g_Config.iCurrentStateSlot = 0;
else if (g_Config.iCurrentStateSlot >= SaveState::NUM_SLOTS)
g_Config.iCurrentStateSlot = SaveState::NUM_SLOTS - 1;
for (int i = 0; i < ARRAY_SIZE(savestateSlot); i++) {
CheckMenuItem(menu, savestateSlot[i], MF_BYCOMMAND | ((i == g_Config.iCurrentStateSlot) ? MF_CHECKED : MF_UNCHECKED));
}
bool allowD3D9 = g_Config.IsBackendEnabled(GPUBackend::DIRECT3D9);
bool allowD3D11 = g_Config.IsBackendEnabled(GPUBackend::DIRECT3D11);
bool allowOpenGL = g_Config.IsBackendEnabled(GPUBackend::OPENGL);
bool allowVulkan = g_Config.IsBackendEnabled(GPUBackend::VULKAN);
switch (GetGPUBackend()) {
case GPUBackend::DIRECT3D9:
EnableMenuItem(menu, ID_OPTIONS_DIRECT3D9, MF_GRAYED);
EnableMenuItem(menu, ID_OPTIONS_DIRECT3D11, allowD3D11 ? MF_ENABLED : MF_GRAYED);
EnableMenuItem(menu, ID_OPTIONS_OPENGL, allowOpenGL ? MF_ENABLED : MF_GRAYED);
EnableMenuItem(menu, ID_OPTIONS_VULKAN, allowVulkan ? MF_ENABLED : MF_GRAYED);
CheckMenuItem(menu, ID_OPTIONS_DIRECT3D9, MF_CHECKED);
CheckMenuItem(menu, ID_OPTIONS_DIRECT3D11, MF_UNCHECKED);
CheckMenuItem(menu, ID_OPTIONS_OPENGL, MF_UNCHECKED);
CheckMenuItem(menu, ID_OPTIONS_VULKAN, MF_UNCHECKED);
break;
case GPUBackend::OPENGL:
EnableMenuItem(menu, ID_OPTIONS_DIRECT3D9, allowD3D9 ? MF_ENABLED : MF_GRAYED);
EnableMenuItem(menu, ID_OPTIONS_DIRECT3D11, allowD3D11 ? MF_ENABLED : MF_GRAYED);
EnableMenuItem(menu, ID_OPTIONS_OPENGL, MF_GRAYED);
EnableMenuItem(menu, ID_OPTIONS_VULKAN, allowVulkan ? MF_ENABLED : MF_GRAYED);
CheckMenuItem(menu, ID_OPTIONS_DIRECT3D9, MF_UNCHECKED);
CheckMenuItem(menu, ID_OPTIONS_DIRECT3D11, MF_UNCHECKED);
CheckMenuItem(menu, ID_OPTIONS_OPENGL, MF_CHECKED);
CheckMenuItem(menu, ID_OPTIONS_VULKAN, MF_UNCHECKED);
break;
case GPUBackend::VULKAN:
EnableMenuItem(menu, ID_OPTIONS_DIRECT3D9, allowD3D9 ? MF_ENABLED : MF_GRAYED);
EnableMenuItem(menu, ID_OPTIONS_DIRECT3D11, allowD3D11 ? MF_ENABLED : MF_GRAYED);
EnableMenuItem(menu, ID_OPTIONS_OPENGL, allowOpenGL ? MF_ENABLED : MF_GRAYED);
EnableMenuItem(menu, ID_OPTIONS_VULKAN, MF_GRAYED);
CheckMenuItem(menu, ID_OPTIONS_DIRECT3D9, MF_UNCHECKED);
CheckMenuItem(menu, ID_OPTIONS_DIRECT3D11, MF_UNCHECKED);
CheckMenuItem(menu, ID_OPTIONS_OPENGL, MF_UNCHECKED);
CheckMenuItem(menu, ID_OPTIONS_VULKAN, MF_CHECKED);
break;
case GPUBackend::DIRECT3D11:
EnableMenuItem(menu, ID_OPTIONS_DIRECT3D9, allowD3D9 ? MF_ENABLED : MF_GRAYED);
EnableMenuItem(menu, ID_OPTIONS_DIRECT3D11, MF_GRAYED);
EnableMenuItem(menu, ID_OPTIONS_OPENGL, allowOpenGL ? MF_ENABLED : MF_GRAYED);
EnableMenuItem(menu, ID_OPTIONS_VULKAN, allowVulkan ? MF_ENABLED : MF_GRAYED);
CheckMenuItem(menu, ID_OPTIONS_DIRECT3D9, MF_UNCHECKED);
CheckMenuItem(menu, ID_OPTIONS_DIRECT3D11, MF_CHECKED);
CheckMenuItem(menu, ID_OPTIONS_OPENGL, MF_UNCHECKED);
CheckMenuItem(menu, ID_OPTIONS_VULKAN, MF_UNCHECKED);
break;
}
#if !PPSSPP_API(ANY_GL)
EnableMenuItem(menu, ID_DEBUG_GEDEBUGGER, MF_GRAYED);
#endif
UpdateDynamicMenuCheckmarks(menu);
UpdateCommands();
}
void UpdateCommands() {
static GlobalUIState lastGlobalUIState = UISTATE_PAUSEMENU;
static CoreState lastCoreState = CORE_BOOT_ERROR;
HMENU menu = GetMenu(GetHWND());
EnableMenuItem(menu, ID_DEBUG_LOG, !g_Config.bEnableLogging);
SetIngameMenuItemStates(menu, GetUIState());
if (lastGlobalUIState == GetUIState() && lastCoreState == coreState)
return;
lastCoreState = coreState;
lastGlobalUIState = GetUIState();
bool isPaused = Core_IsStepping() && GetUIState() == UISTATE_INGAME;
TranslateMenuItem(menu, ID_TOGGLE_BREAK, L"\tF8", isPaused ? "Run" : "Break");
}
// Message handler for about box.
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_INITDIALOG:
{
W32Util::CenterWindow(hDlg);
HWND versionBox = GetDlgItem(hDlg, IDC_VERSION);
std::string windowText = System_GetPropertyBool(SYSPROP_APP_GOLD) ? "PPSSPP Gold " : "PPSSPP ";
windowText.append(PPSSPP_GIT_VERSION);
SetWindowText(versionBox, ConvertUTF8ToWString(windowText).c_str());
}
return TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
EndDialog(hDlg, LOWORD(wParam));
return TRUE;
}
break;
}
return FALSE;
}
}