ppsspp/Windows/WndMainWindow.cpp
2013-04-23 21:16:27 +08:00

1089 lines
30 KiB
C++

// NOTE: Apologies for the quality of this code, this is really from pre-opensource Dolphin - that is, 2003.
#include <windows.h>
#include <tchar.h>
#include "base/NativeApp.h"
#include "Globals.h"
#include "shellapi.h"
#include "commctrl.h"
#include "input/input_state.h"
#include "Core/Debugger/SymbolMap.h"
#include "Windows/OpenGLBase.h"
#include "Windows/Debugger/Debugger_Disasm.h"
#include "Windows/Debugger/Debugger_MemoryDlg.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 "Windows/EmuThread.h"
#include "resource.h"
#include "Windows/WndMainWindow.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 "GPU/GPUInterface.h"
#include "GPU/GPUState.h"
#include "native/image/png_load.h"
#ifdef THEMES
#include "XPTheme.h"
#endif
#define ENABLE_TOUCH 0
static BOOL g_bFullScreen = FALSE;
static RECT g_normalRC = {0};
extern InputState input_state;
extern unsigned short analog_ctrl_map[];
extern unsigned int key_pad_map[];
extern const char * getVirtualKeyName(unsigned char key);
extern bool saveControlsToFile();
extern bool loadControlsFromFile();
namespace MainWindow
{
HWND hwndMain;
HWND hwndDisplay;
HWND hwndGameList;
static HMENU menu;
static HINSTANCE hInst;
//W32Util::LayeredWindow *layer;
#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);
LRESULT CALLBACK Controls(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_HREDRAW | CS_VREDRAW;
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 = (LPCSTR)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 GetWindowRectAtZoom(int zoom, RECT &rcInner, RECT &rcOuter) {
// GetWindowRect(hwndMain, &rcInner);
rcInner.left = 0;
rcInner.top = 0;
rcInner.right=480*zoom;//+client edge
rcInner.bottom=272*zoom; //+client edge
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;
}
void SavePosition() {
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;
}
}
void ResizeDisplay(bool noWindowMovement = false) {
RECT rc;
GetClientRect(hwndMain, &rc);
if (!noWindowMovement) {
if ((rc.right - rc.left) == PSP_CoreParameter().pixelWidth &&
(rc.bottom - rc.top) == PSP_CoreParameter().pixelHeight)
return;
PSP_CoreParameter().pixelWidth = rc.right - rc.left;
PSP_CoreParameter().pixelHeight = rc.bottom - rc.top;
MoveWindow(hwndDisplay, 0, 0, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight, TRUE);
}
// round up to a zoom factor for the render size.
int zoom = (rc.right - rc.left + 479) / 480;
if (g_Config.SSAntiAliasing) zoom *= 2;
PSP_CoreParameter().renderWidth = 480 * zoom;
PSP_CoreParameter().renderHeight = 272 * zoom;
PSP_CoreParameter().outputWidth = 480 * zoom;
PSP_CoreParameter().outputHeight = 272 * zoom;
if (gpu)
gpu->Resized();
}
void SetZoom(float zoom) {
if (zoom < 5)
g_Config.iWindowZoom = (int) zoom;
RECT rc, rcOuter;
GetWindowRectAtZoom((int) zoom, rc, rcOuter);
MoveWindow(hwndMain, rcOuter.left, rcOuter.top, rcOuter.right - rcOuter.left, rcOuter.bottom - rcOuter.top, TRUE);
ResizeDisplay();
}
BOOL Show(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
int zoom = g_Config.iWindowZoom;
if (zoom < 1) zoom = 1;
if (zoom > 4) zoom = 4;
RECT rc,rcOrig;
GetWindowRectAtZoom(zoom, rcOrig, rc);
u32 style = WS_OVERLAPPEDWINDOW;
hwndMain = CreateWindowEx(0,szWindowClass, "", style,
rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, NULL, NULL, hInstance, NULL);
SetPlaying(0);
if (!hwndMain)
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);
}
hwndDisplay = CreateWindowEx(0,szDisplayClass,TEXT(""),
WS_CHILD|WS_VISIBLE,
0,0,/*rcOrig.left,rcOrig.top,*/rcOrig.right-rcOrig.left,rcOrig.bottom-rcOrig.top,hwndMain,0,hInstance,0);
ShowWindow(hwndMain, nCmdShow);
//accept dragged files
DragAcceptFiles(hwndMain, TRUE);
#if ENABLE_TOUCH
RegisterTouchWindow(hwndDisplay, TWF_WANTPALM);
#endif
SetFocus(hwndMain);
return TRUE;
}
void BrowseAndBoot(std::string defaultPath)
{
std::string fn;
std::string filter = "";
filter += "PSP";
filter += "|";
filter += "*.pbp;*.elf;*.iso;*.cso;*.prx";
filter += "|";
filter += "|";
for (int i=0; i<(int)filter.length(); i++)
{
if (filter[i] == '|')
filter[i] = '\0';
}
if (W32Util::BrowseForFileName(true, GetHWND(), "Load File", defaultPath.size() ? defaultPath.c_str() : 0, filter.c_str(),"*.pbp;*.elf;*.iso;*.cso;",fn))
{
// decode the filename with fullpath
std::string fullpath = fn;
char drive[MAX_PATH];
char dir[MAX_PATH];
char fname[MAX_PATH];
char ext[MAX_PATH];
_splitpath(fullpath.c_str(), drive, dir, fname, ext);
std::string executable = std::string(drive) + std::string(dir) + std::string(fname) + std::string(ext);
NativeMessageReceived("boot", executable.c_str());
}
}
LRESULT CALLBACK DisplayProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_ACTIVATE:
break;
case WM_SETFOCUS:
break;
case WM_SIZE:
break;
case WM_ERASEBKGND:
return DefWindowProc(hWnd, message, wParam, lParam);
case WM_LBUTTONDOWN:
{
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);
input_state.pointer_y[0] = GET_Y_LPARAM(lParam);
if (g_Config.iWindowZoom == 1)
{
input_state.pointer_x[0] *= 2;
input_state.pointer_y[0] *= 2;
}
}
break;
case WM_MOUSEMOVE:
{
lock_guard guard(input_state.lock);
input_state.pointer_x[0] = GET_X_LPARAM(lParam);
input_state.pointer_y[0] = GET_Y_LPARAM(lParam);
if (g_Config.iWindowZoom == 1)
{
input_state.pointer_x[0] *= 2;
input_state.pointer_y[0] *= 2;
}
}
break;
case WM_LBUTTONUP:
{
lock_guard guard(input_state.lock);
input_state.pointer_down[0] = false;
input_state.pointer_x[0] = GET_X_LPARAM(lParam);
input_state.pointer_y[0] = GET_Y_LPARAM(lParam);
if (g_Config.iWindowZoom == 1)
{
input_state.pointer_x[0] *= 2;
input_state.pointer_y[0] *= 2;
}
}
break;
case WM_TOUCH:
{
// TODO: Enabling this section will probably break things on Windows XP.
// We probably need to manually fetch pointers to GetTouchInputInfo and CloseTouchInputHandle.
#if ENABLE_TOUCH
UINT inputCount = LOWORD(wParam);
TOUCHINPUT *inputs = new TOUCHINPUT[inputCount];
if (GetTouchInputInfo((HTOUCHINPUT)lParam,
inputCount,
inputs,
sizeof(TOUCHINPUT)))
{
for (int i = 0; i < inputCount; i++) {
// TODO: process inputs here!
}
if (!CloseTouchInputHandle((HTOUCHINPUT)lParam))
{
// error handling
}
}
else
{
// GetLastError() and error handling
}
delete [] inputs;
return DefWindowProc(hWnd, message, wParam, lParam);
#endif
}
case WM_PAINT:
return DefWindowProc(hWnd, message, wParam, lParam);
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;
switch (message)
{
case WM_CREATE:
loadControlsFromFile();
break;
case WM_MOVE:
SavePosition();
ResizeDisplay();
break;
case WM_SIZE:
SavePosition();
ResizeDisplay();
break;
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case ID_FILE_LOAD:
BrowseAndBoot("");
break;
case ID_FILE_LOAD_MEMSTICK:
{
std::string memStickDir, flash0dir;
GetSysDirectories(memStickDir, flash0dir);
memStickDir += "PSP\\GAME\\";
BrowseAndBoot(memStickDir);
}
break;
case ID_FILE_REFRESHGAMELIST:
break;
case ID_FILE_MEMSTICK:
{
std::string memStickDir, flash0dir;
GetSysDirectories(memStickDir, flash0dir);
ShellExecuteA(NULL, "open", memStickDir.c_str(), 0, 0, SW_SHOW);
}
break;
case ID_EMULATION_RUN:
if (Core_IsStepping()) {
Core_EnableStepping(false);
} else {
NativeMessageReceived("run", "");
}
if (disasmWindow[0])
SendMessage(disasmWindow[0]->GetDlgHandle(), WM_COMMAND, IDC_GO, 0);
break;
case ID_EMULATION_STOP:
if (memoryWindow[0]) {
SendMessage(memoryWindow[0]->GetDlgHandle(), WM_CLOSE, 0, 0);
}
if (disasmWindow[0]) {
SendMessage(disasmWindow[0]->GetDlgHandle(), WM_CLOSE, 0, 0);
}
if (Core_IsStepping()) {
Core_EnableStepping(false);
}
NativeMessageReceived("stop", "");
SetPlaying(0);
Update();
break;
case ID_EMULATION_PAUSE:
if (disasmWindow[0])
{
SendMessage(disasmWindow[0]->GetDlgHandle(), WM_COMMAND, IDC_STOP, 0);
} else if (globalUIState == UISTATE_INGAME) {
Core_EnableStepping(true);
}
break;
case ID_EMULATION_RESET:
NativeMessageReceived("reset", "");
break;
case ID_EMULATION_SPEEDLIMIT:
g_Config.bSpeedLimit = !g_Config.bSpeedLimit;
break;
case ID_FILE_LOADSTATEFILE:
if (W32Util::BrowseForFileName(true, hWnd, "Load state",0,"Save States (*.ppst)\0*.ppst\0All files\0*.*\0\0","ppst",fn))
{
SetCursor(LoadCursor(0,IDC_WAIT));
SaveState::Load(fn, SaveStateActionFinished);
}
break;
case ID_FILE_SAVESTATEFILE:
if (W32Util::BrowseForFileName(false, hWnd, "Save state",0,"Save States (*.ppst)\0*.ppst\0All files\0*.*\0\0","ppst",fn))
{
SetCursor(LoadCursor(0,IDC_WAIT));
SaveState::Save(fn, SaveStateActionFinished);
}
break;
// TODO: Add UI for multiple slots
case ID_FILE_QUICKLOADSTATE:
SetCursor(LoadCursor(0,IDC_WAIT));
SaveState::LoadSlot(0, SaveStateActionFinished);
break;
case ID_FILE_QUICKSAVESTATE:
SetCursor(LoadCursor(0,IDC_WAIT));
SaveState::SaveSlot(0, SaveStateActionFinished);
break;
case ID_OPTIONS_SCREEN1X:
SetZoom(1);
break;
case ID_OPTIONS_SCREEN2X:
SetZoom(2);
break;
case ID_OPTIONS_SCREEN3X:
SetZoom(3);
break;
case ID_OPTIONS_SCREEN4X:
SetZoom(4);
break;
case ID_OPTIONS_MIPMAP:
g_Config.bMipMap = !g_Config.bMipMap;
break;
case ID_OPTIONS_BUFFEREDRENDERING:
g_Config.bBufferedRendering = !g_Config.bBufferedRendering;
if (gpu)
gpu->Resized(); // easy way to force a clear...
break;
case ID_OPTIONS_SHOWDEBUGSTATISTICS:
g_Config.bShowDebugStats = !g_Config.bShowDebugStats;
break;
case ID_OPTIONS_HARDWARETRANSFORM:
g_Config.bHardwareTransform = !g_Config.bHardwareTransform;
break;
case ID_OPTIONS_STRETCHDISPLAY:
g_Config.bStretchToDisplay = !g_Config.bStretchToDisplay;
if (gpu)
gpu->Resized(); // easy way to force a clear...
break;
case ID_OPTIONS_FRAMESKIP:
g_Config.bFrameSkip = !g_Config.bFrameSkip;
break;
case ID_OPTIONS_USEMEDIAENGINE:
g_Config.bUseMediaEngine = !g_Config.bUseMediaEngine;
break;
case ID_FILE_EXIT:
DestroyWindow(hWnd);
break;
case ID_CPU_DYNAREC:
g_Config.bJit = true;
break;
case ID_CPU_INTERPRETER:
g_Config.bJit = false;
break;
case ID_EMULATION_RUNONLOAD:
g_Config.bAutoRun = !g_Config.bAutoRun;
break;
case ID_DEBUG_DUMPNEXTFRAME:
if (gpu)
gpu->DumpNextFrame();
break;
case ID_DEBUG_LOADMAPFILE:
if (W32Util::BrowseForFileName(true, hWnd, "Load .MAP",0,"Maps\0*.map\0All files\0*.*\0\0","map",fn)) {
symbolMap.LoadSymbolMap(fn.c_str());
// HLE_PatchFunctions();
if (disasmWindow[0])
disasmWindow[0]->NotifyMapLoaded();
if (memoryWindow[0])
memoryWindow[0]->NotifyMapLoaded();
}
break;
case ID_DEBUG_SAVEMAPFILE:
if (W32Util::BrowseForFileName(false, hWnd, "Save .MAP",0,"Maps\0*.map\0All files\0*.*\0\0","map",fn))
symbolMap.SaveSymbolMap(fn.c_str());
break;
case ID_DEBUG_RESETSYMBOLTABLE:
symbolMap.ResetSymbolMap();
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_MEMORYVIEW:
if (memoryWindow[0])
memoryWindow[0]->Show(true);
break;
case ID_DEBUG_LOG:
LogManager::GetInstance()->GetConsoleListener()->Show(LogManager::GetInstance()->GetConsoleListener()->Hidden());
break;
case ID_OPTIONS_IGNOREILLEGALREADS:
g_Config.bIgnoreBadMemAccess = !g_Config.bIgnoreBadMemAccess;
break;
case ID_OPTIONS_FULLSCREEN:
g_Config.bFullScreen = !g_Config.bFullScreen ;
if(g_bFullScreen) {
_ViewNormal(hWnd);
} else {
_ViewFullScreen(hWnd);
}
break;
case ID_OPTIONS_WIREFRAME:
g_Config.bDrawWireframe = !g_Config.bDrawWireframe;
break;
case ID_OPTIONS_VERTEXCACHE:
g_Config.bVertexCache = !g_Config.bVertexCache;
break;
case ID_OPTIONS_SHOWFPS:
g_Config.bShowFPSCounter = !g_Config.bShowFPSCounter;
break;
case ID_OPTIONS_DISPLAYRAWFRAMEBUFFER:
g_Config.bDisplayFramebuffer = !g_Config.bDisplayFramebuffer;
break;
case ID_OPTIONS_FASTMEMORY:
g_Config.bFastMemory = !g_Config.bFastMemory;
break;
case ID_OPTIONS_USEVBO:
g_Config.bUseVBO = !g_Config.bUseVBO;
break;
case ID_OPTIONS_LINEARFILTERING:
g_Config.bLinearFiltering = !g_Config.bLinearFiltering;
break;
case ID_OPTIONS_SIMPLE2XSSAA:
g_Config.SSAntiAliasing = !g_Config.SSAntiAliasing;
ResizeDisplay(true);
break;
case ID_OPTIONS_CONTROLS:
DialogManager::EnableAll(FALSE);
DialogBox(hInst, (LPCTSTR)IDD_CONTROLS, hWnd, (DLGPROC)Controls);
DialogManager::EnableAll(TRUE);
break;
case ID_EMULATION_SOUND:
g_Config.bEnableSound = !g_Config.bEnableSound;
break;
case ID_HELP_OPENWEBSITE:
ShellExecute(NULL, "open", "http://www.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;
default:
{
MessageBox(hwndMain,"Unimplemented","Sorry",0);
}
break;
}
break;
case WM_KEYDOWN:
{
static int mojs=0;
mojs ^= 1;
//SetSkinMode(mojs);
}
return 0;
case WM_DROPFILES:
{
HDROP hdrop = (HDROP)wParam;
int count = DragQueryFile(hdrop,0xFFFFFFFF,0,0);
if (count != 1)
{
MessageBox(hwndMain,"You can only load one file at a time","Error",MB_ICONINFORMATION);
}
else
{
TCHAR filename[512];
DragQueryFile(hdrop,0,filename,512);
TCHAR *type = filename+_tcslen(filename)-3;
SendMessage(hWnd, WM_COMMAND, ID_EMULATION_STOP, 0);
// Ugly, need to wait for the stop message to process in the EmuThread.
Sleep(20);
MainWindow::SetPlaying(filename);
MainWindow::Update();
NativeMessageReceived("boot", filename);
}
}
break;
case WM_CLOSE:
Core_Stop();
Core_WaitInactive(200);
EmuThread_Stop();
/*
if (g_Config.bConfirmOnQuit && CCore::IsRunning())
{
if (IDNO==MessageBox(hwndMain,"A game is in progress. Are you sure you want to exit?","Are you sure?",MB_YESNO|MB_ICONQUESTION))
return 1;//or 1?
else
return DefWindowProc(hWnd,message,wParam,lParam);
break;
}
else
*/
return DefWindowProc(hWnd,message,wParam,lParam);
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_USER+1:
if (disasmWindow[0])
SendMessage(disasmWindow[0]->GetDlgHandle(), WM_CLOSE, 0, 0);
if (memoryWindow[0])
SendMessage(memoryWindow[0]->GetDlgHandle(), WM_CLOSE, 0, 0);
disasmWindow[0] = new CDisasm(MainWindow::GetHInstance(), MainWindow::GetHWND(), currentDebugMIPS);
DialogManager::AddDlg(disasmWindow[0]);
disasmWindow[0]->Show(g_Config.bShowDebuggerOnLoad);
if (g_Config.bFullScreen) _ViewFullScreen(hWnd);
memoryWindow[0] = new CMemoryDlg(MainWindow::GetHInstance(), MainWindow::GetHWND(), currentDebugMIPS);
DialogManager::AddDlg(memoryWindow[0]);
if (disasmWindow[0])
disasmWindow[0]->NotifyMapLoaded();
if (memoryWindow[0])
memoryWindow[0]->NotifyMapLoaded();
SetForegroundWindow(hwndMain);
break;
case WM_MENUSELECT:
// This happens when a menu drops down, so this is the only place
// we need to call UpdateMenus.
UpdateMenus();
break;
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_EMULATION_SPEEDLIMIT,g_Config.bSpeedLimit);
// CHECK(ID_OPTIONS_ENABLEFRAMEBUFFER,g_Config.bEnableFrameBuffer);
// CHECK(ID_OPTIONS_EMULATESYSCALL,g_bEmulateSyscall);
CHECKITEM(ID_OPTIONS_DISPLAYRAWFRAMEBUFFER, g_Config.bDisplayFramebuffer);
CHECKITEM(ID_OPTIONS_IGNOREILLEGALREADS,g_Config.bIgnoreBadMemAccess);
CHECKITEM(ID_CPU_INTERPRETER,g_Config.bJit == false);
CHECKITEM(ID_CPU_DYNAREC,g_Config.bJit == true);
CHECKITEM(ID_OPTIONS_BUFFEREDRENDERING, g_Config.bBufferedRendering);
CHECKITEM(ID_OPTIONS_SHOWDEBUGSTATISTICS, g_Config.bShowDebugStats);
CHECKITEM(ID_OPTIONS_WIREFRAME, g_Config.bDrawWireframe);
CHECKITEM(ID_OPTIONS_HARDWARETRANSFORM, g_Config.bHardwareTransform);
CHECKITEM(ID_OPTIONS_FASTMEMORY, g_Config.bFastMemory);
CHECKITEM(ID_OPTIONS_LINEARFILTERING, g_Config.bLinearFiltering);
CHECKITEM(ID_OPTIONS_SIMPLE2XSSAA, g_Config.SSAntiAliasing);
CHECKITEM(ID_OPTIONS_STRETCHDISPLAY, g_Config.bStretchToDisplay);
CHECKITEM(ID_EMULATION_RUNONLOAD, g_Config.bAutoRun);
CHECKITEM(ID_OPTIONS_USEVBO, g_Config.bUseVBO);
CHECKITEM(ID_OPTIONS_VERTEXCACHE, g_Config.bVertexCache);
CHECKITEM(ID_OPTIONS_SHOWFPS, g_Config.bShowFPSCounter);
CHECKITEM(ID_OPTIONS_FRAMESKIP, g_Config.bFrameSkip);
CHECKITEM(ID_OPTIONS_USEMEDIAENGINE, g_Config.bUseMediaEngine);
CHECKITEM(ID_OPTIONS_MIPMAP, g_Config.bMipMap);
CHECKITEM(ID_EMULATION_SOUND, g_Config.bEnableSound);
EnableMenuItem(menu,ID_EMULATION_RUN, (Core_IsStepping() || globalUIState == UISTATE_PAUSEMENU) ? MF_ENABLED : MF_GRAYED);
EnableMenuItem(menu,ID_EMULATION_PAUSE, globalUIState == UISTATE_INGAME ? MF_ENABLED : MF_GRAYED);
EnableMenuItem(menu,ID_EMULATION_STOP, globalUIState == UISTATE_INGAME ? MF_ENABLED : MF_GRAYED);
EnableMenuItem(menu,ID_EMULATION_RESET, globalUIState == UISTATE_INGAME ? MF_ENABLED : MF_GRAYED);
UINT enable = globalUIState == UISTATE_MENU ? MF_ENABLED : MF_GRAYED;
EnableMenuItem(menu,ID_FILE_LOAD,enable);
EnableMenuItem(menu,ID_FILE_LOAD_MEMSTICK,enable);
EnableMenuItem(menu,ID_FILE_SAVESTATEFILE,!enable);
EnableMenuItem(menu,ID_FILE_LOADSTATEFILE,!enable);
EnableMenuItem(menu,ID_FILE_QUICKSAVESTATE,!enable);
EnableMenuItem(menu,ID_FILE_QUICKLOADSTATE,!enable);
EnableMenuItem(menu,ID_CPU_DYNAREC,enable);
EnableMenuItem(menu,ID_CPU_INTERPRETER,enable);
EnableMenuItem(menu,ID_EMULATION_STOP,!enable);
static const int zoomitems[4] = {
ID_OPTIONS_SCREEN1X,
ID_OPTIONS_SCREEN2X,
ID_OPTIONS_SCREEN3X,
ID_OPTIONS_SCREEN4X,
};
for (int i = 0; i < 4; i++) {
CheckMenuItem(menu, zoomitems[i], MF_BYCOMMAND | ((i == g_Config.iWindowZoom - 1) ? MF_CHECKED : MF_UNCHECKED));
}
}
// 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);
char temp[256];
sprintf(temp, "PPSSPP %s", PPSSPP_GIT_VERSION);
SetWindowText(versionBox, temp);
}
return TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return TRUE;
}
break;
}
return FALSE;
}
static const char *controllist[] = {
"TURBO MODE\tHold TAB",
"Start\tSpace",
"Select\tV",
"Square\tA",
"Triangle\tS",
"Circle\tX",
"Cross\tZ",
"Left Trigger\tQ",
"Right Trigger\tW",
"Up\tArrow Up",
"Down\tArrow Down",
"Left\tArrow Left",
"Right\tArrow Right",
"Analog Up\tI",
"Analog Down\tK",
"Analog Left\tJ",
"Analog Right\tL",
"Rapid Fire\tShift",
};
static HHOOK pKeydownHook;
static const int control_map_size = IDC_EDIT_KEY_ANALOG_RIGHT - IDC_EDIT_KEY_TURBO + 1;
static u8 control_map[control_map_size];
RECT getRedrawRect(HWND hWnd) {
RECT rc;
HWND hDlg = GetParent(hWnd);
GetWindowRect(hWnd, &rc);
POINT pt = {0, 0};
ScreenToClient(hDlg, &pt);
rc.left += pt.x;
rc.right += pt.x;
rc.top += pt.y;
rc.bottom += pt.y;
return rc;
}
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
HWND hEdit = GetFocus();
UINT nCtrlID = GetDlgCtrlID(hEdit);
if (nCtrlID < IDC_EDIT_KEY_TURBO || nCtrlID > IDC_EDIT_KEY_ANALOG_RIGHT) {
return CallNextHookEx(pKeydownHook, nCode, wParam, lParam);
}
if (!(lParam&(1<<31))) {
// key down
HWND hDlg = GetParent(hEdit);
const char *str = getVirtualKeyName(wParam);
if (str) {
control_map[nCtrlID - IDC_EDIT_KEY_TURBO] = wParam;
SetWindowTextA(hEdit, str);
RECT rc = getRedrawRect(hEdit);
InvalidateRect(hDlg, &rc, false);
}
else
MessageBoxA(hDlg, "Not supported!", "controller", MB_OK);
}
return 1;
}
HBITMAP LoadImageFromResource(HINSTANCE hInstance,LPCTSTR pszResourceName, LPCTSTR lpType)
{
HRSRC hrsrc = FindResource(hInstance, pszResourceName, lpType);
if (!hrsrc)
return FALSE;
DWORD dwlen = SizeofResource(hInstance, hrsrc);
BYTE *lpRsrc = (BYTE*)LoadResource(hInstance, hrsrc);
if (!lpRsrc)
return FALSE;
int width, height;
unsigned char *image_data = 0;
bool bResult = pngLoadPtr(lpRsrc, dwlen, &width, &height, &image_data, false) != 0;
FreeResource(lpRsrc);
if (!bResult)
return 0;
HBITMAP hbm = CreateBitmap(width, height, 1, 32, image_data);
free(image_data);
return hbm;
}
void BitBlt(HBITMAP hbm, HDC dstDC, int dstX, int dstY, int width, int height, int srcX, int srcY)
{
HDC hCompDC = CreateCompatibleDC(dstDC);
HBITMAP oldbm = (HBITMAP)SelectObject(hCompDC, hbm);
BitBlt(dstDC, dstX, dstY, width, height, hCompDC, srcX, srcY, SRCCOPY);
SelectObject(hCompDC, oldbm);
DeleteObject(hCompDC);
}
// Message handler for control box.
LRESULT CALLBACK Controls(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
static HBITMAP hbm = 0;
switch (message)
{
case WM_INITDIALOG:
W32Util::CenterWindow(hDlg);
{
// TODO: connect to keyboard device instead
if (!hbm)
hbm = LoadImageFromResource(hInst, MAKEINTRESOURCE(IDB_IMAGE_PSP), "IMAGE");
int key_pad_size = (IDC_EDIT_KEYRIGHT - IDC_EDIT_KEY_TURBO + 1);
for (u32 i = 0; i <= IDC_EDIT_KEY_ANALOG_RIGHT - IDC_EDIT_KEY_TURBO; i++) {
HWND hEdit = GetDlgItem(hDlg, IDC_EDIT_KEY_TURBO + i);
if (IDC_EDIT_KEY_TURBO + i <= IDC_EDIT_KEYRIGHT)
control_map[i] = key_pad_map[i * 2];
else
control_map[i] = analog_ctrl_map[(i - key_pad_size) * 2];
SetWindowTextA(hEdit, getVirtualKeyName(control_map[i]));
}
ComboBox_AddString(GetDlgItem(hDlg, IDC_FORCE_INPUT_DEVICE), "None");
ComboBox_AddString(GetDlgItem(hDlg, IDC_FORCE_INPUT_DEVICE), "XInput");
ComboBox_AddString(GetDlgItem(hDlg, IDC_FORCE_INPUT_DEVICE), "DirectInput");
if ((g_Config.iForceInputDevice < 0) || (g_Config.iForceInputDevice > 1))
{
ComboBox_SetCurSel(GetDlgItem(hDlg, IDC_FORCE_INPUT_DEVICE), 0);
}
else
{
ComboBox_SetCurSel(GetDlgItem(hDlg, IDC_FORCE_INPUT_DEVICE), (g_Config.iForceInputDevice + 1));
}
DWORD dwThreadID = GetWindowThreadProcessId(hDlg,NULL);
pKeydownHook = SetWindowsHookEx(WH_KEYBOARD,KeyboardProc, NULL, dwThreadID);
}
return TRUE;
case WM_PAINT:
{
PAINTSTRUCT pst;
HDC hdc = BeginPaint(hDlg, &pst);
BITMAP bm;
GetObject(hbm, sizeof(BITMAP), &bm);
int width = bm.bmWidth;
int height = bm.bmHeight;
BitBlt(hbm, hdc, 0, 0, width, height, 0 , 0);
EndPaint(hDlg, &pst);
return TRUE;
}
case WM_CTLCOLORSTATIC:
{
HDC hdc=(HDC)wParam;
SetBkMode(hdc, TRANSPARENT);
return (LRESULT)GetStockObject(NULL_BRUSH);
}
case WM_CTLCOLOREDIT:
{
if ((HWND)lParam == GetDlgItem(hDlg, IDC_FORCE_INPUT_DEVICE))
return FALSE;
HDC hdc = (HDC)wParam;
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, RGB(255, 0, 0));
HWND hEdit = (HWND)lParam;
RECT rc = getRedrawRect(hEdit);
RECT clientrc;
GetClientRect(hEdit, &clientrc);
BitBlt(hbm, hdc, 0, 0, rc.right - rc.left, rc.bottom - rc.top, rc.left, rc.top);
char str[11];
GetWindowTextA(hEdit, str, 10);
DrawTextA(hdc, str, (int)strlen(str), &clientrc, DT_CENTER|DT_SINGLELINE);
return (LRESULT)GetStockObject(NULL_BRUSH);
}
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
if (LOWORD(wParam) == IDOK) {
g_Config.iForceInputDevice = (ComboBox_GetCurSel(GetDlgItem(hDlg, IDC_FORCE_INPUT_DEVICE)) - 1);
int key_pad_size = (IDC_EDIT_KEYRIGHT - IDC_EDIT_KEY_TURBO + 1);
for (u32 i = 0; i <= IDC_EDIT_KEY_ANALOG_RIGHT - IDC_EDIT_KEY_TURBO; i++) {
if (IDC_EDIT_KEY_TURBO + i <= IDC_EDIT_KEYRIGHT)
key_pad_map[i * 2] = control_map[i];
else
analog_ctrl_map[(i - key_pad_size) * 2] = control_map[i];
}
saveControlsToFile();
}
UnhookWindowsHookEx(pKeydownHook);
EndDialog(hDlg, LOWORD(wParam));
if (hbm) {
DeleteObject(hbm);
hbm = 0;
}
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 _ViewNormal(HWND hWnd)
{
// put caption and border styles back
DWORD dwOldStyle = ::GetWindowLong(hWnd, GWL_STYLE);
DWORD dwNewStyle = dwOldStyle | WS_CAPTION | WS_THICKFRAME;
::SetWindowLong(hWnd, GWL_STYLE, dwNewStyle);
// put back the menu bar
::SetMenu(hWnd, menu);
// resize to normal view
// NOTE: use SWP_FRAMECHANGED to force redraw non-client
const int x = g_normalRC.left;
const int y = g_normalRC.top;
const int cx = g_normalRC.right - g_normalRC.left;
const int cy = g_normalRC.bottom - g_normalRC.top;
::SetWindowPos(hWnd, HWND_NOTOPMOST, x, y, cx, cy, SWP_FRAMECHANGED);
// reset full screen indicator
g_bFullScreen = FALSE;
ResizeDisplay();
}
void _ViewFullScreen(HWND hWnd)
{
// keep in mind normal window rectangle
::GetWindowRect(hWnd, &g_normalRC);
// remove caption and border styles
DWORD dwOldStyle = ::GetWindowLong(hWnd, GWL_STYLE);
DWORD dwNewStyle = dwOldStyle & ~(WS_CAPTION | WS_THICKFRAME);
::SetWindowLong(hWnd, GWL_STYLE, dwNewStyle);
// remove the menu bar
::SetMenu(hWnd, NULL);
// resize to full screen view
// NOTE: use SWP_FRAMECHANGED to force redraw non-client
const int x = 0;
const int y = 0;
const int cx = ::GetSystemMetrics(SM_CXSCREEN);
const int cy = ::GetSystemMetrics(SM_CYSCREEN);
::SetWindowPos(hWnd, HWND_TOPMOST, x, y, cx, cy, SWP_FRAMECHANGED);
// set full screen indicator
g_bFullScreen = TRUE;
ResizeDisplay();
}
void SetPlaying(const char *text)
{
char temp[256];
if (text == 0)
snprintf(temp, 256, "PPSSPP %s", PPSSPP_GIT_VERSION);
else
snprintf(temp, 256, "%s - PPSSPP %s", text, PPSSPP_GIT_VERSION);
temp[255] = '\0';
SetWindowText(hwndMain, temp);
}
void SaveStateActionFinished(bool result, void *userdata)
{
if (!result)
MessageBox(0, "Savestate failure. Using savestates between different PPSSPP versions is not supported.", "Sorry", MB_OK);
SetCursor(LoadCursor(0, IDC_ARROW));
}
HINSTANCE GetHInstance()
{
return hInst;
}
}