RAM Watch: Adding multiple watches in the dialog, when adding multiple watches from other window, the watches is also in the address edit control.

I realized that the watch operation need to be decoupled from the RAM Watch list, but things seem get too complicated, hoping this is not get even worse.
This commit is contained in:
owomomo 2020-02-05 22:56:30 +08:00
parent a9c4bc9592
commit 7a278b10a8
8 changed files with 441 additions and 281 deletions

View File

@ -1553,7 +1553,7 @@ void CreateCheatMap()
void ReleaseCheatMap()
{
--CheatMapUsers;
printf("CheatMapUsers: %d\n", CheatMapUsers);
// printf("CheatMapUsers: %d\n", CheatMapUsers);
if (!CheatMapUsers)
FCEUI_ReleaseCheatMap();
}

View File

@ -1982,32 +1982,30 @@ INT_PTR CALLBACK RamSearchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lPara
int selCount = SendMessage(ramListControl, LVM_GETSELECTEDCOUNT, 0, 0);
if (selCount > 0)
{
AddressWatcher tempWatch;
tempWatch.Size = rs_type_size;
tempWatch.Type = rs_t;
tempWatch.WrongEndian = 0; //Replace when I get little endian working
tempWatch.comment = NULL;
WatcherMsg msg;
msg.Size = rs_type_size;
msg.Type = rs_t;
msg.WrongEndian = 0; //Replace when I get little endian working
msg.comment = NULL;
bool inserted = false;
AddressWatcher* watches = (AddressWatcher*)malloc(selCount * sizeof(AddressWatcher));
msg.Addresses = (unsigned int*)malloc(selCount * sizeof(unsigned int));
int i = 0;
int watchItemIndex = -1;
while ((watchItemIndex = SendMessage(ramListControl, LVM_GETNEXTITEM, watchItemIndex, LVNI_SELECTED)) >= 0)
{
tempWatch.Address = CALL_WITH_T_SIZE_TYPES_1(GetHardwareAddressFromItemIndex, rs_type_size, rs_t == 's', noMisalign, watchItemIndex);
watches[i] = tempWatch;
msg.Addresses[i] = CALL_WITH_T_SIZE_TYPES_1(GetHardwareAddressFromItemIndex, rs_type_size, rs_t == 's', noMisalign, watchItemIndex);
++i;
}
msg.count = i;
// bring up the ram watch window if it's not already showing so the user knows where the watch went
if ((selCount == 1 ?
InsertWatch(watches[0], hDlg) : InsertWatches(watches, hDlg, selCount))
if (InsertWatches(&msg, hDlg, selCount)
&& !RamWatchHWnd)
SendMessage(hWnd, WM_COMMAND, ID_RAM_WATCH, 0);
SetForegroundWindow(RamSearchHWnd);
free(watches);
if (msg.Addresses) free(msg.Addresses);
if (msg.comment) free(msg.comment);
}
break;
}

View File

