@@ -1,1282 +1,1291 @@
//RamWatch dialog was copied and adapted from GENS11: http://code.google.com/p/gens-rerecording/
//Authors: Upthorn, Nitsuja, adelikat

#include "../port.h"
#include "../snes9x.h"
#include "../display.h"
#include "../memmap.h"
#include "../cheats.h"
#include "wsnes9x.h"
#include "rsrc/resource.h"
#include "ramwatch.h"
#include "ram_search.h"
#include <assert.h>
#include <windows.h>
#include <commctrl.h>
#include <string>

#ifdef UNICODE
#define _tToChar WideToUtf8
#define _tFromChar Utf8ToWide
#else
#define _tToChar
#define _tFromChar
#endif

extern SCheatData Cheat;

/*
#include <commctrl.h>
#pragma comment(lib, "comctl32.lib")
#include <shellapi.h>
#pragma comment(lib, "shell32.lib")
#include <commdlg.h>
#pragma comment(lib, "comdlg32.lib")
*/

static HMENU ramwatchmenu;
static HMENU rwrecentmenu;
/*static*/ HACCEL RamWatchAccels = NULL;
TCHAR rw_recent_files[MAX_RECENT_WATCHES][1024];
//TCHAR Watch_Dir[1024]=TEXT("");
bool RWfileChanged = false; //Keeps track of whether the current watch file has been changed, if so, ramwatch will prompt to save changes
bool AutoRWLoad = false; //Keeps track of whether Auto-load is checked
bool RWSaveWindowPos = false; //Keeps track of whether Save Window position is checked
TCHAR currentWatch[1024];
int ramw_x, ramw_y; //Used to store ramwatch dialog window positions
AddressWatcher rswatches[MAX_WATCH_COUNT];
int WatchCount=0;

TCHAR applicationPath[2048];
struct InitRamWatch
{
InitRamWatch()
{
GetModuleFileName(NULL, applicationPath, 2048);
}
} initRamWatch;

HWND RamWatchHWnd;
#define gamefilename _tFromChar(S9xBasename(S9xGetFilename("", DEFAULT_DIR)))
#define hWnd GUI.hWnd
#define hInst GUI.hInstance
static TCHAR Str_Tmp [1024];

void init_list_box(HWND Box, const TCHAR* Strs[], int numColumns, int *columnWidths); //initializes the ram search and/or ram watch listbox

#define MESSAGEBOXPARENT (RamWatchHWnd ? RamWatchHWnd : hWnd)

bool QuickSaveWatches();
bool ResetWatches();

void RefreshWatchListSelectedCountControlStatus(HWND hDlg);

unsigned int GetCurrentValue(AddressWatcher& watch)
{
return ReadValueAtHardwareAddress(watch.Address, watch.Size == TEXT('d') ? 4 : watch.Size == TEXT('w') ? 2 : 1);
}

bool IsSameWatch(const AddressWatcher& l, const AddressWatcher& r)
{
return ((l.Address == r.Address) && (l.Size == r.Size) && (l.Type == r.Type)/* && (l.WrongEndian == r.WrongEndian)*/);
}

bool VerifyWatchNotAlreadyAdded(const AddressWatcher& watch)
{
for (int j = 0; j < WatchCount; j++)
{
if (IsSameWatch(rswatches[j], watch))
{
if(RamWatchHWnd)
SetForegroundWindow(RamWatchHWnd);
return false;
}
}
return true;
}


bool InsertWatch(const AddressWatcher& Watch, TCHAR *Comment)
{
if(!VerifyWatchNotAlreadyAdded(Watch))
return false;

if(WatchCount >= MAX_WATCH_COUNT)
return false;

int i = WatchCount++;
AddressWatcher& NewWatch = rswatches[i];
NewWatch = Watch;
//if (NewWatch.comment) free(NewWatch.comment);
NewWatch.comment = (TCHAR *) malloc((lstrlen(Comment)+2) * sizeof(TCHAR));
NewWatch.CurValue = GetCurrentValue(NewWatch);
lstrcpy(NewWatch.comment, Comment);
ListView_SetItemCount(GetDlgItem(RamWatchHWnd,IDC_WATCHLIST),WatchCount);
RWfileChanged=true;

return true;
}

LRESULT CALLBACK PromptWatchNameProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) //Gets the description of a watched address
{
RECT r;
RECT r2;
int dx1, dy1, dx2, dy2;

switch(uMsg)
{
case WM_INITDIALOG:
//Clear_Sound_Buffer();

GetWindowRect(hWnd, &r);
dx1 = (r.right - r.left) / 2;
dy1 = (r.bottom - r.top) / 2;

GetWindowRect(hDlg, &r2);
dx2 = (r2.right - r2.left) / 2;
dy2 = (r2.bottom - r2.top) / 2;

//SetWindowPos(hDlg, NULL, max(0, r.left + (dx1 - dx2)), max(0, r.top + (dy1 - dy2)), NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
SetWindowPos(hDlg, NULL, r.left, r.top, NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
lstrcpy(Str_Tmp,TEXT("Enter a name for this RAM address."));
SendDlgItemMessage(hDlg,IDC_PROMPT_TEXT,WM_SETTEXT,0,(LPARAM)Str_Tmp);
lstrcpy(Str_Tmp,TEXT(""));
SendDlgItemMessage(hDlg,IDC_PROMPT_TEXT2,WM_SETTEXT,0,(LPARAM)Str_Tmp);
return true;
break;

case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDOK:
{
GetDlgItemText(hDlg,IDC_PROMPT_EDIT,Str_Tmp,80);
InsertWatch(rswatches[WatchCount],Str_Tmp);
EndDialog(hDlg, true);
return true;
break;
}
case IDCANCEL:
EndDialog(hDlg, false);
return false;
break;
}
break;

case WM_CLOSE:
EndDialog(hDlg, false);
return false;
break;
}

return false;
}

bool InsertWatch(const AddressWatcher& Watch, HWND parent)
{
if(!VerifyWatchNotAlreadyAdded(Watch))
return false;

if(!parent)
parent = RamWatchHWnd;
if(!parent)
parent = hWnd;

int prevWatchCount = WatchCount;

rswatches[WatchCount] = Watch;
rswatches[WatchCount].CurValue = GetCurrentValue(rswatches[WatchCount]);
DialogBox(hInst, MAKEINTRESOURCE(IDD_PROMPT), parent, (DLGPROC) PromptWatchNameProc);

return WatchCount > prevWatchCount;
}

void Update_RAM_Watch()
{
BOOL watchChanged[MAX_WATCH_COUNT] = {0};

if(WatchCount)
{
// update cached values and detect changes to displayed listview items

for(int i = 0; i < WatchCount; i++)
{
unsigned int prevCurValue = rswatches[i].CurValue;
unsigned int newCurValue = GetCurrentValue(rswatches[i]);
if(prevCurValue != newCurValue)
{
rswatches[i].CurValue = newCurValue;
watchChanged[i] = TRUE;
}
}
}

// refresh any visible parts of the listview box that changed
HWND lv = GetDlgItem(RamWatchHWnd,IDC_WATCHLIST);
int top = ListView_GetTopIndex(lv);
int bottom = top + ListView_GetCountPerPage(lv) + 1; // +1 is so we will update a partially-displayed last item
if(top < 0) top = 0;
if(bottom > WatchCount) bottom = WatchCount;
int start = -1;
for(int i = top; i <= bottom; i++)
{
if(start == -1)
{
if(i != bottom && watchChanged[i])
{
start = i;
//somethingChanged = true;
}
}
else
{
if(i == bottom || !watchChanged[i])
{
ListView_RedrawItems(lv, start, i-1);
start = -1;
}
}
}
}

bool AskSave()
{
//This function asks to save changes if the watch file contents have changed
//returns false only if a save was attempted but failed or was cancelled
if (RWfileChanged)
{
int answer = MessageBox(MESSAGEBOXPARENT, TEXT("Save Changes?"), TEXT("Ram Watch"), MB_YESNOCANCEL);
if(answer == IDYES)
if(!QuickSaveWatches())
return false;
return (answer != IDCANCEL);
}
return true;
}

void WriteRecentRWFiles()
{
// TODO
/*
TCHAR str[2048];
for (int i = 0; i < MAX_RECENT_WATCHES; i++)
{
_stprintf(str, TEXT("recentWatch%d"), i+1);
regSetStringValue(str, &rw_recent_files[i][0]);
}
*/
}

void UpdateRW_RMenu(HMENU menu, unsigned int mitem, unsigned int baseid)
{
MENUITEMINFO moo;
int x;

moo.cbSize = sizeof(moo);
moo.fMask = MIIM_SUBMENU | MIIM_STATE;

GetMenuItemInfo(GetSubMenu(ramwatchmenu, 0), mitem, FALSE, &moo);
moo.hSubMenu = menu;
moo.fState = lstrlen(rw_recent_files[0]) ? MFS_ENABLED : MFS_GRAYED;

SetMenuItemInfo(GetSubMenu(ramwatchmenu, 0), mitem, FALSE, &moo);

// Remove all recent files submenus
for(x = 0; x < MAX_RECENT_WATCHES; x++)
{
RemoveMenu(menu, baseid + x, MF_BYCOMMAND);
}

// Recreate the menus
for(x = MAX_RECENT_WATCHES - 1; x >= 0; x--)
{
TCHAR tmp[128 + 5];

// Skip empty strings
if(!lstrlen(rw_recent_files[x]))
{
continue;
}

moo.cbSize = sizeof(moo);
moo.fMask = MIIM_DATA | MIIM_ID | MIIM_TYPE;

// Fill in the menu text.
if(lstrlen(rw_recent_files[x]) < 128)
{
_stprintf(tmp, TEXT("&%d. %s"), ( x + 1 ) % 10, rw_recent_files[x]);
}
else
{
_stprintf(tmp, TEXT("&%d. %s"), ( x + 1 ) % 10, rw_recent_files[x] + lstrlen( rw_recent_files[x] ) - 127);
}

// Insert the menu item
moo.cch = lstrlen(tmp);
moo.fType = 0;
moo.wID = baseid + x;
moo.dwTypeData = tmp;
InsertMenuItem(menu, 0, 1, &moo);
}

// I don't think one function shall do so many things in a row
// WriteRecentRWFiles(); // write recent menu to ini
}

void UpdateRWRecentArray(const TCHAR* addString, unsigned int arrayLen, HMENU menu, unsigned int menuItem, unsigned int baseId)
{
const size_t len = 1024; // Avoid magic numbers

// Try to find out if the filename is already in the recent files list.
for(unsigned int x = 0; x < arrayLen; x++)
{
if(lstrlen(rw_recent_files[x]))
{
if(_tcsnccmp(rw_recent_files[x], addString, 1024)) // Item is already in list.
{
// If the filename is in the file list don't add it again.
// Move it up in the list instead.

int y;
TCHAR tmp[len];

// Save pointer.
lstrcpyn(tmp, rw_recent_files[x], len);

for(y = x; y; y--)
{
// Move items down.
lstrcpyn(rw_recent_files[y],rw_recent_files[y - 1], len);
}

// Put item on top.
lstrcpyn(rw_recent_files[0],tmp, len);

// Update the recent files menu
UpdateRW_RMenu(menu, menuItem, baseId);

return;
}
}
}

// The filename wasn't found in the list. That means we need to add it.

// Move the other items down.
for(unsigned int x = arrayLen - 1; x; x--)
{
lstrcpyn(rw_recent_files[x],rw_recent_files[x - 1], len);
}

// Add the new item.
lstrcpyn(rw_recent_files[0], addString, len);

// Update the recent files menu
UpdateRW_RMenu(menu, menuItem, baseId);
}

void RWAddRecentFile(const TCHAR *filename)
{
UpdateRWRecentArray(filename, MAX_RECENT_WATCHES, rwrecentmenu, RAMMENU_FILE_RECENT, RW_MENU_FIRST_RECENT_FILE);
}

