2014-06-12 21:48:04 -04:00
|
|
|
#include "stdafx.h"
|
2014-06-21 19:03:13 -04:00
|
|
|
#include "Resource.h"
|
2014-06-12 21:48:04 -04:00
|
|
|
#include "MainWindow.h"
|
2014-06-23 19:02:09 -04:00
|
|
|
#include "../Core/Console.h"
|
|
|
|
#include "../Utilities/Timer.h"
|
2014-06-21 15:43:41 -04:00
|
|
|
#include "InputManager.h"
|
|
|
|
|
2014-06-12 21:48:04 -04:00
|
|
|
using namespace DirectX;
|
|
|
|
|
2014-06-23 13:52:53 -04:00
|
|
|
namespace NES {
|
2014-06-20 21:48:55 -04:00
|
|
|
MainWindow* MainWindow::Instance = nullptr;
|
|
|
|
|
2014-06-12 21:48:04 -04:00
|
|
|
bool MainWindow::Initialize()
|
|
|
|
{
|
|
|
|
if(FAILED(InitWindow())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-06-23 19:02:09 -04:00
|
|
|
_renderer.reset(new Renderer(_hWnd));
|
|
|
|
_soundManager.reset(new SoundManager(_hWnd));
|
2014-06-22 22:15:35 -04:00
|
|
|
|
2014-06-12 21:48:04 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-06-15 11:25:29 -04:00
|
|
|
void CreateConsole()
|
|
|
|
{
|
|
|
|
CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
|
|
|
|
int consoleHandleR, consoleHandleW;
|
|
|
|
long stdioHandle;
|
|
|
|
FILE *fptr;
|
|
|
|
|
|
|
|
AllocConsole();
|
|
|
|
std::wstring strW = L"Dev Console";
|
|
|
|
SetConsoleTitle(strW.c_str());
|
|
|
|
|
|
|
|
EnableMenuItem(GetSystemMenu(GetConsoleWindow(), FALSE), SC_CLOSE, MF_GRAYED);
|
|
|
|
DrawMenuBar(GetConsoleWindow());
|
|
|
|
|
|
|
|
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &consoleInfo);
|
|
|
|
|
|
|
|
stdioHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
|
|
|
|
consoleHandleR = _open_osfhandle(stdioHandle, _O_TEXT);
|
|
|
|
fptr = _fdopen(consoleHandleR, "r");
|
|
|
|
*stdin = *fptr;
|
|
|
|
setvbuf(stdin, NULL, _IONBF, 0);
|
|
|
|
|
|
|
|
stdioHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
consoleHandleW = _open_osfhandle(stdioHandle, _O_TEXT);
|
|
|
|
fptr = _fdopen(consoleHandleW, "w");
|
|
|
|
*stdout = *fptr;
|
|
|
|
setvbuf(stdout, NULL, _IONBF, 0);
|
|
|
|
|
|
|
|
stdioHandle = (long)GetStdHandle(STD_ERROR_HANDLE);
|
|
|
|
*stderr = *fptr;
|
|
|
|
setvbuf(stderr, NULL, _IONBF, 0);
|
|
|
|
}
|
|
|
|
|
2014-06-12 21:48:04 -04:00
|
|
|
int MainWindow::Run()
|
|
|
|
{
|
2014-06-23 19:02:09 -04:00
|
|
|
#if _DEBUG
|
|
|
|
CreateConsole();
|
|
|
|
#endif
|
2014-06-15 11:25:29 -04:00
|
|
|
|
2014-06-12 21:48:04 -04:00
|
|
|
Initialize();
|
|
|
|
|
2014-06-23 13:52:53 -04:00
|
|
|
InitializeOptions();
|
2014-06-21 15:43:41 -04:00
|
|
|
InputManager inputManager;
|
|
|
|
ControlManager::RegisterControlDevice(&inputManager, 0);
|
2014-06-23 13:52:53 -04:00
|
|
|
|
|
|
|
HACCEL hAccel = LoadAccelerators(_hInstance, MAKEINTRESOURCE(IDC_Accelerator));
|
2014-06-21 19:03:13 -04:00
|
|
|
if(hAccel == nullptr) {
|
|
|
|
//error
|
|
|
|
std::cout << "error";
|
|
|
|
}
|
2014-06-23 13:52:53 -04:00
|
|
|
|
2014-06-12 21:48:04 -04:00
|
|
|
MSG msg = { 0 };
|
|
|
|
while(WM_QUIT != msg.message) {
|
|
|
|
if(PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
2014-06-21 19:03:13 -04:00
|
|
|
if(!TranslateAccelerator(_hWnd, hAccel, &msg)) {
|
|
|
|
TranslateMessage(&msg);
|
|
|
|
DispatchMessage(&msg);
|
|
|
|
}
|
2014-06-12 21:48:04 -04:00
|
|
|
} else {
|
2014-06-23 19:02:09 -04:00
|
|
|
_renderer->Render();
|
2014-06-12 21:48:04 -04:00
|
|
|
}
|
2014-06-18 22:54:23 -04:00
|
|
|
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(1));
|
2014-06-12 21:48:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return (int)msg.wParam;
|
|
|
|
}
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
// Register class and create window
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
HRESULT MainWindow::InitWindow()
|
|
|
|
{
|
|
|
|
// Register class
|
|
|
|
WNDCLASSEX wcex;
|
|
|
|
wcex.cbSize = sizeof(WNDCLASSEX);
|
|
|
|
wcex.style = CS_HREDRAW | CS_VREDRAW;
|
|
|
|
wcex.lpfnWndProc = WndProc;
|
|
|
|
wcex.cbClsExtra = 0;
|
|
|
|
wcex.cbWndExtra = 0;
|
|
|
|
wcex.hInstance = _hInstance;
|
|
|
|
wcex.hIcon = LoadIcon(_hInstance, (LPCTSTR)IDI_GUI);
|
|
|
|
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
|
|
|
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
|
|
|
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_GUI);
|
|
|
|
wcex.lpszClassName = L"NESEmu";
|
|
|
|
wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);
|
|
|
|
if(!RegisterClassEx(&wcex))
|
|
|
|
return E_FAIL;
|
|
|
|
|
|
|
|
// Create window
|
2014-06-22 22:15:35 -04:00
|
|
|
RECT rc = { 0, 0, 800, 700 };
|
2014-06-12 21:48:04 -04:00
|
|
|
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
|
|
|
|
_hWnd = CreateWindow(L"NESEmu", L"NESEmu",
|
|
|
|
WS_OVERLAPPEDWINDOW,
|
|
|
|
CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, nullptr, nullptr, _hInstance,
|
|
|
|
nullptr);
|
|
|
|
if(!_hWnd) {
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ShowWindow(_hWnd, _nCmdShow);
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
INT_PTR CALLBACK MainWindow::About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
|
|
|
UNREFERENCED_PARAMETER(lParam);
|
2014-06-23 13:52:53 -04:00
|
|
|
switch(message) {
|
|
|
|
case WM_INITDIALOG:
|
2014-06-12 21:48:04 -04:00
|
|
|
return (INT_PTR)TRUE;
|
2014-06-23 13:52:53 -04:00
|
|
|
|
|
|
|
case WM_COMMAND:
|
|
|
|
if(LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
|
|
|
|
EndDialog(hDlg, LOWORD(wParam));
|
|
|
|
return (INT_PTR)TRUE;
|
|
|
|
}
|
|
|
|
break;
|
2014-06-12 21:48:04 -04:00
|
|
|
}
|
|
|
|
return (INT_PTR)FALSE;
|
|
|
|
}
|
|
|
|
|
2014-06-23 13:52:53 -04:00
|
|
|
void MainWindow::InitializeOptions()
|
|
|
|
{
|
|
|
|
Console::SetFlags(EmulationFlags::LimitFPS);
|
|
|
|
}
|
|
|
|
|
|
|
|
wstring MainWindow::SelectROM()
|
2014-06-12 21:48:04 -04:00
|
|
|
{
|
2014-06-20 21:48:55 -04:00
|
|
|
wchar_t buffer[2000];
|
|
|
|
|
|
|
|
OPENFILENAME ofn;
|
2014-06-23 13:52:53 -04:00
|
|
|
ZeroMemory(&ofn, sizeof(ofn));
|
2014-06-20 21:48:55 -04:00
|
|
|
ofn.lStructSize = sizeof(ofn);
|
|
|
|
ofn.hwndOwner = nullptr;
|
|
|
|
ofn.lpstrFile = buffer;
|
|
|
|
ofn.lpstrFile[0] = '\0';
|
|
|
|
ofn.nMaxFile = sizeof(buffer);
|
|
|
|
ofn.lpstrFilter = L"NES Roms\0*.NES\0All\0*.*";
|
|
|
|
ofn.nFilterIndex = 1;
|
|
|
|
ofn.lpstrFileTitle = nullptr;
|
2014-06-23 13:52:53 -04:00
|
|
|
ofn.nMaxFileTitle = 0;
|
|
|
|
ofn.lpstrInitialDir = nullptr;
|
|
|
|
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
|
2014-06-20 21:48:55 -04:00
|
|
|
|
|
|
|
GetOpenFileName(&ofn);
|
|
|
|
|
2014-06-23 13:52:53 -04:00
|
|
|
return wstring(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::Start(wstring romFilename = L"")
|
|
|
|
{
|
|
|
|
if(_emuThread) {
|
|
|
|
Stop(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(romFilename.length() > 0) {
|
|
|
|
_currentROM = romFilename;
|
|
|
|
_console.reset(new Console(_currentROM));
|
|
|
|
}
|
|
|
|
|
2014-06-23 14:18:52 -04:00
|
|
|
if(!_console && _currentROM.length() > 0) {
|
2014-06-23 13:52:53 -04:00
|
|
|
_console.reset(new Console(_currentROM));
|
|
|
|
}
|
2014-06-20 21:48:55 -04:00
|
|
|
|
2014-06-23 13:52:53 -04:00
|
|
|
if(_console) {
|
2014-06-21 19:03:13 -04:00
|
|
|
_emuThread.reset(new thread(&Console::Run, _console.get()));
|
2014-06-23 13:52:53 -04:00
|
|
|
|
|
|
|
SetMenuEnabled(ID_NES_PAUSE, true);
|
|
|
|
SetMenuEnabled(ID_NES_RESET, true);
|
|
|
|
SetMenuEnabled(ID_NES_STOP, true);
|
|
|
|
SetMenuEnabled(ID_NES_RESUME, false);
|
2014-06-20 21:48:55 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-23 13:52:53 -04:00
|
|
|
void MainWindow::Stop(bool powerOff)
|
2014-06-20 21:48:55 -04:00
|
|
|
{
|
2014-06-23 19:02:09 -04:00
|
|
|
_soundManager->Reset();
|
2014-06-20 21:48:55 -04:00
|
|
|
if(_console) {
|
|
|
|
_console->Stop();
|
2014-06-23 13:52:53 -04:00
|
|
|
if(powerOff) {
|
|
|
|
_console.release();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(_emuThread) {
|
2014-06-21 19:03:13 -04:00
|
|
|
_emuThread->join();
|
2014-06-23 13:52:53 -04:00
|
|
|
_emuThread.release();
|
|
|
|
}
|
|
|
|
|
|
|
|
SetMenuEnabled(ID_NES_PAUSE, false);
|
|
|
|
SetMenuEnabled(ID_NES_RESET, !powerOff);
|
|
|
|
SetMenuEnabled(ID_NES_STOP, !powerOff);
|
|
|
|
SetMenuEnabled(ID_NES_RESUME, true);
|
|
|
|
}
|
2014-06-21 19:03:13 -04:00
|
|
|
|
2014-06-23 13:52:53 -04:00
|
|
|
void MainWindow::Reset()
|
|
|
|
{
|
|
|
|
if(_console) {
|
2014-06-23 19:02:09 -04:00
|
|
|
_soundManager->Reset();
|
2014-06-23 13:52:53 -04:00
|
|
|
_console->Reset();
|
2014-06-20 21:48:55 -04:00
|
|
|
}
|
2014-06-12 21:48:04 -04:00
|
|
|
}
|
|
|
|
|
2014-06-23 13:52:53 -04:00
|
|
|
void MainWindow::SetMenuEnabled(int resourceID, bool enabled)
|
|
|
|
{
|
|
|
|
HMENU hMenu = GetMenu(_hWnd);
|
|
|
|
EnableMenuItem(hMenu, resourceID, enabled ? MF_ENABLED : MF_GRAYED);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MainWindow::IsMenuChecked(int resourceID)
|
|
|
|
{
|
|
|
|
HMENU hMenu = GetMenu(_hWnd);
|
|
|
|
return (GetMenuState(hMenu, resourceID, MF_BYCOMMAND) & MF_CHECKED) == MF_CHECKED;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MainWindow::SetMenuCheck(int resourceID, bool checked)
|
2014-06-21 19:03:13 -04:00
|
|
|
{
|
|
|
|
HMENU hMenu = GetMenu(_hWnd);
|
2014-06-23 13:52:53 -04:00
|
|
|
CheckMenuItem(hMenu, resourceID, MF_BYCOMMAND | (checked ? MF_CHECKED : MF_UNCHECKED));
|
|
|
|
return checked;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MainWindow::ToggleMenuCheck(int resourceID)
|
|
|
|
{
|
|
|
|
return SetMenuCheck(resourceID, !IsMenuChecked(resourceID));
|
2014-06-21 19:03:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::LimitFPS_Click()
|
|
|
|
{
|
|
|
|
if(ToggleMenuCheck(ID_OPTIONS_LIMITFPS)) {
|
|
|
|
Console::SetFlags(EmulationFlags::LimitFPS);
|
|
|
|
} else {
|
|
|
|
Console::ClearFlags(EmulationFlags::LimitFPS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-22 08:38:42 -04:00
|
|
|
void MainWindow::SaveTestResult()
|
|
|
|
{
|
|
|
|
if(_console) {
|
|
|
|
_console->SaveTestResult();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
vector<wstring> MainWindow::GetFilesInFolder(wstring folderMask)
|
|
|
|
{
|
|
|
|
HANDLE hFind;
|
|
|
|
WIN32_FIND_DATA data;
|
|
|
|
|
|
|
|
vector<wstring> files;
|
|
|
|
|
|
|
|
hFind = FindFirstFile(folderMask.c_str(), &data);
|
|
|
|
if(hFind != INVALID_HANDLE_VALUE) {
|
|
|
|
do {
|
|
|
|
files.push_back(data.cFileName);
|
|
|
|
} while(FindNextFile(hFind, &data));
|
|
|
|
FindClose(hFind);
|
|
|
|
}
|
|
|
|
|
|
|
|
return files;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::RunTests()
|
|
|
|
{
|
2014-06-23 13:52:53 -04:00
|
|
|
Stop(true);
|
2014-06-22 08:40:57 -04:00
|
|
|
int passCount = 0;
|
2014-06-22 08:42:47 -04:00
|
|
|
int failCount = 0;
|
2014-06-22 08:40:57 -04:00
|
|
|
int totalCount = 0;
|
2014-06-22 08:38:42 -04:00
|
|
|
for(wstring testROM : GetFilesInFolder(L"TestSuite/*.nes")) {
|
|
|
|
ifstream testResult(L"TestSuite/" + testROM + L".trt", ios::in | ios::binary);
|
|
|
|
|
|
|
|
if(testResult) {
|
|
|
|
uint8_t* expectedResult = new uint8_t[256 * 240 * 4];
|
|
|
|
|
|
|
|
Console *console = new Console(L"TestSuite/" + testROM);
|
|
|
|
std::wcout << testROM << ": ";
|
|
|
|
if(console->RunTest(expectedResult)) {
|
|
|
|
std::cout << "Passed";
|
2014-06-22 08:40:57 -04:00
|
|
|
passCount++;
|
2014-06-22 08:38:42 -04:00
|
|
|
} else {
|
|
|
|
std::cout << "FAILED";
|
2014-06-22 08:42:47 -04:00
|
|
|
failCount++;
|
2014-06-22 08:38:42 -04:00
|
|
|
}
|
|
|
|
std::cout << std::endl;
|
|
|
|
|
|
|
|
testResult.close();
|
|
|
|
|
|
|
|
delete[] expectedResult;
|
|
|
|
} else {
|
2014-06-22 08:42:47 -04:00
|
|
|
std::wcout << testROM << ": [No result]" << std::endl;
|
2014-06-22 08:38:42 -04:00
|
|
|
}
|
2014-06-22 08:40:57 -04:00
|
|
|
totalCount++;
|
2014-06-22 08:38:42 -04:00
|
|
|
}
|
2014-06-23 13:52:53 -04:00
|
|
|
Stop(true);
|
2014-06-22 08:40:57 -04:00
|
|
|
|
|
|
|
std::cout << "------------------------" << std::endl;
|
2014-06-22 08:42:47 -04:00
|
|
|
std::cout << passCount << " / " << totalCount << " + " << failCount << " FAILED" << std::endl;
|
2014-06-22 08:40:57 -04:00
|
|
|
std::cout << "------------------------" << std::endl;
|
2014-06-22 08:38:42 -04:00
|
|
|
}
|
|
|
|
|
2014-06-12 21:48:04 -04:00
|
|
|
LRESULT CALLBACK MainWindow::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
2014-06-21 19:03:13 -04:00
|
|
|
static MainWindow *mainWindow = MainWindow::GetInstance();
|
2014-06-23 13:52:53 -04:00
|
|
|
wstring filename;
|
2014-06-12 21:48:04 -04:00
|
|
|
PAINTSTRUCT ps;
|
|
|
|
int wmId, wmEvent;
|
|
|
|
HDC hdc;
|
|
|
|
|
|
|
|
switch(message) {
|
|
|
|
case WM_COMMAND:
|
|
|
|
wmId = LOWORD(wParam);
|
|
|
|
wmEvent = HIWORD(wParam);
|
|
|
|
// Parse the menu selections:
|
2014-06-21 19:03:13 -04:00
|
|
|
switch (wmId) {
|
2014-06-22 08:38:42 -04:00
|
|
|
case ID_FILE_OPEN:
|
2014-06-23 13:52:53 -04:00
|
|
|
filename = mainWindow->SelectROM();
|
|
|
|
if(filename.length() > 0) {
|
|
|
|
mainWindow->Start(filename);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ID_FILE_EXIT:
|
|
|
|
DestroyWindow(hWnd);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ID_NES_RESUME:
|
|
|
|
mainWindow->Start();
|
|
|
|
break;
|
|
|
|
case ID_NES_PAUSE:
|
|
|
|
mainWindow->Stop(false);
|
2014-06-20 21:48:55 -04:00
|
|
|
break;
|
2014-06-23 13:52:53 -04:00
|
|
|
case ID_NES_STOP:
|
|
|
|
mainWindow->Stop(true);
|
|
|
|
break;
|
|
|
|
case ID_NES_RESET:
|
|
|
|
mainWindow->Reset();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ID_OPTIONS_LIMITFPS:
|
|
|
|
mainWindow->LimitFPS_Click();
|
|
|
|
break;
|
|
|
|
|
2014-06-22 08:38:42 -04:00
|
|
|
case ID_TESTS_RUNTESTS:
|
|
|
|
mainWindow->RunTests();
|
|
|
|
break;
|
|
|
|
case ID_TESTS_SAVETESTRESULT:
|
|
|
|
mainWindow->SaveTestResult();
|
2014-06-21 19:03:13 -04:00
|
|
|
break;
|
2014-06-23 13:52:53 -04:00
|
|
|
|
2014-06-22 08:38:42 -04:00
|
|
|
case ID_HELP_ABOUT:
|
2014-06-12 21:48:04 -04:00
|
|
|
DialogBox(nullptr, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
|
|
|
|
break;
|
2014-06-23 13:52:53 -04:00
|
|
|
|
2014-06-12 21:48:04 -04:00
|
|
|
default:
|
|
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_PAINT:
|
|
|
|
hdc = BeginPaint(hWnd, &ps);
|
|
|
|
EndPaint(hWnd, &ps);
|
|
|
|
break;
|
|
|
|
|
2014-06-21 19:03:13 -04:00
|
|
|
case WM_WINDOWPOSCHANGING:
|
|
|
|
WINDOWPOS* windowPos;
|
|
|
|
windowPos = (WINDOWPOS*)lParam;
|
|
|
|
|
|
|
|
RECT clientRect;
|
|
|
|
RECT windowRect;
|
|
|
|
LONG xGap;
|
|
|
|
LONG yGap;
|
|
|
|
GetWindowRect(hWnd, &windowRect);
|
|
|
|
GetClientRect(hWnd, &clientRect);
|
|
|
|
|
|
|
|
xGap = (windowRect.right - windowRect.left) - (clientRect.right - clientRect.left);
|
|
|
|
yGap = (windowRect.bottom - windowRect.top) - (clientRect.bottom - clientRect.top);
|
|
|
|
|
|
|
|
windowPos->cy = (windowPos->cx - xGap) * 240 / 256 + yGap;
|
|
|
|
break;
|
|
|
|
|
2014-06-12 21:48:04 -04:00
|
|
|
case WM_DESTROY:
|
2014-06-23 13:52:53 -04:00
|
|
|
mainWindow->Stop(true);
|
2014-06-12 21:48:04 -04:00
|
|
|
PostQuitMessage(0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|