@ -143,45 +143,37 @@ bool InsertWatch(const AddressWatcher& Watch, HWND parent)
int prevWatchCount = WatchCount;
int tmpWatchIndex;
if (parent == RamWatchHWnd)
tmpWatchIndex = WatchCount;
else if (parent == RamSearchHWnd)
tmpWatchIndex = -2;
else if (parent == hCheat)
tmpWatchIndex = -3;
else
tmpWatchIndex = -4;
rswatches[tmpWatchIndex] = Watch;
rswatches[tmpWatchIndex].CurValue = GetCurrentValue(rswatches[tmpWatchIndex]);
DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_EDITWATCH), parent, EditWatchProc, tmpWatchIndex);
rswatches.erase(tmpWatchIndex);
WatcherMsg msg = WatcherMsg::FromAddressWatches(&Watch);
if (DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_EDITWATCH), parent, EditWatchProc, (LPARAM)&msg))
{
AddressWatcher* watcher = msg.ToAddressWatches();
InsertWatch(*watcher);
if (watcher->comment)
free(watcher->comment);
free(watcher);
}
if (msg.Addresses) free(msg.Addresses);
if (msg.comment) free(msg.comment);
return WatchCount > prevWatchCount;
}
bool InsertWatches(const AddressWatcher* watches, HWND parent, const int count)
bool InsertWatches(WatcherMsg* msg, HWND parent, int count)
{
if (count == 1)
return InsertWatch(watches[0], parent);
else
bool success = false;
if (DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_EDITWATCH), parent, EditWatchProc, (LPARAM)msg))
{
bool success = false;
char comment[256];
rswatches[-1] = watches[0];
rswatches[-1].comment = comment;
if(DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_EDITWATCH), parent, EditWatchProc, (LPARAM)-1))
for (int i = 0; i < count; ++i)
{
AddressWatcher watcher = watches[i];
watcher.comment = rswatches[-1].comment;
success |= InsertWatch(watcher);
}
rswatches.erase(-1);
return success;
AddressWatcher* watches = msg->ToAddressWatches();
for (int i = 0; i < count; ++i)
{
success |= InsertWatch(watches[i]);
if (watches[i].comment)
free(watches[i].comment);
}
free(watches);
}
return false;
return success;
}
void Update_RAM_Watch()
@ -781,193 +773,253 @@ INT_PTR CALLBACK EditWatchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lPara
// since there are 3 windows can pops up the add watch dialog, we should store them separately.
// 0 for ram watch, 1 for ram search, 2 for cheat dialog.
static int indexes[4];
static WatcherMsg* msgs[4];
switch(uMsg)
{
case WM_INITDIALOG:
{
HWND parent = GetParent(hDlg);
WatcherMsg* msg = (WatcherMsg*)lParam;
msgs[GetDlgStoreIndex(parent)] = (WatcherMsg*)msg;
RECT r;
GetWindowRect(hDlg, &r);
SetWindowPos(hDlg, NULL, r.left, r.top, NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
char title[128];
UINT addrCtrlID;
switch (msg->msg)
{
HWND parent = GetParent(hDlg);
indexes[GetDlgStoreIndex(parent)] = lParam;
RECT r;
GetWindowRect(hDlg, &r);
SetWindowPos(hDlg, NULL, r.left, r.top, NULL, NULL, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
AddressWatcher& watcher = rswatches[lParam];
if (watcher.Type != 'S') {
char Str_Tmp[1024];
// -1 means batch add
if (lParam != -1)
{
sprintf(Str_Tmp, "%04X", watcher.Address);
SetDlgItemText(hDlg, IDC_EDIT_COMPAREADDRESS, Str_Tmp);
} else
// Add multiple watches
SetDlgItemText(hDlg, IDC_EDIT_COMPAREADDRESS, "(multiple)");
switch (watcher.Size)
{
case 'b':
SendDlgItemMessage(hDlg, IDC_1_BYTE, BM_SETCHECK, BST_CHECKED, 0);
break;
case 'w':
SendDlgItemMessage(hDlg, IDC_2_BYTES, BM_SETCHECK, BST_CHECKED, 0);
break;
case 'd':
SendDlgItemMessage(hDlg, IDC_4_BYTES, BM_SETCHECK, BST_CHECKED, 0);
break;
}
switch (watcher.Type)
{
case 's':
SendDlgItemMessage(hDlg, IDC_SIGNED, BM_SETCHECK, BST_CHECKED, 0);
break;
case 'u':
SendDlgItemMessage(hDlg, IDC_UNSIGNED, BM_SETCHECK, BST_CHECKED, 0);
break;
case 'h':
SendDlgItemMessage(hDlg, IDC_HEX, BM_SETCHECK, BST_CHECKED, 0);
break;
case 'b':
SendDlgItemMessage(hDlg, IDC_BINARY, BM_SETCHECK, BST_CHECKED, 0);
break;
}
} else
SetDlgItemText(hDlg, IDC_EDIT_COMPAREADDRESS, "---------");
if (watcher.comment != NULL)
SetDlgItemText(hDlg, IDC_PROMPT_EDIT, watcher.comment);
if (watcher.Type == 'S' || parent == RamSearchHWnd || parent == hCheat)
{
EnableWindow(GetDlgItem(hDlg, IDC_SPECIFICADDRESS), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_DATATYPE_GROUPBOX), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_DATASIZE_GROUPBOX), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_EDIT_COMPAREADDRESS), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_SIGNED), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_UNSIGNED), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_HEX), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_BINARY), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_1_BYTE), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_2_BYTES), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_4_BYTES), FALSE);
SetFocus(GetDlgItem(hDlg, IDC_PROMPT_EDIT));
}
else {
SetFocus(GetDlgItem(hDlg, IDC_EDIT_COMPAREADDRESS));
SendDlgItemMessage(hDlg, IDC_EDIT_COMPAREADDRESS, EM_SETLIMITTEXT, 4, 0);
DefaultEditCtrlProc = (WNDPROC)SetWindowLongPtr(GetDlgItem(hDlg, IDC_EDIT_COMPAREADDRESS), GWL_WNDPROC, (LONG)FilterEditCtrlProc);
}
case WATCHER_MSG_EDIT:
// Editing multiple watch is currently not supported
addrCtrlID = IDC_EDIT_COMPAREADDRESS;
// limit the length to 4 since currently doesn't support batch editing
SendDlgItemMessage(hDlg, addrCtrlID, EM_SETLIMITTEXT, 4, 0);
strcpy(title, "Edit ");
break;
case WATCHER_MSG_ADD:
strcpy(title, "Add ");
addrCtrlID = IDC_EDIT_COMPAREADDRESSES;
break;
case WATCHER_MSG_DUP:
default:
strcpy(title, "Duplicate ");
addrCtrlID = IDC_EDIT_COMPAREADDRESSES;
break;
}
return true;
break;
// The information is needed to fill to the UI, and separetor doesn't have them
if (msg->Type != 'S')
{
strcat(title, "Watch");
if (msg->Addresses != NULL)
{
// fill the information refers to the message provided
char str_addr[7];
char str_addrs[1024] = { 0 };
// In case more than one watches to be batch added
if (msg->count > 0)
{
// Don't worry about editing a watch since currently doesn't support multiple watch editing, the msg->count would always be 1 in edit mode.
for (int i = 0; i < msg->count; ++i)
{
sprintf(str_addr, "%04X, ", msg->Addresses[i]);
strcat(str_addrs, str_addr);
}
str_addrs[strlen(str_addrs) - 2] = 0;
}
// Set address(es) to the edit control
SetDlgItemText(hDlg, addrCtrlID, str_addrs);
switch (msg->Size)
{
case 'b':
SendDlgItemMessage(hDlg, IDC_1_BYTE, BM_SETCHECK, BST_CHECKED, 0);
break;
case 'w':
SendDlgItemMessage(hDlg, IDC_2_BYTES, BM_SETCHECK, BST_CHECKED, 0);
break;
case 'd':
SendDlgItemMessage(hDlg, IDC_4_BYTES, BM_SETCHECK, BST_CHECKED, 0);
break;
}
switch (msg->Type)
{
case 's':
SendDlgItemMessage(hDlg, IDC_SIGNED, BM_SETCHECK, BST_CHECKED, 0);
break;
case 'u':
SendDlgItemMessage(hDlg, IDC_UNSIGNED, BM_SETCHECK, BST_CHECKED, 0);
break;
case 'h':
SendDlgItemMessage(hDlg, IDC_HEX, BM_SETCHECK, BST_CHECKED, 0);
break;
case 'b':
SendDlgItemMessage(hDlg, IDC_BINARY, BM_SETCHECK, BST_CHECKED, 0);
break;
}
}
}
else
strcat(title, "Separator");
// Set the comment
if (msg->comment != NULL)
SetDlgItemText(hDlg, IDC_PROMPT_EDIT, msg->comment);
// show the proper edit control
ShowWindow(GetDlgItem(hDlg, addrCtrlID), SW_SHOW);
ShowWindow(GetDlgItem(hDlg, IDC_SPECIFICADDRESS), SW_SHOW);
// Adjust the dialog to proper state when editing/adding separator or adding watch from windows other than RAM Watch
if (msg->Type == 'S' || parent != RamWatchHWnd)
{
// same part
EnableWindow(GetDlgItem(hDlg, IDC_DATATYPE_GROUPBOX), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_DATASIZE_GROUPBOX), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_SIGNED), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_UNSIGNED), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_HEX), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_BINARY), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_1_BYTE), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_2_BYTES), FALSE);
EnableWindow(GetDlgItem(hDlg, IDC_4_BYTES), FALSE);
// When it is a separator, focus on the comment
if (msg->Type == 'S')
SetFocus(GetDlgItem(hDlg, IDC_PROMPT_EDIT));
// When it is from RAM Search or Cheat Window, all the information required was already given, make the addresses read-only
if (parent != RamWatchHWnd)
{
EnableWindow(GetDlgItem(hDlg, addrCtrlID), TRUE);
EnableWindow(GetDlgItem(hDlg, IDC_SPECIFICADDRESS), TRUE);
SetFocus(GetDlgItem(hDlg, IDC_PROMPT_EDIT));
SendDlgItemMessage(hDlg, addrCtrlID, EM_SETREADONLY, TRUE, 0);
}
}
else
{
EnableWindow(GetDlgItem(hDlg, addrCtrlID), TRUE);
EnableWindow(GetDlgItem(hDlg, IDC_SPECIFICADDRESS), TRUE);
SetFocus(GetDlgItem(hDlg, addrCtrlID));
}
// limit the text
DefaultEditCtrlProc = (WNDPROC)SetWindowLongPtr(GetDlgItem(hDlg, addrCtrlID), GWL_WNDPROC, (LONG)FilterEditCtrlProc);
SetWindowText(hDlg, title);
}
return false;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDOK:
{
char Str_Tmp[256];
HWND parent = GetParent(hDlg);
int index = indexes[GetDlgStoreIndex(parent)];
WatcherMsg* msg = msgs[GetDlgStoreIndex(parent)];
UINT addrCtrlID;
// not a single watch editing operation
if (index != -1)
switch (msg->msg)
{
// a normal watch, copy it to a temporary one
AddressWatcher watcher = rswatches[index];
// if (watcher.comment != NULL)
// watcher.comment = strcpy((char*)malloc(strlen(watcher.comment) + 2), watcher.comment);
case WATCHER_MSG_EDIT:
addrCtrlID = IDC_EDIT_COMPAREADDRESS;
break;
case WATCHER_MSG_ADD:
case WATCHER_MSG_DUP:
addrCtrlID = IDC_EDIT_COMPAREADDRESSES;
break;
}
// It's from ram watch window, not a separator
// When it's from ram search or cheat window, all the information required is already set,
// so this is also unecessary
if (RamWatchHWnd && RamWatchHWnd == GetParent(hDlg) && watcher.Type != 'S')
{
GetDlgItemText(hDlg, IDC_PROMPT_EDIT, Str_Tmp, 256);
// The information is needed to parse from the UI, and separetor doesn't need them
if (parent == RamWatchHWnd && msg->Type != 'S')
{
// Don't worry about editing mode because it always has only 1 address in the edit control
// type
if (SendDlgItemMessage(hDlg, IDC_SIGNED, BM_GETCHECK, 0, 0) == BST_CHECKED)
watcher.Type = 's';
else if (SendDlgItemMessage(hDlg, IDC_UNSIGNED, BM_GETCHECK, 0, 0) == BST_CHECKED)
watcher.Type = 'u';
else if (SendDlgItemMessage(hDlg, IDC_HEX, BM_GETCHECK, 0, 0) == BST_CHECKED)
watcher.Type = 'h';
else if (SendDlgItemMessage(hDlg, IDC_BINARY, BM_GETCHECK, 0, 0) == BST_CHECKED)
watcher.Type = 'b';
else {
MessageBox(hDlg, "Type must be specified.", "Error", MB_OK | MB_ICONERROR);
return true;
}
// Do the always same part first.
// size
if (SendDlgItemMessage(hDlg, IDC_1_BYTE, BM_GETCHECK, 0, 0) == BST_CHECKED)
watcher.Size = 'b';
else if (SendDlgItemMessage(hDlg, IDC_2_BYTES, BM_GETCHECK, 0, 0) == BST_CHECKED)
watcher.Size = 'w';
else if (SendDlgItemMessage(hDlg, IDC_4_BYTES, BM_GETCHECK, 0, 0) == BST_CHECKED)
watcher.Size = 'd';
else {
MessageBox(hDlg, "Size must be specified.", "Error", MB_OK | MB_ICONERROR);
return true;
}
if (watcher.Type == 'b' && (watcher.Size == 'd' || watcher.Size == 'w'))
{
MessageBox(hDlg, "Only 1 byte is supported on binary format.", "Error", MB_OK | MB_ICONERROR);
return true;
}
// address
GetDlgItemText(hDlg, IDC_EDIT_COMPAREADDRESS, Str_Tmp, 1024);
char *addrstr = Str_Tmp;
if (strlen(Str_Tmp) > 8)
addrstr = &Str_Tmp[strlen(Str_Tmp) - 9];
for (int i = 0; addrstr[i]; ++i)
if (toupper(addrstr[i]) == 'O')
addrstr[i] = '0';
sscanf(addrstr, "%04X", &watcher.Address);
if ((watcher.Address & ~0xFFFFFF) == ~0xFFFFFF)
watcher.Address &= 0xFFFFFF;
if (!IsHardwareAddressValid(watcher.Address))
{
MessageBox(hDlg, "Invalid Address.", "Error", MB_OK | MB_ICONERROR);
return true;
}
// get the type
if (SendDlgItemMessage(hDlg, IDC_SIGNED, BM_GETCHECK, 0, 0) == BST_CHECKED)
msg->Type = 's';
else if (SendDlgItemMessage(hDlg, IDC_UNSIGNED, BM_GETCHECK, 0, 0) == BST_CHECKED)
msg->Type = 'u';
else if (SendDlgItemMessage(hDlg, IDC_HEX, BM_GETCHECK, 0, 0) == BST_CHECKED)
msg->Type = 'h';
else if (SendDlgItemMessage(hDlg, IDC_BINARY, BM_GETCHECK, 0, 0) == BST_CHECKED)
msg->Type = 'b';
else {
MessageBox(hDlg, "Type must be specified.", "Error", MB_OK | MB_ICONERROR);
return true;
}
// comment
GetDlgItemText(hDlg, IDC_PROMPT_EDIT, Str_Tmp, 80);
watcher.comment = Str_Tmp;
// get the size
if (SendDlgItemMessage(hDlg, IDC_1_BYTE, BM_GETCHECK, 0, 0) == BST_CHECKED)
msg->Size = 'b';
else if (SendDlgItemMessage(hDlg, IDC_2_BYTES, BM_GETCHECK, 0, 0) == BST_CHECKED)
msg->Size = 'w';
else if (SendDlgItemMessage(hDlg, IDC_4_BYTES, BM_GETCHECK, 0, 0) == BST_CHECKED)
msg->Size = 'd';
else {
MessageBox(hDlg, "Size must be specified.", "Error", MB_OK | MB_ICONERROR);
return true;
}
// finallly update the watch list
if (index >= 0 && index < WatchCount)
// it's a watch editing operation.
// Only ram watch window can edit a watch, the ram search window and cheat window only add watch.
EditWatch(index, watcher);
if (msg->Type == 'b' && (msg->Size == 'd' || msg->Size == 'w'))
{
MessageBox(hDlg, "Only 1 byte is supported on binary format.", "Error", MB_OK | MB_ICONERROR);
return true;
}
// get the address(es)
char str_addrs[1024];
GetDlgItemText(hDlg, addrCtrlID, str_addrs, 1024);
char* next = strtok(str_addrs, ",");
int i = 0;
unsigned int addrSize = 4;
if (msg->Addresses)
msg->Addresses = (unsigned int*)malloc(sizeof(int) * addrSize);
else
InsertWatch(watcher);
if (RamWatchHWnd)
ListView_SetItemCount(GetDlgItem(RamWatchHWnd, IDC_WATCHLIST), WatchCount);
}
else {
// a multiple watches insert operation, just asking for a comment
AddressWatcher& watcher = rswatches[index];
// comment
GetDlgItemText(hDlg, IDC_PROMPT_EDIT, Str_Tmp, 80);
strcpy(watcher.comment, Str_Tmp);
}
EndDialog(hDlg, true);
msg->Addresses = (unsigned int*)realloc(msg->Addresses, sizeof(int) * addrSize);
RWfileChanged = true;
return true;
break;
do {
sscanf(next, "%04X", &msg->Addresses[i]);
// When an invalid address is in it
if (!IsHardwareAddressValid(msg->Addresses[i]))
{
MessageBox(hDlg, "You have entered an invalid address.", "Error", MB_ICONERROR | MB_OK);
// Comment out, leave it unfreed until next ID_OK event or close the dialog
// free(msg->Addresses);
// msg->Addresses = NULL;
char* start = next + strspn(next, ", ");
char* end = start + strcspn(start, ", ");
SendDlgItemMessage(hDlg, addrCtrlID, EM_SETSEL, start - str_addrs, end - str_addrs);
SetFocus(GetDlgItem(hDlg, addrCtrlID));
return true;
}
++i;
if (i == addrSize)
msg->Addresses = (unsigned int*)realloc(msg->Addresses, sizeof(unsigned int) * (addrSize += 4));
} while (next = strtok(NULL, ","));
msg->count = i;
}
// get the new comment, every AddressWatcher would have a comment, no matter what type it is
if (msg->comment)
msg->comment = (char*)realloc(msg->comment, 1024 * sizeof(char));
else
msg->comment = (char*)malloc(1024 * sizeof(char));
GetDlgItemText(hDlg, IDC_PROMPT_EDIT, msg->comment, 1024);
EndDialog(hDlg, true);
return false;
}
case IDCANCEL:
EndDialog(hDlg, false);
@ -1308,19 +1360,43 @@ INT_PTR CALLBACK RamWatchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam
watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg,IDC_WATCHLIST));
if(watchIndex != -1)
{
DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_EDITWATCH), hDlg, EditWatchProc, watchIndex);
SetFocus(GetDlgItem(hDlg,IDC_WATCHLIST));
WatcherMsg msg = WatcherMsg::FromAddressWatches(&rswatches[watchIndex]);
msg.msg = WATCHER_MSG_EDIT;
if (DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_EDITWATCH), hDlg, EditWatchProc, (LPARAM)&msg))
{
AddressWatcher watcher = msg.ToAddressWatches()[0];
EditWatch(watchIndex, watcher);
SetFocus(GetDlgItem(hDlg, IDC_WATCHLIST));
if (watcher.comment)
free(watcher.comment);
}
if (msg.Addresses)
free(msg.Addresses);
if (msg.comment)
free(msg.comment);
}
return true;
case IDC_C_WATCH:
{
AddressWatcher& target = rswatches[WatchCount];
target.Address = 0;
target.WrongEndian = 0;
target.Size = 'b';
target.Type = 's';
DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_EDITWATCH), hDlg, EditWatchProc, WatchCount);
SetFocus(GetDlgItem(hDlg, IDC_WATCHLIST));
WatcherMsg msg;
msg.Addresses = (unsigned int*)calloc(1, sizeof(unsigned int));
msg.WrongEndian = 0;
msg.Size = 'b';
msg.Type = 's';
if (DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_EDITWATCH), hDlg, EditWatchProc, (LPARAM)&msg))
{
AddressWatcher* target = msg.ToAddressWatches();
for (int i = 0; i < msg.count; ++i)
InsertWatch(target[i]);
SetFocus(GetDlgItem(hDlg, IDC_WATCHLIST));
if (target->comment)
free(target->comment);
free(target);
}
if (msg.Addresses)
free(msg.Addresses);
if (msg.comment)
free(msg.comment);
return true;
}
case IDC_C_WATCH_DUPLICATE:
@ -1328,27 +1404,46 @@ INT_PTR CALLBACK RamWatchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam
watchIndex = ListView_GetSelectionMark(GetDlgItem(hDlg, IDC_WATCHLIST));
if (watchIndex != -1)
{
char str_tmp[1024];
AddressWatcher* target = &rswatches[WatchCount];
AddressWatcher* source = &rswatches[watchIndex];
memcpy(target, source, sizeof(AddressWatcher));
target->comment = strcpy(str_tmp, source->comment);
DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_EDITWATCH), hDlg, EditWatchProc, WatchCount);
SetFocus(GetDlgItem(hDlg, IDC_WATCHLIST));
WatcherMsg msg = WatcherMsg::FromAddressWatches(&rswatches[watchIndex]);
msg.msg = WATCHER_MSG_DUP;
if (DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_EDITWATCH), hDlg, EditWatchProc, (LPARAM)&msg))
{
SetFocus(GetDlgItem(hDlg, IDC_WATCHLIST));
int count;
AddressWatcher* target = msg.ToAddressWatches(&count);
for (int i = 0; i < count; ++i)
{
InsertWatch(target[i]);
if (target[i].comment)
free(target[i].comment);
}
}
if(msg.Addresses)
free(msg.Addresses);
if(msg.comment)
free(msg.comment);
}
return true;
}
case IDC_C_WATCH_SEPARATE:
{
AddressWatcher* target = &rswatches[WatchCount];
target->Address = 0;
target->WrongEndian = false;
target->Size = 'S';
target->Type = 'S';
WatcherMsg msg;
msg.WrongEndian = 0;
msg.Size = 'S';
msg.Type = 'S';
msg.msg = WATCHER_MSG_ADD;
msg.count = 1;
DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_EDITWATCH), hDlg, EditWatchProc, (LPARAM)WatchCount);
if (DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_EDITWATCH), hDlg, EditWatchProc, (LPARAM)&msg))
{
AddressWatcher* sep = msg.ToAddressWatches();
InsertWatch(*sep);
if (sep->comment)
free(sep->comment);
}
if (msg.comment)
free(msg.comment);
// InsertWatch(separator, "----------------------------");
SetFocus(GetDlgItem(hDlg, IDC_WATCHLIST));
return true;
}
@ -1593,3 +1688,59 @@ SeparatorCache::SeparatorCache(HWND hwnd, char* text) {
labelOffY = 0;
}
}
AddressWatcher* WatcherMsg::ToAddressWatches(int* _count)
{
AddressWatcher* watches = (AddressWatcher*)malloc(count * sizeof(AddressWatcher));
int i = 0;
while(i < count)
{
AddressWatcher watcher;
watcher.Size = Size;
if (Type != 'S')
watcher.Address = Addresses[i];
watcher.Type = Type;
watcher.WrongEndian = 0;
if (comment)
{
watcher.comment = (char*)malloc(strlen(comment) * sizeof(char) + 1);
strcpy(watcher.comment, comment);
}
else
watcher.comment = NULL;
watches[i] = watcher;
++i;
}
if (_count)
*_count = i;
return watches;
}
WatcherMsg WatcherMsg::FromAddressWatches(const AddressWatcher* watches, int count)
{
WatcherMsg msg;
if (watches[0].comment)
{
msg.comment = (char*)malloc(strlen(watches[0].comment) + 1);
strcpy(msg.comment, watches[0].comment);
}
msg.count = count;
msg.Size = watches->Size;
msg.Type = watches->Type;
msg.WrongEndian = watches->WrongEndian;
// Because currently doesn't support adding multiple separators at once,
// and a separator doesn't have address values,
// so when add separator, there's only 1 item in the list
if (watches->Type != 'S')
{
msg.Addresses = (unsigned int*)malloc(count * sizeof(unsigned int));
for (int i = 0; i < count; ++i)
msg.Addresses[i] = watches[i].Address;
}
return msg;
}