void OpenRWRecentFile(int memwRFileNumber)
{
if(!ResetWatches())
return;

int rnum = memwRFileNumber;
if ((unsigned int)rnum >= MAX_RECENT_WATCHES)
return; //just in case

TCHAR* x;

while(true)
{
x = rw_recent_files[rnum];
if (!*x)
return; //If no recent files exist just return. Useful for Load last file on startup (or if something goes screwy)

if (rnum) //Change order of recent files if not most recent
{
RWAddRecentFile(x);
rnum = 0;
}
else
{
break;
}
}

lstrcpy(currentWatch,x);
lstrcpy(Str_Tmp,currentWatch);

//loadwatches here
FILE *WatchFile = _tfopen(Str_Tmp,TEXT("rb"));
if (!WatchFile)
{
int answer = MessageBox(MESSAGEBOXPARENT,TEXT("Error opening file."),TEXT("ERROR"),MB_OKCANCEL);
if (answer == IDOK)
{
rw_recent_files[rnum][0] = TEXT('\0'); //Clear file from list
if (rnum) //Update the ramwatch list
RWAddRecentFile(rw_recent_files[0]);
else
RWAddRecentFile(rw_recent_files[1]);
}
return;
}
const TCHAR DELIM = TEXT('\t');
AddressWatcher Temp;
TCHAR mode;
_fgetts(Str_Tmp,1024,WatchFile);
_stscanf(Str_Tmp,TEXT("%c%*s"),&mode);
int WatchAdd;
_fgetts(Str_Tmp,1024,WatchFile);
_stscanf(Str_Tmp,TEXT("%d%*s"),&WatchAdd);
WatchAdd+=WatchCount;
for (int i = WatchCount; i < WatchAdd; i++)
{
TCHAR TempAddressStr[11];
while (i < 0)
i++;
do {
_fgetts(Str_Tmp,1024,WatchFile);
} while (Str_Tmp[0] == TEXT('\n'));
_stscanf(Str_Tmp,TEXT("%*05X%*c%6s%*c%c%*c%c%*c%d"),TempAddressStr,&(Temp.Size),&(Temp.Type),&(Temp.WrongEndian));
Temp.Address = DisplayToRWInternalAddress(TempAddressStr);
Temp.WrongEndian = 0;
TCHAR *Comment = _tcsrchr(Str_Tmp,DELIM) + 1;
*_tcsrchr(Comment,TEXT('\n')) = TEXT('\0');
InsertWatch(Temp,Comment);
}

fclose(WatchFile);
if (RamWatchHWnd) {
ListView_SetItemCount(GetDlgItem(RamWatchHWnd,IDC_WATCHLIST),WatchCount);
RefreshWatchListSelectedCountControlStatus(RamWatchHWnd);
}
RWfileChanged=false;
return;
}

int Change_File_L(TCHAR *Dest, TCHAR *Dir, TCHAR *Titre, TCHAR *Filter, TCHAR *Ext, HWND hwnd)
{
OPENFILENAME ofn;

SetCurrentDirectory(applicationPath);

if (!lstrcmp(Dest, TEXT("")))
{
lstrcpy(Dest, TEXT("default."));
lstrcat(Dest, Ext);
}

memset(&ofn, 0, sizeof(OPENFILENAME));

ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hwnd;
ofn.hInstance = hInst;
ofn.lpstrFile = Dest;
ofn.nMaxFile = 2047;
ofn.lpstrFilter = Filter;
ofn.nFilterIndex = 1;
ofn.lpstrInitialDir = Dir;
ofn.lpstrTitle = Titre;
ofn.lpstrDefExt = Ext;
ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;

if (GetOpenFileName(&ofn)) return 1;

return 0;
}

int Change_File_S(TCHAR *Dest, TCHAR *Dir, TCHAR *Titre, TCHAR *Filter, TCHAR *Ext, HWND hwnd)
{
OPENFILENAME ofn;

SetCurrentDirectory(applicationPath);

if (!lstrcmp(Dest, TEXT("")))
{
lstrcpy(Dest, TEXT("default."));
lstrcat(Dest, Ext);
}

memset(&ofn, 0, sizeof(OPENFILENAME));

ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hwnd;
ofn.hInstance = hInst;
ofn.lpstrFile = Dest;
ofn.nMaxFile = 2047;
ofn.lpstrFilter = Filter;
ofn.nFilterIndex = 1;
ofn.lpstrInitialDir = Dir;
ofn.lpstrTitle = Titre;
ofn.lpstrDefExt = Ext;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;

if (GetSaveFileName(&ofn)) return 1;

return 0;
}

bool Save_Watches()
{
const TCHAR* slash = std::max(_tcsrchr(gamefilename, TEXT('|')), std::max(_tcsrchr(gamefilename, TEXT('\\')), _tcsrchr(gamefilename, TEXT('/'))));
lstrcpy(Str_Tmp,slash ? slash+1 : gamefilename);
TCHAR* dot = _tcsrchr(Str_Tmp, TEXT('.'));
if(dot) *dot = 0;
lstrcat(Str_Tmp,TEXT(".wch"));
if(Change_File_S(Str_Tmp, applicationPath, TEXT("Save Watches"), TEXT("Watchlist\0*.wch\0All Files\0*.*\0\0"), TEXT("wch"), RamWatchHWnd))
{
FILE *WatchFile = _tfopen(Str_Tmp,TEXT("r+b"));
if (!WatchFile) WatchFile = _tfopen(Str_Tmp,TEXT("w+b"));
_fputtc(TEXT('\n'),WatchFile);
lstrcpy(currentWatch,Str_Tmp);
RWAddRecentFile(currentWatch);
_stprintf(Str_Tmp,TEXT("%d\n"),WatchCount);
_fputts(Str_Tmp,WatchFile);
const TCHAR DELIM = TEXT('\t');
for (int i = 0; i < WatchCount; i++)
{
_stprintf(Str_Tmp,TEXT("%05X%c%-6s%c%c%c%c%c%d%c%s\n"),i,DELIM,RWInternalToDisplayAddress(rswatches[i].Address),DELIM,rswatches[i].Size,DELIM,rswatches[i].Type,DELIM,rswatches[i].WrongEndian,DELIM,rswatches[i].comment);
_fputts(Str_Tmp,WatchFile);
}

fclose(WatchFile);
RWfileChanged=false;
//TODO: Add to recent list function call here
return true;
}
return false;
}

bool QuickSaveWatches()
{
if (RWfileChanged==false) return true; //If file has not changed, no need to save changes
if (currentWatch[0] == NULL) //If there is no currently loaded file, run to Save as and then return
{
return Save_Watches();
}

lstrcpy(Str_Tmp,currentWatch);
FILE *WatchFile = _tfopen(Str_Tmp,TEXT("r+b"));
if (!WatchFile) WatchFile = _tfopen(Str_Tmp,TEXT("w+b"));
_fputtc(TEXT('\n'),WatchFile);
_stprintf(Str_Tmp,TEXT("%d\n"),WatchCount);
_fputts(Str_Tmp,WatchFile);
const TCHAR DELIM = TEXT('\t');
for (int i = 0; i < WatchCount; i++)
{
_stprintf(Str_Tmp,TEXT("%05X%c%-6s%c%c%c%c%c%d%c%s\n"),i,DELIM,RWInternalToDisplayAddress(rswatches[i].Address),DELIM,rswatches[i].Size,DELIM,rswatches[i].Type,DELIM,rswatches[i].WrongEndian,DELIM,rswatches[i].comment);
_fputts(Str_Tmp,WatchFile);
}
fclose(WatchFile);
RWfileChanged=false;
return true;
}

bool Load_Watches(bool clear, const TCHAR* filename)
{
const TCHAR DELIM = TEXT('\t');
FILE* WatchFile = _tfopen(filename,TEXT("rb"));
if (!WatchFile)
{
MessageBox(MESSAGEBOXPARENT,TEXT("Error opening file."),TEXT("ERROR"),MB_OK);
return false;
}
if(clear)
{
if(!ResetWatches())
{
fclose(WatchFile);
return false;
}
}
lstrcpy(currentWatch,filename);
RWAddRecentFile(currentWatch);
AddressWatcher Temp;
TCHAR mode;
_fgetts(Str_Tmp,1024,WatchFile);
_stscanf(Str_Tmp,TEXT("%c%*s"),&mode);
int WatchAdd;
_fgetts(Str_Tmp,1024,WatchFile);
_stscanf(Str_Tmp,TEXT("%d%*s"),&WatchAdd);
WatchAdd+=WatchCount;
for (int i = WatchCount; i < WatchAdd; i++)
{
TCHAR TempAddressStr[11];
while (i < 0)
i++;
do {
_fgetts(Str_Tmp,1024,WatchFile);
} while (Str_Tmp[0] == TEXT('\n'));
_stscanf(Str_Tmp,TEXT("%*05X%*c%6s%*c%c%*c%c%*c%d"),TempAddressStr,&(Temp.Size),&(Temp.Type),&(Temp.WrongEndian));
Temp.Address = DisplayToRWInternalAddress(TempAddressStr);
Temp.WrongEndian = 0;
TCHAR *Comment = _tcsrchr(Str_Tmp,DELIM) + 1;
*_tcsrchr(Comment,TEXT('\n')) = TEXT('\0');
InsertWatch(Temp,Comment);
}

fclose(WatchFile);
if (RamWatchHWnd)
ListView_SetItemCount(GetDlgItem(RamWatchHWnd,IDC_WATCHLIST),WatchCount);
RWfileChanged=false;
return true;
}

bool Load_Watches(bool clear)
{
const TCHAR* slash = std::max(_tcsrchr(gamefilename, TEXT('|')), std::max(_tcsrchr(gamefilename, TEXT('\\')), _tcsrchr(gamefilename, TEXT('/'))));
lstrcpy(Str_Tmp,slash ? slash+1 : gamefilename);
TCHAR* dot = _tcsrchr(Str_Tmp, TEXT('.'));
if(dot) *dot = 0;
lstrcat(Str_Tmp,TEXT(".wch"));
if(Change_File_L(Str_Tmp, applicationPath, TEXT("Load Watches"), TEXT("Watchlist\0*.wch\0All Files\0*.*\0\0"), TEXT("wch"), RamWatchHWnd))
{
return Load_Watches(clear, Str_Tmp);
}
return false;
}

bool ResetWatches()
{
if(!AskSave())
return false;
for (;WatchCount>=0;WatchCount--)
{
free(rswatches[WatchCount].comment);
rswatches[WatchCount].comment = NULL;
}
WatchCount++;
if (RamWatchHWnd) {
ListView_SetItemCount(GetDlgItem(RamWatchHWnd,IDC_WATCHLIST),WatchCount);
RefreshWatchListSelectedCountControlStatus(RamWatchHWnd);
}
RWfileChanged = false;
currentWatch[0] = NULL;
return true;
}

void RemoveWatch(int watchIndex)
{
free(rswatches[watchIndex].comment);
rswatches[watchIndex].comment = NULL;
for (int i = watchIndex; i <= WatchCount; i++)
rswatches[i] = rswatches[i+1];
WatchCount--;
}

void RefreshWatchListSelectedItemControlStatus(HWND hDlg)
{
static int prevSelIndex=-1;
int selIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_RAMLIST));
if(selIndex != prevSelIndex)
{
if(selIndex == -1 || prevSelIndex == -1)
{
EnableWindow(GetDlgItem(hDlg, IDC_C_ADDCHEAT), (selIndex != -1) ? TRUE : FALSE);
}
prevSelIndex = selIndex;
}
}

