ppsspp/Windows/WndMainWindow.cpp
Henrik Rydgard 2430c283a5 More GPU cleaning, removing uses of GPUState.h where not needed.
Want to get rid of direct accesses to GPUState in modules that may be reused in
my future next-gen backends, that will reformat display lists into command lists that will
then be optimized and executed, out of sync with the real GPUState.

Candidate modules that may be reused in full are Framebuffer and Depal, possibly TextureCache to some degree.
2015-07-29 12:37:49 +02:00

2069 lines
64 KiB
C++

// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
// NOTE: Apologies for the quality of this code, this is really from pre-opensource Dolphin - that is, 2003.
// It's improving slowly, though. :)
#include "Common/CommonWindows.h"
#include "Common/KeyMap.h"
#include <map>
#include <string>
#include "base/NativeApp.h"
#include "Globals.h"
#include "shellapi.h"
#include "commctrl.h"
#include "i18n/i18n.h"
#include "input/input_state.h"
#include "input/keycodes.h"
#include "thread/threadutil.h"
#include "util/text/utf8.h"
#include "Core/Debugger/SymbolMap.h"
#include "Windows/InputBox.h"
#include "Windows/OpenGLBase.h"
#include "Windows/Debugger/Debugger_Disasm.h"
#include "Windows/Debugger/Debugger_MemoryDlg.h"
#include "Windows/GEDebugger/GEDebugger.h"
#include "main.h"
#include "Core/Core.h"
#include "Core/MemMap.h"
#include "Core/SaveState.h"
#include "Core/System.h"
#include "Core/Config.h"
#include "Core/MIPS/JitCommon/NativeJit.h"
#include "Core/MIPS/JitCommon/JitBlockCache.h"
#include "Core/FileSystems/MetaFileSystem.h"
#include "Windows/EmuThread.h"
#include "resource.h"
#include "Windows/WndMainWindow.h"
#include "Windows/WindowsHost.h"
#include "Common/LogManager.h"
#include "Common/ConsoleListener.h"
#include "Windows/W32Util/DialogManager.h"
#include "Windows/W32Util/ShellUtil.h"
#include "Windows/W32Util/Misc.h"
#include "Windows/RawInput.h"
#include "Windows/TouchInputHandler.h"
#include "GPU/GPUInterface.h"
#include "gfx_es2/gpu_features.h"
#include "GPU/GLES/TextureScaler.h"
#include "GPU/GLES/TextureCache.h"
#include "GPU/GLES/Framebuffer.h"
#include "UI/OnScreenDisplay.h"
#include "GPU/Common/PostShader.h"
#include "Core/HLE/sceUmd.h"
#ifdef THEMES
#include "XPTheme.h"
#endif
#define MOUSEEVENTF_FROMTOUCH_NOPEN 0xFF515780 //http://msdn.microsoft.com/en-us/library/windows/desktop/ms703320(v=vs.85).aspx
#define MOUSEEVENTF_MASK_PLUS_PENTOUCH 0xFFFFFF80
static const int numCPUs = 1;
int verysleepy__useSendMessage = 1;
const UINT WM_VERYSLEEPY_MSG = WM_APP + 0x3117;
// Respond TRUE to a message with this param value to indicate support.
const WPARAM VERYSLEEPY_WPARAM_SUPPORTED = 0;
// Respond TRUE to a message wit this param value after filling in the addr name.
const WPARAM VERYSLEEPY_WPARAM_GETADDRINFO = 1;
struct VerySleepy_AddrInfo
{
// Always zero for now.
int flags;
// This is the pointer (always passed as 64 bits.)
unsigned long long addr;
// Write the name here.
wchar_t name[256];
};
extern std::map<int, int> windowsTransTable;
static RECT g_normalRC = {0};
static std::wstring windowTitle;
extern bool g_TakeScreenshot;
extern InputState input_state;
#define TIMER_CURSORUPDATE 1
#define TIMER_CURSORMOVEUPDATE 2
#define CURSORUPDATE_INTERVAL_MS 1000
#define CURSORUPDATE_MOVE_TIMESPAN_MS 500
namespace MainWindow
{
HWND hwndMain;
HWND hwndDisplay;
HWND hwndGameList;
TouchInputHandler touchHandler;
static HMENU menu;
static HINSTANCE hInst;
static int cursorCounter = 0;
static int prevCursorX = -1;
static int prevCursorY = -1;
static bool mouseButtonDown = false;
static bool hideCursor = false;
static std::map<int, std::string> initialMenuKeys;
static std::vector<std::string> countryCodes;
static std::vector<std::string> availableShaders;
static W32Util::AsyncBrowseDialog *browseDialog;
static bool browsePauseAfter;
static bool g_inModeSwitch; // when true, don't react to WM_SIZE
static int g_WindowState;
#define MAX_LOADSTRING 100
const TCHAR *szTitle = TEXT("PPSSPP");
const TCHAR *szWindowClass = TEXT("PPSSPPWnd");
const TCHAR *szDisplayClass = TEXT("PPSSPPDisplay");
// Forward declarations of functions included in this code module:
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK DisplayProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
HWND GetHWND() {
return hwndMain;
}
HWND GetDisplayHWND() {
return hwndDisplay;
}
void Init(HINSTANCE hInstance) {
#ifdef THEMES
WTL::CTheme::IsThemingSupported();
#endif
//Register classes
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_PARENTDC;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_PPSSPP);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wcex.lpszMenuName = (LPCWSTR)IDR_MENU1;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = (HICON)LoadImage(hInstance, (LPCTSTR)IDI_PPSSPP, IMAGE_ICON, 16, 16, LR_SHARED);
RegisterClassEx(&wcex);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)DisplayProc;
wcex.hIcon = 0;
wcex.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wcex.lpszMenuName = 0;
wcex.lpszClassName = szDisplayClass;
wcex.hIconSm = 0;
RegisterClassEx(&wcex);
}
void SavePosition() {
if (g_Config.bFullScreen)
return;
WINDOWPLACEMENT placement;
GetWindowPlacement(hwndMain, &placement);
if (placement.showCmd == SW_SHOWNORMAL) {
RECT rc;
GetWindowRect(hwndMain, &rc);
g_Config.iWindowX = rc.left;
g_Config.iWindowY = rc.top;
g_Config.iWindowWidth = rc.right - rc.left;
g_Config.iWindowHeight = rc.bottom - rc.top;
}
}
static void GetWindowRectAtResolution(int xres, int yres, RECT &rcInner, RECT &rcOuter) {
rcInner.left = 0;
rcInner.top = 0;
rcInner.right = xres;
rcInner.bottom = yres;
rcOuter = rcInner;
AdjustWindowRect(&rcOuter, WS_OVERLAPPEDWINDOW, TRUE);
rcOuter.right += g_Config.iWindowX - rcOuter.left;
rcOuter.bottom += g_Config.iWindowY - rcOuter.top;
rcOuter.left = g_Config.iWindowX;
rcOuter.top = g_Config.iWindowY;
}
static void ShowScreenResolution() {
I18NCategory *gr = GetI18NCategory("Graphics");
std::ostringstream messageStream;
messageStream << gr->T("Internal Resolution") << ": ";
messageStream << PSP_CoreParameter().renderWidth << "x" << PSP_CoreParameter().renderHeight << " ";
messageStream << gr->T("Window Size") << ": ";
messageStream << PSP_CoreParameter().pixelWidth << "x" << PSP_CoreParameter().pixelHeight;
osm.Show(messageStream.str(), 2.0f);
}
static void UpdateRenderResolution() {
RECT rc;
GetClientRect(hwndMain, &rc);
// Actually, auto mode should be more granular...
// Round up to a zoom factor for the render size.
int zoom = g_Config.iInternalResolution;
if (zoom == 0) { // auto mode
// Use the longest dimension
if (g_Config.IsPortrait()) {
zoom = (rc.bottom - rc.top + 479) / 480;
} else {
zoom = (rc.right - rc.left + 479) / 480;
}
}
if (zoom <= 1)
zoom = 1;
if (g_Config.IsPortrait()) {
PSP_CoreParameter().renderWidth = 272 * zoom;
PSP_CoreParameter().renderHeight = 480 * zoom;
} else {
PSP_CoreParameter().renderWidth = 480 * zoom;
PSP_CoreParameter().renderHeight = 272 * zoom;
}
}
static bool IsWindowSmall() {
return g_Config.IsPortrait() ? (g_Config.iWindowHeight < 480 + 80) : (g_Config.iWindowWidth < 480 + 80);
}
static void ResizeDisplay(bool noWindowMovement = false) {
AssertCurrentThreadName("Main");
int width = 0, height = 0;
RECT rc;
GetClientRect(hwndMain, &rc);
if (!noWindowMovement) {
width = rc.right - rc.left;
height = rc.bottom - rc.top;
// Moves the internal window, not the frame. TODO: Get rid of the internal window. Tried before but Intel drivers screw up when minimizing, or something?
MoveWindow(hwndDisplay, 0, 0, width, height, TRUE);
// This is taken care of anyway later, but makes sure that ShowScreenResolution gets the right numbers.
// Need to clean all of this up...
PSP_CoreParameter().pixelWidth = width;
PSP_CoreParameter().pixelHeight = height;
}
UpdateRenderResolution();
if (!noWindowMovement) {
if (UpdateScreenScale(width, height, IsWindowSmall())) {
NativeMessageReceived("gpu resized", "");
}
}
}
void SetWindowSize(int zoom) {
AssertCurrentThreadName("Main");
RECT rc, rcOuter;
// Actually, auto mode should be more granular...
if (g_Config.IsPortrait()) {
GetWindowRectAtResolution(272 * (int)zoom, 480 * (int)zoom, rc, rcOuter);
} else {
GetWindowRectAtResolution(480 * (int)zoom, 272 * (int)zoom, rc, rcOuter);
}
MoveWindow(hwndMain, rcOuter.left, rcOuter.top, rcOuter.right - rcOuter.left, rcOuter.bottom - rcOuter.top, TRUE);
ResizeDisplay(false);
ShowScreenResolution();
}
void SetInternalResolution(int res = -1) {
if (res >= 0 && res <= RESOLUTION_MAX)
g_Config.iInternalResolution = res;
else {
if (++g_Config.iInternalResolution > RESOLUTION_MAX)
g_Config.iInternalResolution = 0;
}
// Taking auto-texture scaling into account
if (g_Config.iTexScalingLevel == TEXSCALING_AUTO)
setTexScalingMultiplier(0);
if (gpu)
gpu->Resized();
UpdateRenderResolution();
ShowScreenResolution();
}
void CorrectCursor() {
bool autoHide = g_Config.bFullScreen && !mouseButtonDown && GetUIState() == UISTATE_INGAME;
if (autoHide && hideCursor) {
while (cursorCounter >= 0) {
cursorCounter = ShowCursor(FALSE);
}
} else {
hideCursor = !autoHide;
if (cursorCounter < 0) {
cursorCounter = ShowCursor(TRUE);
SetCursor(LoadCursor(NULL, IDC_ARROW));
}
}
}
void ToggleFullscreen(HWND hWnd, bool goingFullscreen) {
// Make sure no rendering is happening during the switch.
Core_NotifyWindowHidden(true);
g_inModeSwitch = true; // Make sure WM_SIZE doesn't call Core_NotifyWindowHidden(false)...
DWORD dwOldStyle;
DWORD dwNewStyle;
if (!goingFullscreen) {
// Put caption and border styles back.
dwOldStyle = ::GetWindowLong(hWnd, GWL_STYLE);
dwOldStyle &= ~WS_POPUP;
dwNewStyle = dwOldStyle | WS_CAPTION | WS_THICKFRAME | WS_SYSMENU;
// Put back the menu bar.
::SetMenu(hWnd, menu);
} else {
// If the window was maximized before going fullscreen, make sure to restore first
// in order not to have the taskbar show up on top of PPSSPP.
if (g_WindowState == SIZE_MAXIMIZED) {
ShowWindow(hwndMain, SW_RESTORE);
}
// Remember the normal window rectangle.
::GetWindowRect(hWnd, &g_normalRC);
// Remove caption and border styles.
dwOldStyle = ::GetWindowLong(hWnd, GWL_STYLE);
dwNewStyle = dwOldStyle & ~(WS_CAPTION | WS_THICKFRAME | WS_SYSMENU);
// Add WS_POPUP
dwNewStyle |= WS_POPUP;
}
::SetWindowLong(hWnd, GWL_STYLE, dwNewStyle);
// Remove the menu bar.
::SetMenu(hWnd, goingFullscreen ? NULL : menu);
// Resize to the appropriate view.
// If we're returning to window mode, re-apply the appropriate size setting.
if (goingFullscreen) {
ShowWindow(hwndMain, SW_MAXIMIZE);
} else {
ShowWindow(hwndMain, g_WindowState == SIZE_MAXIMIZED ? SW_MAXIMIZE : SW_RESTORE);
}
g_Config.bFullScreen = goingFullscreen;
CorrectCursor();
bool showOSM = (g_Config.iInternalResolution == RESOLUTION_AUTO);
ResizeDisplay(false);
if (showOSM) {
ShowScreenResolution();
}
ShowOwnedPopups(hwndMain, goingFullscreen ? FALSE : TRUE);
W32Util::MakeTopMost(hwndMain, g_Config.bTopMost);
g_inModeSwitch = false;
Core_NotifyWindowHidden(false);
WindowsRawInput::NotifyMenu();
}
RECT DetermineWindowRectangle() {
RECT rc;
const int screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
const int screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
const int screenX = GetSystemMetrics(SM_XVIRTUALSCREEN);
const int screenY = GetSystemMetrics(SM_YVIRTUALSCREEN);
if (!g_Config.bFullScreen) {
bool visibleHorizontally = ((g_Config.iWindowX + g_Config.iWindowWidth) >= screenX) &&
((g_Config.iWindowX + g_Config.iWindowWidth) < (screenWidth + g_Config.iWindowWidth));
bool visibleVertically = ((g_Config.iWindowY + g_Config.iWindowHeight) >= screenY) &&
((g_Config.iWindowY + g_Config.iWindowHeight) < (screenHeight + g_Config.iWindowHeight));
if (!visibleHorizontally)
g_Config.iWindowX = -1;
if (!visibleVertically)
g_Config.iWindowY = -1;
}
rc.left = g_Config.iWindowX;
rc.top = g_Config.iWindowY;
// First, get the w/h right.
if (g_Config.iWindowWidth <= 0 || g_Config.iWindowHeight <= 0) {
RECT rcInner = rc, rcOuter;
bool portrait = g_Config.IsPortrait();
GetWindowRectAtResolution(2 * (portrait ? 272 : 480), 2 * (portrait ? 480 : 272), rcInner, rcOuter);
rc.right = rc.left + (rcOuter.right - rcOuter.left);
rc.bottom = rc.top + (rcOuter.bottom - rcOuter.top);
g_Config.iWindowWidth = rc.right - rc.left;
g_Config.iWindowHeight = rc.bottom - rc.top;
} else {
rc.right = rc.left + g_Config.iWindowWidth;
rc.bottom = rc.top + g_Config.iWindowHeight;
}
// Then center if necessary.
if (g_Config.iWindowX == -1 && g_Config.iWindowY == -1) {
// Center the window.
const int primaryScreenWidth = GetSystemMetrics(SM_CXSCREEN);
const int primaryScreenHeight = GetSystemMetrics(SM_CYSCREEN);
g_Config.iWindowX = (primaryScreenWidth - g_Config.iWindowWidth) / 2;
g_Config.iWindowY = (primaryScreenHeight - g_Config.iWindowHeight) / 2;
rc.left = g_Config.iWindowX;
rc.top = g_Config.iWindowY;
rc.right = rc.left + g_Config.iWindowWidth;
rc.bottom = rc.top + g_Config.iWindowHeight;
}
return rc;
}
void SetIngameMenuItemStates(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_TOGGLE_PAUSE, menuEnable);
EnableMenuItem(menu, ID_EMULATION_STOP, menuEnable);
EnableMenuItem(menu, ID_EMULATION_RESET, menuEnable);
EnableMenuItem(menu, ID_EMULATION_SWITCH_UMD, umdSwitchEnable);
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_EXTRACTFILE, menuEnable);
}
// These are used as an offset
// to determine which menu item to change.
// Make sure to count(from 0) the separators too, when dealing with submenus!!
enum MenuItemPosition {
// Main menus
MENU_FILE = 0,
MENU_EMULATION = 1,
MENU_DEBUG = 2,
MENU_OPTIONS = 3,
MENU_HELP = 4,
// File submenus
SUBMENU_FILE_SAVESTATE_SLOT = 6,
// Game Settings submenus
SUBMENU_CUSTOM_SHADERS = 10,
SUBMENU_RENDERING_RESOLUTION = 11,
SUBMENU_WINDOW_SIZE = 12,
SUBMENU_RENDERING_BACKEND = 13,
SUBMENU_RENDERING_MODE = 14,
SUBMENU_FRAME_SKIPPING = 15,
SUBMENU_TEXTURE_FILTERING = 16,
SUBMENU_BUFFER_FILTER = 17,
SUBMENU_TEXTURE_SCALING = 18,
};
std::string GetMenuItemText(int menuID) {
MENUITEMINFO menuInfo;
memset(&menuInfo, 0, sizeof(menuInfo));
menuInfo.cbSize = sizeof(MENUITEMINFO);
menuInfo.fMask = MIIM_STRING;
menuInfo.dwTypeData = 0;
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(const int menuID) {
if (initialMenuKeys.find(menuID) == initialMenuKeys.end()) {
initialMenuKeys[menuID] = GetMenuItemText(menuID);
}
return initialMenuKeys[menuID];
}
void CreateHelpMenu() {
I18NCategory *des = GetI18NCategory("DesktopUI");
const std::wstring help = ConvertUTF8ToWString(des->T("Help"));
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 aboutPPSSPP = ConvertUTF8ToWString(des->T("About PPSSPP..."));
// Simply remove the old help menu and create a new one.
RemoveMenu(menu, MENU_HELP, MF_BYPOSITION);
HMENU helpMenu = CreatePopupMenu();
InsertMenu(menu, MENU_HELP, MF_POPUP | MF_STRING | MF_BYPOSITION, (UINT_PTR)helpMenu, help.c_str());
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_SEPARATOR, 0, 0);
AppendMenu(helpMenu, MF_STRING | MF_BYCOMMAND, ID_HELP_ABOUT, aboutPPSSPP.c_str());
}
void UpdateDynamicMenuCheckmarks() {
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));
}
void CreateShadersSubmenu() {
I18NCategory *des = GetI18NCategory("DesktopUI");
I18NCategory *ps = GetI18NCategory("PostShaders");
const std::wstring key = ConvertUTF8ToWString(des->T("Postprocessing Shader"));
HMENU optionsMenu = GetSubMenu(menu, MENU_OPTIONS);
HMENU shaderMenu = CreatePopupMenu();
RemoveMenu(optionsMenu, SUBMENU_CUSTOM_SHADERS, MF_BYPOSITION);
InsertMenu(optionsMenu, SUBMENU_CUSTOM_SHADERS, MF_POPUP | MF_STRING | MF_BYPOSITION, (UINT_PTR)shaderMenu, key.c_str());
std::vector<ShaderInfo> info = GetAllPostShaderInfo();
availableShaders.clear();
int item = ID_SHADERS_BASE + 1;
int checkedStatus = -1;
const char *translatedShaderName = nullptr;
for (auto i = info.begin(); i != info.end(); ++i) {
checkedStatus = MF_UNCHECKED;
availableShaders.push_back(i->section);
if (g_Config.sPostShaderName == i->section) {
checkedStatus = MF_CHECKED;
}
translatedShaderName = ps->T(i->section.c_str());
AppendMenu(shaderMenu, MF_STRING | MF_BYPOSITION | checkedStatus, item++, ConvertUTF8ToWString(translatedShaderName).c_str());
}
}
void _TranslateMenuItem(const int menuIDOrPosition, const char *key, bool byCommand = false, const std::wstring& accelerator = L"", const HMENU hMenu = menu) {
I18NCategory *des = GetI18NCategory("DesktopUI");
std::wstring translated = ConvertUTF8ToWString(des->T(key));
translated.append(accelerator);
u32 flags = MF_STRING | (byCommand ? MF_BYCOMMAND : MF_BYPOSITION);
ModifyMenu(hMenu, menuIDOrPosition, flags, menuIDOrPosition, translated.c_str());
}
void TranslateMenuItem(const int menuID, const std::wstring& accelerator = L"", const char *key = "", const HMENU hMenu = menu) {
if(key == nullptr || !strcmp(key, ""))
_TranslateMenuItem(menuID, GetMenuItemInitialText(menuID).c_str(), true, accelerator, hMenu);
else
_TranslateMenuItem(menuID, key, true, accelerator, hMenu);
}
void TranslateMenu(const char *key, const MenuItemPosition mainMenuPosition, const std::wstring& accelerator = L"") {
_TranslateMenuItem(mainMenuPosition, key, false, accelerator);
}
void TranslateSubMenu(const char *key, const MenuItemPosition mainMenuItem, const MenuItemPosition subMenuItem, const std::wstring& accelerator = L"") {
_TranslateMenuItem(subMenuItem, key, false, accelerator, GetSubMenu(menu, mainMenuItem));
}
void TranslateMenus() {
// Menu headers and submenu headers don't have resource IDs,
// So we have to hardcode strings here, unfortunately.
TranslateMenu("File", MENU_FILE);
TranslateMenu("Emulation", MENU_EMULATION);
TranslateMenu("Debugging", MENU_DEBUG);
TranslateMenu("Game Settings", MENU_OPTIONS);
TranslateMenu("Help", MENU_HELP);
CreateShadersSubmenu();
// File menu
TranslateMenuItem(ID_FILE_LOAD);
TranslateMenuItem(ID_FILE_LOAD_DIR);
TranslateMenuItem(ID_FILE_LOAD_MEMSTICK);
TranslateMenuItem(ID_FILE_MEMSTICK);
TranslateSubMenu("Savestate Slot", MENU_FILE, SUBMENU_FILE_SAVESTATE_SLOT, L"\tF3");
TranslateMenuItem(ID_FILE_QUICKLOADSTATE, L"\tF4");
TranslateMenuItem(ID_FILE_QUICKSAVESTATE, L"\tF2");
TranslateMenuItem(ID_FILE_LOADSTATEFILE);
TranslateMenuItem(ID_FILE_SAVESTATEFILE);
TranslateMenuItem(ID_FILE_EXIT, L"\tAlt+F4");
// Emulation menu
TranslateMenuItem(ID_TOGGLE_PAUSE, L"\tF8", "Pause");
TranslateMenuItem(ID_EMULATION_STOP, L"\tCtrl+W");
TranslateMenuItem(ID_EMULATION_RESET, L"\tCtrl+B");
TranslateMenuItem(ID_EMULATION_SWITCH_UMD, L"\tCtrl+U", "Switch UMD");
// Debug menu
TranslateMenuItem(ID_DEBUG_LOADMAPFILE);
TranslateMenuItem(ID_DEBUG_SAVEMAPFILE);
TranslateMenuItem(ID_DEBUG_LOADSYMFILE);
TranslateMenuItem(ID_DEBUG_SAVESYMFILE);
TranslateMenuItem(ID_DEBUG_RESETSYMBOLTABLE);
TranslateMenuItem(ID_DEBUG_DUMPNEXTFRAME);
TranslateMenuItem(ID_DEBUG_TAKESCREENSHOT, L"\tF12");
TranslateMenuItem(ID_DEBUG_SHOWDEBUGSTATISTICS);
TranslateMenuItem(ID_DEBUG_IGNOREILLEGALREADS);
TranslateMenuItem(ID_DEBUG_RUNONLOAD);
TranslateMenuItem(ID_DEBUG_DISASSEMBLY, L"\tCtrl+D");
TranslateMenuItem(ID_DEBUG_GEDEBUGGER,L"\tCtrl+G");
TranslateMenuItem(ID_DEBUG_EXTRACTFILE);
TranslateMenuItem(ID_DEBUG_LOG, L"\tCtrl+L");
TranslateMenuItem(ID_DEBUG_MEMORYVIEW, L"\tCtrl+M");
// Options menu
TranslateMenuItem(ID_OPTIONS_LANGUAGE);
TranslateMenuItem(ID_OPTIONS_TOPMOST);
TranslateMenuItem(ID_OPTIONS_PAUSE_FOCUS);
TranslateMenuItem(ID_OPTIONS_IGNOREWINKEY);
TranslateMenuItem(ID_OPTIONS_MORE_SETTINGS);
TranslateMenuItem(ID_OPTIONS_CONTROLS);
TranslateMenuItem(ID_OPTIONS_STRETCHDISPLAY);
TranslateMenuItem(ID_OPTIONS_FULLSCREEN, L"\tAlt+Return, F11");
TranslateMenuItem(ID_OPTIONS_VSYNC);
TranslateSubMenu("Postprocessing Shader", MENU_OPTIONS, SUBMENU_CUSTOM_SHADERS);
TranslateSubMenu("Rendering Resolution", MENU_OPTIONS, SUBMENU_RENDERING_RESOLUTION, L"\tCtrl+1");
TranslateMenuItem(ID_OPTIONS_SCREENAUTO);
// Skip rendering resolution 2x-5x..
TranslateSubMenu("Window Size", MENU_OPTIONS, SUBMENU_WINDOW_SIZE);
// Skip window size 1x-4x..
TranslateSubMenu("Backend", MENU_OPTIONS, SUBMENU_RENDERING_BACKEND);
TranslateMenuItem(ID_OPTIONS_DIRECTX);
TranslateMenuItem(ID_OPTIONS_OPENGL);
TranslateSubMenu("Rendering Mode", MENU_OPTIONS, SUBMENU_RENDERING_MODE, L"\tF5");
TranslateMenuItem(ID_OPTIONS_NONBUFFEREDRENDERING);
TranslateMenuItem(ID_OPTIONS_BUFFEREDRENDERING);
TranslateMenuItem(ID_OPTIONS_READFBOTOMEMORYCPU);
TranslateMenuItem(ID_OPTIONS_READFBOTOMEMORYGPU);
TranslateSubMenu("Frame Skipping", MENU_OPTIONS, SUBMENU_FRAME_SKIPPING, L"\tF7");
TranslateMenuItem(ID_OPTIONS_FRAMESKIP_AUTO);
TranslateMenuItem(ID_OPTIONS_FRAMESKIP_0);
// Skip frameskipping 1-8..
TranslateSubMenu("Texture Filtering", MENU_OPTIONS, SUBMENU_TEXTURE_FILTERING);
TranslateMenuItem(ID_OPTIONS_TEXTUREFILTERING_AUTO);
TranslateMenuItem(ID_OPTIONS_NEARESTFILTERING);
TranslateMenuItem(ID_OPTIONS_LINEARFILTERING);
TranslateMenuItem(ID_OPTIONS_LINEARFILTERING_CG);
TranslateSubMenu("Screen Scaling Filter", MENU_OPTIONS, SUBMENU_BUFFER_FILTER);
TranslateMenuItem(ID_OPTIONS_BUFLINEARFILTER);
TranslateMenuItem(ID_OPTIONS_BUFNEARESTFILTER);
TranslateSubMenu("Texture Scaling", MENU_OPTIONS, SUBMENU_TEXTURE_SCALING);
TranslateMenuItem(ID_TEXTURESCALING_OFF);
// Skip texture scaling 2x-5x...
TranslateMenuItem(ID_TEXTURESCALING_XBRZ);
TranslateMenuItem(ID_TEXTURESCALING_HYBRID);
TranslateMenuItem(ID_TEXTURESCALING_BICUBIC);
TranslateMenuItem(ID_TEXTURESCALING_HYBRID_BICUBIC);
TranslateMenuItem(ID_TEXTURESCALING_DEPOSTERIZE);
TranslateMenuItem(ID_OPTIONS_HARDWARETRANSFORM, L"\tF6");
TranslateMenuItem(ID_OPTIONS_VERTEXCACHE);
TranslateMenuItem(ID_OPTIONS_SHOWFPS);
TranslateMenuItem(ID_EMULATION_SOUND);
TranslateMenuItem(ID_EMULATION_CHEATS, L"\tCtrl+T");
// Help menu: it's translated in CreateHelpMenu.
CreateHelpMenu();
// TODO: Urgh! Why do we need this here?
// The menu is supposed to enable/disable this stuff directly afterward.
SetIngameMenuItemStates(GetUIState());
DrawMenuBar(hwndMain);
UpdateMenus();
}
void setTexScalingMultiplier(int level) {
g_Config.iTexScalingLevel = level;
NativeMessageReceived("gpu clear cache", "");
}
void setTexFiltering(int type) {
g_Config.iTexFiltering = type;
}
void setBufFilter(int type) {
g_Config.iBufFilter = type;
}
void setTexScalingType(int type) {
g_Config.iTexScalingType = type;
NativeMessageReceived("gpu clear cache", "");
}
void setRenderingMode(int mode = -1) {
if (mode >= FB_NON_BUFFERED_MODE)
g_Config.iRenderingMode = mode;
else {
if (++g_Config.iRenderingMode > FB_READFBOMEMORY_GPU)
g_Config.iRenderingMode = FB_NON_BUFFERED_MODE;
}
I18NCategory *gr = GetI18NCategory("Graphics");
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;
case FB_READFBOMEMORY_CPU:
osm.Show(gr->T("Read Framebuffers To Memory (CPU)"));
break;
case FB_READFBOMEMORY_GPU:
osm.Show(gr->T("Read Framebuffers To Memory (GPU)"));
break;
}
NativeMessageReceived("gpu resized", "");
}
void setFpsLimit(int fps) {
g_Config.iFpsLimit = fps;
}
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());
}
void enableCheats(bool cheats) {
g_Config.bEnableCheats = cheats;
}
void UpdateWindowTitle() {
// Seems to be fine to call now since we use a UNICODE build...
SetWindowText(hwndMain, windowTitle.c_str());
}
void SetWindowTitle(const wchar_t *title) {
windowTitle = title;
}
BOOL Show(HINSTANCE hInstance) {
hInst = hInstance; // Store instance handle in our global variable.
RECT rc = DetermineWindowRectangle();
u32 style = WS_OVERLAPPEDWINDOW;
hwndMain = CreateWindowEx(0,szWindowClass, L"", style,
rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hInstance, NULL);
if (!hwndMain)
return FALSE;
RECT rcClient;
GetClientRect(hwndMain, &rcClient);
hwndDisplay = CreateWindowEx(0, szDisplayClass, L"", WS_CHILD | WS_VISIBLE,
0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, hwndMain, 0, hInstance, 0);
if (!hwndDisplay)
return FALSE;
menu = GetMenu(hwndMain);
#ifdef FINAL
RemoveMenu(menu,2,MF_BYPOSITION);
RemoveMenu(menu,2,MF_BYPOSITION);
#endif
MENUINFO info;
ZeroMemory(&info,sizeof(MENUINFO));
info.cbSize = sizeof(MENUINFO);
info.cyMax = 0;
info.dwStyle = MNS_CHECKORBMP;
info.fMask = MIM_STYLE;
for (int i = 0; i < GetMenuItemCount(menu); i++) {
SetMenuInfo(GetSubMenu(menu,i),&info);
}
UpdateMenus();
// Accept dragged files.
DragAcceptFiles(hwndMain, TRUE);
hideCursor = true;
SetTimer(hwndMain, TIMER_CURSORUPDATE, CURSORUPDATE_INTERVAL_MS, 0);
ToggleFullscreen(hwndMain, g_Config.bFullScreen);
W32Util::MakeTopMost(hwndMain, g_Config.bTopMost);
touchHandler.registerTouchWindow(hwndDisplay);
WindowsRawInput::Init();
SetFocus(hwndMain);
return TRUE;
}
void CreateDebugWindows() {
disasmWindow[0] = new CDisasm(MainWindow::GetHInstance(), MainWindow::GetHWND(), currentDebugMIPS);
DialogManager::AddDlg(disasmWindow[0]);
disasmWindow[0]->Show(g_Config.bShowDebuggerOnLoad);
geDebuggerWindow = new CGEDebugger(MainWindow::GetHInstance(), MainWindow::GetHWND());
DialogManager::AddDlg(geDebuggerWindow);
memoryWindow[0] = new CMemoryDlg(MainWindow::GetHInstance(), MainWindow::GetHWND(), currentDebugMIPS);
DialogManager::AddDlg(memoryWindow[0]);
}
void DestroyDebugWindows() {
DialogManager::RemoveDlg(disasmWindow[0]);
if (disasmWindow[0])
delete disasmWindow[0];
disasmWindow[0] = 0;
DialogManager::RemoveDlg(geDebuggerWindow);
if (geDebuggerWindow)
delete geDebuggerWindow;
geDebuggerWindow = 0;
DialogManager::RemoveDlg(memoryWindow[0]);
if (memoryWindow[0])
delete memoryWindow[0];
memoryWindow[0] = 0;
}
void BrowseAndBoot(std::string defaultPath, bool browseDirectory) {
static std::wstring filter = L"All supported file types (*.iso *.cso *.pbp *.elf *.prx *.zip)|*.pbp;*.elf;*.iso;*.cso;*.prx;*.zip|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);
}
// TODO: What is this for / what does it fix?
if (browseDialog->GetType() != W32Util::AsyncBrowseDialog::DIR) {
// Decode the filename with fullpath.
char drive[MAX_PATH];
char dir[MAX_PATH];
char fname[MAX_PATH];
char ext[MAX_PATH];
_splitpath(filename.c_str(), drive, dir, fname, ext);
filename = std::string(drive) + std::string(dir) + std::string(fname) + std::string(ext);
}
filename = ReplaceAll(filename, "\\", "/");
NativeMessageReceived("boot", filename.c_str());
}
W32Util::MakeTopMost(GetHWND(), g_Config.bTopMost);
delete browseDialog;
browseDialog = 0;
}
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);
}
}
LRESULT CALLBACK DisplayProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
// Only apply a factor > 1 in windowed mode.
int factor = !IsZoomed(GetHWND()) && !g_Config.bFullScreen && IsWindowSmall() ? 2 : 1;
static bool firstErase = true;
switch (message) {
case WM_ACTIVATE:
if (wParam == WA_ACTIVE || wParam == WA_CLICKACTIVE) {
g_activeWindow = WINDOW_MAINWINDOW;
}
break;
case WM_SETFOCUS:
break;
case WM_ERASEBKGND:
if (firstErase) {
firstErase = false;
// Paint black on first erase while OpenGL stuff is loading
return DefWindowProc(hWnd, message, wParam, lParam);
}
// Then never erase, let the OpenGL drawing take care of everything.
return 1;
// Poor man's touch - mouse input. We send the data both as an input_state pointer,
// and as asynchronous touch events for minimal latency.
case WM_LBUTTONDOWN:
if (!touchHandler.hasTouch() ||
(GetMessageExtraInfo() & MOUSEEVENTF_MASK_PLUS_PENTOUCH) != MOUSEEVENTF_FROMTOUCH_NOPEN)
{
// Hack: Take the opportunity to show the cursor.
mouseButtonDown = true;
{
lock_guard guard(input_state.lock);
input_state.mouse_valid = true;
input_state.pointer_down[0] = true;
input_state.pointer_x[0] = GET_X_LPARAM(lParam) * factor;
input_state.pointer_y[0] = GET_Y_LPARAM(lParam) * factor;
}
TouchInput touch;
touch.id = 0;
touch.flags = TOUCH_DOWN;
touch.x = input_state.pointer_x[0];
touch.y = input_state.pointer_y[0];
NativeTouch(touch);
SetCapture(hWnd);
}
break;
case WM_MOUSEMOVE:
if (!touchHandler.hasTouch() ||
(GetMessageExtraInfo() & MOUSEEVENTF_MASK_PLUS_PENTOUCH) != MOUSEEVENTF_FROMTOUCH_NOPEN)
{
// Hack: Take the opportunity to show the cursor.
mouseButtonDown = (wParam & MK_LBUTTON) != 0;
int cursorX = GET_X_LPARAM(lParam);
int cursorY = GET_Y_LPARAM(lParam);
if (abs(cursorX - prevCursorX) > 1 || abs(cursorY - prevCursorY) > 1) {
hideCursor = false;
SetTimer(hwndMain, TIMER_CURSORMOVEUPDATE, CURSORUPDATE_MOVE_TIMESPAN_MS, 0);
}
prevCursorX = cursorX;
prevCursorY = cursorY;
{
lock_guard guard(input_state.lock);
input_state.pointer_x[0] = GET_X_LPARAM(lParam) * factor;
input_state.pointer_y[0] = GET_Y_LPARAM(lParam) * factor;
}
if (wParam & MK_LBUTTON) {
TouchInput touch;
touch.id = 0;
touch.flags = TOUCH_MOVE;
touch.x = input_state.pointer_x[0];
touch.y = input_state.pointer_y[0];
NativeTouch(touch);
}
}
break;
case WM_LBUTTONUP:
if (!touchHandler.hasTouch() ||
(GetMessageExtraInfo() & MOUSEEVENTF_MASK_PLUS_PENTOUCH) != MOUSEEVENTF_FROMTOUCH_NOPEN)
{
// Hack: Take the opportunity to hide the cursor.
mouseButtonDown = false;
{
lock_guard guard(input_state.lock);
input_state.pointer_down[0] = false;
input_state.pointer_x[0] = GET_X_LPARAM(lParam) * factor;
input_state.pointer_y[0] = GET_Y_LPARAM(lParam) * factor;
}
TouchInput touch;
touch.id = 0;
touch.flags = TOUCH_UP;
touch.x = input_state.pointer_x[0];
touch.y = input_state.pointer_y[0];
NativeTouch(touch);
ReleaseCapture();
}
break;
case WM_TOUCH:
{
touchHandler.handleTouchEvent(hWnd, message, wParam, lParam);
return 0;
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
int wmId, wmEvent;
std::string fn;
static bool noFocusPause = false; // TOGGLE_PAUSE state to override pause on lost focus
switch (message) {
case WM_CREATE:
break;
case WM_GETMINMAXINFO:
{
MINMAXINFO *minmax = reinterpret_cast<MINMAXINFO *>(lParam);
RECT rc = { 0 };
bool portrait = g_Config.IsPortrait();
rc.right = portrait ? 272 : 480;
rc.bottom = portrait ? 480 : 272;
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, TRUE);
minmax->ptMinTrackSize.x = rc.right - rc.left;
minmax->ptMinTrackSize.y = rc.bottom - rc.top;
}
return 0;
case WM_ACTIVATE:
{
bool pause = true;
if (wParam == WA_ACTIVE || wParam == WA_CLICKACTIVE) {
WindowsRawInput::GainFocus();
InputDevice::GainFocus();
g_activeWindow = WINDOW_MAINWINDOW;
pause = false;
}
if (!noFocusPause && g_Config.bPauseOnLostFocus && GetUIState() == UISTATE_INGAME) {
if (pause != Core_IsStepping()) { // != is xor for bools
if (disasmWindow[0])
SendMessage(disasmWindow[0]->GetDlgHandle(), WM_COMMAND, IDC_STOPGO, 0);
else
Core_EnableStepping(pause);
}
}
if (wParam == WA_ACTIVE) {
NativeMessageReceived("got_focus", "");
}
if (wParam == WA_INACTIVE) {
NativeMessageReceived("lost_focus", "");
WindowsRawInput::LoseFocus();
InputDevice::LoseFocus();
}
}
break;
case WM_ERASEBKGND:
// This window is always covered by DisplayWindow. No reason to erase.
return 1;
case WM_MOVE:
SavePosition();
break;
case WM_SIZE:
if (!g_inModeSwitch) {
switch (wParam) {
case SIZE_MAXIMIZED:
case SIZE_RESTORED:
Core_NotifyWindowHidden(false);
if (!g_Config.bPauseWhenMinimized) {
NativeMessageReceived("window minimized", "false");
}
SavePosition();
ResizeDisplay();
g_WindowState = wParam;
break;
case SIZE_MINIMIZED:
Core_NotifyWindowHidden(true);
if (!g_Config.bPauseWhenMinimized) {
NativeMessageReceived("window minimized", "true");
}
break;
default:
break;
}
}
break;
case WM_TIMER:
// Hack: Take the opportunity to also show/hide the mouse cursor in fullscreen mode.
switch (wParam) {
case TIMER_CURSORUPDATE:
CorrectCursor();
return 0;
case TIMER_CURSORMOVEUPDATE:
hideCursor = true;
KillTimer(hWnd, TIMER_CURSORMOVEUPDATE);
return 0;
}
break;
// For some reason, need to catch this here rather than in DisplayProc.
case WM_MOUSEWHEEL:
{
int wheelDelta = (short)(wParam >> 16);
KeyInput key;
key.deviceId = DEVICE_ID_MOUSE;
if (wheelDelta < 0) {
key.keyCode = NKCODE_EXT_MOUSEWHEEL_DOWN;
wheelDelta = -wheelDelta;
} else {
key.keyCode = NKCODE_EXT_MOUSEWHEEL_UP;
}
// There's no separate keyup event for mousewheel events, let's pass them both together.
// This also means it really won't work great for key mapping :( Need to build a 1 frame delay or something.
key.flags = KEY_DOWN | KEY_UP | KEY_HASWHEELDELTA | (wheelDelta << 16);
NativeKey(key);
}
break;
case WM_COMMAND:
{
if (!EmuThread_Ready())
return DefWindowProc(hWnd, message, wParam, lParam);
I18NCategory *gr = GetI18NCategory("Graphics");
wmId = LOWORD(wParam);
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_PAUSE:
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_STOP:
if (Core_IsStepping())
Core_EnableStepping(false);
Core_Stop();
NativeMessageReceived("stop", "");
Core_WaitInactive();
Update();
break;
case ID_EMULATION_RESET:
NativeMessageReceived("reset", "");
Core_EnableStepping(false);
break;
case ID_EMULATION_SWITCH_UMD:
UmdSwitchAction();
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();
break;
}
case ID_FILE_SAVESTATE_NEXT_SLOT_HC:
{
if (KeyMap::g_controllerMap[VIRTKEY_NEXT_SLOT].empty())
{
SaveState::NextSlot();
}
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(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(g_Config.iCurrentStateSlot, SaveStateActionFinished);
}
break;
}
case ID_FILE_QUICKSAVESTATE:
{
SetCursor(LoadCursor(0, IDC_WAIT));
SaveState::SaveSlot(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(g_Config.iCurrentStateSlot, SaveStateActionFinished);
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_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(TextureScaler::XBRZ); break;
case ID_TEXTURESCALING_HYBRID: setTexScalingType(TextureScaler::HYBRID); break;
case ID_TEXTURESCALING_BICUBIC: setTexScalingType(TextureScaler::BICUBIC); break;
case ID_TEXTURESCALING_HYBRID_BICUBIC: setTexScalingType(TextureScaler::HYBRID_BICUBIC); break;
case ID_TEXTURESCALING_DEPOSTERIZE:
g_Config.bTexDeposterize = !g_Config.bTexDeposterize;
NativeMessageReceived("gpu clear cache", "");
break;
case ID_OPTIONS_DIRECTX:
g_Config.iTempGPUBackend = GPU_BACKEND_DIRECT3D9;
g_Config.bRestartRequired = true;
PostMessage(MainWindow::GetHWND(), WM_CLOSE, 0, 0);
break;
case ID_OPTIONS_OPENGL:
g_Config.iTempGPUBackend = GPU_BACKEND_OPENGL;
g_Config.bRestartRequired = true;
PostMessage(MainWindow::GetHWND(), WM_CLOSE, 0, 0);
break;
case ID_OPTIONS_NONBUFFEREDRENDERING: setRenderingMode(FB_NON_BUFFERED_MODE); break;
case ID_OPTIONS_BUFFEREDRENDERING: setRenderingMode(FB_BUFFERED_MODE); break;
case ID_OPTIONS_READFBOTOMEMORYCPU: setRenderingMode(FB_READFBOMEMORY_CPU); break;
case ID_OPTIONS_READFBOTOMEMORYGPU: setRenderingMode(FB_READFBOMEMORY_GPU); break;
// Dummy option to let the buffered rendering hotkey cycle through all the options.
case ID_OPTIONS_BUFFEREDRENDERINGDUMMY:
setRenderingMode();
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_STRETCHDISPLAY:
g_Config.bStretchToDisplay = !g_Config.bStretchToDisplay;
NativeMessageReceived("gpu resized", "");
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_FRAMESKIPDUMMY:
setFrameSkipping();
break;
case ID_FILE_EXIT:
PostMessage(hwndMain, WM_CLOSE, 0, 0);
break;
case ID_DEBUG_RUNONLOAD:
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)) {
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))
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)) {
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))
symbolMap.SaveNocashSym(fn.c_str());
break;
case ID_DEBUG_RESETSYMBOLTABLE:
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 (geDebuggerWindow)
geDebuggerWindow->Show(true);
break;
case ID_DEBUG_MEMORYVIEW:
if (memoryWindow[0])
memoryWindow[0]->Show(true);
break;
case ID_DEBUG_EXTRACTFILE:
{
std::string filename;
if (!InputBox_GetString(hInst, hwndMain, 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(hwndMain, L"File does not exist.", L"Sorry",0);
} else if (info.type == FILETYPE_DIRECTORY) {
MessageBox(hwndMain, 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)) {
FILE *fp = fopen(fn.c_str(), "wb");
u32 handle = pspFileSystem.OpenFile(filename, FILEACCESS_READ, "");
u8 buffer[4096];
while (pspFileSystem.ReadFile(handle, buffer, sizeof(buffer)) > 0) {
fwrite(buffer, sizeof(buffer), 1, fp);
}
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:
PostMessage(hWnd, WM_USER_TOGGLE_FULLSCREEN, 0, 0);
break;
case ID_OPTIONS_VERTEXCACHE:
g_Config.bVertexCache = !g_Config.bVertexCache;
break;
case ID_OPTIONS_SHOWFPS:
g_Config.iShowFPSCounter = !g_Config.iShowFPSCounter;
break;
case ID_OPTIONS_TEXTUREFILTERING_AUTO: setTexFiltering(AUTO); break;
case ID_OPTIONS_NEARESTFILTERING: setTexFiltering(NEAREST); break;
case ID_OPTIONS_LINEARFILTERING: setTexFiltering(LINEAR); break;
case ID_OPTIONS_LINEARFILTERING_CG: setTexFiltering(LINEARFMV); 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"http://www.ppsspp.org/", NULL, NULL, SW_SHOWNORMAL);
break;
case ID_HELP_BUYGOLD:
ShellExecute(NULL, L"open", L"http://central.ppsspp.org/buygold", NULL, NULL, SW_SHOWNORMAL);
break;
case ID_HELP_OPENFORUM:
ShellExecute(NULL, L"open", L"http://forums.ppsspp.org/", 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;
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(hwndMain, L"Unimplemented", L"Sorry",0);
}
break;
}
}
break;
case WM_USER_TOGGLE_FULLSCREEN:
ToggleFullscreen(hwndMain, !g_Config.bFullScreen);
break;
case WM_INPUT:
return WindowsRawInput::Process(hWnd, wParam, lParam);
// TODO: Could do something useful with WM_INPUT_DEVICE_CHANGE?
// Not sure why we are actually getting WM_CHAR even though we use RawInput, but alright..
case WM_CHAR:
return WindowsRawInput::ProcessChar(hWnd, wParam, lParam);
case WM_VERYSLEEPY_MSG:
switch (wParam) {
case VERYSLEEPY_WPARAM_SUPPORTED:
return TRUE;
case VERYSLEEPY_WPARAM_GETADDRINFO:
{
VerySleepy_AddrInfo *info = (VerySleepy_AddrInfo *)lParam;
const u8 *ptr = (const u8 *)info->addr;
std::string name;
if (MIPSComp::jit && MIPSComp::jit->DescribeCodePtr(ptr, name)) {
swprintf_s(info->name, L"Jit::%S", name.c_str());
return TRUE;
}
if (gpu && gpu->DescribeCodePtr(ptr, name)) {
swprintf_s(info->name, L"GPU::%S", name.c_str());
return TRUE;
}
}
return FALSE;
default:
return FALSE;
}
break;
case WM_DROPFILES:
{
if (!EmuThread_Ready())
return DefWindowProc(hWnd, message, wParam, lParam);
HDROP hdrop = (HDROP)wParam;
int count = DragQueryFile(hdrop,0xFFFFFFFF,0,0);
if (count != 1) {
MessageBox(hwndMain,L"You can only load one file at a time",L"Error",MB_ICONINFORMATION);
}
else
{
TCHAR filename[512];
DragQueryFile(hdrop,0,filename,512);
TCHAR *type = filename+_tcslen(filename)-3;
Update();
NativeMessageReceived("boot", ConvertWStringToUTF8(filename).c_str());
Core_EnableStepping(false);
}
}
break;
case WM_CLOSE:
EmuThread_Stop();
InputDevice::StopPolling();
WindowsRawInput::Shutdown();
return DefWindowProc(hWnd,message,wParam,lParam);
case WM_DESTROY:
KillTimer(hWnd, TIMER_CURSORUPDATE);
KillTimer(hWnd, TIMER_CURSORMOVEUPDATE);
PostQuitMessage(0);
break;
case WM_USER + 1:
if (disasmWindow[0])
disasmWindow[0]->NotifyMapLoaded();
if (memoryWindow[0])
memoryWindow[0]->NotifyMapLoaded();
if (disasmWindow[0])
disasmWindow[0]->UpdateDialog();
SetForegroundWindow(hwndMain);
break;
case WM_USER_SAVESTATE_FINISH:
SetCursor(LoadCursor(0, IDC_ARROW));
break;
case WM_USER_UPDATE_UI:
TranslateMenus();
Update();
break;
case WM_USER_UPDATE_SCREEN:
ResizeDisplay(true);
ShowScreenResolution();
break;
case WM_USER_WINDOW_TITLE_CHANGED:
UpdateWindowTitle();
break;
case WM_USER_BROWSE_BOOT_DONE:
BrowseAndBootDone();
break;
case WM_MENUSELECT:
// Unfortunately, accelerate keys (hotkeys) shares the same enabled/disabled states
// with corresponding menu items.
UpdateMenus();
WindowsRawInput::NotifyMenu();
break;
// Turn off the screensaver.
// Note that if there's a screensaver password, this simple method
// doesn't work on Vista or higher.
case WM_SYSCOMMAND:
{
switch (wParam) {
case SC_SCREENSAVE:
return 0;
case SC_MONITORPOWER:
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
void UpdateMenus() {
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_OPTIONS_STRETCHDISPLAY, g_Config.bStretchToDisplay);
CHECKITEM(ID_DEBUG_RUNONLOAD, 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 != 0);
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);
// 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[4] = {
ID_OPTIONS_WINDOW1X,
ID_OPTIONS_WINDOW2X,
ID_OPTIONS_WINDOW3X,
ID_OPTIONS_WINDOW4X,
};
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 (!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 < TextureScaler::XBRZ)
g_Config.iTexScalingType = TextureScaler::XBRZ;
else if(g_Config.iTexScalingType > TextureScaler::HYBRID_BICUBIC)
g_Config.iTexScalingType = TextureScaler::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 < AUTO)
g_Config.iTexFiltering = AUTO;
else if(g_Config.iTexFiltering > LINEARFMV)
g_Config.iTexFiltering = LINEARFMV;
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,
ID_OPTIONS_READFBOTOMEMORYCPU,
ID_OPTIONS_READFBOTOMEMORYGPU,
};
if(g_Config.iRenderingMode < FB_NON_BUFFERED_MODE)
g_Config.iRenderingMode = FB_NON_BUFFERED_MODE;
else if(g_Config.iRenderingMode > FB_READFBOMEMORY_GPU)
g_Config.iRenderingMode = FB_READFBOMEMORY_GPU;
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,
};
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));
}
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::SAVESTATESLOTS)
g_Config.iCurrentStateSlot = SaveState::SAVESTATESLOTS - 1;
for (int i = 0; i < ARRAY_SIZE(savestateSlot); i++) {
CheckMenuItem(menu, savestateSlot[i], MF_BYCOMMAND | (( i == g_Config.iCurrentStateSlot )? MF_CHECKED : MF_UNCHECKED));
}
if (g_Config.iGPUBackend == GPU_BACKEND_DIRECT3D9) {
EnableMenuItem(menu, ID_OPTIONS_DIRECTX, MF_GRAYED);
CheckMenuItem(menu, ID_OPTIONS_DIRECTX, MF_CHECKED);
EnableMenuItem(menu, ID_OPTIONS_OPENGL, MF_ENABLED);
} else {
EnableMenuItem(menu, ID_OPTIONS_OPENGL, MF_GRAYED);
CheckMenuItem(menu, ID_OPTIONS_OPENGL, MF_CHECKED);
EnableMenuItem(menu, ID_OPTIONS_DIRECTX, MF_ENABLED);
}
UpdateDynamicMenuCheckmarks();
UpdateCommands();
}
void UpdateCommands() {
static GlobalUIState lastGlobalUIState = UISTATE_PAUSEMENU;
static CoreState lastCoreState = CORE_ERROR;
if (lastGlobalUIState == GetUIState() && lastCoreState == coreState)
return;
lastCoreState = coreState;
lastGlobalUIState = GetUIState();
HMENU menu = GetMenu(GetHWND());
bool isPaused = Core_IsStepping() && GetUIState() == UISTATE_INGAME;
TranslateMenuItem(ID_TOGGLE_PAUSE, L"\tF8", isPaused ? "Run" : "Pause");
SetIngameMenuItemStates(GetUIState());
EnableMenuItem(menu, ID_DEBUG_LOG, !g_Config.bEnableLogging);
}
// 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 = "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;
}
void Update() {
InvalidateRect(hwndDisplay,0,0);
UpdateWindow(hwndDisplay);
SendMessage(hwndMain,WM_SIZE,0,0);
}
void Redraw() {
InvalidateRect(hwndDisplay,0,0);
}
void SaveStateActionFinished(bool result, void *userdata) {
PostMessage(hwndMain, WM_USER_SAVESTATE_FINISH, 0, 0);
}
HINSTANCE GetHInstance() {
return hInst;
}
void ToggleDebugConsoleVisibility() {
if (!g_Config.bEnableLogging) {
LogManager::GetInstance()->GetConsoleListener()->Show(false);
EnableMenuItem(menu, ID_DEBUG_LOG, MF_GRAYED);
}
else {
LogManager::GetInstance()->GetConsoleListener()->Show(true);
EnableMenuItem(menu, ID_DEBUG_LOG, MF_ENABLED);
}
}
}