View File

@ -39,6 +39,7 @@ struct SeparatorCache
};
#define MAX_WATCH_COUNT 256
extern int WatchCount; // number of valid items in rswatches
extern char Watch_Dir[1024];
@ -58,9 +59,28 @@ struct AddressWatcher
short Cheats; // how many bytes are affected by cheat
};
// the struct for communicating with add watch window
#define WATCHER_MSG_ADD 0
#define WATCHER_MSG_EDIT 1
#define WATCHER_MSG_DUP 2
struct WatcherMsg {
int msg = WATCHER_MSG_ADD;
int count = 0; // how many addresses are there
unsigned int* Addresses = NULL; // Address list
char* comment = NULL;
bool WrongEndian;
char Size;
char Type;
AddressWatcher* ToAddressWatches(int* _count = NULL);
static WatcherMsg FromAddressWatches(const AddressWatcher* watches, int count = 1);
};
bool InsertWatch(const AddressWatcher& Watch);
bool InsertWatch(const AddressWatcher& Watch, HWND parent); // asks user for comment
bool InsertWatches(const AddressWatcher* watches, HWND parent, const int count);
bool InsertWatches(WatcherMsg* msg, HWND parent, int count);
bool InsertWatch(int watchIndex, const AddressWatcher& watcher);
bool EditWatch(int watchIndex, const AddressWatcher& watcher);
bool RemoveWatch(int watchIndex);