LRESULT CALLBACK EditWatchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) //Gets info for a RAM Watch, and then inserts it into the Watch List
{
RECT r;
RECT r2;
int dx1, dy1, dx2, dy2;
static int index;
static char s,t = s = 0;

switch(uMsg)
{
case WM_INITDIALOG:
//Clear_Sound_Buffer();


GetWindowRect(hWnd, &r);
dx1 = (r.right - r.left) / 2;
dy1 = (r.bottom - r.top) / 2;

GetWindowRect(hDlg, &r2);
dx2 = (r2.right - r2.left) / 2;
dy2 = (r2.bottom - r2.top) / 2;

//SetWindowPos(hDlg, NULL, max(0, r.left + (dx1 - dx2)), max(0, r.top + (dy1 - dy2)), NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
SetWindowPos(hDlg, NULL, r.left, r.top, NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
index = (int)lParam;
lstrcpy(Str_Tmp, RWInternalToDisplayAddress(rswatches[index].Address));
SetDlgItemText(hDlg,IDC_EDIT_COMPAREADDRESS,Str_Tmp);
if (rswatches[index].comment != NULL)
SetDlgItemText(hDlg,IDC_PROMPT_EDIT,rswatches[index].comment);
s = rswatches[index].Size;
t = rswatches[index].Type;
switch (s)
{
case TEXT('b'):
SendDlgItemMessage(hDlg, IDC_1_BYTE, BM_SETCHECK, BST_CHECKED, 0);
break;
case TEXT('w'):
SendDlgItemMessage(hDlg, IDC_2_BYTES, BM_SETCHECK, BST_CHECKED, 0);
break;
case TEXT('d'):
SendDlgItemMessage(hDlg, IDC_4_BYTES, BM_SETCHECK, BST_CHECKED, 0);
break;
default:
s = 0;
break;
}
switch (t)
{
case TEXT('s'):
SendDlgItemMessage(hDlg, IDC_SIGNED, BM_SETCHECK, BST_CHECKED, 0);
break;
case TEXT('u'):
SendDlgItemMessage(hDlg, IDC_UNSIGNED, BM_SETCHECK, BST_CHECKED, 0);
break;
case TEXT('h'):
SendDlgItemMessage(hDlg, IDC_HEX, BM_SETCHECK, BST_CHECKED, 0);
break;
default:
t = 0;
break;
}

return true;
break;

case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDC_SIGNED:
t=TEXT('s');
return true;
case IDC_UNSIGNED:
t=TEXT('u');
return true;
case IDC_HEX:
t=TEXT('h');
return true;
case IDC_1_BYTE:
s = TEXT('b');
return true;
case IDC_2_BYTES:
s = TEXT('w');
return true;
case IDC_4_BYTES:
s = TEXT('d');
return true;
case IDOK:
{
if (s && t)
{
AddressWatcher Temp;
Temp.Size = s;
Temp.Type = t;
Temp.WrongEndian = false; //replace this when I get little endian working properly
GetDlgItemText(hDlg,IDC_EDIT_COMPAREADDRESS,Str_Tmp,1024);
TCHAR *addrstr = Str_Tmp;
if (lstrlen(Str_Tmp) > 8) addrstr = &(Str_Tmp[lstrlen(Str_Tmp) - 9]);
for(int i = 0; addrstr[i]; i++) {if(_totupper(addrstr[i]) == TEXT('O')) addrstr[i] = TEXT('0');}
Temp.Address = DisplayToRWInternalAddress(addrstr);

if((Temp.Address & ~0xFFFFFF) == ~0xFFFFFF)
Temp.Address &= 0xFFFFFF;

if(IsRAMWatchAddressValid(Temp.Address))
{
GetDlgItemText(hDlg,IDC_PROMPT_EDIT,Str_Tmp,80);
if (index < WatchCount) RemoveWatch(index);
InsertWatch(Temp,Str_Tmp);
if(RamWatchHWnd)
{
ListView_SetItemCount(GetDlgItem(RamWatchHWnd,IDC_WATCHLIST),WatchCount);
}
EndDialog(hDlg, true);
}
else
{
MessageBox(hDlg,TEXT("Invalid Address"),TEXT("ERROR"),MB_OK);
}
}
else
{
lstrcpy(Str_Tmp,TEXT("Error:"));
if (!s)
lstrcat(Str_Tmp,TEXT(" Size must be specified."));
if (!t)
lstrcat(Str_Tmp,TEXT(" Type must be specified."));
MessageBox(hDlg,Str_Tmp,TEXT("ERROR"),MB_OK);
}
RWfileChanged=true;
return true;
break;
}
case IDCANCEL:
EndDialog(hDlg, false);
return false;
break;
}
break;

case WM_CLOSE:
EndDialog(hDlg, false);
return false;
break;
}

return false;
}




void RamWatchEnableCommand(HWND hDlg, HMENU hMenu, UINT uIDEnableItem, bool enable)
{
EnableWindow(GetDlgItem(hDlg, uIDEnableItem), (enable?TRUE:FALSE));
if (hMenu != NULL) {
if (uIDEnableItem == ID_WATCHES_UPDOWN) {
EnableMenuItem(hMenu, IDC_C_WATCH_UP, MF_BYCOMMAND | (enable?MF_ENABLED:MF_GRAYED));
EnableMenuItem(hMenu, IDC_C_WATCH_DOWN, MF_BYCOMMAND | (enable?MF_ENABLED:MF_GRAYED));
}
else
EnableMenuItem(hMenu, uIDEnableItem, MF_BYCOMMAND | (enable?MF_ENABLED:MF_GRAYED));
}
}

void RefreshWatchListSelectedCountControlStatus(HWND hDlg)
{
static int prevSelCount=-1;
int selCount = ListView_GetSelectedCount(GetDlgItem(hDlg,IDC_WATCHLIST));
if(selCount != prevSelCount)
{
if(selCount < 2 || prevSelCount < 2)
{
RamWatchEnableCommand(hDlg, ramwatchmenu, IDC_C_WATCH_EDIT, selCount == 1);
RamWatchEnableCommand(hDlg, ramwatchmenu, IDC_C_WATCH_REMOVE, selCount >= 1);
RamWatchEnableCommand(hDlg, ramwatchmenu, IDC_C_WATCH_DUPLICATE, selCount == 1);
RamWatchEnableCommand(hDlg, ramwatchmenu, IDC_C_ADDCHEAT, selCount == 1);
RamWatchEnableCommand(hDlg, ramwatchmenu, ID_WATCHES_UPDOWN, selCount == 1);
}
prevSelCount = selCount;
}
}

LRESULT CALLBACK RamWatchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
RECT r;
RECT r2;
int dx1, dy1, dx2, dy2;
static int watchIndex=0;

