#include #include #include "CommonWindows.h" #include #include "resource.h" #include "i18n/i18n.h" #include "util/text/utf8.h" #include "base/NativeApp.h" #include "gfx_es2/gpu_features.h" #include "Common/Log.h" #include "Common/LogManager.h" #include "Common/ConsoleListener.h" #include "Common/OSVersion.h" #include "Common/Vulkan/VulkanLoader.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/FramebufferCommon.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 "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; namespace MainWindow { extern HINSTANCE hInst; static const int numCPUs = 1; // what? extern bool noFocusPause; static W32Util::AsyncBrowseDialog *browseDialog; static W32Util::AsyncBrowseDialog *browseImageDialog; static bool browsePauseAfter; static std::unordered_map initialMenuKeys; static std::vector availableShaders; static std::string menuLanguageID = ""; static int menuKeymapGeneration = -1; static bool menuShaderInfoLoaded = false; std::vector 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) { I18NCategory *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.sPostShaderName == availableShaders[i]) ? 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 info = GetAllPostShaderInfo(); if (menuShaderInfo.size() == info.size() && std::equal(info.begin(), info.end(), menuShaderInfo.begin())) { return false; } I18NCategory *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(); if (GetGPUBackend() == GPUBackend::DIRECT3D9) { translatedShaderName = ps->T("Not available in Direct3D9 backend"); AppendMenu(shaderMenu, MF_STRING | MF_BYPOSITION | MF_GRAYED, item++, ConvertUTF8ToWString(translatedShaderName).c_str()); } else { for (auto i = info.begin(); i != info.end(); ++i) { int checkedStatus = MF_UNCHECKED; availableShaders.push_back(i->section); if (g_Config.sPostShaderName == i->section) { 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) { I18NCategory *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_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_LINEARFILTERING_CG); 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"); // 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 *.png)|*.jpg;*.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;*.png;"); } void BrowseBackgroundDone() { std::string filename; if (browseImageDialog->GetResult(filename)) { std::wstring src = ConvertUTF8ToWString(filename); std::wstring dest; if (filename.size() >= 4 && filename.substr(filename.size() - 4) == ".jpg") { 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) { I18NCategory *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; } I18NCategory *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; } I18NCategory *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; I18NCategory *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_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[0]) SendMessage(disasmWindow[0]->GetDlgHandle(), WM_COMMAND, IDC_STOPGO, 0); } else if (Core_IsStepping()) { // It is paused, then continue to run. if (disasmWindow[0]) SendMessage(disasmWindow[0]->GetDlgHandle(), WM_COMMAND, IDC_STOPGO, 0); else Core_EnableStepping(false); } else { if (disasmWindow[0]) SendMessage(disasmWindow[0]->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_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, 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, 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; 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; 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; 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[0]) disasmWindow[0]->NotifyMapLoaded(); if (memoryWindow[0]) memoryWindow[0]->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[0]) disasmWindow[0]->NotifyMapLoaded(); if (memoryWindow[0]) memoryWindow[0]->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(); for (int i = 0; i < numCPUs; i++) if (disasmWindow[i]) disasmWindow[i]->NotifyMapLoaded(); for (int i = 0; i < numCPUs; i++) if (memoryWindow[i]) memoryWindow[i]->NotifyMapLoaded(); break; case ID_DEBUG_DISASSEMBLY: if (disasmWindow[0]) disasmWindow[0]->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[0]) memoryWindow[0]->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) == PSP_DEV_TYPE_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_NEAREST); break; case ID_OPTIONS_LINEARFILTERING: setTexFiltering(TEX_FILTER_LINEAR); break; case ID_OPTIONS_LINEARFILTERING_CG: setTexFiltering(TEX_FILTER_LINEAR_VIDEO); 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.sPostShaderName = availableShaders[index]; 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, ID_OPTIONS_LINEARFILTERING_CG, }; if (g_Config.iTexFiltering < TEX_FILTER_AUTO) g_Config.iTexFiltering = TEX_FILTER_AUTO; else if (g_Config.iTexFiltering > TEX_FILTER_LINEAR_VIDEO) g_Config.iTexFiltering = TEX_FILTER_LINEAR_VIDEO; 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_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; } }