View File

@ -2205,8 +2205,9 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSM
CAPTION " Edit Watch"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CTEXT "&Address:",IDC_SPECIFICADDRESS,15,12,30,8
EDITTEXT IDC_EDIT_COMPAREADDRESS,48,10,65,12,ES_UPPERCASE | ES_AUTOHSCROLL
CTEXT "&Address:",IDC_SPECIFICADDRESS,15,12,30,8,NOT WS_VISIBLE | WS_DISABLED
EDITTEXT IDC_EDIT_COMPAREADDRESS,48,10,65,12,ES_UPPERCASE | ES_AUTOHSCROLL | NOT WS_VISIBLE | WS_DISABLED
EDITTEXT IDC_EDIT_COMPAREADDRESSES,48,10,65,12,ES_UPPERCASE | ES_AUTOHSCROLL | NOT WS_VISIBLE | WS_DISABLED
CTEXT "&Notes:",IDC_PROMPT_TEXT,23,24,22,8
EDITTEXT IDC_PROMPT_EDIT,48,22,119,12,ES_AUTOHSCROLL
GROUPBOX "Data Type",IDC_DATATYPE_GROUPBOX,14,37,75,53,0,WS_EX_TRANSPARENT

View File

@ -1,12 +1,12 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file
// for res.rc
// Microsoft Visual C++ 生成的包含文件。
// 供 res.rc 使用
//
#define CLOSE_BUTTON 1
#define BUTTON_CLOSE 1
#define BTN_CLOSE 1
#define MENU_OPEN_FILE 100
#define EDIT_ROM 100
#define EDIT_ROM 100
#define LBL_LOG_TEXT 100
#define LBL_KEY_COMBO 100
#define LBL_CDLOGGER_CODECOUNT 100
@ -216,7 +216,7 @@
#define IDD_TASEDITOR_FINDNOTE 198
#define IDD_TASEDITOR_ABOUT 199
#define MENU_RESET 200
#define BUTTON_ROM 200
#define BUTTON_ROM 200
#define TXT_PAD1 200
#define BTN_RESTORE_DEFAULTS 200
#define BTN_CLEAR 200
@ -765,6 +765,7 @@
#define IDC_EDIT_COMPAREVALUE 1225
#define IDC_EDIT_COMPAREADDRESS 1226
#define IDC_EDIT_COMPARECHANGES 1227
#define IDC_EDIT_COMPAREADDRESSES 1227
#define IDC_SIGNED 1228
#define IDC_UNSIGNED 1229
#define IDC_HEX 1230