switch(uMsg)
{
case WM_MOVE: {
RECT wrect;
GetWindowRect(hDlg,&wrect);
ramw_x = wrect.left;
ramw_y = wrect.top;
// TODO
//regSetDwordValue(RAMWX, ramw_x);
//regSetDwordValue(RAMWY, ramw_y);
} break;

case WM_INITDIALOG: {
GetWindowRect(hWnd, &r); //Ramwatch window
dx1 = (r.right - r.left) / 2;
dy1 = (r.bottom - r.top) / 2;

GetWindowRect(hDlg, &r2); // TASer window
dx2 = (r2.right - r2.left) / 2;
dy2 = (r2.bottom - r2.top) / 2;


// push it away from the main window if we can
const int width = (r.right-r.left);
const int height = (r.bottom - r.top);
const int width2 = (r2.right-r2.left);
if(r.left+width2 + width < GetSystemMetrics(SM_CXSCREEN))
{
r.right += width;
r.left += width;
}
else if((int)r.left - (int)width2 > 0)
{
r.right -= width2;
r.left -= width2;
}

//-----------------------------------------------------------------------------------
//If user has Save Window Pos selected, override default positioning
if (RWSaveWindowPos)
{
//If ramwindow is for some reason completely off screen, use default instead
if (ramw_x > (-width*2) || ramw_x < (width*2 + GetSystemMetrics(SM_CYSCREEN)) )
r.left = ramw_x; //This also ignores cases of windows -32000 error codes
//If ramwindow is for some reason completely off screen, use default instead
if (ramw_y > (0-height*2) ||ramw_y < (height*2 + GetSystemMetrics(SM_CYSCREEN)) )
r.top = ramw_y; //This also ignores cases of windows -32000 error codes
}
//-------------------------------------------------------------------------------------
SetWindowPos(hDlg, NULL, r.left, r.top, NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);

ramwatchmenu=GetMenu(hDlg);
rwrecentmenu=CreateMenu();
UpdateRW_RMenu(rwrecentmenu, RAMMENU_FILE_RECENT, RW_MENU_FIRST_RECENT_FILE);

const TCHAR* names[3] = {TEXT("Address"),TEXT("Value"),TEXT("Notes")};
int widths[3] = {62,64,64+51+53};
init_list_box(GetDlgItem(hDlg,IDC_WATCHLIST),names,3,widths);
if (!ResultCount)
reset_address_info();
else
signal_new_frame();
ListView_SetItemCount(GetDlgItem(hDlg,IDC_WATCHLIST),WatchCount);
if (!noMisalign) SendDlgItemMessage(hDlg, IDC_MISALIGN, BM_SETCHECK, BST_CHECKED, 0);
//if (littleEndian) SendDlgItemMessage(hDlg, IDC_ENDIAN, BM_SETCHECK, BST_CHECKED, 0);

RamWatchAccels = LoadAccelerators(hInst, MAKEINTRESOURCE(IDR_RWACCELERATOR));

// due to some bug in windows, the arrow button width from the resource gets ignored, so we have to set it here
SetWindowPos(GetDlgItem(hDlg,ID_WATCHES_UPDOWN), 0,0,0, 30,60, SWP_NOMOVE);

Update_RAM_Watch();

DragAcceptFiles(hDlg, TRUE);

RefreshWatchListSelectedCountControlStatus(hDlg);
return false;
} break;

case WM_INITMENU:
CheckMenuItem(ramwatchmenu, RAMMENU_FILE_AUTOLOAD, AutoRWLoad ? MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(ramwatchmenu, RAMMENU_FILE_SAVEWINDOW, RWSaveWindowPos ? MF_CHECKED : MF_UNCHECKED);
break;

case WM_MENUSELECT:
case WM_ENTERSIZEMOVE:
//Clear_Sound_Buffer();
break;

case WM_NOTIFY:
{
switch(wParam)
{
case ID_WATCHES_UPDOWN:
{
switch(((LPNMUPDOWN)lParam)->hdr.code)
{
case UDN_DELTAPOS: {
int delta = ((LPNMUPDOWN)lParam)->iDelta;
SendMessage(hDlg, WM_COMMAND, delta<0 ? IDC_C_WATCH_UP : IDC_C_WATCH_DOWN,0);
} break;
}
} break;

default:
{
LPNMHDR lP = (LPNMHDR) lParam;
switch (lP->code)
{
case LVN_ITEMCHANGED: // selection changed event
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)lP;
if(pNMListView->uNewState & LVIS_FOCUSED ||
(pNMListView->uNewState ^ pNMListView->uOldState) & LVIS_SELECTED)
{
// disable buttons that we don't have the right number of selected items for
RefreshWatchListSelectedCountControlStatus(hDlg);
}
} break;

case LVN_GETDISPINFO:
{
LV_DISPINFO *Item = (LV_DISPINFO *)lParam;

//RamWatch dialog was copied and adapted from GENS11: http://code.google.com/p/gens-rerecording/
//Authors: Upthorn, Nitsuja, adelikat

#include "../port.h"
#include "../snes9x.h"
#include "../display.h"
#include "../memmap.h"
#include "../cheats.h"
#include "wsnes9x.h"
#include "rsrc/resource.h"
#include "ramwatch.h"
#include "ram_search.h"
#include <assert.h>
#include <windows.h>
#include <commctrl.h>
#include <string>

#ifdef UNICODE
#define _tToChar WideToUtf8
#define _tFromChar Utf8ToWide
#else
#define _tToChar
#define _tFromChar
#endif

extern SCheatData Cheat;

/*
#include <commctrl.h>
#pragma comment(lib, "comctl32.lib")
#include <shellapi.h>
#pragma comment(lib, "shell32.lib")
#include <commdlg.h>
#pragma comment(lib, "comdlg32.lib")
*/

static HMENU ramwatchmenu;
static HMENU rwrecentmenu;
/*static*/ HACCEL RamWatchAccels = NULL;
TCHAR rw_recent_files[MAX_RECENT_WATCHES][1024];
//TCHAR Watch_Dir[1024]=TEXT("");
bool RWfileChanged = false; //Keeps track of whether the current watch file has been changed, if so, ramwatch will prompt to save changes
bool AutoRWLoad = false; //Keeps track of whether Auto-load is checked
bool RWSaveWindowPos = false; //Keeps track of whether Save Window position is checked
TCHAR currentWatch[1024];
int ramw_x, ramw_y; //Used to store ramwatch dialog window positions
AddressWatcher rswatches[MAX_WATCH_COUNT];
int WatchCount=0;

TCHAR applicationPath[2048];
struct InitRamWatch
{
InitRamWatch()
{
GetModuleFileName(NULL, applicationPath, 2048);
}
} initRamWatch;

HWND RamWatchHWnd;
#define gamefilename _tFromChar(S9xBasename(S9xGetFilename("", DEFAULT_DIR)))
#define hWnd GUI.hWnd
#define hInst GUI.hInstance
static TCHAR Str_Tmp [1024];
static char Str_Tmp_Char [1024];

void init_list_box(HWND Box, const TCHAR* Strs[], int numColumns, int *columnWidths); //initializes the ram search and/or ram watch listbox

#define MESSAGEBOXPARENT (RamWatchHWnd ? RamWatchHWnd : hWnd)

bool QuickSaveWatches();
bool ResetWatches();

void RefreshWatchListSelectedCountControlStatus(HWND hDlg);

unsigned int GetCurrentValue(AddressWatcher& watch)
{
return ReadValueAtHardwareAddress(watch.Address, watch.Size == TEXT('d') ? 4 : watch.Size == TEXT('w') ? 2 : 1);
}

bool IsSameWatch(const AddressWatcher& l, const AddressWatcher& r)
{
return ((l.Address == r.Address) && (l.Size == r.Size) && (l.Type == r.Type)/* && (l.WrongEndian == r.WrongEndian)*/);
}

bool VerifyWatchNotAlreadyAdded(const AddressWatcher& watch)
{
for (int j = 0; j < WatchCount; j++)
{
if (IsSameWatch(rswatches[j], watch))
{
if(RamWatchHWnd)
SetForegroundWindow(RamWatchHWnd);
return false;
}
}
return true;
}


bool InsertWatch(const AddressWatcher& Watch, TCHAR *Comment)
{
if(!VerifyWatchNotAlreadyAdded(Watch))
return false;

if(WatchCount >= MAX_WATCH_COUNT)
return false;

int i = WatchCount++;
AddressWatcher& NewWatch = rswatches[i];
NewWatch = Watch;
//if (NewWatch.comment) free(NewWatch.comment);
NewWatch.comment = (TCHAR *) malloc((lstrlen(Comment)+2) * sizeof(TCHAR));
NewWatch.CurValue = GetCurrentValue(NewWatch);
lstrcpy(NewWatch.comment, Comment);
ListView_SetItemCount(GetDlgItem(RamWatchHWnd,IDC_WATCHLIST),WatchCount);
RWfileChanged=true;

return true;
}

LRESULT CALLBACK PromptWatchNameProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) //Gets the description of a watched address
{
RECT r;
RECT r2;
int dx1, dy1, dx2, dy2;

switch(uMsg)
{
case WM_INITDIALOG:
//Clear_Sound_Buffer();

GetWindowRect(hWnd, &r);
dx1 = (r.right - r.left) / 2;
dy1 = (r.bottom - r.top) / 2;

GetWindowRect(hDlg, &r2);
dx2 = (r2.right - r2.left) / 2;
dy2 = (r2.bottom - r2.top) / 2;

//SetWindowPos(hDlg, NULL, max(0, r.left + (dx1 - dx2)), max(0, r.top + (dy1 - dy2)), NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
SetWindowPos(hDlg, NULL, r.left, r.top, NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
lstrcpy(Str_Tmp,TEXT("Enter a name for this RAM address."));
SendDlgItemMessage(hDlg,IDC_PROMPT_TEXT,WM_SETTEXT,0,(LPARAM)Str_Tmp);
lstrcpy(Str_Tmp,TEXT(""));
SendDlgItemMessage(hDlg,IDC_PROMPT_TEXT2,WM_SETTEXT,0,(LPARAM)Str_Tmp);
return true;
break;

case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDOK:
{
GetDlgItemText(hDlg,IDC_PROMPT_EDIT,Str_Tmp,80);
InsertWatch(rswatches[WatchCount],Str_Tmp);
EndDialog(hDlg, true);
return true;
break;
}
case IDCANCEL:
EndDialog(hDlg, false);
return false;
break;
}
break;

case WM_CLOSE:
EndDialog(hDlg, false);
return false;
break;
}

return false;
}

bool InsertWatch(const AddressWatcher& Watch, HWND parent)
{
if(!VerifyWatchNotAlreadyAdded(Watch))
return false;

if(!parent)
parent = RamWatchHWnd;
if(!parent)
parent = hWnd;

int prevWatchCount = WatchCount;

rswatches[WatchCount] = Watch;
rswatches[WatchCount].CurValue = GetCurrentValue(rswatches[WatchCount]);
DialogBox(hInst, MAKEINTRESOURCE(IDD_PROMPT), parent, (DLGPROC) PromptWatchNameProc);

return WatchCount > prevWatchCount;
}

void Update_RAM_Watch()
{
BOOL watchChanged[MAX_WATCH_COUNT] = {0};

if(WatchCount)
{
// update cached values and detect changes to displayed listview items

for(int i = 0; i < WatchCount; i++)
{
unsigned int prevCurValue = rswatches[i].CurValue;
unsigned int newCurValue = GetCurrentValue(rswatches[i]);
if(prevCurValue != newCurValue)
{
rswatches[i].CurValue = newCurValue;
watchChanged[i] = TRUE;
}
}
}

// refresh any visible parts of the listview box that changed
HWND lv = GetDlgItem(RamWatchHWnd,IDC_WATCHLIST);
int top = ListView_GetTopIndex(lv);
int bottom = top + ListView_GetCountPerPage(lv) + 1; // +1 is so we will update a partially-displayed last item
if(top < 0) top = 0;
if(bottom > WatchCount) bottom = WatchCount;
int start = -1;
for(int i = top; i <= bottom; i++)
{
if(start == -1)
{
if(i != bottom && watchChanged[i])
{
start = i;
//somethingChanged = true;
}
}
else
{
if(i == bottom || !watchChanged[i])
{
ListView_RedrawItems(lv, start, i-1);
start = -1;
}
}
}
}

bool AskSave()
{
//This function asks to save changes if the watch file contents have changed
//returns false only if a save was attempted but failed or was cancelled
if (RWfileChanged)
{
int answer = MessageBox(MESSAGEBOXPARENT, TEXT("Save Changes?"), TEXT("Ram Watch"), MB_YESNOCANCEL);
if(answer == IDYES)
if(!QuickSaveWatches())
return false;
return (answer != IDCANCEL);
}
return true;
}

void WriteRecentRWFiles()
{
// TODO
/*
TCHAR str[2048];
for (int i = 0; i < MAX_RECENT_WATCHES; i++)
{
_stprintf(str, TEXT("recentWatch%d"), i+1);
regSetStringValue(str, &rw_recent_files[i][0]);
}
*/
}

void UpdateRW_RMenu(HMENU menu, unsigned int mitem, unsigned int baseid)
{
MENUITEMINFO moo;
int x;

moo.cbSize = sizeof(moo);
moo.fMask = MIIM_SUBMENU | MIIM_STATE;

GetMenuItemInfo(GetSubMenu(ramwatchmenu, 0), mitem, FALSE, &moo);
moo.hSubMenu = menu;
moo.fState = lstrlen(rw_recent_files[0]) ? MFS_ENABLED : MFS_GRAYED;

SetMenuItemInfo(GetSubMenu(ramwatchmenu, 0), mitem, FALSE, &moo);

// Remove all recent files submenus
for(x = 0; x < MAX_RECENT_WATCHES; x++)
{
RemoveMenu(menu, baseid + x, MF_BYCOMMAND);
}

// Recreate the menus
for(x = MAX_RECENT_WATCHES - 1; x >= 0; x--)
{
TCHAR tmp[128 + 5];

// Skip empty strings
if(!lstrlen(rw_recent_files[x]))
{
continue;
}

moo.cbSize = sizeof(moo);
moo.fMask = MIIM_DATA | MIIM_ID | MIIM_TYPE;

// Fill in the menu text.
if(lstrlen(rw_recent_files[x]) < 128)
{
_stprintf(tmp, TEXT("&%d. %s"), ( x + 1 ) % 10, rw_recent_files[x]);
}
else
{
_stprintf(tmp, TEXT("&%d. %s"), ( x + 1 ) % 10, rw_recent_files[x] + lstrlen( rw_recent_files[x] ) - 127);
}

// Insert the menu item
moo.cch = lstrlen(tmp);
moo.fType = 0;
moo.wID = baseid + x;
moo.dwTypeData = tmp;
InsertMenuItem(menu, 0, 1, &moo);
}

// I don't think one function shall do so many things in a row
// WriteRecentRWFiles(); // write recent menu to ini
}

void UpdateRWRecentArray(const TCHAR* addString, unsigned int arrayLen, HMENU menu, unsigned int menuItem, unsigned int baseId)
{
const size_t len = 1024; // Avoid magic numbers

// Try to find out if the filename is already in the recent files list.
for(unsigned int x = 0; x < arrayLen; x++)
{
if(lstrlen(rw_recent_files[x]))
{
if(_tcsnccmp(rw_recent_files[x], addString, 1024)) // Item is already in list.
{
// If the filename is in the file list don't add it again.
// Move it up in the list instead.

int y;
TCHAR tmp[len];

// Save pointer.
lstrcpyn(tmp, rw_recent_files[x], len);

for(y = x; y; y--)
{
// Move items down.
lstrcpyn(rw_recent_files[y],rw_recent_files[y - 1], len);
}

// Put item on top.
lstrcpyn(rw_recent_files[0],tmp, len);

// Update the recent files menu
UpdateRW_RMenu(menu, menuItem, baseId);

return;
}
}
}

// The filename wasn't found in the list. That means we need to add it.

// Move the other items down.
for(unsigned int x = arrayLen - 1; x; x--)
{
lstrcpyn(rw_recent_files[x],rw_recent_files[x - 1], len);
}

// Add the new item.
lstrcpyn(rw_recent_files[0], addString, len);

// Update the recent files menu
UpdateRW_RMenu(menu, menuItem, baseId);
}

void RWAddRecentFile(const TCHAR *filename)
{
UpdateRWRecentArray(filename, MAX_RECENT_WATCHES, rwrecentmenu, RAMMENU_FILE_RECENT, RW_MENU_FIRST_RECENT_FILE);
}

void OpenRWRecentFile(int memwRFileNumber)
{
if(!ResetWatches())
return;

int rnum = memwRFileNumber;
if ((unsigned int)rnum >= MAX_RECENT_WATCHES)
return; //just in case

TCHAR* x;

while(true)
{
x = rw_recent_files[rnum];
if (!*x)
return; //If no recent files exist just return. Useful for Load last file on startup (or if something goes screwy)

if (rnum) //Change order of recent files if not most recent
{
RWAddRecentFile(x);
rnum = 0;
}
else
{
break;
}
}

lstrcpy(currentWatch,x);
lstrcpy(Str_Tmp,currentWatch);

//loadwatches here
FILE *WatchFile = _tfopen(Str_Tmp,TEXT("rb"));
if (!WatchFile)
{
int answer = MessageBox(MESSAGEBOXPARENT,TEXT("Error opening file."),TEXT("ERROR"),MB_OKCANCEL);
if (answer == IDOK)
{
rw_recent_files[rnum][0] = TEXT('\0'); //Clear file from list
if (rnum) //Update the ramwatch list
RWAddRecentFile(rw_recent_files[0]);
else
RWAddRecentFile(rw_recent_files[1]);
}
return;
}
const char DELIM = '\t';
AddressWatcher Temp;
char mode;
fgets(Str_Tmp_Char,1024,WatchFile);
sscanf(Str_Tmp_Char,"%c%*s",&mode);
int WatchAdd;
fgets(Str_Tmp_Char,1024,WatchFile);
sscanf(Str_Tmp_Char,"%d%*s",&WatchAdd);
WatchAdd+=WatchCount;
for (int i = WatchCount; i < WatchAdd; i++)
{
char TempAddressStr[11];
char TempSize;
char TempType;
while (i < 0)
i++;
do {
fgets(Str_Tmp_Char,1024,WatchFile);
} while (Str_Tmp_Char[0] == '\n');
sscanf(Str_Tmp_Char,"%*05X%*c%6s%*c%c%*c%c%*c%d",TempAddressStr,&TempSize,&TempType,&(Temp.WrongEndian));
Temp.Address = DisplayToRWInternalAddress(_tFromChar(TempAddressStr));
Temp.Size = TempSize;
Temp.Type = TempType;
Temp.WrongEndian = 0;
char *Comment = strrchr(Str_Tmp_Char,DELIM) + 1;
*strrchr(Comment,'\n') = '\0';
InsertWatch(Temp,_tFromChar(Comment));
}

fclose(WatchFile);
if (RamWatchHWnd) {
ListView_SetItemCount(GetDlgItem(RamWatchHWnd,IDC_WATCHLIST),WatchCount);
RefreshWatchListSelectedCountControlStatus(RamWatchHWnd);
}
RWfileChanged=false;
return;
}

int Change_File_L(TCHAR *Dest, TCHAR *Dir, TCHAR *Titre, TCHAR *Filter, TCHAR *Ext, HWND hwnd)
{
OPENFILENAME ofn;

SetCurrentDirectory(applicationPath);

if (!lstrcmp(Dest, TEXT("")))
{
lstrcpy(Dest, TEXT("default."));
lstrcat(Dest, Ext);
}

memset(&ofn, 0, sizeof(OPENFILENAME));

ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hwnd;
ofn.hInstance = hInst;
ofn.lpstrFile = Dest;
ofn.nMaxFile = 2047;
ofn.lpstrFilter = Filter;
ofn.nFilterIndex = 1;
ofn.lpstrInitialDir = Dir;
ofn.lpstrTitle = Titre;
ofn.lpstrDefExt = Ext;
ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;

if (GetOpenFileName(&ofn)) return 1;

return 0;
}

int Change_File_S(TCHAR *Dest, TCHAR *Dir, TCHAR *Titre, TCHAR *Filter, TCHAR *Ext, HWND hwnd)
{
OPENFILENAME ofn;

SetCurrentDirectory(applicationPath);

if (!lstrcmp(Dest, TEXT("")))
{
lstrcpy(Dest, TEXT("default."));
lstrcat(Dest, Ext);
}

memset(&ofn, 0, sizeof(OPENFILENAME));

ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hwnd;
ofn.hInstance = hInst;
ofn.lpstrFile = Dest;
ofn.nMaxFile = 2047;
ofn.lpstrFilter = Filter;
ofn.nFilterIndex = 1;
ofn.lpstrInitialDir = Dir;
ofn.lpstrTitle = Titre;
ofn.lpstrDefExt = Ext;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;

if (GetSaveFileName(&ofn)) return 1;

return 0;
}

bool Save_Watches()
{
const TCHAR* slash = std::max(_tcsrchr(gamefilename, TEXT('|')), std::max(_tcsrchr(gamefilename, TEXT('\\')), _tcsrchr(gamefilename, TEXT('/'))));
lstrcpy(Str_Tmp,slash ? slash+1 : gamefilename);
TCHAR* dot = _tcsrchr(Str_Tmp, TEXT('.'));
if(dot) *dot = 0;
lstrcat(Str_Tmp,TEXT(".wch"));
if(Change_File_S(Str_Tmp, applicationPath, TEXT("Save Watches"), TEXT("Watchlist\0*.wch\0All Files\0*.*\0\0"), TEXT("wch"), RamWatchHWnd))
{
FILE *WatchFile = _tfopen(Str_Tmp,TEXT("r+b"));
if (!WatchFile) WatchFile = _tfopen(Str_Tmp,TEXT("w+b"));
fputc('\n',WatchFile);
lstrcpy(currentWatch,Str_Tmp);
RWAddRecentFile(currentWatch);
sprintf(Str_Tmp_Char,"%d\n",WatchCount);
fputs(Str_Tmp_Char,WatchFile);
const char DELIM = '\t';
for (int i = 0; i < WatchCount; i++)
{
sprintf(Str_Tmp_Char,"%05X%c%-6s%c%c%c%c%c%d%c%s\n",i,DELIM,_tToChar(RWInternalToDisplayAddress(rswatches[i].Address)),DELIM,rswatches[i].Size,DELIM,rswatches[i].Type,DELIM,rswatches[i].WrongEndian,DELIM,_tToChar(rswatches[i].comment));
fputs(Str_Tmp_Char,WatchFile);
}

fclose(WatchFile);
RWfileChanged=false;
//TODO: Add to recent list function call here
return true;
}
return false;
}

bool QuickSaveWatches()
{
if (RWfileChanged==false) return true; //If file has not changed, no need to save changes
if (currentWatch[0] == NULL) //If there is no currently loaded file, run to Save as and then return
{
return Save_Watches();
}

lstrcpy(Str_Tmp,currentWatch);
FILE *WatchFile = _tfopen(Str_Tmp,TEXT("r+b"));
if (!WatchFile) WatchFile = _tfopen(Str_Tmp,TEXT("w+b"));
fputc('\n',WatchFile);
sprintf(Str_Tmp_Char,"%d\n",WatchCount);
fputs(Str_Tmp_Char,WatchFile);
const char DELIM = '\t';
for (int i = 0; i < WatchCount; i++)
{
sprintf(Str_Tmp_Char,"%05X%c%-6s%c%c%c%c%c%d%c%s\n",i,DELIM,_tToChar(RWInternalToDisplayAddress(rswatches[i].Address)),DELIM,rswatches[i].Size,DELIM,rswatches[i].Type,DELIM,rswatches[i].WrongEndian,DELIM,_tToChar(rswatches[i].comment));
fputs(Str_Tmp_Char,WatchFile);
}
fclose(WatchFile);
RWfileChanged=false;
return true;
}

bool Load_Watches(bool clear, const TCHAR* filename)
{
const char DELIM = '\t';
FILE* WatchFile = _tfopen(filename,TEXT("rb"));
if (!WatchFile)
{
MessageBox(MESSAGEBOXPARENT,TEXT("Error opening file."),TEXT("ERROR"),MB_OK);
return false;
}
if(clear)
{
if(!ResetWatches())
{
fclose(WatchFile);
return false;
}
}
lstrcpy(currentWatch,filename);
RWAddRecentFile(currentWatch);
AddressWatcher Temp;
char mode;
fgets(Str_Tmp_Char,1024,WatchFile);
sscanf(Str_Tmp_Char,"%c%*s",&mode);
int WatchAdd;
fgets(Str_Tmp_Char,1024,WatchFile);
sscanf(Str_Tmp_Char,"%d%*s",&WatchAdd);
WatchAdd+=WatchCount;
for (int i = WatchCount; i < WatchAdd; i++)
{
char TempAddressStr[11];
char TempSize;
char TempType;
while (i < 0)
i++;
do {
fgets(Str_Tmp_Char,1024,WatchFile);
} while (Str_Tmp_Char[0] == '\n');
sscanf(Str_Tmp_Char,"%*05X%*c%6s%*c%c%*c%c%*c%d",TempAddressStr,&TempSize,&TempType,&(Temp.WrongEndian));
Temp.Address = DisplayToRWInternalAddress(_tFromChar(TempAddressStr));
Temp.Size = TempSize;
Temp.Type = TempType;
Temp.WrongEndian = 0;
char *Comment = strrchr(Str_Tmp_Char,DELIM) + 1;
*strrchr(Comment,'\n') = '\0';
InsertWatch(Temp,_tFromChar(Comment));
}

fclose(WatchFile);
if (RamWatchHWnd)
ListView_SetItemCount(GetDlgItem(RamWatchHWnd,IDC_WATCHLIST),WatchCount);
RWfileChanged=false;
return true;
}

bool Load_Watches(bool clear)
{
const TCHAR* slash = std::max(_tcsrchr(gamefilename, TEXT('|')), std::max(_tcsrchr(gamefilename, TEXT('\\')), _tcsrchr(gamefilename, TEXT('/'))));
lstrcpy(Str_Tmp,slash ? slash+1 : gamefilename);
TCHAR* dot = _tcsrchr(Str_Tmp, TEXT('.'));
if(dot) *dot = 0;
lstrcat(Str_Tmp,TEXT(".wch"));
if(Change_File_L(Str_Tmp, applicationPath, TEXT("Load Watches"), TEXT("Watchlist\0*.wch\0All Files\0*.*\0\0"), TEXT("wch"), RamWatchHWnd))
{
return Load_Watches(clear, Str_Tmp);
}
return false;
}

bool ResetWatches()
{
if(!AskSave())
return false;
for (;WatchCount>=0;WatchCount--)
{
free(rswatches[WatchCount].comment);
rswatches[WatchCount].comment = NULL;
}
WatchCount++;
if (RamWatchHWnd) {
ListView_SetItemCount(GetDlgItem(RamWatchHWnd,IDC_WATCHLIST),WatchCount);
RefreshWatchListSelectedCountControlStatus(RamWatchHWnd);
}
RWfileChanged = false;
currentWatch[0] = NULL;
return true;
}

void RemoveWatch(int watchIndex)
{
free(rswatches[watchIndex].comment);
rswatches[watchIndex].comment = NULL;
for (int i = watchIndex; i <= WatchCount; i++)
rswatches[i] = rswatches[i+1];
WatchCount--;
}

void RefreshWatchListSelectedItemControlStatus(HWND hDlg)
{
static int prevSelIndex=-1;
int selIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_RAMLIST));
if(selIndex != prevSelIndex)
{
if(selIndex == -1 || prevSelIndex == -1)
{
EnableWindow(GetDlgItem(hDlg, IDC_C_ADDCHEAT), (selIndex != -1) ? TRUE : FALSE);
}
prevSelIndex = selIndex;
}
}

LRESULT CALLBACK EditWatchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) //Gets info for a RAM Watch, and then inserts it into the Watch List
{
RECT r;
RECT r2;
int dx1, dy1, dx2, dy2;
static int index;
static char s,t = s = 0;

switch(uMsg)
{
case WM_INITDIALOG:
//Clear_Sound_Buffer();


GetWindowRect(hWnd, &r);
dx1 = (r.right - r.left) / 2;
dy1 = (r.bottom - r.top) / 2;

GetWindowRect(hDlg, &r2);
dx2 = (r2.right - r2.left) / 2;
dy2 = (r2.bottom - r2.top) / 2;

//SetWindowPos(hDlg, NULL, max(0, r.left + (dx1 - dx2)), max(0, r.top + (dy1 - dy2)), NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
SetWindowPos(hDlg, NULL, r.left, r.top, NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
index = (int)lParam;
lstrcpy(Str_Tmp, RWInternalToDisplayAddress(rswatches[index].Address));
SetDlgItemText(hDlg,IDC_EDIT_COMPAREADDRESS,Str_Tmp);
if (rswatches[index].comment != NULL)
SetDlgItemText(hDlg,IDC_PROMPT_EDIT,rswatches[index].comment);
s = rswatches[index].Size;
t = rswatches[index].Type;
switch (s)
{
case TEXT('b'):
SendDlgItemMessage(hDlg, IDC_1_BYTE, BM_SETCHECK, BST_CHECKED, 0);
break;
case TEXT('w'):
SendDlgItemMessage(hDlg, IDC_2_BYTES, BM_SETCHECK, BST_CHECKED, 0);
break;
case TEXT('d'):
SendDlgItemMessage(hDlg, IDC_4_BYTES, BM_SETCHECK, BST_CHECKED, 0);
break;
default:
s = 0;
break;
}
switch (t)
{
case TEXT('s'):
SendDlgItemMessage(hDlg, IDC_SIGNED, BM_SETCHECK, BST_CHECKED, 0);
break;
case TEXT('u'):
SendDlgItemMessage(hDlg, IDC_UNSIGNED, BM_SETCHECK, BST_CHECKED, 0);
break;
case TEXT('h'):
SendDlgItemMessage(hDlg, IDC_HEX, BM_SETCHECK, BST_CHECKED, 0);
break;
default:
t = 0;
break;
}

return true;
break;

case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDC_SIGNED:
t=TEXT('s');
return true;
case IDC_UNSIGNED:
t=TEXT('u');
return true;
case IDC_HEX:
t=TEXT('h');
return true;
case IDC_1_BYTE:
s = TEXT('b');
return true;
case IDC_2_BYTES:
s = TEXT('w');
return true;
case IDC_4_BYTES:
s = TEXT('d');
return true;
case IDOK:
{
if (s && t)
{
AddressWatcher Temp;
Temp.Size = s;
Temp.Type = t;
Temp.WrongEndian = false; //replace this when I get little endian working properly
GetDlgItemText(hDlg,IDC_EDIT_COMPAREADDRESS,Str_Tmp,1024);
TCHAR *addrstr = Str_Tmp;
if (lstrlen(Str_Tmp) > 8) addrstr = &(Str_Tmp[lstrlen(Str_Tmp) - 9]);
for(int i = 0; addrstr[i]; i++) {if(_totupper(addrstr[i]) == TEXT('O')) addrstr[i] = TEXT('0');}
Temp.Address = DisplayToRWInternalAddress(addrstr);

if((Temp.Address & ~0xFFFFFF) == ~0xFFFFFF)
Temp.Address &= 0xFFFFFF;

if(IsRAMWatchAddressValid(Temp.Address))
{
GetDlgItemText(hDlg,IDC_PROMPT_EDIT,Str_Tmp,80);
if (index < WatchCount) RemoveWatch(index);
InsertWatch(Temp,Str_Tmp);
if(RamWatchHWnd)
{
ListView_SetItemCount(GetDlgItem(RamWatchHWnd,IDC_WATCHLIST),WatchCount);
}
EndDialog(hDlg, true);
}
else
{
MessageBox(hDlg,TEXT("Invalid Address"),TEXT("ERROR"),MB_OK);
}
}
else
{
lstrcpy(Str_Tmp,TEXT("Error:"));
if (!s)
lstrcat(Str_Tmp,TEXT(" Size must be specified."));
if (!t)
lstrcat(Str_Tmp,TEXT(" Type must be specified."));
MessageBox(hDlg,Str_Tmp,TEXT("ERROR"),MB_OK);
}
RWfileChanged=true;
return true;
break;
}
case IDCANCEL:
EndDialog(hDlg, false);
return false;
break;
}
break;

case WM_CLOSE:
EndDialog(hDlg, false);
return false;
break;
}

return false;
}




void RamWatchEnableCommand(HWND hDlg, HMENU hMenu, UINT uIDEnableItem, bool enable)
{
EnableWindow(GetDlgItem(hDlg, uIDEnableItem), (enable?TRUE:FALSE));
if (hMenu != NULL) {
if (uIDEnableItem == ID_WATCHES_UPDOWN) {
EnableMenuItem(hMenu, IDC_C_WATCH_UP, MF_BYCOMMAND | (enable?MF_ENABLED:MF_GRAYED));
EnableMenuItem(hMenu, IDC_C_WATCH_DOWN, MF_BYCOMMAND | (enable?MF_ENABLED:MF_GRAYED));
}
else
EnableMenuItem(hMenu, uIDEnableItem, MF_BYCOMMAND | (enable?MF_ENABLED:MF_GRAYED));
}
}

void RefreshWatchListSelectedCountControlStatus(HWND hDlg)
{
static int prevSelCount=-1;
int selCount = ListView_GetSelectedCount(GetDlgItem(hDlg,IDC_WATCHLIST));
if(selCount != prevSelCount)
{
if(selCount < 2 || prevSelCount < 2)
{
RamWatchEnableCommand(hDlg, ramwatchmenu, IDC_C_WATCH_EDIT, selCount == 1);
RamWatchEnableCommand(hDlg, ramwatchmenu, IDC_C_WATCH_REMOVE, selCount >= 1);
RamWatchEnableCommand(hDlg, ramwatchmenu, IDC_C_WATCH_DUPLICATE, selCount == 1);
RamWatchEnableCommand(hDlg, ramwatchmenu, IDC_C_ADDCHEAT, selCount == 1);
RamWatchEnableCommand(hDlg, ramwatchmenu, ID_WATCHES_UPDOWN, selCount == 1);
}
prevSelCount = selCount;
}
}

LRESULT CALLBACK RamWatchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
RECT r;
RECT r2;
int dx1, dy1, dx2, dy2;
static int watchIndex=0;

switch(uMsg)
{
case WM_MOVE: {
RECT wrect;
GetWindowRect(hDlg,&wrect);
ramw_x = wrect.left;
ramw_y = wrect.top;
// TODO
//regSetDwordValue(RAMWX, ramw_x);
//regSetDwordValue(RAMWY, ramw_y);
} break;

case WM_INITDIALOG: {
GetWindowRect(hWnd, &r); //Ramwatch window
dx1 = (r.right - r.left) / 2;
dy1 = (r.bottom - r.top) / 2;

GetWindowRect(hDlg, &r2); // TASer window
dx2 = (r2.right - r2.left) / 2;
dy2 = (r2.bottom - r2.top) / 2;


// push it away from the main window if we can
const int width = (r.right-r.left);
const int height = (r.bottom - r.top);
const int width2 = (r2.right-r2.left);
if(r.left+width2 + width < GetSystemMetrics(SM_CXSCREEN))
{
r.right += width;
r.left += width;
}
else if((int)r.left - (int)width2 > 0)
{
r.right -= width2;
r.left -= width2;
}

//-----------------------------------------------------------------------------------
//If user has Save Window Pos selected, override default positioning
if (RWSaveWindowPos)
{
//If ramwindow is for some reason completely off screen, use default instead
if (ramw_x > (-width*2) || ramw_x < (width*2 + GetSystemMetrics(SM_CYSCREEN)) )
r.left = ramw_x; //This also ignores cases of windows -32000 error codes
//If ramwindow is for some reason completely off screen, use default instead
if (ramw_y > (0-height*2) ||ramw_y < (height*2 + GetSystemMetrics(SM_CYSCREEN)) )
r.top = ramw_y; //This also ignores cases of windows -32000 error codes
}
//-------------------------------------------------------------------------------------
SetWindowPos(hDlg, NULL, r.left, r.top, NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);

ramwatchmenu=GetMenu(hDlg);
rwrecentmenu=CreateMenu();
UpdateRW_RMenu(rwrecentmenu, RAMMENU_FILE_RECENT, RW_MENU_FIRST_RECENT_FILE);

const TCHAR* names[3] = {TEXT("Address"),TEXT("Value"),TEXT("Notes")};
int widths[3] = {62,64,64+51+53};
init_list_box(GetDlgItem(hDlg,IDC_WATCHLIST),names,3,widths);
if (!ResultCount)
reset_address_info();
else
signal_new_frame();
ListView_SetItemCount(GetDlgItem(hDlg,IDC_WATCHLIST),WatchCount);
if (!noMisalign) SendDlgItemMessage(hDlg, IDC_MISALIGN, BM_SETCHECK, BST_CHECKED, 0);
//if (littleEndian) SendDlgItemMessage(hDlg, IDC_ENDIAN, BM_SETCHECK, BST_CHECKED, 0);

RamWatchAccels = LoadAccelerators(hInst, MAKEINTRESOURCE(IDR_RWACCELERATOR));

// due to some bug in windows, the arrow button width from the resource gets ignored, so we have to set it here
SetWindowPos(GetDlgItem(hDlg,ID_WATCHES_UPDOWN), 0,0,0, 30,60, SWP_NOMOVE);

Update_RAM_Watch();

DragAcceptFiles(hDlg, TRUE);

RefreshWatchListSelectedCountControlStatus(hDlg);
return false;
} break;

case WM_INITMENU:
CheckMenuItem(ramwatchmenu, RAMMENU_FILE_AUTOLOAD, AutoRWLoad ? MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(ramwatchmenu, RAMMENU_FILE_SAVEWINDOW, RWSaveWindowPos ? MF_CHECKED : MF_UNCHECKED);
break;

case WM_MENUSELECT:
case WM_ENTERSIZEMOVE:
//Clear_Sound_Buffer();
break;

case WM_NOTIFY:
{
switch(wParam)
{
case ID_WATCHES_UPDOWN:
{
switch(((LPNMUPDOWN)lParam)->hdr.code)
{
case UDN_DELTAPOS: {
int delta = ((LPNMUPDOWN)lParam)->iDelta;
SendMessage(hDlg, WM_COMMAND, delta<0 ? IDC_C_WATCH_UP : IDC_C_WATCH_DOWN,0);
} break;
}
} break;

default:
{
LPNMHDR lP = (LPNMHDR) lParam;
switch (lP->code)
{
case LVN_ITEMCHANGED: // selection changed event
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)lP;
if(pNMListView->uNewState & LVIS_FOCUSED ||
(pNMListView->uNewState ^ pNMListView->uOldState) & LVIS_SELECTED)
{
// disable buttons that we don't have the right number of selected items for
RefreshWatchListSelectedCountControlStatus(hDlg);
}
} break;

case LVN_GETDISPINFO:
{
LV_DISPINFO *Item = (LV_DISPINFO *)lParam;

// ignore unnecessary requests
Item->item.mask &= LVIF_TEXT;


// set content only when it's requested
if (Item->item.mask & LVIF_TEXT)
{
const unsigned int iNum = Item->item.iItem;
static TCHAR num[11];
switch (Item->item.iSubItem)
{
case 0:
lstrcpy(num, RWInternalToDisplayAddress(rswatches[iNum].Address));
Item->item.pszText = num;
break;
case 1: {
int i = rswatches[iNum].CurValue;
int t = rswatches[iNum].Type;
int size = rswatches[iNum].Size;
const TCHAR* formatString = ((t==TEXT('s')) ? TEXT("%d") : (t==TEXT('u')) ? TEXT("%u") : (size==TEXT('d') ? TEXT("%08X") : size==TEXT('w') ? TEXT("%04X") : TEXT("%02X")));
switch (size)
{
case TEXT('b'):
default: _stprintf(num, formatString, t==TEXT('s') ? (char)(i&0xff) : (unsigned char)(i&0xff)); break;
case TEXT('w'): _stprintf(num, formatString, t==TEXT('s') ? (short)(i&0xffff) : (unsigned short)(i&0xffff)); break;
case TEXT('d'): _stprintf(num, formatString, t==TEXT('s') ? (long)(i&0xffffffff) : (unsigned long)(i&0xffffffff)); break;
}

Item->item.pszText = num;
} break;
case 2:
Item->item.pszText = rswatches[iNum].comment ? rswatches[iNum].comment : TEXT("");
break;
}
}
} break;
case LVN_ODFINDITEM:
{
// disable search by keyboard typing,
// because it interferes with some of the accelerators
// and it isn't very useful here anyway
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST)));
return 1;
}
}
}
}
} break;

