diff --git a/win32/CWindow.cpp b/win32/CWindow.cpp new file mode 100644 index 00000000..0ba23dcc --- /dev/null +++ b/win32/CWindow.cpp @@ -0,0 +1,861 @@ +//MemView dialog was copied and adapted from DeSmuME: http://sourceforge.net/projects/desmume/ +//Authors: DeSmuME team + +// gocha: +// 1. According to Normmatt, YopYop had released the final version of original desmume as public domain. +// 2. I asked DeSmuME team about the problem between GPL and Snes9x license, but no one raised any objections. +// Therefore, I decided to include this code into snes9x-rr project. +// Dear DeSmuME coders: if it is not allowed, please tell me. + +/* Copyright (C) 2006 yopyop + Copyright (C) 2006-2010 DeSmuME team + + This file is part of DeSmuME + + DeSmuME 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; either version 2 of the License, or + (at your option) any later version. + + DeSmuME 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 for more details. + + You should have received a copy of the GNU General Public License + along with DeSmuME; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#define NOMINMAX +#include +#include +#include +#include "CWindow.h" +#include "wsnes9x.h" +#include "rsrc/resource.h" + +using namespace std; + +#define hAppInst GUI.hInstance + +//----------------------------------------------------------------------------- +// The Toolkit - Helpers +//----------------------------------------------------------------------------- + +DWORD GetFontQuality() +{ + BOOL aaEnabled = FALSE; + UINT aaType = FE_FONTSMOOTHINGSTANDARD; + + SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &aaEnabled, 0); + if (aaEnabled == FALSE) + return NONANTIALIASED_QUALITY; + + if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &aaType, 0) == FALSE) + return ANTIALIASED_QUALITY; + + if (aaType == FE_FONTSMOOTHINGCLEARTYPE) + return CLEARTYPE_QUALITY; + else + return ANTIALIASED_QUALITY; +} + +int DrawText(HDC hDC, TCHAR* text, int X, int Y, int Width, int Height, UINT format) +{ + RECT rc; + SetRect(&rc, X, Y, X+Width, Y+Height); + return DrawText(hDC, text, -1, &rc, format); +} + +void GetFontSize(HWND hWnd, HFONT hFont, LPSIZE size) +{ + HDC dc = GetDC(hWnd); + HFONT oldfont = (HFONT)SelectObject(dc, hFont); + + GetTextExtentPoint32(dc, TEXT(" "), 1, size); + + SelectObject(dc, oldfont); + ReleaseDC(hWnd, dc); +} + +void MakeBitmapPseudoTransparent(HBITMAP hBmp, COLORREF cKeyColor, COLORREF cNewKeyColor) +{ + uint8 keyr = (cKeyColor >> 16) & 0xFF; + uint8 keyg = (cKeyColor >> 8) & 0xFF; + uint8 keyb = cKeyColor & 0xFF; + uint8 nkeyr = (cNewKeyColor >> 16) & 0xFF; + uint8 nkeyg = (cNewKeyColor >> 8) & 0xFF; + uint8 nkeyb = cNewKeyColor & 0xFF; + + BITMAP bmp; + BITMAPINFO bmpinfo; + uint8* bmpdata = NULL; + + HDC dc = CreateCompatibleDC(NULL); + + GetObject(hBmp, sizeof(BITMAP), &bmp); + + memset(&bmpinfo, 0, sizeof(BITMAPINFO)); + bmpinfo.bmiHeader.biSize = sizeof(BITMAPINFO); + bmpinfo.bmiHeader.biBitCount = 24; + bmpinfo.bmiHeader.biCompression = BI_RGB; + bmpinfo.bmiHeader.biHeight = bmp.bmHeight; + bmpinfo.bmiHeader.biWidth = bmp.bmWidth; + bmpinfo.bmiHeader.biPlanes = bmp.bmPlanes; + bmpdata = new uint8[bmp.bmHeight * bmp.bmWidth * 3]; + + GetDIBits(dc, hBmp, 0, bmp.bmHeight, (LPVOID)bmpdata, &bmpinfo, DIB_RGB_COLORS); + + int y2 = 0; + for (int y = 0; y < bmp.bmHeight; y++) + { + for (int x = 0; x < bmp.bmWidth; x++) + { + int idx = y2 + x*3; + uint8 r = bmpdata[idx + 0]; + uint8 g = bmpdata[idx + 1]; + uint8 b = bmpdata[idx + 2]; + + if ((r == keyr) && (g == keyg) && (b == keyb)) + { + bmpdata[idx + 0] = nkeyr; + bmpdata[idx + 1] = nkeyg; + bmpdata[idx + 2] = nkeyb; + } + } + y2 += bmp.bmWidth * 3; + } + + SetDIBits(dc, hBmp, 0, bmp.bmHeight, (LPVOID)bmpdata, &bmpinfo, DIB_RGB_COLORS); + DeleteDC(dc); + delete[] bmpdata; +} + +//----------------------------------------------------------------------------- +// Window class handling +//----------------------------------------------------------------------------- + +vector ReggedWndClasses; + +bool RegWndClass(tstring name, WNDPROC wndProc, UINT style, int extraSize) +{ + return RegWndClass(name, wndProc, style, NULL, extraSize); +} + +bool RegWndClass(tstring name, WNDPROC wndProc, UINT style, HICON icon, int extraSize) +{ + // If the class is already regged, don't re-reg it + if (find(ReggedWndClasses.begin(), ReggedWndClasses.end(), name) != ReggedWndClasses.end()) + return true; + + WNDCLASSEX wc; + + wc.cbSize = sizeof(wc); + wc.lpszClassName = name.c_str(); + wc.hInstance = hAppInst; + wc.lpfnWndProc = wndProc; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hIcon = icon; + wc.lpszMenuName = 0; + wc.hbrBackground = GetSysColorBrush(COLOR_BTNFACE); + wc.style = style; + wc.cbClsExtra = 0; + wc.cbWndExtra = DWLP_USER + extraSize; + wc.hIconSm = 0; + + if (RegisterClassEx(&wc) != 0) + { + // If registration succeeded, add the class name into the list + ReggedWndClasses.push_back(name); + return true; + } + else + return false; +} + +void UnregWndClass(tstring name) +{ + vector::iterator it = find(ReggedWndClasses.begin(), ReggedWndClasses.end(), name); + + // If the class wasn't regged, we can't unreg it :P + if (it == ReggedWndClasses.end()) + return; + + // Otherwise unreg the class and remove its name from the list + // ONLY if unregging was successful. Unregging will fail if one + // or more windows using the class still exist. + if (UnregisterClass(name.c_str(), hAppInst) != 0) + ReggedWndClasses.erase(it); +} + +//----------------------------------------------------------------------------- +// Base toolwindow class +//----------------------------------------------------------------------------- + +CToolWindow::CToolWindow(TCHAR* _className, WNDPROC _proc, TCHAR* _title, int _width, int _height) + : hWnd(NULL) + , className(_className) + , proc((DLGPROC)_proc) + , title(_title) + , width(_width) + , height(_height) + , whichInit(0) +{ +} + +CToolWindow::CToolWindow(int _ID, DLGPROC _proc, TCHAR* _title) + : hWnd(NULL) + , ID(_ID) + , proc(_proc) + , title(_title) + , whichInit(1) +{ +} + +void CToolWindow::PostInitialize() +{ + if(whichInit==0) + { + DWORD style = WS_CAPTION | WS_SYSMENU | WS_SIZEBOX | WS_MINIMIZEBOX | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; + RECT rc; + + rc.left = 0; + rc.right = width; + rc.top = 0; + rc.bottom = height; + AdjustWindowRect(&rc, style, FALSE); + + hWnd = CreateWindow(className, title.c_str(), style, CW_USEDEFAULT, CW_USEDEFAULT, + rc.right - rc.left, rc.bottom - rc.top, HWND_DESKTOP, NULL, hAppInst, (LPVOID)this); + } + else + { + hWnd = CreateDialogParam(hAppInst, MAKEINTRESOURCE(ID), HWND_DESKTOP, proc, (LPARAM)this); + if (hWnd == NULL) + return; + + SetWindowText(hWnd, title.c_str()); + //SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)LoadIcon(hAppInst, MAKEINTRESOURCE(ICONDESMUME))); + } +} + +CToolWindow::~CToolWindow() +{ +} + +//----------------------------------------------------------------------------- +// Toolwindow handling +//----------------------------------------------------------------------------- + +CToolWindow* ToolWindowList = NULL; + +bool OpenToolWindow(CToolWindow* wnd) +{ + // A hWnd value of NULL indicates failure to create the window. + // In this case, just delete the toolwindow and return failure. + if (wnd->hWnd == NULL) + { + delete wnd; + return false; + } + + // Add the toolwindow to the list + if (ToolWindowList == NULL) + { + ToolWindowList = wnd; + wnd->prev = NULL; + wnd->next = NULL; + } + else + { + wnd->prev = NULL; + wnd->next = ToolWindowList; + wnd->next->prev = wnd; + ToolWindowList = wnd; + } + + // Show the toolwindow (otherwise it won't show :P ) + wnd->Show(); + + return true; +} + +void CloseToolWindow(CToolWindow* wnd) +{ + // Remove the toolwindow from the list + if (wnd == ToolWindowList) + { + ToolWindowList = wnd->next; + if (wnd->next) wnd->next->prev = NULL; + wnd->next = NULL; + } + else + { + wnd->prev->next = wnd->next; + if (wnd->next) wnd->next->prev = wnd->prev; + wnd->prev = NULL; + wnd->next = NULL; + } + + // Delete the toolwindow object + // its destructor will destroy the window + delete wnd; +} + +void CloseAllToolWindows() +{ + CToolWindow* wnd; + CToolWindow* wnd_next; + + wnd = ToolWindowList; + while (wnd) + { + wnd_next = wnd->next; + + wnd->prev = NULL; + wnd->next = NULL; + delete wnd; + + wnd = wnd_next; + } + + ToolWindowList = NULL; +} + +void RefreshAllToolWindows() +{ + CToolWindow* wnd; + + if (ToolWindowList == NULL) + return; + + wnd = ToolWindowList; + while (wnd) + { + wnd->Refresh(); + wnd = wnd->next; + } +} + +//----------------------------------------------------------------------------- +// The Toolkit - Toolbar API wrapper +//----------------------------------------------------------------------------- + +CToolBar::CToolBar(HWND hParent) + : hidden(false) +{ + // Create the toolbar + // Note: dropdown buttons look like crap without TBSTYLE_FLAT + hWnd = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, + WS_CHILD | WS_VISIBLE | WS_BORDER | TBSTYLE_FLAT | TBSTYLE_TOOLTIPS, + 0, 0, 0, 0, hParent, NULL, hAppInst, NULL); + + // Send it a few messages to finish setting it up + SendMessage(hWnd, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0); + SendMessage(hWnd, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_DRAWDDARROWS); +} + +CToolBar::~CToolBar() +{ + // Delete all the HBITMAPs we kept stored + for (TBitmapList::iterator it = hBitmaps.begin(); + it != hBitmaps.end(); it++) + { + DeleteObject(it->second.second); + } + + hBitmaps.clear(); +} + +void CToolBar::Show(bool bShow) +{ + DWORD style = GetWindowLong(hWnd, GWL_STYLE); + + if (bShow) + { + hidden = false; + style |= WS_VISIBLE; + } + else + { + hidden = true; + style &= ~WS_VISIBLE; + } + + SetWindowLong(hWnd, GWL_STYLE, style); +} + +void CToolBar::OnSize() +{ + SetWindowPos(hWnd, NULL, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + SWP_NOZORDER | SWP_NOMOVE); +} + +void CToolBar::AppendButton(int uID, int uBitmapID, DWORD dwState, bool bDropdown) +{ + HBITMAP hbmp; + TBADDBITMAP bmp; + TBBUTTON btn; + + // Get the bitmap and replace the key color (magenta) with the right color + hbmp = LoadBitmap(hAppInst, MAKEINTRESOURCE(uBitmapID)); + MakeBitmapPseudoTransparent(hbmp, RGB(255, 0, 255), GetSysColor(COLOR_BTNFACE)); + + // Add the bitmap to the toolbar's image list + bmp.hInst = NULL; + bmp.nID = (UINT_PTR)hbmp; + + int bmpid = SendMessage(hWnd, TB_ADDBITMAP, 1, (LPARAM)&bmp); + + // Save the bitmap (if it gets deleted, the toolbar is too) + hBitmaps[uBitmapID] = TBitmapPair(bmpid, hbmp); + + // And finally add the button + memset(&btn, 0, sizeof(TBBUTTON)); + btn.fsStyle = bDropdown ? TBSTYLE_DROPDOWN : TBSTYLE_BUTTON; + btn.fsState = (BYTE) dwState; + btn.idCommand = uID; + btn.iString = -1; + btn.iBitmap = bmpid; + + SendMessage(hWnd, TB_ADDBUTTONS, 1, (LPARAM)&btn); +} + +void CToolBar::AppendSeparator() +{ + TBBUTTON btn; + + memset(&btn, 0, sizeof(TBBUTTON)); + btn.fsStyle = TBSTYLE_SEP; + btn.idCommand = -1; + btn.iString = -1; + + SendMessage(hWnd, TB_ADDBUTTONS, 1, (LPARAM)&btn); +} + +void CToolBar::ChangeButtonBitmap(int uID, int uBitmapID) +{ + int bmpid = 0; + + // If we don't already have the bitmap, retrieve it, + // adapt it and store it in the list + TBitmapList::iterator it = hBitmaps.find(uBitmapID); + + if (it == hBitmaps.end()) + { + HBITMAP hbmp; + TBADDBITMAP bmp; + + hbmp = LoadBitmap(hAppInst, MAKEINTRESOURCE(uBitmapID)); + MakeBitmapPseudoTransparent(hbmp, RGB(255, 0, 255), GetSysColor(COLOR_BTNFACE)); + + bmp.hInst = NULL; + bmp.nID = (UINT_PTR)hbmp; + + bmpid = SendMessage(hWnd, TB_ADDBITMAP, 1, (LPARAM)&bmp); + + hBitmaps[uBitmapID] = TBitmapPair(bmpid, hbmp); + } + else + bmpid = hBitmaps[uBitmapID].first; + + // Finally change the bitmap + SendMessage(hWnd, TB_CHANGEBITMAP, uID, MAKELPARAM(bmpid, 0)); +} + +void CToolBar::EnableButtonDropdown(int uID, bool bDropdown) +{ + TBBUTTONINFO btninfo; + + memset(&btninfo, 0, sizeof(TBBUTTONINFO)); + btninfo.cbSize = sizeof(TBBUTTONINFO); + btninfo.dwMask = TBIF_STYLE; + + SendMessage(hWnd, TB_GETBUTTONINFO, uID, (LPARAM)&btninfo); + + btninfo.dwMask = TBIF_STYLE; + if (bDropdown) + { + btninfo.fsStyle &= ~TBSTYLE_BUTTON; + btninfo.fsStyle |= TBSTYLE_DROPDOWN; + } + else + { + btninfo.fsStyle |= TBSTYLE_BUTTON; + btninfo.fsStyle &= ~TBSTYLE_DROPDOWN; + } + + SendMessage(hWnd, TB_SETBUTTONINFO, uID, (LPARAM)&btninfo); +} + +int CToolBar::GetHeight() +{ + if (hidden) return 0; + + RECT rc; GetWindowRect(hWnd, &rc); + return rc.bottom - rc.top - 1; +} + + +WINCLASS::WINCLASS(LPTSTR rclass, HINSTANCE hInst) +{ + memset(regclass, 0, sizeof(regclass)); + memcpy(regclass, rclass, lstrlen(rclass)); + + hwnd = NULL; + hmenu = NULL; + hInstance = hInst; + + minWidth = 0; + minHeight = 0; +} + +WINCLASS::~WINCLASS() +{ +} + +bool WINCLASS::create(LPTSTR caption, int x, int y, int width, int height, int style, HMENU menu) +{ + if (hwnd != NULL) return false; + + hwnd = CreateWindow(regclass, caption, style, x, y, width, height, NULL, menu, hInstance, NULL); + + if (hwnd != NULL) return true; + return false; +} + +bool WINCLASS::createEx(LPTSTR caption, int x, int y, int width, int height, int style, int styleEx, HMENU menu) +{ + if (hwnd != NULL) return false; + + hwnd = CreateWindowEx(styleEx, regclass, caption, style, x, y, width, height, NULL, menu, hInstance, NULL); + + if (hwnd != NULL) return true; + return false; +} + +bool WINCLASS::setMenu(HMENU menu) +{ + hmenu = menu; + return SetMenu(hwnd, hmenu)!=FALSE; +} + +bool WINCLASS::addMenuItem(uint32 item, bool byPos, LPCMENUITEMINFO info) +{ + return InsertMenuItem(hmenu, item, byPos ? TRUE : FALSE, info) != FALSE; +} + +DWORD WINCLASS::checkMenu(UINT idd, bool check) +{ + return CheckMenuItem(hmenu, idd, MF_BYCOMMAND | (check?MF_CHECKED:MF_UNCHECKED)); +} + +HWND WINCLASS::getHWnd() +{ + return hwnd; +} + +void WINCLASS::Show(int mode) +{ + ShowWindow(hwnd, mode); +} + +void WINCLASS::Hide() +{ + ShowWindow(hwnd, SW_HIDE); +} + +void WINCLASS::setMinSize(int width, int height) +{ + minWidth = width; + minHeight = height; +} + +static void MyAdjustWindowRectEx(RECT* rect, HWND hwnd) +{ + AdjustWindowRectEx(rect,GetWindowStyle(hwnd),TRUE,GetWindowExStyle(hwnd)); + + //get height of one menu to subtract off + int cymenu = GetSystemMetrics(SM_CYMENU); + + //get the height of the actual menu to add back on + MENUBARINFO mbi; + ZeroMemory(&mbi, sizeof(mbi)); + mbi.cbSize = sizeof(mbi); + GetMenuBarInfo(hwnd, OBJID_MENU, 0, &mbi); + int menuHeight = (mbi.rcBar.bottom - mbi.rcBar.top + 1); + + rect->bottom -= cymenu; + rect->bottom += menuHeight; +} + +void WINCLASS::sizingMsg(WPARAM wParam, LPARAM lParam, LONG keepRatio) +{ + RECT *rect = (RECT*)lParam; + + int prevRight = rect->right; + int prevBottom = rect->bottom; + + int _minWidth, _minHeight; + + int tbheight = 0/*MainWindowToolbar->GetHeight()*/; + + RECT adjr; + SetRect(&adjr,0,0,minWidth,minHeight); + MyAdjustWindowRectEx(&adjr,hwnd); + + RECT frameInfo; + SetRect(&frameInfo,0,0,0,0); + MyAdjustWindowRectEx(&frameInfo,hwnd); + int frameWidth = frameInfo.right-frameInfo.left; + int frameHeight = frameInfo.bottom-frameInfo.top + tbheight; + + // Calculate the minimum size in pixels + _minWidth = adjr.right-adjr.left; + _minHeight = adjr.bottom-adjr.top + tbheight; + + /* Clamp the size to the minimum size (256x384) */ + rect->right = (rect->left + std::max(_minWidth, (int)(rect->right - rect->left))); + rect->bottom = (rect->top + std::max(_minHeight, (int)(rect->bottom - rect->top))); + + bool horizontalDrag = (wParam == WMSZ_LEFT) || (wParam == WMSZ_RIGHT); + bool verticalDrag = (wParam == WMSZ_TOP) || (wParam == WMSZ_BOTTOM); + if(verticalDrag && !(keepRatio & KEEPY)) + { + int clientHeight = rect->bottom - rect->top - frameHeight; + if(clientHeight < minHeight) + rect->bottom += minHeight - clientHeight; + } + else if(horizontalDrag && !(keepRatio & KEEPX)) + { + int clientWidth = rect->right - rect->left - frameWidth; + if(clientWidth < minWidth) + rect->right += minWidth - clientWidth; + } + else + { + //Apply the ratio stuff + + float ratio1 = ((rect->right - rect->left - frameWidth ) / (float)minWidth); + float ratio2 = ((rect->bottom - rect->top - frameHeight) / (float)minHeight); + float ratio = std::min(ratio1,ratio2); + if(keepRatio & FULLSCREEN) + { + keepRatio |= WINCLASS::KEEPX | WINCLASS::KEEPY; + ratio1 = ratio; + ratio2 = ratio; + } + + LONG correctedHeight = (LONG)((rect->top + frameHeight + (minHeight * ratio1))); + LONG correctedWidth = (LONG)((rect->left + frameWidth + (minWidth * ratio2))); + + if(keepRatio & KEEPX) + { + if((keepRatio & KEEPY) || (rect->bottom < correctedHeight)) + { + if(verticalDrag) + rect->right = correctedWidth; + else + rect->bottom = correctedHeight; + } + } + //else + { + if((keepRatio & KEEPY) && (rect->right < correctedWidth) || (keepRatio&FULLSCREEN)) + { + if(horizontalDrag) + rect->bottom = correctedHeight; + else + rect->right = correctedWidth; + } + } + } + + // prevent "pushing" the window across the screen when resizing from the left or top + if(wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT || wParam == WMSZ_BOTTOMLEFT) + { + rect->left -= rect->right - prevRight; + rect->right = prevRight; + } + if(wParam == WMSZ_TOP || wParam == WMSZ_TOPLEFT || wParam == WMSZ_TOPRIGHT) + { + rect->top -= rect->bottom - prevBottom; + rect->bottom = prevBottom; + } + + // windows screws up the window size if the top of the window goes too high above the top of the screen + if(keepRatio & KEEPY) + { + int titleBarHeight = GetSystemMetrics(SM_CYSIZE); + int topExceeded = -(titleBarHeight + rect->top); + if(topExceeded > 0) + { + rect->top += topExceeded; + rect->bottom += topExceeded; + } + } +} + +void WINCLASS::setClientSize(int width, int height) +{ + height += 0/*MainWindowToolbar->GetHeight()*/; + + //yep, do it twice, once in case the menu wraps, and once to accomodate that wrap + for(int i=0;i<2;i++) + { + RECT rect; + SetRect(&rect,0,0,width,height); + MyAdjustWindowRectEx(&rect,hwnd); + SetWindowPos(hwnd,0,0,0,rect.right-rect.left,rect.bottom-rect.top,SWP_NOMOVE|SWP_NOZORDER); + } +} + +//========================================================= Thread class +extern DWORD WINAPI ThreadProc(LPVOID lpParameter) +{ + THREADCLASS *tmp = (THREADCLASS *)lpParameter; + return tmp->ThreadFunc(); +} + +THREADCLASS::THREADCLASS() +{ + hThread = NULL; +} + +THREADCLASS::~THREADCLASS() +{ + closeThread(); +} + +void THREADCLASS::closeThread() +{ + if (hThread) + { + CloseHandle(hThread); + hThread = NULL; + } +} + +bool THREADCLASS::createThread() +{ + if (hThread) return false; + + hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, this, 0, &threadID); + if (!hThread) return false; + //WaitForSingleObject(hThread, INFINITE); + return true; +} + +//========================================================= Tools class +TOOLSCLASS::TOOLSCLASS(HINSTANCE hInst, int IDD, DLGPROC dlgproc) +{ + this->dlgproc = dlgproc; + hwnd = NULL; + hInstance = hInst; + idd=IDD; + memset(class_name, 0, sizeof(class_name)); + memset(class_name2, 0, sizeof(class_name2)); +} + +TOOLSCLASS::~TOOLSCLASS() +{ + close(); +} + +bool TOOLSCLASS::open(bool useThread) +{ + if(useThread) + { + if (!createThread()) return false; + else return true; + } + + if(doOpen()) return false; + else return true; +} + +bool TOOLSCLASS::close() +{ + return true; +} + +DWORD TOOLSCLASS::doOpen() +{ + GetLastError(); + hwnd = CreateDialogW(hInstance, MAKEINTRESOURCEW(idd), NULL, (DLGPROC) dlgproc); + + if (!hwnd) + { + return (-2); + } + + ShowWindow(hwnd, SW_SHOW); + UpdateWindow(hwnd); + + return 0; +} + +void TOOLSCLASS::doClose() +{ + unregClass(); + hwnd = NULL; +} + +DWORD TOOLSCLASS::ThreadFunc() +{ + DWORD ret = doOpen(); + if(ret) return ret; + + MSG messages; + while (GetMessage (&messages, NULL, 0, 0)) + { + TranslateMessage(&messages); + DispatchMessage(&messages); + } + + doClose(); + + closeThread(); + return 0; +} + +void TOOLSCLASS::regClass(LPTSTR class_name, WNDPROC wproc, bool SecondReg) +{ + WNDCLASSEX wc; + + wc.cbSize = sizeof(wc); + if (SecondReg) + lstrcpy(this->class_name2, class_name); + else + lstrcpy(this->class_name, class_name); + + wc.lpszClassName = class_name; + wc.hInstance = hInstance; + wc.lpfnWndProc = wproc; + wc.hCursor = LoadCursor (NULL, IDC_ARROW); + wc.hIcon = 0; + wc.lpszMenuName = 0; + wc.hbrBackground = (HBRUSH)GetSysColorBrush(COLOR_BTNFACE); + wc.style = 0; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hIconSm = 0; + + RegisterClassEx(&wc); +} + +void TOOLSCLASS::unregClass() +{ + if (class_name[0]) + { + UnregisterClass(class_name, hInstance); + } + if (class_name2[0]) + { + UnregisterClass(class_name2, hInstance); + } + memset(class_name, 0, sizeof(class_name)); + memset(class_name2, 0, sizeof(class_name2)); +} diff --git a/win32/CWindow.h b/win32/CWindow.h new file mode 100644 index 00000000..a48cb798 --- /dev/null +++ b/win32/CWindow.h @@ -0,0 +1,325 @@ +//MemView dialog was copied and adapted from DeSmuME: http://sourceforge.net/projects/desmume/ +//Authors: DeSmuME team + +// gocha: +// 1. According to Normmatt, YopYop had released the final version of original desmume as public domain. +// 2. I asked DeSmuME team about the problem between GPL and Snes9x license, but no one raised any objections. +// Therefore, I decided to include this code into snes9x-rr project. +// Dear DeSmuME coders: if it is not allowed, please tell me. + +/* Copyright (C) 2006 yopyop + Copyright (C) 2006-2009 DeSmuME team + + This file is part of DeSmuME + + DeSmuME 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; either version 2 of the License, or + (at your option) any later version. + + DeSmuME 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 for more details. + + You should have received a copy of the GNU General Public License + along with DeSmuME; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef CWINDOW_H +#define CWINDOW_H + +#include +#include +#include +#include +#include + +#include "../port.h" + +namespace std { +#ifndef tstring +#ifdef UNICODE + typedef wstring tstring; +#else + typedef string tstring; +#endif +#endif +} + +extern CRITICAL_SECTION win_execute_sync; + +//----------------------------------------------------------------------------- +// The Toolkit - RECT wrapper +//----------------------------------------------------------------------------- + +class CRect +{ +public: + CRect(int x, int y, int width, int height) + { + rcRect.left = x; rcRect.top = y; + rcRect.right = x + width; + rcRect.bottom = y + height; + } + CRect(RECT rc) + { + memcpy(&rcRect, &rc, sizeof(RECT)); + //rcRect = rc; + } + + ~CRect() {} + + RECT ToMSRect() { return rcRect; } + +private: + RECT rcRect; +}; + +// GetFontQuality() +// Returns a font quality value that can be passed to +// CreateFont(). The value depends on whether font +// antialiasing is enabled or not. +DWORD GetFontQuality(); + +int DrawText(HDC hDC, TCHAR* text, int X, int Y, int Width, int Height, UINT format); +void GetFontSize(HWND hWnd, HFONT hFont, LPSIZE size); + +// MakeBitmapPseudoTransparent(HBITMAP hBmp, COLORREF cKeyColor, COLORREF cNewKeyColor) +// Replaces the RGB color cKeyColor with cNewKeyColor in the bitmap hBmp. +// For use with toolbars and such. Replace a key color (like magenta) with the right +// system color to make the bitmap pseudo-transparent. +void MakeBitmapPseudoTransparent(HBITMAP hBmp, COLORREF cKeyColor, COLORREF cNewKeyColor); + +//----------------------------------------------------------------------------- +// Window class handling +//----------------------------------------------------------------------------- + +// RegWndClass() +// Registers a window class. +// Incase the class was already registered, the function +// just does nothing and returns true. +// Returns false if registration failed. +bool RegWndClass(std::tstring name, WNDPROC wndProc, UINT style, int extraSize = 0); +bool RegWndClass(std::tstring name, WNDPROC wndProc, UINT style, HICON icon, int extraSize = 0); + +// UnregWndClass() +// Unregisters a previously registered window class. +// This function will silently fail if one or more windows +// using the class still exist. +void UnregWndClass(std::tstring name); + +//----------------------------------------------------------------------------- +// Base toolwindow class +//----------------------------------------------------------------------------- + +class CToolWindow +{ +public: + // CToolWindow constructor #1 + // Creates a window using CreateWindow(). + // If the window creation failed for whatever reason, + // hWnd will be NULL. + CToolWindow(TCHAR* className, WNDPROC proc, TCHAR* title, int width, int height); + + // CToolWindow constructor #2 + // Creates a window from a dialog template resource. + // If the window creation failed for whatever reason, + // hWnd will be NULL. + CToolWindow(int ID, DLGPROC proc, TCHAR* title); + + // CToolWindow destructor + // Dummy destructor. The derivated toolwindow classes must + // destroy the window in their own destructors. Thus, they + // can unregister any window classes they use. + virtual ~CToolWindow(); + + // this must be called by the derived class constructor. sigh. + void PostInitialize(); + + // Show(), Hide() + // These ones are quite self-explanatory, I guess. + void Show() { ShowWindow(hWnd, SW_SHOW); } + void Hide() { ShowWindow(hWnd, SW_HIDE); } + + // SetTitle() + // Changes the title of the window. + void SetTitle(TCHAR* title) { SetWindowText(hWnd, title); } + + // Refresh() + // Refreshes the window. Called by RefreshAllToolWindows(). + void Refresh() { InvalidateRect(hWnd, NULL, FALSE); } + + // Double-linked toolwindow list. + CToolWindow* prev; + CToolWindow* next; + + // Handle to the window. + HWND hWnd; + +private: + int ID; + DLGPROC proc; + std::tstring title; + TCHAR* className; + int width, height; + int whichInit; +}; + +//----------------------------------------------------------------------------- +// Toolwindow handling +//----------------------------------------------------------------------------- + +// OpenToolWindow() +// Adds the CToolWindow instance to the toolwindow list. +// The instance will be deleted if its hWnd member is NULL. +bool OpenToolWindow(CToolWindow* wnd); + +// CloseToolWindow() +// Removes the CToolWindow instance from the toolwindow list +// and deletes it. +void CloseToolWindow(CToolWindow* wnd); + +// CloseAllToolWindows() +// Deletes all the toolwindows in the list and flushes the list. +void CloseAllToolWindows(); + +// RefreshAllToolWindows() +// Refreshes all the toolwindows in the list. +// Called once per frame when the emu is running. +void RefreshAllToolWindows(); + +//----------------------------------------------------------------------------- +// The Toolkit - Toolbar API wrapper +//----------------------------------------------------------------------------- + +class CToolBar +{ +public: + CToolBar(HWND hParent); + ~CToolBar(); + + HWND GetHWnd() { return hWnd; } + + void Show(bool bShow); + bool Visible() { return !hidden; } + + void OnSize(); + + void AppendButton(int uID, int uBitmapID, DWORD dwState, bool bDropdown); + void AppendSeparator(); + + void EnableButton(int uID, bool bEnable) { + SendMessage(hWnd, TB_ENABLEBUTTON, uID, bEnable ? TRUE:FALSE); } + void CheckButton(int uID, bool bCheck) { + SendMessage(hWnd, TB_CHECKBUTTON, uID, bCheck ? TRUE:FALSE); } + + void ChangeButtonBitmap(int uID, int uBitmapID); + void EnableButtonDropdown(int uID, bool bDropdown); + void ChangeButtonID(int uIndex, int uNewID) { + SendMessage(hWnd, TB_SETCMDID, uIndex, MAKELPARAM(uNewID, 0)); } + + int GetHeight(); + +private: + HWND hWnd; + // We have to keep the bitmaps here because destroying them + // directly after use would also destroy the toolbar. + // They'll be destroyed when the CToolBar destructor is called. + typedef std::pair TBitmapPair; + typedef std::map TBitmapList; + TBitmapList hBitmaps; + + bool hidden; +}; + + +class WINCLASS +{ +private: + HWND hwnd; + HMENU hmenu; + HINSTANCE hInstance; + TCHAR regclass[256]; + int minWidth, minHeight; +public: + WINCLASS(LPTSTR rclass, HINSTANCE hInst); + ~WINCLASS(); + + bool create(LPTSTR caption, int x, int y, int width, int height, int style, + HMENU menu); + bool createEx(LPTSTR caption, int x, int y, int width, int height, int style, int styleEx, + HMENU menu); + + bool setMenu(HMENU menu); + bool addMenuItem(uint32 item, bool byPos, LPCMENUITEMINFO info); + DWORD checkMenu(UINT idd, bool check); + + void Show(int mode); + void Hide(); + + HWND getHWnd(); + + CRect GetRect() + { + RECT rc; GetWindowRect(hwnd, &rc); + return CRect(rc); + } + + void setMinSize(int width, int height); + + enum // keepRatio flags + { + NOKEEP = 0x0, + KEEPX = 0x1, + KEEPY = 0x2, + FULLSCREEN = 0x4, + }; + + void sizingMsg(WPARAM wParam, LPARAM lParam, LONG keepRatio = NOKEEP); + void setClientSize(int width, int height); +}; + +class THREADCLASS +{ + friend DWORD WINAPI ThreadProc(LPVOID lpParameter); + HANDLE hThread; + +public: + THREADCLASS(); + virtual ~THREADCLASS(); + bool createThread(); + void closeThread(); + +protected: + DWORD threadID; + virtual DWORD ThreadFunc()=NULL; +}; + +class TOOLSCLASS : public THREADCLASS +{ +private: + HWND hwnd; + HINSTANCE hInstance; + DLGPROC dlgproc; + int idd; + TCHAR class_name[256]; + TCHAR class_name2[256]; + + DWORD doOpen(); + void doClose(); + +protected: + DWORD ThreadFunc(); + +public: + TOOLSCLASS(HINSTANCE hInst, int IDD, DLGPROC wndproc); + virtual ~TOOLSCLASS(); + bool open(bool useThread=true); + bool close(); + void regClass(LPTSTR class_name, WNDPROC wproc, bool SecondReg = false); + void unregClass(); +}; + +#endif diff --git a/win32/memView.cpp b/win32/memView.cpp new file mode 100644 index 00000000..0df26fea --- /dev/null +++ b/win32/memView.cpp @@ -0,0 +1,949 @@ +//MemView dialog was copied and adapted from DeSmuME: http://sourceforge.net/projects/desmume/ +//Authors: DeSmuME team + +// gocha: +// 1. According to Normmatt, YopYop had released the final version of original desmume as public domain. +// 2. I asked DeSmuME team about the problem between GPL and Snes9x license, but no one raised any objections. +// Therefore, I decided to include this code into snes9x-rr project. +// Dear DeSmuME coders: if it is not allowed, please tell me. + +/* Copyright (C) 2006 yopyop + yopyop156@ifrance.com + yopyop156.ifrance.com + + This file is part of DeSmuME + + DeSmuME 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; either version 2 of the License, or + (at your option) any later version. + + DeSmuME 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 for more details. + + You should have received a copy of the GNU General Public License + along with DeSmuME; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include +#include "../port.h" +#include "../snes9x.h" +#include "../memmap.h" +#include "CWindow.h" +#include "rsrc/resource.h" +#include "wsnes9x.h" +#include "memView.h" + +#include "../apu/bapu/snes/snes.hpp" +#if defined(DEBUGGER) + extern SNES::SMPDebugger SNES::smp; +#else + extern SNES::SMP SNES::smp; +#endif +#define APURAM SNES::smp.apuram + +using namespace std; + +typedef uint32_t HWAddressType; + +enum RegionType { + MEMVIEW_RAM = 0, + MEMVIEW_SRAM, + MEMVIEW_FILLRAM, + MEMVIEW_ARAM +}; + +struct MemViewRegion +{ + TCHAR name[16]; // name of this region (ex. SRAM) + HWAddressType hardwareAddress; // hardware address of the start of this region + unsigned int size; // number of bytes to the end of this region +}; + +static const MemViewRegion s_ramRegion = { TEXT("RAM"), 0x7e0000, 0x20000 }; +static const MemViewRegion s_sramRegion = { TEXT("SRAM"), 0x000000, 0x20000 }; +static const MemViewRegion s_fillRAMRegion = { TEXT("FillRAM"), 0x000000, 0x8000 }; +static const MemViewRegion s_aramRegion = { TEXT("ARAM"), 0x000000, 0x10000 }; + +typedef std::vector MemoryList; +static MemoryList s_memoryRegions; + +////////////////////////////////////////////////////////////////////////////// + +uint8_t memRead8 (RegionType regionType, HWAddressType address) +{ + MemViewRegion& region = s_memoryRegions[regionType]; + if (address < region.hardwareAddress || address >= (region.hardwareAddress + region.size)) + { + return 0; + } + + switch (regionType) + { + case MEMVIEW_RAM: + return Memory.RAM[address - 0x7e0000]; + case MEMVIEW_SRAM: + return Memory.SRAM[address]; + case MEMVIEW_FILLRAM: + return Memory.FillRAM[address]; + case MEMVIEW_ARAM: + return APURAM[address]; + } + return 0; +} + +uint16_t memRead16 (RegionType regionType, HWAddressType address) +{ + MemViewRegion& region = s_memoryRegions[regionType]; + if (address < region.hardwareAddress || (address + 1) >= (region.hardwareAddress + region.size)) + { + return 0; + } + + switch (regionType) + { + case MEMVIEW_RAM: + return *(uint16_t*)(&Memory.RAM[address - 0x7e0000]); + case MEMVIEW_SRAM: + return *(uint16_t*)(&Memory.SRAM[address]); + case MEMVIEW_FILLRAM: + return *(uint16_t*)(&Memory.FillRAM[address]); + case MEMVIEW_ARAM: + return *(uint16_t*)(&APURAM[address]); + } + return 0; +} + +uint32_t memRead32 (RegionType regionType, HWAddressType address) +{ + MemViewRegion& region = s_memoryRegions[regionType]; + if (address < region.hardwareAddress || (address + 3) >= (region.hardwareAddress + region.size)) + { + return 0; + } + + uint32_t value = memRead16(regionType, address); + value |= (memRead16(regionType, address + 2) << 16); + return value; +} + +void memRead(uint8_t* buffer, RegionType regionType, HWAddressType address, size_t size) +{ + for (size_t i = 0; i < size; i++) + { + buffer[i] = memRead8(regionType, address + i); + } +} + +void memWrite8 (RegionType regionType, HWAddressType address, uint8_t value) +{ + switch (regionType) + { + case MEMVIEW_RAM: + Memory.RAM[address - 0x7e0000] = value; + break; + case MEMVIEW_SRAM: + Memory.SRAM[address] = value; + break; + case MEMVIEW_FILLRAM: + Memory.FillRAM[address] = value; + break; + case MEMVIEW_ARAM: + APURAM[address] = value; + break; + } +} + +void memWrite16 (RegionType regionType, HWAddressType address, uint16_t value) +{ + switch (regionType) + { + case MEMVIEW_RAM: + *(uint16_t*)(&Memory.RAM[address - 0x7e0000]) = value; + break; + case MEMVIEW_SRAM: + *(uint16_t*)(&Memory.SRAM[address]) = value; + break; + case MEMVIEW_FILLRAM: + *(uint16_t*)(&Memory.FillRAM[address]) = value; + break; + case MEMVIEW_ARAM: + *(uint16_t*)(&APURAM[address]) = value; + break; + } +} + +void memWrite32 (RegionType regionType, HWAddressType address, uint32_t value) +{ + memWrite16(regionType, address, value & 0xffff); + memWrite16(regionType, address + 2, (value >> 16) & 0xffff); +} + +////////////////////////////////////////////////////////////////////////////// + +CMemView::CMemView() + : CToolWindow(IDD_MEM_VIEW, MemView_DlgProc, TEXT("Memory View")) + , region(MEMVIEW_RAM) + , viewMode(0) + , sel(FALSE) + , selPart(0) + , selAddress(0x00000000) + , selNewVal(0x00000000) +{ + // initialize memory regions + if (s_memoryRegions.empty()) + { + s_memoryRegions.push_back(s_ramRegion); + s_memoryRegions.push_back(s_sramRegion); + s_memoryRegions.push_back(s_fillRAMRegion); + s_memoryRegions.push_back(s_aramRegion); + } + address = s_memoryRegions.front().hardwareAddress; + + PostInitialize(); +} + +CMemView::~CMemView() +{ + DestroyWindow(hWnd); + hWnd = NULL; + + UnregWndClass(TEXT("MemView_ViewBox")); +} + +////////////////////////////////////////////////////////////////////////////// + +INT_PTR CALLBACK MemView_DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + CMemView* wnd = (CMemView*)GetWindowLongPtr(hDlg, DWLP_USER); + if((wnd == NULL) && (uMsg != WM_INITDIALOG)) + return 0; + + switch(uMsg) + { + case WM_INITDIALOG: + { + HWND cur; + + wnd = (CMemView*)lParam; + SetWindowLongPtr(hDlg, DWLP_USER, (LONG)wnd); + SetWindowLongPtr(GetDlgItem(hDlg, IDC_MEMVIEWBOX), DWLP_USER, (LONG)wnd); + + wnd->font = CreateFont(16, 0, 0, 0, FW_MEDIUM, FALSE, FALSE, FALSE, DEFAULT_CHARSET, + OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, GetFontQuality(), FIXED_PITCH, TEXT("Courier New")); + + MemViewRegion& region = s_memoryRegions[wnd->region]; + + cur = GetDlgItem(hDlg, IDC_REGION); + for(MemoryList::iterator iter = s_memoryRegions.begin(); iter != s_memoryRegions.end(); ++iter) + { + SendMessage(cur, CB_ADDSTRING, 0, (LPARAM)iter->name); + } + SendMessage(cur, CB_SETCURSEL, 0, 0); + + cur = GetDlgItem(hDlg, IDC_VIEWMODE); + SendMessage(cur, CB_ADDSTRING, 0, (LPARAM)TEXT("Bytes")); + SendMessage(cur, CB_ADDSTRING, 0, (LPARAM)TEXT("Halfwords")); + SendMessage(cur, CB_ADDSTRING, 0, (LPARAM)TEXT("Words")); + SendMessage(cur, CB_SETCURSEL, 0, 0); + + cur = GetDlgItem(hDlg, IDC_ADDRESS); + SendMessage(cur, EM_SETLIMITTEXT, 8, 0); + TCHAR addressText[9]; + wsprintf(addressText, TEXT("%06X"), wnd->address); + SetWindowText(cur, addressText); + + SetScrollRange(GetDlgItem(hDlg, IDC_MEMVIEWBOX), SB_VERT, 0x00000000, (region.size - 1) >> 4, TRUE); + SetScrollPos(GetDlgItem(hDlg, IDC_MEMVIEWBOX), SB_VERT, 0x00000000, TRUE); + + wnd->Refresh(); + } + return 1; + + case WM_CLOSE: + CloseToolWindow(wnd); + return 1; + + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDCANCEL: + CloseToolWindow(wnd); + return 1; + + case IDC_REGION: + if ((HIWORD(wParam) == CBN_SELCHANGE) || (HIWORD(wParam) == CBN_CLOSEUP)) + { + wnd->region = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0); + + MemViewRegion& region = s_memoryRegions[wnd->region]; + wnd->address = region.hardwareAddress; + SetScrollRange(GetDlgItem(hDlg, IDC_MEMVIEWBOX), SB_VERT, 0x00000000, (region.size - 1) >> 4, TRUE); + SetScrollPos(GetDlgItem(hDlg, IDC_MEMVIEWBOX), SB_VERT, 0x00000000, TRUE); + + wnd->sel = FALSE; + wnd->selAddress = 0x00000000; + wnd->selPart = 0; + wnd->selNewVal = 0x00000000; + + wnd->Refresh(); + } + return 1; + + case IDC_VIEWMODE: + if ((HIWORD(wParam) == CBN_SELCHANGE) || (HIWORD(wParam) == CBN_CLOSEUP)) + { + wnd->viewMode = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0); + + wnd->sel = FALSE; + wnd->selAddress = 0x00000000; + wnd->selPart = 0; + wnd->selNewVal = 0x00000000; + + wnd->Refresh(); + } + return 1; + + case IDC_GO: + { + TCHAR addrstr[9]; + int len; + int i; + int shift; + BOOL error = FALSE; + uint32 address = 0x00000000; + MemViewRegion& region = s_memoryRegions[wnd->region]; + HWAddressType addrMin = (region.hardwareAddress) & 0xFFFFFF00; + HWAddressType addrMax = max(addrMin, (region.hardwareAddress + region.size - 0x100 - 1) & 0xFFFFFF00); + + len = GetWindowText(GetDlgItem(hDlg, IDC_ADDRESS), addrstr, 9); + + for(i = 0; i < len; i++) + { + TCHAR ch = addrstr[i]; + + if((ch >= TEXT('0')) && (ch <= TEXT('9'))) + continue; + + if((ch >= TEXT('A')) && (ch <= TEXT('F'))) + continue; + + if((ch >= TEXT('a')) && (ch <= TEXT('f'))) + continue; + + if(ch == '\0') + break; + + error = TRUE; + break; + } + + if(error) + { + MessageBox(hDlg, TEXT("Error:\nInvalid address specified.\nThe address must be an hexadecimal value."), TEXT("Error"), (MB_OK | MB_ICONERROR)); + SetWindowText(GetDlgItem(hDlg, IDC_ADDRESS), TEXT("")); + return 1; + } + + for(i = (len-1), shift = 0; i >= 0; i--, shift += 4) + { + TCHAR ch = addrstr[i]; + + if((ch >= TEXT('0')) && (ch <= TEXT('9'))) + address |= ((ch - TEXT('0')) << shift); + else if((ch >= TEXT('A')) && (ch <= TEXT('F'))) + address |= ((ch - TEXT('A') + 0xA) << shift); + else if((ch >= TEXT('a')) && (ch <= TEXT('f'))) + address |= ((ch - TEXT('a') + 0xA) << shift); + } + + wnd->address = max((uint32)addrMin, min((uint32)addrMax, (address & 0xFFFFFFF0))); + + wnd->sel = FALSE; + wnd->selAddress = 0x00000000; + wnd->selPart = 0; + wnd->selNewVal = 0x00000000; + + SetScrollPos(GetDlgItem(hDlg, IDC_MEMVIEWBOX), SB_VERT, (((wnd->address - region.hardwareAddress) >> 4) & 0x000FFFFF), TRUE); + wnd->Refresh(); + } + return 1; + + case IDC_TEXTDUMP: + { + TCHAR fileName[256] = TEXT(""); + OPENFILENAME ofn; + + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = hDlg; + ofn.lpstrFilter = TEXT("Text file (*.txt)\0*.txt\0Any file (*.*)\0*.*\0\0"); + ofn.nFilterIndex = 1; + ofn.lpstrFile = fileName; + ofn.nMaxFile = 256; + ofn.lpstrDefExt = TEXT("txt"); + ofn.Flags = OFN_NOCHANGEDIR | OFN_NOREADONLYRETURN | OFN_PATHMUSTEXIST; + + if(GetSaveFileName(&ofn)) + { + FILE *f; + uint8 memory[0x100]; + int line; + + memRead(memory, (RegionType)wnd->region, wnd->address, 0x100); + + f = _tfopen(fileName, TEXT("a")); + + for(line = 0; line < 16; line++) + { + int i; + + fprintf(f, "%08X\t\t", (wnd->address + (line << 4))); + + switch(wnd->viewMode) + { + case 0: + { + for(i = 0; i < 16; i++) + { + fprintf(f, "%02X ", memory[(line << 4) + i]); + } + fprintf(f, "\t"); + } + break; + + case 1: + { + for(i = 0; i < 16; i += 2) + { + fprintf(f, "%04X ", *(uint16_t*)(&memory[(line << 4) + i])); + } + fprintf(f, "\t\t"); + } + break; + + case 2: + { + for(i = 0; i < 16; i += 4) + { + fprintf(f, "%08X ", *(uint32_t*)(&memory[(line << 4) + i])); + } + fprintf(f, "\t\t\t"); + } + break; + } + + for(i = 0; i < 16; i++) + { + uint8 val = memory[(line << 4) + i]; + + if((val >= 32) && (val <= 127)) + fprintf(f, "%c", (TCHAR)val); + else + fprintf(f, "."); + } + fprintf(f, "\n"); + } + + fclose(f); + } + } + return 1; + + case IDC_DUMPALL: + case IDC_RAWDUMP: + { + TCHAR fileName[256] = TEXT(""); + OPENFILENAME ofn; + + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = hDlg; + ofn.lpstrFilter = TEXT("Binary file (*.bin)\0*.bin\0Any file (*.*)\0*.*\0\0"); + ofn.nFilterIndex = 1; + ofn.lpstrFile = fileName; + ofn.nMaxFile = 256; + ofn.lpstrDefExt = TEXT("bin"); + ofn.Flags = OFN_NOCHANGEDIR | OFN_NOREADONLYRETURN | OFN_PATHMUSTEXIST; + + if(GetSaveFileName(&ofn)) + { + if(LOWORD(wParam) == IDC_RAWDUMP) + { + FILE *f = _tfopen(fileName, TEXT("ab")); + if (f) + { + uint8 memory[0x100]; + memRead(memory, (RegionType)wnd->region, wnd->address, 0x100); + fwrite(memory, 0x100, 1, f); + fclose(f); + } + } + else + { + const size_t blocksize = 0x100; + byte* memory = new byte[blocksize]; + if (memory) + { + FILE *f = _tfopen(fileName, TEXT("wb")); + if (f) + { + MemViewRegion& region = s_memoryRegions[wnd->region]; + for (HWAddressType address = region.hardwareAddress; + address < region.hardwareAddress + region.size; address += blocksize) + { + size_t size = blocksize; + if (address + size > region.hardwareAddress + region.size) + { + size = region.size - (address - region.hardwareAddress); + } + memRead(memory, (RegionType)wnd->region, address, size); + fwrite(memory, size, 1, f); + } + fclose(f); + } + delete [] memory; + } + } + } + } + return 1; + } + return 0; + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// + +LRESULT MemView_ViewBoxPaint(CMemView* wnd, HWND hCtl, WPARAM wParam, LPARAM lParam) +{ + HDC hdc; + PAINTSTRUCT ps; + RECT rc; + int w, h; + SIZE fontsize; + HDC mem_hdc; + HBITMAP mem_bmp; + uint32 addr = wnd->address; + uint8 memory[0x100]; + TCHAR text[80]; + int startx; + int curx, cury; + int line; + + GetClientRect(hCtl, &rc); + w = (rc.right - rc.left); + h = (rc.bottom - rc.top); + + hdc = BeginPaint(hCtl, &ps); + + mem_hdc = CreateCompatibleDC(hdc); + mem_bmp = CreateCompatibleBitmap(hdc, w, h); + SelectObject(mem_hdc, mem_bmp); + + SelectObject(mem_hdc, wnd->font); + + SetBkMode(mem_hdc, OPAQUE); + SetBkColor(mem_hdc, RGB(255, 255, 255)); + SetTextColor(mem_hdc, RGB(0, 0, 0)); + + FillRect(mem_hdc, &rc, (HBRUSH)GetStockObject(WHITE_BRUSH)); + + GetTextExtentPoint32(mem_hdc, TEXT(" "), 1, &fontsize); + startx = ((fontsize.cx * 8) + 5); + curx = 0; + cury = (fontsize.cy + 3); + + MoveToEx(mem_hdc, ((fontsize.cx * 8) + 2), 0, NULL); + LineTo(mem_hdc, ((fontsize.cx * 8) + 2), h); + + MoveToEx(mem_hdc, 0, (fontsize.cy + 1), NULL); + LineTo(mem_hdc, w, (fontsize.cy + 1)); + + switch(wnd->viewMode) + { + case 0: _stprintf(text, TEXT(" 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF")); break; + case 1: _stprintf(text, TEXT(" 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF")); break; + case 2: _stprintf(text, TEXT(" 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF")); break; + } + TextOut(mem_hdc, startx, 0, text, lstrlen(text)); + + memRead(memory, (RegionType)wnd->region, wnd->address, 0x100); + + for(line = 0; line < 16; line++, addr += 0x10) + { + int i; + + _stprintf(text, TEXT("%08X"), addr); + TextOut(mem_hdc, 0, cury, text, lstrlen(text)); + + curx = startx; + + switch(wnd->viewMode) + { + case 0: + { + curx += (fontsize.cx * 2); + for(i = 0; i < 16; i++) + { + uint8 val = memory[(line << 4) + i]; + if(wnd->sel && (wnd->selAddress == (addr + i))) + { + SetBkColor(mem_hdc, GetSysColor(COLOR_HIGHLIGHT)); + SetTextColor(mem_hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); + + switch(wnd->selPart) + { + case 0: _stprintf(text, TEXT("%02X"), val); break; + case 1: _stprintf(text, TEXT("%01X."), wnd->selNewVal); break; + } + } + else + { + SetBkColor(mem_hdc, RGB(255, 255, 255)); + SetTextColor(mem_hdc, RGB(0, 0, 0)); + + _stprintf(text, TEXT("%02X"), val); + } + + TextOut(mem_hdc, curx, cury, text, lstrlen(text)); + curx += (fontsize.cx * (2+1)); + } + curx += (fontsize.cx * 2); + } + break; + + case 1: + { + curx += (fontsize.cx * 6); + for(i = 0; i < 16; i += 2) + { + uint16 val = *(uint16_t*)(&memory[(line << 4) + i]); + if(wnd->sel && (wnd->selAddress == (addr + i))) + { + SetBkColor(mem_hdc, GetSysColor(COLOR_HIGHLIGHT)); + SetTextColor(mem_hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); + + switch(wnd->selPart) + { + case 0: _stprintf(text, TEXT("%04X"), val); break; + case 1: _stprintf(text, TEXT("%01X..."), wnd->selNewVal); break; + case 2: _stprintf(text, TEXT("%02X.."), wnd->selNewVal); break; + case 3: _stprintf(text, TEXT("%03X."), wnd->selNewVal); break; + } + } + else + { + SetBkColor(mem_hdc, RGB(255, 255, 255)); + SetTextColor(mem_hdc, RGB(0, 0, 0)); + + _stprintf(text, TEXT("%04X"), val); + } + + TextOut(mem_hdc, curx, cury, text, lstrlen(text)); + curx += (fontsize.cx * (4+1)); + } + curx += (fontsize.cx * 6); + } + break; + + case 2: + { + curx += (fontsize.cx * 8); + for(i = 0; i < 16; i += 4) + { + uint32 val = *(uint32_t*)(&memory[(line << 4) + i]); + if(wnd->sel && (wnd->selAddress == (addr + i))) + { + SetBkColor(mem_hdc, GetSysColor(COLOR_HIGHLIGHT)); + SetTextColor(mem_hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); + + switch(wnd->selPart) + { + case 0: _stprintf(text, TEXT("%08X"), val); break; + case 1: _stprintf(text, TEXT("%01X......."), wnd->selNewVal); break; + case 2: _stprintf(text, TEXT("%02X......"), wnd->selNewVal); break; + case 3: _stprintf(text, TEXT("%03X....."), wnd->selNewVal); break; + case 4: _stprintf(text, TEXT("%04X...."), wnd->selNewVal); break; + case 5: _stprintf(text, TEXT("%05X..."), wnd->selNewVal); break; + case 6: _stprintf(text, TEXT("%06X.."), wnd->selNewVal); break; + case 7: _stprintf(text, TEXT("%07X."), wnd->selNewVal); break; + } + } + else + { + SetBkColor(mem_hdc, RGB(255, 255, 255)); + SetTextColor(mem_hdc, RGB(0, 0, 0)); + + _stprintf(text, TEXT("%08X"), val); + } + + TextOut(mem_hdc, curx, cury, text, lstrlen(text)); + curx += (fontsize.cx * (8+1)); + } + curx += (fontsize.cx * 8); + } + break; + } + + SetBkColor(mem_hdc, RGB(255, 255, 255)); + SetTextColor(mem_hdc, RGB(0, 0, 0)); + + for(i = 0; i < 16; i++) + { + uint8 val = memory[(line << 4) + i]; + + if((val >= 32) && (val <= 127)) + text[i] = (TCHAR)val; + else + text[i] = TEXT('.'); + } + text[16] = '\0'; + TextOut(mem_hdc, curx, cury, text, lstrlen(text)); + + cury += fontsize.cy; + } + + BitBlt(hdc, 0, 0, w, h, mem_hdc, 0, 0, SRCCOPY); + + DeleteDC(mem_hdc); + DeleteObject(mem_bmp); + + EndPaint(hCtl, &ps); + + return 0; +} + +LRESULT CALLBACK MemView_ViewBoxProc(HWND hCtl, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + CMemView* wnd = (CMemView*)GetWindowLongPtr(hCtl, DWLP_USER); + + switch(uMsg) + { + case WM_NCCREATE: + SetScrollRange(hCtl, SB_VERT, 0x00000000, 0x000FFFF0, TRUE); + SetScrollPos(hCtl, SB_VERT, 0x00000000, TRUE); + return 1; + + case WM_NCDESTROY: + return 1; + + case WM_ERASEBKGND: + return 1; + + case WM_PAINT: + MemView_ViewBoxPaint(wnd, hCtl, wParam, lParam); + return 1; + + case WM_LBUTTONDOWN: + { + HDC hdc; + HFONT font; + SIZE fontsize; + int x, y; + + wnd->sel = FALSE; + wnd->selAddress = 0x00000000; + wnd->selPart = 0; + wnd->selNewVal = 0x00000000; + + hdc = GetDC(hCtl); + font = (HFONT)SelectObject(hdc, wnd->font); + GetTextExtentPoint32(hdc, TEXT(" "), 1, &fontsize); + + x = LOWORD(lParam); + y = HIWORD(lParam); + + if((x >= ((fontsize.cx * 8) + 5)) && (y >= (fontsize.cy + 3))) + { + int line, col; + + x -= ((fontsize.cx * 8) + 5); + y -= (fontsize.cy + 3); + + line = (y / fontsize.cy); + + switch(wnd->viewMode) + { + case 0: + { + if((x < (fontsize.cx * 2)) || (x >= (fontsize.cx * (2 + ((2+1) * 16))))) + break; + + col = ((x - (fontsize.cx * 2)) / (fontsize.cx * (2+1))); + + wnd->sel = TRUE; + + } + break; + + case 1: + { + if((x < (fontsize.cx * 6)) || (x >= (fontsize.cx * (6 + ((4+1) * 8))))) + break; + + col = ((x - (fontsize.cx * 6)) / (fontsize.cx * (4+1)) * 2); + + wnd->sel = TRUE; + + } + break; + + case 2: + { + if((x < (fontsize.cx * 8)) || (x >= (fontsize.cx * (8 + ((8+1) * 4))))) + break; + + col = ((x - (fontsize.cx * 8)) / (fontsize.cx * (8+1)) * 4); + + wnd->sel = TRUE; + + } + break; + } + + wnd->selAddress = (wnd->address + (line << 4) + col); + wnd->selPart = 0; + wnd->selNewVal = 0x00000000; + } + + SelectObject(hdc, font); + ReleaseDC(hCtl, hdc); + + SetFocus(hCtl); /* Required to receive keyboard messages */ + wnd->Refresh(); + } + return 1; + + case WM_CHAR: + { + TCHAR ch = (TCHAR)wParam; + + if(((ch >= TEXT('0')) && (ch <= TEXT('9'))) || ((ch >= TEXT('A')) && (ch <= TEXT('F'))) || ((ch >= TEXT('a')) && (ch <= TEXT('f')))) + { + //if ((wnd->region == ARMCPU_ARM7) && ((wnd->selAddress & 0xFFFF0000) == 0x04800000)) + // return DefWindowProc(hCtl, uMsg, wParam, lParam); + + uint8 maxSelPart[3] = {2, 4, 8}; + + wnd->selNewVal <<= 4; + wnd->selPart++; + + if((ch >= TEXT('0')) && (ch <= TEXT('9'))) + wnd->selNewVal |= (ch - TEXT('0')); + else if((ch >= TEXT('A')) && (ch <= TEXT('F'))) + wnd->selNewVal |= (ch - TEXT('A') + 0xA); + else if((ch >= TEXT('a')) && (ch <= TEXT('f'))) + wnd->selNewVal |= (ch - TEXT('a') + 0xA); + + if(wnd->selPart >= maxSelPart[wnd->viewMode]) + { + switch(wnd->viewMode) + { + case 0: memWrite8((RegionType)wnd->region, wnd->selAddress, (uint8)wnd->selNewVal); wnd->selAddress++; break; + case 1: memWrite16((RegionType)wnd->region, wnd->selAddress, (uint16)wnd->selNewVal); wnd->selAddress += 2; break; + case 2: memWrite32((RegionType)wnd->region, wnd->selAddress, wnd->selNewVal); wnd->selAddress += 4; break; + } + wnd->selPart = 0; + wnd->selNewVal = 0x00000000; + + if(wnd->selAddress == 0x00000000) + { + wnd->sel = FALSE; + } + else if(wnd->selAddress >= (wnd->address + 0x100)) + { + MemViewRegion& region = s_memoryRegions[wnd->region]; + HWAddressType addrMin = (region.hardwareAddress) & 0xFFFFFF00; + HWAddressType addrMax = max(addrMin, (region.hardwareAddress + region.size - 0x100 - 1) & 0xFFFFFF00); + if (wnd->address + 0x10 <= addrMax) + { + wnd->address += 0x10; + SetScrollPos(hCtl, SB_VERT, (((wnd->address - region.hardwareAddress) >> 4) & 0x000FFFFF), TRUE); + } + else + { + switch(wnd->viewMode) + { + case 0: wnd->selAddress--; break; + case 1: wnd->selAddress -= 2; break; + case 2: wnd->selAddress -= 4; break; + } + } + } + } + } + + wnd->Refresh(); + } + return 1; + + case WM_VSCROLL: + { + int firstpos = GetScrollPos(hCtl, SB_VERT); + MemViewRegion& region = s_memoryRegions[wnd->region]; + HWAddressType addrMin = (region.hardwareAddress) & 0xFFFFFF00; + HWAddressType addrMax = (region.hardwareAddress + region.size - 1) & 0xFFFFFF00; + + switch(LOWORD(wParam)) + { + case SB_LINEUP: + wnd->address = (uint32)max((int)addrMin, ((int)wnd->address - 0x10)); + break; + + case SB_LINEDOWN: + wnd->address = min((uint32)addrMax, (wnd->address + 0x10)); + break; + + case SB_PAGEUP: + wnd->address = (uint32)max((int)addrMin, ((int)wnd->address - 0x100)); + break; + + case SB_PAGEDOWN: + wnd->address = min((uint32)addrMax, (wnd->address + 0x100)); + break; + + case SB_THUMBTRACK: + case SB_THUMBPOSITION: + { + SCROLLINFO si; + + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_TRACKPOS; + + GetScrollInfo(hCtl, SB_VERT, &si); + + wnd->address = min((uint32)addrMax, (wnd->address + ((si.nTrackPos - firstpos) * 16))); + } + break; + } + + if((wnd->selAddress < wnd->address) || (wnd->selAddress >= (wnd->address + 0x100))) + { + wnd->sel = FALSE; + wnd->selAddress = 0x00000000; + wnd->selPart = 0; + wnd->selNewVal = 0x00000000; + } + + SetScrollPos(hCtl, SB_VERT, (((wnd->address - region.hardwareAddress) >> 4) & 0x000FFFFF), TRUE); + wnd->Refresh(); + } + return 1; + } + + return DefWindowProc(hCtl, uMsg, wParam, lParam); +} + +////////////////////////////////////////////////////////////////////////////// diff --git a/win32/memView.h b/win32/memView.h new file mode 100644 index 00000000..c06a8528 --- /dev/null +++ b/win32/memView.h @@ -0,0 +1,58 @@ +//MemView dialog was copied and adapted from DeSmuME: http://sourceforge.net/projects/desmume/ +//Authors: DeSmuME team + +// gocha: +// 1. According to Normmatt, YopYop had released the final version of original desmume as public domain. +// 2. I asked DeSmuME team about the problem between GPL and Snes9x license, but no one raised any objections. +// Therefore, I decided to include this code into snes9x-rr project. +// Dear DeSmuME coders: if it is not allowed, please tell me. + +/* Copyright (C) 2006 yopyop + yopyop156@ifrance.com + yopyop156.ifrance.com + + This file is part of DeSmuME + + DeSmuME 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; either version 2 of the License, or + (at your option) any later version. + + DeSmuME 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 for more details. + + You should have received a copy of the GNU General Public License + along with DeSmuME; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef MEM_VIEW_H +#define MEM_VIEW_H + +#include +#include "../port.h" + +INT_PTR CALLBACK MemView_DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK MemView_ViewBoxProc(HWND hCtl, UINT uMsg, WPARAM wParam, LPARAM lParam); + +class CMemView : public CToolWindow +{ +public: + CMemView(); + ~CMemView(); + + HFONT font; + + uint32 region; + uint32 address; + uint32 viewMode; + + BOOL sel; + uint32 selPart; + uint32 selAddress; + uint32 selNewVal; +}; + +#endif diff --git a/win32/rsrc/resource.h b/win32/rsrc/resource.h index e4485c77..f714ece7 100644 --- a/win32/rsrc/resource.h +++ b/win32/rsrc/resource.h @@ -42,6 +42,7 @@ #define IDD_EDITWATCH 502 #define IDD_PROMPT 503 #define IDR_RWACCELERATOR 504 +#define IDD_MEM_VIEW 505 #define IDC_DRIVER 1001 #define IDC_BUFLEN 1002 #define IDC_RATE 1003 @@ -407,6 +408,14 @@ #define IDC_PROMPT_TEXT2 5037 #define IDC_PROMPT_EDIT 5038 #define IDC_WATCHES_GROUP 5039 +#define IDC_GO 5040 +#define IDC_TEXTDUMP 5041 +#define IDC_MEMVIEWBOX 5042 +//#define IDC_ADDRESS 5043 +#define IDC_RAWDUMP 5044 +#define IDC_REGION 5045 +#define IDC_VIEWMODE 5046 +#define IDC_DUMPALL 5047 #define ID_FILE_EXIT 40001 #define ID_WINDOW_HIDEMENUBAR 40004 #define ID_FILE_AVI_RECORDING 40005 @@ -552,6 +561,7 @@ #define ID_RAM_WATCH 45009 #define RW_MENU_FIRST_RECENT_FILE 45010 #define RW_MENU_LAST_RECENT_FILE 45011 +#define IDM_MEMORY 45031 // Next default values for new objects // diff --git a/win32/rsrc/snes9x.rc b/win32/rsrc/snes9x.rc index fb83c951..2c48d44f 100644 --- a/win32/rsrc/snes9x.rc +++ b/win32/rsrc/snes9x.rc @@ -697,6 +697,25 @@ BEGIN EDITTEXT IDC_PROMPT_EDIT,10,15,167,14,ES_AUTOHSCROLL END +IDD_MEM_VIEW DIALOGEX 0, 0, 444, 283 +STYLE DS_SETFONT | DS_CENTER | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + PUSHBUTTON "Close",IDCANCEL,378,262,60,15 + GROUPBOX "View mode",IDC_STATIC,66,6,72,30 + GROUPBOX "Address",IDC_STATIC,144,6,108,30 + PUSHBUTTON "Go",IDC_GO,222,18,25,14 + PUSHBUTTON "Text dump",IDC_TEXTDUMP,264,18,49,14 + CONTROL "1",IDC_MEMVIEWBOX,"MemView_ViewBox",WS_VSCROLL | WS_TABSTOP,6,42,432,204 + EDITTEXT IDC_ADDRESS,150,18,72,14,ES_AUTOHSCROLL,WS_EX_TRANSPARENT + PUSHBUTTON "Raw dump",IDC_RAWDUMP,317,18,48,14 + GROUPBOX "Dump view",IDC_STATIC,258,6,180,30 + GROUPBOX "Region",IDC_STATIC,6,6,54,30 + COMBOBOX IDC_REGION,12,18,42,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_VIEWMODE,72,18,60,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Dump All",IDC_DUMPALL,369,18,48,14 +END + ///////////////////////////////////////////////////////////////////////////// // @@ -825,6 +844,12 @@ BEGIN TOPMARGIN, 7 BOTTOMMARGIN, 83 END + + IDD_MEM_VIEW, DIALOG + BEGIN + RIGHTMARGIN, 438 + BOTTOMMARGIN, 270 + END END #endif // APSTUDIO_INVOKED @@ -1077,6 +1102,7 @@ BEGIN MENUITEM SEPARATOR MENUITEM "RAM Watch...", ID_RAM_WATCH, GRAYED MENUITEM "RAM &Search...\tAlt+A", ID_RAM_SEARCH, GRAYED + MENUITEM "View &Memory", IDM_MEMORY END POPUP "&Netplay" BEGIN diff --git a/win32/snes9xw.vcproj b/win32/snes9xw.vcproj index a0a95caa..82560b79 100644 --- a/win32/snes9xw.vcproj +++ b/win32/snes9xw.vcproj @@ -3014,6 +3014,14 @@ RelativePath=".\AVIOutput.h" > + + + + @@ -3022,6 +3030,14 @@ RelativePath=".\InputCustom.h" > + + + + diff --git a/win32/wsnes9x.cpp b/win32/wsnes9x.cpp index 66c794bf..1c67980b 100644 --- a/win32/wsnes9x.cpp +++ b/win32/wsnes9x.cpp @@ -214,6 +214,8 @@ #include "../statemanager.h" #include "AVIOutput.h" #include "InputCustom.h" +#include "CWindow.h" +#include "memView.h" #include "ram_search.h" #include "ramwatch.h" #include @@ -2240,6 +2242,12 @@ LRESULT CALLBACK WinProc( else SetForegroundWindow(RamWatchHWnd); break; + case IDM_MEMORY: + if (!RegWndClass(TEXT("MemView_ViewBox"), MemView_ViewBoxProc, 0, sizeof(CMemView*))) + return 0; + + OpenToolWindow(new CMemView()); + return 0; case ID_CHEAT_APPLY: Settings.ApplyCheats = !Settings.ApplyCheats; if (!Settings.ApplyCheats){ @@ -3488,7 +3496,7 @@ int WINAPI WinMain( } S9xMainLoop(); - Update_RAM_Search(); // Update_RAM_Watch() is also called. + UpdateToolWindows(); GUI.FrameCount++; } @@ -3513,6 +3521,8 @@ int WINAPI WinMain( loop_exit: + CloseAllToolWindows(); + Settings.StopEmulation = TRUE; // stop sound playback @@ -3561,6 +3571,13 @@ loop_exit: return msg.wParam; } +void UpdateToolWindows() +{ + Update_RAM_Search(); //Update_RAM_Watch() is also called + + RefreshAllToolWindows(); +} + void FreezeUnfreeze (int slot, bool8 freeze) { const char *filename; @@ -10123,11 +10140,11 @@ INT_PTR CALLBACK DlgCheatSearchAdd(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP strncpy(new_cheat->name,_tToChar(tempBuf),22); new_cheat->enabled=TRUE; - for(int byteIndex = 0; byteIndex < new_cheat->size; byteIndex++) - { - S9xAddCheat(new_cheat->enabled,new_cheat->saved_val,new_cheat->address+byteIndex,(new_cheat->new_val>>(8*byteIndex))&0xFF); - strcpy(Cheat.c[Cheat.num_cheats-1].name,new_cheat->name); - } + for(int byteIndex = 0; byteIndex < new_cheat->size; byteIndex++) + { + S9xAddCheat(new_cheat->enabled,new_cheat->saved_val,new_cheat->address+byteIndex,(new_cheat->new_val>>(8*byteIndex))&0xFF); + strcpy(Cheat.c[Cheat.num_cheats-1].name,new_cheat->name); + } ret=0; } } diff --git a/win32/wsnes9x.h b/win32/wsnes9x.h index 11f97be9..3023d1fd 100644 --- a/win32/wsnes9x.h +++ b/win32/wsnes9x.h @@ -612,4 +612,6 @@ bool GetFilterHiResSupport(RenderFilter filterID); const TCHAR * S9xGetDirectoryT (enum s9x_getdirtype); RECT GetWindowMargins(HWND hwnd, UINT width); +void UpdateToolWindows(); + #endif // !defined(SNES9X_H_INCLUDED)