View File

@ -3293,7 +3293,7 @@ LRESULT APIENTRY FilterEditCtrlProc(HWND hwnd, UINT msg, WPARAM wP, LPARAM lP)
{
through = false;
// Show Edit control tip, just like the control with ES_NUMBER do
ShowLetterIllegalError(hwnd, IsLetterLegal);
ShowLetterIllegalBalloonTip(hwnd, IsLetterLegal);
break;
}
}
@ -3309,7 +3309,7 @@ LRESULT APIENTRY FilterEditCtrlProc(HWND hwnd, UINT msg, WPARAM wP, LPARAM lP)
bool(*IsLetterLegal)(char) = GetIsLetterLegal(GetDlgCtrlID(hwnd));
through = IsInputLegal(IsLetterLegal, wP);
if (!through)
ShowLetterIllegalError(hwnd, IsLetterLegal);
ShowLetterIllegalBalloonTip(hwnd, IsLetterLegal);
}
}
@ -3353,11 +3353,14 @@ bool inline (*GetIsLetterLegal(UINT id))(char letter)
case MW_ADDR12: case MW_ADDR13: case MW_ADDR14: case MW_ADDR15:
case MW_ADDR16: case MW_ADDR17: case MW_ADDR18: case MW_ADDR19:
case MW_ADDR20: case MW_ADDR21: case MW_ADDR22: case MW_ADDR23:
case IDC_EDIT_COMPAREADDRESS:
return IsLetterLegalHex;
// Specific Address in RAM Search
// RAM Watch / RAM Search / Cheat -> Add watch
case IDC_EDIT_COMPAREADDRESS:
return IsLetterLegalHex;
// RAM Watch / RAM Search / Cheat -> Add watch (current only in adding watch operation)
case IDC_EDIT_COMPAREADDRESSES:
return IsLetterLegalHexList;
// Size multiplier and TV Aspect in Video Config
case IDC_WINSIZE_MUL_X: case IDC_WINSIZE_MUL_Y:
@ -3390,63 +3393,45 @@ bool inline (*GetIsLetterLegal(UINT id))(char letter)
return NULL;
}
inline void ShowLetterIllegalError(HWND hwnd, bool(*IsLetterLegal)(char letter), bool balloon)
{
(balloon ? ShowLetterIllegalBalloonTip : ShowLetterIllegalMessageBox)(hwnd, IsLetterLegal);
}
void ShowLetterIllegalBalloonTip(HWND hwnd, bool(*IsLetterLegal)(char letter))
{
char* title = "Unacceptable Character";
int uLen = MultiByteToWideChar(CP_ACP, NULL, title, -1, NULL, 0);
wchar_t* titleW = (wchar_t*)malloc(sizeof(wchar_t) * uLen);
MultiByteToWideChar(CP_ACP, 0, title, -1, (LPWSTR)titleW, uLen);
char* msg = GetLetterIllegalErrMsg(IsLetterLegal);
uLen = MultiByteToWideChar(CP_ACP, NULL, msg, -1, NULL, 0);
wchar_t* msgW = (wchar_t*)malloc(sizeof(wchar_t) * uLen);
MultiByteToWideChar(CP_ACP, 0, msg, -1, (LPWSTR)msgW, uLen);
wchar_t* title = L"Unacceptable Character";
wchar_t* msg = GetLetterIllegalErrMsg(IsLetterLegal);
EDITBALLOONTIP tip;
tip.cbStruct = sizeof(EDITBALLOONTIP);
tip.pszText = msgW;
tip.pszTitle = titleW;
tip.pszText = msg;
tip.pszTitle = title;
tip.ttiIcon = TTI_ERROR;
SendMessage(hwnd, EM_SHOWBALLOONTIP, 0, (LPARAM)&tip);
// make a sound
MessageBeep(0xFFFFFFFF);
free(titleW);
free(msgW);
}
inline void ShowLetterIllegalMessageBox(HWND hwnd, bool(*IsLetterLegal)(char letter))
{
MessageBox(hwnd, GetLetterIllegalErrMsg(IsLetterLegal), _T("Unacceptable Character"), MB_OK | MB_ICONERROR);
}
inline TCHAR* GetLetterIllegalErrMsg(bool(*IsLetterLegal)(char letter))
inline wchar_t* GetLetterIllegalErrMsg(bool(*IsLetterLegal)(char letter))
{
if (IsLetterLegal == IsLetterLegalGG)
return "You can only type Game Genie characters:\nA P Z L G I T Y E O X U K S V N";
return L"You can only type Game Genie characters:\nA P Z L G I T Y E O X U K S V N";
if (IsLetterLegal == IsLetterLegalHex)
return "You can only type characters for hexadecimal number (0-9,A-F).";
return L"You can only type characters for hexadecimal number (0-9,A-F).";
if (IsLetterLegal == IsLetterLegalHexList)
return L"You can only type characters for hexademical number (0-9,A-F), each number is separated by a comma (,)";
if (IsLetterLegal == IsLetterLegalCheat)
return
"The cheat code comes into the following 2 formats:\n"
L"The cheat code comes into the following 2 formats:\n"
"AAAA:VV freezes the value in Address $AAAA to $VV.\n"
"AAAA?CC:VV changes the value in Address $AAAA to $VV only when it's $CC.\n"
"All the characters are hexadecimal number (0-9,A-F).\n";
if (IsLetterLegal == IsLetterLegalFloat)
return "You can only type decimal number (decimal point is acceptable).";
return L"You can only type decimal number (decimal point is acceptable).";
if (IsLetterLegal == IsLetterLegalSize)
return "You can only type decimal number followed with B, KB or MB.";
return L"You can only type decimal number followed with B, KB or MB.";
if (IsLetterLegal == IsLetterLegalDec)
return "You can only type decimal number (sign character is acceptable).";
return L"You can only type decimal number (sign character is acceptable).";
if (IsLetterLegal == IsLetterLegalDecHexMixed)
return
"You can only type decimal or hexademical number\n"
L"You can only type decimal or hexademical number\n"
"(sign character is acceptable).\n\n"
"When your number contains letter A-F,\n"
"it is regarded as hexademical number,\n"
@ -3457,7 +3442,7 @@ inline TCHAR* GetLetterIllegalErrMsg(bool(*IsLetterLegal)(char letter))
"$10 means a hexademical number that is 16 in decimal.";
if (IsLetterLegal == IsLetterLegalUnsignedDecHexMixed)
return
"You can only type decimal or hexademical number.\n\n"
L"You can only type decimal or hexademical number.\n\n"
"When your number contains letter A-F,\n"
"it is regarded as hexademical number,\n"
"however, if you want to express a heademical number\n"
@ -3466,7 +3451,7 @@ inline TCHAR* GetLetterIllegalErrMsg(bool(*IsLetterLegal)(char letter))
"eg. 10 is a decimal number,\n"
"$10 means a hexademical number that is 16 in decimal.";
return "Your input contains invalid characters.";
return L"Your input contains invalid characters.";
}
inline bool IsInputLegal(bool (*IsLetterLegal)(char letter), char letter)
@ -3488,6 +3473,11 @@ inline bool IsLetterLegalHex(char letter)
return letter >= '0' && letter <= '9' || letter >= 'A' && letter <= 'F' || letter >= 'a' && letter <= 'f';
}
inline bool IsLetterLegalHexList(char letter)
{
return IsLetterLegalHex(letter) || letter == ',' || letter == ' ';
}
inline bool IsLetterLegalCheat(char letter)
{
return letter >= '0' && letter <= ':' || letter >= 'A' && letter <= 'F' || letter >= 'a' && letter <= 'f' || letter == '?';

View File

@ -129,13 +129,12 @@ void UpdateMenuHotkeys(FCEUMENU_INDEX index);
int GetCurrentContextIndex();
inline bool (*GetIsLetterLegal(UINT id))(char letter);
inline char* GetLetterIllegalErrMsg(bool(*IsLetterLegal)(char letter));
inline void ShowLetterIllegalError(HWND hwnd, bool(*IsLetterLegal)(char letter), bool balloon = true);
inline wchar_t* GetLetterIllegalErrMsg(bool(*IsLetterLegal)(char letter));
void ShowLetterIllegalBalloonTip(HWND hwnd, bool(*IsLetterLegal)(char letter));
inline void ShowLetterIllegalMessageBox(HWND hwnd, bool(*IsLetterLegal)(char letter));
inline bool IsInputLegal(bool(*IsLetterLegal)(char letter), char letter);
inline bool IsLetterLegalGG(char letter);
inline bool IsLetterLegalHex(char letter);
inline bool IsLetterLegalHexList(char letter);
inline bool IsLetterLegalCheat(char letter);
inline bool IsLetterLegalDec(char letter);
inline bool IsLetterLegalSize(char letter);