case WM_COMMAND:
switch(LOWORD(wParam))
{
case RAMMENU_FILE_SAVE:
QuickSaveWatches();
break;

case RAMMENU_FILE_SAVEAS:
//case IDC_C_SAVE:
return Save_Watches();
case RAMMENU_FILE_OPEN:
return Load_Watches(true);
case RAMMENU_FILE_APPEND:
//case IDC_C_LOAD:
return Load_Watches(false);
case RAMMENU_FILE_NEW:
//case IDC_C_RESET:
ResetWatches();
return true;
case IDC_C_WATCH_REMOVE:
{
HWND watchListControl = GetDlgItem(hDlg, IDC_WATCHLIST);
watchIndex = ListView_GetNextItem(watchListControl, -1, LVNI_ALL | LVNI_SELECTED);
while (watchIndex >= 0)
{
RemoveWatch(watchIndex);
ListView_DeleteItem(watchListControl, watchIndex);
watchIndex = ListView_GetNextItem(watchListControl, -1, LVNI_ALL | LVNI_SELECTED);
}
RWfileChanged=true;
SetFocus(GetDlgItem(hDlg,IDC_WATCHLIST));
return true;
}
case IDC_C_WATCH_EDIT:
watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST));
if(watchIndex != -1)
{
DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_EDITWATCH), hDlg, (DLGPROC) EditWatchProc,(LPARAM) watchIndex);
SetFocus(GetDlgItem(hDlg,IDC_WATCHLIST));
}
return true;
case IDC_C_WATCH:
rswatches[WatchCount].Address = rswatches[WatchCount].WrongEndian = 0;
rswatches[WatchCount].Size = TEXT('b');
rswatches[WatchCount].Type = TEXT('s');
DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_EDITWATCH), hDlg, (DLGPROC) EditWatchProc,(LPARAM) WatchCount);
SetFocus(GetDlgItem(hDlg,IDC_WATCHLIST));
return true;
case IDC_C_WATCH_DUPLICATE:
watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST));
if(watchIndex != -1)
{
rswatches[WatchCount].Address = rswatches[watchIndex].Address;
rswatches[WatchCount].WrongEndian = rswatches[watchIndex].WrongEndian;
rswatches[WatchCount].Size = rswatches[watchIndex].Size;
rswatches[WatchCount].Type = rswatches[watchIndex].Type;
DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_EDITWATCH), hDlg, (DLGPROC) EditWatchProc,(LPARAM) WatchCount);
SetFocus(GetDlgItem(hDlg,IDC_WATCHLIST));
}
return true;
case IDC_C_WATCH_UP:
{
watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST));
if (watchIndex == 0 || watchIndex == -1)
return true;
void *tmp = malloc(sizeof(AddressWatcher));
memcpy(tmp,&(rswatches[watchIndex]),sizeof(AddressWatcher));
memcpy(&(rswatches[watchIndex]),&(rswatches[watchIndex - 1]),sizeof(AddressWatcher));
memcpy(&(rswatches[watchIndex - 1]),tmp,sizeof(AddressWatcher));
free(tmp);
ListView_SetItemState(GetDlgItem(hDlg,IDC_WATCHLIST),watchIndex,0,LVIS_FOCUSED|LVIS_SELECTED);
ListView_SetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST),watchIndex-1);
ListView_SetItemState(GetDlgItem(hDlg,IDC_WATCHLIST),watchIndex-1,LVIS_FOCUSED|LVIS_SELECTED,LVIS_FOCUSED|LVIS_SELECTED);
ListView_SetItemCount(GetDlgItem(hDlg,IDC_WATCHLIST),WatchCount);
RWfileChanged=true;
return true;
}
case IDC_C_WATCH_DOWN:
{
watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST));
if (watchIndex >= WatchCount - 1 || watchIndex == -1)
return true;
void *tmp = malloc(sizeof(AddressWatcher));
memcpy(tmp,&(rswatches[watchIndex]),sizeof(AddressWatcher));
memcpy(&(rswatches[watchIndex]),&(rswatches[watchIndex + 1]),sizeof(AddressWatcher));
memcpy(&(rswatches[watchIndex + 1]),tmp,sizeof(AddressWatcher));
free(tmp);
ListView_SetItemState(GetDlgItem(hDlg,IDC_WATCHLIST),watchIndex,0,LVIS_FOCUSED|LVIS_SELECTED);
ListView_SetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST),watchIndex+1);
ListView_SetItemState(GetDlgItem(hDlg,IDC_WATCHLIST),watchIndex+1,LVIS_FOCUSED|LVIS_SELECTED,LVIS_FOCUSED|LVIS_SELECTED);
ListView_SetItemCount(GetDlgItem(hDlg,IDC_WATCHLIST),WatchCount);
RWfileChanged=true;
return true;
}
case ID_WATCHES_UPDOWN:
{
int delta = ((LPNMUPDOWN)lParam)->iDelta;
SendMessage(hDlg, WM_COMMAND, delta<0 ? IDC_C_WATCH_UP : IDC_C_WATCH_DOWN,0);
break;
}
case RAMMENU_FILE_AUTOLOAD:
{
AutoRWLoad ^= 1;
CheckMenuItem(ramwatchmenu, RAMMENU_FILE_AUTOLOAD, AutoRWLoad ? MF_CHECKED : MF_UNCHECKED);
//regSetDwordValue(AUTORWLOAD, AutoRWLoad); TODO
break;
}
case RAMMENU_FILE_SAVEWINDOW:
{
RWSaveWindowPos ^=1;
CheckMenuItem(ramwatchmenu, RAMMENU_FILE_SAVEWINDOW, RWSaveWindowPos ? MF_CHECKED : MF_UNCHECKED);
//regSetDwordValue(RWSAVEPOS, RWSaveWindowPos); TODO
break;
}
case IDC_C_ADDCHEAT:
{
watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST));
if(watchIndex >= 0)
{
unsigned int address = rswatches[watchIndex].Address;

int sizeType = -1;
if(rswatches[watchIndex].Size == TEXT('b'))
sizeType = 0;
else if(rswatches[watchIndex].Size == TEXT('w'))
sizeType = 1;
else if(rswatches[watchIndex].Size == TEXT('d'))
sizeType = 2;

int numberType = -1;
if(rswatches[watchIndex].Type == TEXT('s'))
numberType = 0;
else if(rswatches[watchIndex].Type == TEXT('u'))
numberType = 1;
else if(rswatches[watchIndex].Type == TEXT('h'))
numberType = 2;

if (sizeType == -1 || numberType == -1)
break;

struct ICheat cht;
const int fmtTable[] = { 2, 1, 3 };
ZeroMemory(&cht, sizeof(struct SCheat));
cht.address = RWInternalToHardwareAddress(address);
cht.size = 1 << sizeType;
cht.format = fmtTable[numberType];
cht.new_val = rswatches[watchIndex].CurValue;
cht.saved_val = rswatches[watchIndex].CurValue;
extern INT_PTR CALLBACK DlgCheatSearchAdd(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
if(DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_CHEAT_FROM_SEARCH), hDlg, DlgCheatSearchAdd, (LPARAM)&cht))
{
int p;
for(p=0; p<cht.size; p++)
{
S9xAddCheat(TRUE, cht.saved, cht.address +p, ((cht.new_val>>(8*p))&0xFF));
//add cheat
strcpy(Cheat.c[Cheat.num_cheats-1].name, cht.name);
}
}
}
}
break;
case IDOK:
case IDCANCEL:
RamWatchHWnd = NULL;
DragAcceptFiles(hDlg, FALSE);
EndDialog(hDlg, true);
return true;
default:
if (LOWORD(wParam) >= RW_MENU_FIRST_RECENT_FILE && LOWORD(wParam) < RW_MENU_FIRST_RECENT_FILE+MAX_RECENT_WATCHES && LOWORD(wParam) <= RW_MENU_LAST_RECENT_FILE)
OpenRWRecentFile(LOWORD(wParam) - RW_MENU_FIRST_RECENT_FILE);
}
break;

