diff --git a/src/drivers/win/cheat.cpp b/src/drivers/win/cheat.cpp index 7ba4779f..0fdc721d 100644 --- a/src/drivers/win/cheat.cpp +++ b/src/drivers/win/cheat.cpp @@ -1553,7 +1553,7 @@ void CreateCheatMap() void ReleaseCheatMap() { --CheatMapUsers; - printf("CheatMapUsers: %d\n", CheatMapUsers); + // printf("CheatMapUsers: %d\n", CheatMapUsers); if (!CheatMapUsers) FCEUI_ReleaseCheatMap(); } diff --git a/src/drivers/win/ram_search.cpp b/src/drivers/win/ram_search.cpp index ac69a91a..ae045ce7 100644 --- a/src/drivers/win/ram_search.cpp +++ b/src/drivers/win/ram_search.cpp @@ -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; } diff --git a/src/drivers/win/ramwatch.cpp b/src/drivers/win/ramwatch.cpp index e96ce86a..9fc3e013 100644 --- a/src/drivers/win/ramwatch.cpp +++ b/src/drivers/win/ramwatch.cpp @@ -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; +} diff --git a/src/drivers/win/ramwatch.h b/src/drivers/win/ramwatch.h index 23153f39..fd625b9d 100644 --- a/src/drivers/win/ramwatch.h +++ b/src/drivers/win/ramwatch.h @@ -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); diff --git a/src/drivers/win/res.rc b/src/drivers/win/res.rc index 413195a0..2218afcb 100644 --- a/src/drivers/win/res.rc +++ b/src/drivers/win/res.rc @@ -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 diff --git a/src/drivers/win/resource.h b/src/drivers/win/resource.h index a7233fc7..055f8398 100644 --- a/src/drivers/win/resource.h +++ b/src/drivers/win/resource.h @@ -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 diff --git a/src/drivers/win/window.cpp b/src/drivers/win/window.cpp index ae70b7fa..548d621d 100644 --- a/src/drivers/win/window.cpp +++ b/src/drivers/win/window.cpp @@ -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 == '?'; diff --git a/src/drivers/win/window.h b/src/drivers/win/window.h index 0ba033ef..0127ad97 100644 --- a/src/drivers/win/window.h +++ b/src/drivers/win/window.h @@ -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);