case WM_KEYDOWN: // handle accelerator keys
{
SetFocus(GetDlgItem(hDlg,IDC_WATCHLIST));
MSG msg;
msg.hwnd = hDlg;
msg.message = uMsg;
msg.wParam = wParam;
msg.lParam = lParam;
if(RamWatchAccels && TranslateAccelerator(hDlg, RamWatchAccels, &msg))
return true;
} break;

case WM_GETMINMAXINFO:
{
LPMINMAXINFO lpmm = (LPMINMAXINFO)lParam;
lpmm->ptMinTrackSize.x = 240;
lpmm->ptMinTrackSize.y = 320;
} break;

case WM_SIZE:
{
UINT nType = (UINT) wParam;
int cx = (int) LOWORD(lParam);
int cy = (int) HIWORD(lParam);

// more flexible resize routine is welcome
SetWindowPos(GetDlgItem(hDlg, IDC_WATCHES_GROUP), (HWND)NULL, cx-71, 18, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
SetWindowPos(GetDlgItem(hDlg, IDC_WATCHLIST), (HWND)NULL, 0, 0, cx-89, cy-31, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
SetWindowPos(GetDlgItem(hDlg, IDC_C_WATCH_EDIT), (HWND)NULL, cx-65, 107, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
SetWindowPos(GetDlgItem(hDlg, IDC_C_WATCH_REMOVE), (HWND)NULL, cx-65, 135, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
SetWindowPos(GetDlgItem(hDlg, IDC_C_WATCH), (HWND)NULL, cx-65, 163, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
SetWindowPos(GetDlgItem(hDlg, IDC_C_WATCH_DUPLICATE), (HWND)NULL, cx-65, 190, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
SetWindowPos(GetDlgItem(hDlg, IDC_C_ADDCHEAT), (HWND)NULL, cx-71, 228, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
SetWindowPos(GetDlgItem(hDlg, ID_WATCHES_UPDOWN), (HWND)NULL, cx-56, 37, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
RedrawWindow(hDlg, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN); // workaround for groupbox repainting
} break;

// case WM_CLOSE:
// RamWatchHWnd = NULL;
// DragAcceptFiles(hDlg, FALSE);
// DestroyWindow(hDlg);
// return false;

case WM_DESTROY:
// this is the correct place
RamWatchHWnd = NULL;
DragAcceptFiles(hDlg, FALSE);
WriteRecentRWFiles(); // write recent menu to ini
break;

case WM_DROPFILES:
{
HDROP hDrop = (HDROP)wParam;
DragQueryFile(hDrop, 0, Str_Tmp, 1024);
DragFinish(hDrop);
return Load_Watches(true, Str_Tmp);
} break;
}

return false;
}
const unsigned int iNum = Item->item.iItem;
static TCHAR num[11];
switch (Item->item.iSubItem)
{
case 0:
lstrcpy(num, RWInternalToDisplayAddress(rswatches[iNum].Address));
Item->item.pszText = num;
break;
case 1: {
int i = rswatches[iNum].CurValue;
int t = rswatches[iNum].Type;
int size = rswatches[iNum].Size;
const TCHAR* formatString = ((t==TEXT('s')) ? TEXT("%d") : (t==TEXT('u')) ? TEXT("%u") : (size==TEXT('d') ? TEXT("%08X") : size==TEXT('w') ? TEXT("%04X") : TEXT("%02X")));
switch (size)
{
case TEXT('b'):
default: _stprintf(num, formatString, t==TEXT('s') ? (char)(i&0xff) : (unsigned char)(i&0xff)); break;
case TEXT('w'): _stprintf(num, formatString, t==TEXT('s') ? (short)(i&0xffff) : (unsigned short)(i&0xffff)); break;
case TEXT('d'): _stprintf(num, formatString, t==TEXT('s') ? (long)(i&0xffffffff) : (unsigned long)(i&0xffffffff)); break;
}

Item->item.pszText = num;
} break;
case 2:
Item->item.pszText = rswatches[iNum].comment ? rswatches[iNum].comment : TEXT("");
break;
}
}
} break;
case LVN_ODFINDITEM:
{
// disable search by keyboard typing,
// because it interferes with some of the accelerators
// and it isn't very useful here anyway
SetWindowLongPtr(hDlg, DWLP_MSGRESULT, ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST)));
return 1;
}
}
}
}
} break;

case WM_COMMAND:
switch(LOWORD(wParam))
{
case RAMMENU_FILE_SAVE:
QuickSaveWatches();
break;

case RAMMENU_FILE_SAVEAS:
//case IDC_C_SAVE:
return Save_Watches();
case RAMMENU_FILE_OPEN:
return Load_Watches(true);
case RAMMENU_FILE_APPEND:
//case IDC_C_LOAD:
return Load_Watches(false);
case RAMMENU_FILE_NEW:
//case IDC_C_RESET:
ResetWatches();
return true;
case IDC_C_WATCH_REMOVE:
{
HWND watchListControl = GetDlgItem(hDlg, IDC_WATCHLIST);
watchIndex = ListView_GetNextItem(watchListControl, -1, LVNI_ALL | LVNI_SELECTED);
while (watchIndex >= 0)
{
RemoveWatch(watchIndex);
ListView_DeleteItem(watchListControl, watchIndex);
watchIndex = ListView_GetNextItem(watchListControl, -1, LVNI_ALL | LVNI_SELECTED);
}
RWfileChanged=true;
SetFocus(GetDlgItem(hDlg,IDC_WATCHLIST));
return true;
}
case IDC_C_WATCH_EDIT:
watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST));
if(watchIndex != -1)
{
DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_EDITWATCH), hDlg, (DLGPROC) EditWatchProc,(LPARAM) watchIndex);
SetFocus(GetDlgItem(hDlg,IDC_WATCHLIST));
}
return true;
case IDC_C_WATCH:
rswatches[WatchCount].Address = rswatches[WatchCount].WrongEndian = 0;
rswatches[WatchCount].Size = TEXT('b');
rswatches[WatchCount].Type = TEXT('s');
DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_EDITWATCH), hDlg, (DLGPROC) EditWatchProc,(LPARAM) WatchCount);
SetFocus(GetDlgItem(hDlg,IDC_WATCHLIST));
return true;
case IDC_C_WATCH_DUPLICATE:
watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST));
if(watchIndex != -1)
{
rswatches[WatchCount].Address = rswatches[watchIndex].Address;
rswatches[WatchCount].WrongEndian = rswatches[watchIndex].WrongEndian;
rswatches[WatchCount].Size = rswatches[watchIndex].Size;
rswatches[WatchCount].Type = rswatches[watchIndex].Type;
DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_EDITWATCH), hDlg, (DLGPROC) EditWatchProc,(LPARAM) WatchCount);
SetFocus(GetDlgItem(hDlg,IDC_WATCHLIST));
}
return true;
case IDC_C_WATCH_UP:
{
watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST));
if (watchIndex == 0 || watchIndex == -1)
return true;
void *tmp = malloc(sizeof(AddressWatcher));
memcpy(tmp,&(rswatches[watchIndex]),sizeof(AddressWatcher));
memcpy(&(rswatches[watchIndex]),&(rswatches[watchIndex - 1]),sizeof(AddressWatcher));
memcpy(&(rswatches[watchIndex - 1]),tmp,sizeof(AddressWatcher));
free(tmp);
ListView_SetItemState(GetDlgItem(hDlg,IDC_WATCHLIST),watchIndex,0,LVIS_FOCUSED|LVIS_SELECTED);
ListView_SetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST),watchIndex-1);
ListView_SetItemState(GetDlgItem(hDlg,IDC_WATCHLIST),watchIndex-1,LVIS_FOCUSED|LVIS_SELECTED,LVIS_FOCUSED|LVIS_SELECTED);
ListView_SetItemCount(GetDlgItem(hDlg,IDC_WATCHLIST),WatchCount);
RWfileChanged=true;
return true;
}
case IDC_C_WATCH_DOWN:
{
watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST));
if (watchIndex >= WatchCount - 1 || watchIndex == -1)
return true;
void *tmp = malloc(sizeof(AddressWatcher));
memcpy(tmp,&(rswatches[watchIndex]),sizeof(AddressWatcher));
memcpy(&(rswatches[watchIndex]),&(rswatches[watchIndex + 1]),sizeof(AddressWatcher));
memcpy(&(rswatches[watchIndex + 1]),tmp,sizeof(AddressWatcher));
free(tmp);
ListView_SetItemState(GetDlgItem(hDlg,IDC_WATCHLIST),watchIndex,0,LVIS_FOCUSED|LVIS_SELECTED);
ListView_SetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST),watchIndex+1);
ListView_SetItemState(GetDlgItem(hDlg,IDC_WATCHLIST),watchIndex+1,LVIS_FOCUSED|LVIS_SELECTED,LVIS_FOCUSED|LVIS_SELECTED);
ListView_SetItemCount(GetDlgItem(hDlg,IDC_WATCHLIST),WatchCount);
RWfileChanged=true;
return true;
}
case ID_WATCHES_UPDOWN:
{
int delta = ((LPNMUPDOWN)lParam)->iDelta;
SendMessage(hDlg, WM_COMMAND, delta<0 ? IDC_C_WATCH_UP : IDC_C_WATCH_DOWN,0);
break;
}
case RAMMENU_FILE_AUTOLOAD:
{
AutoRWLoad ^= 1;
CheckMenuItem(ramwatchmenu, RAMMENU_FILE_AUTOLOAD, AutoRWLoad ? MF_CHECKED : MF_UNCHECKED);
//regSetDwordValue(AUTORWLOAD, AutoRWLoad); TODO
break;
}
case RAMMENU_FILE_SAVEWINDOW:
{
RWSaveWindowPos ^=1;
CheckMenuItem(ramwatchmenu, RAMMENU_FILE_SAVEWINDOW, RWSaveWindowPos ? MF_CHECKED : MF_UNCHECKED);
//regSetDwordValue(RWSAVEPOS, RWSaveWindowPos); TODO
break;
}
case IDC_C_ADDCHEAT:
{
watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST));
if(watchIndex >= 0)
{
unsigned int address = rswatches[watchIndex].Address;

int sizeType = -1;
if(rswatches[watchIndex].Size == TEXT('b'))
sizeType = 0;
else if(rswatches[watchIndex].Size == TEXT('w'))
sizeType = 1;
else if(rswatches[watchIndex].Size == TEXT('d'))
sizeType = 2;

int numberType = -1;
if(rswatches[watchIndex].Type == TEXT('s'))
numberType = 0;
else if(rswatches[watchIndex].Type == TEXT('u'))
numberType = 1;
else if(rswatches[watchIndex].Type == TEXT('h'))
numberType = 2;

if (sizeType == -1 || numberType == -1)
break;

struct ICheat cht;
const int fmtTable[] = { 2, 1, 3 };
ZeroMemory(&cht, sizeof(struct SCheat));
cht.address = RWInternalToHardwareAddress(address);
cht.size = 1 << sizeType;
cht.format = fmtTable[numberType];
cht.new_val = rswatches[watchIndex].CurValue;
cht.saved_val = rswatches[watchIndex].CurValue;
extern INT_PTR CALLBACK DlgCheatSearchAdd(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
if(DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_CHEAT_FROM_SEARCH), hDlg, DlgCheatSearchAdd, (LPARAM)&cht))
{
int p;
for(p=0; p<cht.size; p++)
{
S9xAddCheat(TRUE, cht.saved, cht.address +p, ((cht.new_val>>(8*p))&0xFF));
//add cheat
strcpy(Cheat.c[Cheat.num_cheats-1].name, cht.name);
}
}
}
}
break;
case IDOK:
case IDCANCEL:
RamWatchHWnd = NULL;
DragAcceptFiles(hDlg, FALSE);
EndDialog(hDlg, true);
return true;
default:
if (LOWORD(wParam) >= RW_MENU_FIRST_RECENT_FILE && LOWORD(wParam) < RW_MENU_FIRST_RECENT_FILE+MAX_RECENT_WATCHES && LOWORD(wParam) <= RW_MENU_LAST_RECENT_FILE)
OpenRWRecentFile(LOWORD(wParam) - RW_MENU_FIRST_RECENT_FILE);
}
break;

case WM_KEYDOWN: // handle accelerator keys
{
SetFocus(GetDlgItem(hDlg,IDC_WATCHLIST));
MSG msg;
msg.hwnd = hDlg;
msg.message = uMsg;
msg.wParam = wParam;
msg.lParam = lParam;
if(RamWatchAccels && TranslateAccelerator(hDlg, RamWatchAccels, &msg))
return true;
} break;

case WM_GETMINMAXINFO:
{
LPMINMAXINFO lpmm = (LPMINMAXINFO)lParam;
lpmm->ptMinTrackSize.x = 240;
lpmm->ptMinTrackSize.y = 320;
} break;

case WM_SIZE:
{
UINT nType = (UINT) wParam;
int cx = (int) LOWORD(lParam);
int cy = (int) HIWORD(lParam);

// more flexible resize routine is welcome
SetWindowPos(GetDlgItem(hDlg, IDC_WATCHES_GROUP), (HWND)NULL, cx-71, 18, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
SetWindowPos(GetDlgItem(hDlg, IDC_WATCHLIST), (HWND)NULL, 0, 0, cx-89, cy-31, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
SetWindowPos(GetDlgItem(hDlg, IDC_C_WATCH_EDIT), (HWND)NULL, cx-65, 107, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
SetWindowPos(GetDlgItem(hDlg, IDC_C_WATCH_REMOVE), (HWND)NULL, cx-65, 135, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
SetWindowPos(GetDlgItem(hDlg, IDC_C_WATCH), (HWND)NULL, cx-65, 163, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
SetWindowPos(GetDlgItem(hDlg, IDC_C_WATCH_DUPLICATE), (HWND)NULL, cx-65, 190, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
SetWindowPos(GetDlgItem(hDlg, IDC_C_ADDCHEAT), (HWND)NULL, cx-71, 228, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
SetWindowPos(GetDlgItem(hDlg, ID_WATCHES_UPDOWN), (HWND)NULL, cx-56, 37, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
RedrawWindow(hDlg, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN); // workaround for groupbox repainting
} break;

// case WM_CLOSE:
// RamWatchHWnd = NULL;
// DragAcceptFiles(hDlg, FALSE);
// DestroyWindow(hDlg);
// return false;

case WM_DESTROY:
// this is the correct place
RamWatchHWnd = NULL;
DragAcceptFiles(hDlg, FALSE);
WriteRecentRWFiles(); // write recent menu to ini
break;

case WM_DROPFILES:
{
HDROP hDrop = (HDROP)wParam;
DragQueryFile(hDrop, 0, Str_Tmp, 1024);
DragFinish(hDrop);
return Load_Watches(true, Str_Tmp);
} break;
}

return false;
}