diff --git a/src/drivers/win/debuggersp.cpp b/src/drivers/win/debuggersp.cpp index c66d4853..1404e8fa 100644 --- a/src/drivers/win/debuggersp.cpp +++ b/src/drivers/win/debuggersp.cpp @@ -1,1396 +1,1396 @@ -/* FCEUXD SP - NES/Famicom Emulator - * - * Copyright notice for this file: - * Copyright (C) 2005 Sebastian Porst - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "common.h" -#include "utils/xstring.h" -#include "debuggersp.h" -#include "debugger.h" -#include "../../fceu.h" -#include "../../debug.h" -#include "../../conddebug.h" - -#include -#include -#include - -int GetNesFileAddress(int A); - -inline int RomPageIndexForAddress(int addr) { return (addr-0x8000)>>(debuggerPageSize); } - -//old -//Name* lastBankNames = 0; -//Name* loadedBankNames = 0; - -//new -Name* pageNames[32] = {0}; //the maximum number of pages we could have is 32, based on 1KB debuggerPageSize - -//old -//int lastBank = -1; -//int loadedBank = -1; - -//new -int pageNumbersLoaded[32] = { - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -}; - -Name* ramBankNames = 0; -bool ramBankNamesLoaded = false; - -extern char LoadedRomFName[2048]; -char NLfilename[2048]; -bool symbDebugEnabled = true; -bool symbRegNames = true; -int debuggerWasActive = 0; -char temp_chr[40] = {0}; -char delimiterChar[2] = "#"; - -INT_PTR CALLBACK nameDebuggerBookmarkCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); -extern WNDPROC DefaultEditCtrlProc; -extern LRESULT APIENTRY FilterEditCtrlProc(HWND hDlg, UINT uMsg, WPARAM wP, LPARAM lP); - -MemoryMappedRegister RegNames[] = { - {"$2000", "PPU_CTRL"}, - {"$2001", "PPU_MASK"}, - {"$2002", "PPU_STATUS"}, - {"$2003", "PPU_OAM_ADDR"}, - {"$2004", "PPU_OAM_DATA"}, - {"$2005", "PPU_SCROLL"}, - {"$2006", "PPU_ADDRESS"}, - {"$2007", "PPU_DATA"}, - {"$4000", "SQ1_VOL"}, - {"$4001", "SQ1_SWEEP"}, - {"$4002", "SQ1_LO"}, - {"$4003", "SQ1_HI"}, - {"$4004", "SQ2_VOL"}, - {"$4005", "SQ2_SWEEP"}, - {"$4006", "SQ2_LO"}, - {"$4007", "SQ2_HI"}, - {"$4008", "TRI_LINEAR"}, -// {"$4009", "UNUSED"}, - {"$400A", "TRI_LO"}, - {"$400B", "TRI_HI"}, - {"$400C", "NOISE_VOL"}, -// {"$400D", "UNUSED"}, - {"$400E", "NOISE_LO"}, - {"$400F", "NOISE_HI"}, - {"$4010", "DMC_FREQ"}, - {"$4011", "DMC_RAW"}, - {"$4012", "DMC_START"}, - {"$4013", "DMC_LEN"}, - {"$4014", "OAM_DMA"}, - {"$4015", "APU_STATUS"}, - {"$4016", "JOY1"}, - {"$4017", "JOY2_FRAME"} -}; - -int RegNameCount = sizeof(RegNames)/sizeof(MemoryMappedRegister); - -/** -* Tests whether a char is a valid hexadecimal character. -* -* @param c The char to test -* @return True or false. -**/ -int isHex(char c) -{ - return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); -} - -/** -* Parses a line from a NL file. -* @param line The line to parse -* @param n The name structure to write the information to -* -* @return 0 if everything went OK. Otherwise an error code is returned. -**/ -int parseLine(char* line, Name* n) -{ - char* pos; - int llen; - - // Check parameters - - if (!line) - { - MessageBox(0, "Invalid parameter \"line\" in function parseLine", "Error", MB_OK | MB_ICONERROR); - return 1; - } - - if (!n) - { - MessageBox(0, "Invalid parameter \"n\" in function parseLine", "Error", MB_OK | MB_ICONERROR); - return 2; - } - - // Allow empty lines - if (*line == '\r' || *line == '\n') - { - return -1; - } - - // Attempt to tokenize the given line - - pos = strstr(line, delimiterChar); - - if (!pos) - { - // Found an invalid line - return 3; - } - - // Check if the first tokenized part (the offset) is valid - - *pos = 0; - - llen = (int)strlen(line); - - if (llen == 5) // Offset size of normal lines of the form $XXXX - { - if (line[0] != '$' - || !isHex(line[1]) - || !isHex(line[2]) - || !isHex(line[3]) - || !isHex(line[4]) - ) - { - return 4; - } - } - else if (llen >= 7) // Offset size of array definition lines of the form $XXXX/YY - { - int i; - if (line[0] != '$' - || !isHex(line[1]) - || !isHex(line[2]) - || !isHex(line[3]) - || !isHex(line[4]) - || line[5] != '/' - ) - { - return 5; - } - - for (i=6;line[i];i++) - { - if (!isHex(line[i])) - { - return 6; - } - } - } - else // Lines that have an invalid size - { - return 7; - } - - - // TODO: Validate if the offset is in the correct NL file. - - // After validating the offset it's OK to store it - n->offset = (char*)malloc(strlen(line) + 1); - strcpy(n->offset, line); - line = pos + 1; - - // Attempt to tokenize the string again to find the name of the address - - pos = strstr(line, delimiterChar); - - if (!pos) - { - // Found an invalid line - return 8; - } - - *pos = 0; - - if (*line) - { - if (strlen(line) > NL_MAX_NAME_LEN) - line[NL_MAX_NAME_LEN + 1] = 0; - n->name = (char*)malloc(strlen(line) + 1); - strcpy(n->name, line); - } - else - { - // Properly initialize zero-length names too - n->name = 0; - } - - // Now it's clear that the line was valid. The rest of the line is the comment - // that belongs to that line. Once again a real string is required though. - - line = pos + 1; - - if ((u8)*line > 0x0D) - { - if (strlen(line) > NL_MAX_MULTILINE_COMMENT_LEN) - line[NL_MAX_MULTILINE_COMMENT_LEN + 1] = 0; - // remove all backslashes after \r\n - char* crlf_pos = strstr(line, "\r\n\\"); - while (crlf_pos) - { - strcpy(crlf_pos + 2, crlf_pos + 3); - crlf_pos = strstr(crlf_pos + 2, "\r\n\\"); - } - - n->comment = (char*)malloc(strlen(line) + 1); - strcpy(n->comment, line); - } - else - { - // Properly initialize zero-length comments too - n->comment = 0; - } - - // Everything went fine. - return 0; -} - -/** -* Parses an array of lines read from a NL file. -* @param lines The lines to parse -* @param filename The name of the file the lines were read from. -* -* @return A pointer to the first Name structure built from the parsed lines. -**/ -Name* parse(char* lines, const char* filename) -{ - char* pos, *size; - Name* prev = 0, *cur, *first = 0; - - // Check the parameters - if (!lines) - { - MessageBox(0, "Invalid parameter \"lines\" in function parse", "Error", MB_OK | MB_ICONERROR); - return 0; - } - - if (!filename) - { - MessageBox(0, "Invalid parameter \"filename\" in function parse", "Error", MB_OK | MB_ICONERROR); - return 0; - } - - // Begin the actual parsing - - do - { - int fail; - - // Allocate a name structure to hold the parsed data from the next line - - cur = (Name*)malloc(sizeof(Name)); - cur->offsetNumeric = 0; - cur->offset = 0; - cur->next = 0; - cur->name = 0; - cur->comment = 0; - - pos = lines; - - // This first loop attempts to read potential multi-line comments and add them - // into a single comment. - for(;;) - { - // Get the end of the next line - pos = strstr(pos, "\n"); - - // If there's no end of line or if the next line does not begin with a \ character - // we can stop. - if (!pos || pos[1] != '\\') break; - - // At this point we have the following situation. pos[0] and pos[1] can be overwritten - /* - pos -1 0 1 2 - ? \n \ ? - */ - - // \r\n is needed in text boxes - if (pos[-1] != '\r') - { - pos[0] = '\r'; - pos[1] = '\n'; - } - pos += 2; - } - - if (!pos) - { - // All lines were parsed - break; - } - - if (pos[-1] == '\r') - pos[-1] = 0; - else - *pos = 0; - - // Attempt to parse the current line - fail = parseLine(lines, cur); - - if (fail == -1) - { - continue; - } else if (fail) - { - // Show an error to allow the user to correct the defect line - const char* fmtString = "Error (Code: %d): Invalid line \"%s\" in NL file \"%s\"\n"; - char* msg = (char*)malloc(strlen(fmtString) + 8 + strlen(lines) + strlen(filename) + 1); - sprintf(msg, fmtString, fail, lines, filename); - MessageBox(0, msg, "Error", MB_OK | MB_ICONERROR); - free(msg); - lines = pos + 1; - //MessageBox(0, lines, "Error", MB_OK | MB_ICONERROR); - continue; - } - - lines = pos + 1; - - // Check if the line is an array definition line - - size = strstr(cur->offset, "/"); - - if (size && cur->name) // Array definition line - { - int arrlen, offset; - - *size = 0; - - // Attempt to read the length of the array and the array offset - if (sscanf(size + 1, "%x", &arrlen) > 0 && sscanf(cur->offset + 1, "%x", &offset) > 0) - { - Name* nn = 0; - int i; - - // Create a node for each element of the array - for (i=0;i<=arrlen;i++) - { - char numbuff[10] = {0}; - - nn = (Name*)malloc(sizeof(Name)); - nn->next = 0; - - // The comment is the same for each array element - nn->comment = strdup(cur->comment); - - // The offset of the node - nn->offset = (char*)malloc(6); - sprintf(nn->offset, "$%04X", offset + i); - nn->offsetNumeric = offset + i; - - // The name of an array address is of the form NAME[INDEX] - sprintf(numbuff, "[%X]", i); - - nn->name = (char*)malloc(strlen(cur->name) + strlen(numbuff) + 1); - strcpy(nn->name, cur->name); - strcat(nn->name, numbuff); - - // Add the new node to the list of address nodes - if (prev) - { - prev->next = nn; - prev = prev->next; - } else - { - first = prev = nn; - } - printf("nnoffset: %s, %d\n", nn->offset, nn->offsetNumeric); - } - - // Free the allocated node - free(cur->name); - free(cur->comment); - free(cur->offset); - free(cur); - - cur = nn; - } - else - { - // I don't think it's possible to get here as the validity of - // offset and array size has already been validated in parseLine - continue; - } - } else - { - unsigned int tempOffset; - sscanf(cur->offset, "%*[$]%4X", &(tempOffset)); - cur->offsetNumeric = (uint16)tempOffset; - // Add the node to the list of address nodes - if (prev) - { - prev->next = cur; - prev = prev->next; - } - else - { - first = prev = cur; - } - } - } while (pos); - - // Return the first node in the list of address nodes - return first; -} - -/** -* Load and parse an entire NL file -* @param filename Name of the file to parse -* -* @return A pointer to the first Name structure built from the file data. -**/ -Name* parseNameFile(const char* filename) -{ - char* buffer; - Name* n = 0; - - // Attempt to read the file - FILE* f = fopen(filename, "rb"); - - if (f) - { -// __asm (".byte 0xcc"); - // Get the file size - int lSize; - fseek (f , 0 , SEEK_END); - lSize = ftell(f) + 1; - - // Allocate sufficient buffer space - rewind (f); - - buffer = (char*)malloc(lSize); - - if (buffer) - { - // Read the file and parse it - memset(buffer, 0, lSize); - fread(buffer, 1, lSize - 1, f); - n = parse(buffer, filename); - - fclose(f); - free(buffer); - } - } - - return n; -} - -/** -* Frees an entire list of Name nodes starting with the given node. -* -* @param n The node to start with (0 is a valid value) -**/ -void freeList(Name* n) -{ - Name* next; - - while (n) - { - if (n->offset) free(n->offset); - if (n->name) free(n->name); - if (n->comment) free(n->comment); - - next = n->next; - free(n); - n = next; - } -} - -/** -* Replaces all offsets in a string with the names that were given to those offsets -* The caller needs to make sure that str is large enough. -* -* @param list NL list of address definitions -* @param str The string where replacing takes place. -* @param addressesLog Vector for collecting addresses that were replaced by names -**/ -void replaceNames(Name* list, char* str, std::vector* addressesLog) -{ - static char buff[1001]; - char* pos; - char* src; - - Name* listRoot = list; - - while (list) - { - if (list->name) - { - //don't do replacements if we're not mapped appropriately - if(listRoot != getNamesPointerForAddress(list->offsetNumeric)) - { - list = list->next; - continue; - } - - *buff = 0; - src = str; - - int addrlen = 5; - - //zero 30-nov-2017 - handle zero page differently - char test[64]; - strcpy(test, list->offset); - if(!strncmp(test,"$00",3)) - { - strcpy(test,"$"); - strcat(test,list->offset+3); - addrlen = 3; - } - - for(;;) - { - int myAddrlen = addrlen; - - //first, check for the (potentially) shortened zero page address - pos = strstr(src, test); - if(pos) - { - //next character must not be another part of a longer hex value - if(pos[3] >= '0' && pos[3] <= '9') pos = NULL; - else if(pos[3] >= 'A' && pos[3] <= 'F') pos = NULL; - } - if(!pos) - { - //if we didnt find that, check for the full $ABCD thing - pos = strstr(src, list->offset); - //if we didnt find that, bail - if(!pos) break; - - //otherwise we found a length-5 token to replace - myAddrlen = 5; - } - - //special hack: sometimes we have #$00, and we dont want to replace that. - //(this isn't a problem with $0000 because on the 6502 we ... dont.. have ... #$XXXX ? - //honestly, I don't know anything about this CPU. don't tell anyone. - //I'm just inferring from the fact that the algorithm previously relied on that assumption... - if(pos[-1] == '#') - { - src = pos + myAddrlen; - continue; - } - - //zero 30-nov-2017 - change how this works so we can display the address still - //append beginning part, plus addrlen for the offset - strncat(buff,src,pos-src+myAddrlen); - //append a space - strcat(buff," "); - //append the label - strcat(buff, list->name); - //begin processing after that offset - src = pos + myAddrlen; - - if (addressesLog) - addressesLog->push_back(list->offsetNumeric); - } - // if any offsets were changed, replace str by buff - if (*buff) - { - strcat(buff, src); - // replace whole str - strcpy(str, buff); - } - } - list = list->next; - } - - for (int i = 0; i < RegNameCount; i++) { - if (!symbRegNames) break; - // copypaste, because Name* is too complex to abstract - *buff = 0; - src = str; - - while (pos = strstr(src, RegNames[i].offset)) { - *pos = 0; - strcat(buff, src); - strcat(buff, RegNames[i].name); - src = pos + 5; - } - if (*buff) { - strcat(buff, src); - strcpy(str, buff); - } - } -} - -/** -* Searches an address node in a list of address nodes. The found node -* has the same offset as the passed parameter offs. -* -* @param node The address node list -* @offs The offset to search -* @return The node that has the given offset or 0. -**/ -Name* findNode(Name* node, const char* offset) -{ - while (node) - { - if (!strcmp(node->offset, offset)) - return node; - - node = node->next; - } - return 0; -} -// same, but with offsetNumeric -Name* findNode(Name* node, uint16 offsetNumeric) -{ - while (node) - { - if (node->offsetNumeric == offsetNumeric) - return node; - - node = node->next; - } - return 0; -} - -char* generateNLFilenameForAddress(uint16 address) -{ - if (address < 0x8000) - { - // The NL file for the RAM addresses has the name nesrom.nes.ram.nl - strcpy(NLfilename, mass_replace(LoadedRomFName, "|", ".").c_str()); - strcat(NLfilename, ".ram.nl"); - } else - { - int bank = getBank(address); - #ifdef DW3_NL_0F_1F_HACK - if(bank == 0x0F) - bank = 0x1F; - #endif - sprintf(NLfilename, "%s.%X.nl", mass_replace(LoadedRomFName, "|", ".").c_str(), bank); - } - return NLfilename; -} -Name* getNamesPointerForAddress(uint16 address) -{ - if(address >= 0x8000) - { - return pageNames[RomPageIndexForAddress(address)]; - } - else - { - return ramBankNames; - } -} -void setNamesPointerForAddress(uint16 address, Name* newNode) -{ - if (address >= 0x8000) - { - pageNames[RomPageIndexForAddress(address)] = newNode; - } - else - { - ramBankNames = newNode; - } -} - -/** -* Loads the necessary NL files -**/ -// TODO: instead of loading from disk every time the "loadedBankNames" changes, it's better to cache loaded linkedlists in memory -void loadNameFiles() -{ - int cb; - - if (!ramBankNamesLoaded) - { - ramBankNamesLoaded = true; - // load RAM names - if (ramBankNames) - free(ramBankNames); - - // Load the address descriptions for the RAM addresses - ramBankNames = parseNameFile(generateNLFilenameForAddress(0x0000)); - } - - int nPages = 1<<(15-debuggerPageSize); - - for(int i=0;i> bookmarks; // first:address second:name - -/** -* Returns the bookmark address of a CPU bookmark identified by its index. -* The caller must make sure that the index is valid. -* -* @param hwnd HWND of the debugger window -* @param index Index of the bookmark -**/ -unsigned int getBookmarkAddress(unsigned int index) -{ - if (index < bookmarks.size()) - return bookmarks[index].first; - else - return 0; -} - -/** -* Takes the offset from the debugger bookmark edit field and adds a debugger -* bookmark with that offset to the bookmark list if the offset is valid. -* -* @param hwnd HWMD of the debugger window -**/ -void AddDebuggerBookmark(HWND hwnd) -{ - char buffer[5] = {0}; - GetDlgItemText(hwnd, IDC_DEBUGGER_BOOKMARK, buffer, 5); - int address = offsetStringToInt(BT_C, buffer); - // Make sure the offset is valid - if (address == -1 || address > 0xFFFF) - { - MessageBox(hwnd, "Invalid offset", "Error", MB_OK | MB_ICONERROR); - return; - } -/* - int index = 0; - for (std::vector>::iterator it = bookmarks.begin(); it != bookmarks.end(); ++it) - { - if (it->first == address) - { - // select this bookmark to notify it already have a bookmark - SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_SETCURSEL, index, 0); - return; - } - ++index; - } - */ - - int index = bookmarks.size(); - // try to find Symbolic name for this address - Name* node = findNode(getNamesPointerForAddress(address), address); - std::pair bookmark(address, node && node->name ? node->name : ""); - if (DialogBoxParam(fceu_hInstance, "NAMEBOOKMARKDLGDEBUGGER", hwnd, nameDebuggerBookmarkCallB, (LPARAM)&bookmark)) - { - bookmarks.push_back(bookmark); - // add new item to ListBox - char buffer[256]; - sprintf(buffer, "%04X %s", bookmark.first, bookmark.second.c_str()); - SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_ADDSTRING, 0, (LPARAM)buffer); - // select this item - SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_SETCURSEL, index, 0); - } - -} - -/** -* Removes a debugger bookmark -* -* @param hwnd HWND of the debugger window -**/ -void DeleteDebuggerBookmark(HWND hwnd) -{ - // Get the selected bookmark - unsigned int selectedItem = SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_GETCURSEL, 0, 0); - - if (selectedItem == LB_ERR) - { - MessageBox(hwnd, "Please select a bookmark from the list", "Error", MB_OK | MB_ICONERROR); - return; - } else - { - // Erase the selected bookmark - bookmarks.erase(bookmarks.begin() + selectedItem); - SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_DELETESTRING, selectedItem, 0); - // Select next item - if (selectedItem >= (bookmarks.size() - 1)) - // select last item - SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_SETCURSEL, bookmarks.size() - 1, 0); - else - SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_SETCURSEL, selectedItem, 0); - - } -} - -void EditDebuggerBookmark(HWND hwnd) -{ - // Get the selected bookmark - int selectedItem = SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_GETCURSEL, 0, 0); - if (selectedItem == LB_ERR || selectedItem >= (int)bookmarks.size()) - { - MessageBox(hwnd, "Please select a bookmark from the list", "Error", MB_OK | MB_ICONERROR); - return; - } else - { - std::pair bookmark = bookmarks[selectedItem]; - if (!bookmark.second.size()) - { - // try to find the same address in bookmarks - for (int i = bookmarks.size() - 1; i>= 0; i--) - { - if (i != selectedItem && bookmarks[i].first == bookmarks[selectedItem].first && bookmarks[i].second.size()) - { - bookmark.second = bookmarks[i].second; - break; - } - } - } - // Show the bookmark name dialog - if (DialogBoxParam(fceu_hInstance, "NAMEBOOKMARKDLGDEBUGGER", hwnd, nameDebuggerBookmarkCallB, (LPARAM)&bookmark)) - { - // Rename the selected bookmark - bookmarks[selectedItem] = bookmark; - SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_DELETESTRING, selectedItem, 0); - char buffer[256]; - sprintf(buffer, "%04X %s", bookmarks[selectedItem].first, bookmarks[selectedItem].second.c_str()); - SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_INSERTSTRING, selectedItem, (LPARAM)buffer); - // Reselect the item (selection disappeared when it was deleted) - SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_SETCURSEL, selectedItem, 0); - } - } -} - -void DeleteAllDebuggerBookmarks() -{ - bookmarks.resize(0); -} - -void FillDebuggerBookmarkListbox(HWND hwnd) -{ - SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_RESETCONTENT, 0, 0); - char buffer[256]; - for (unsigned int i = 0; i < bookmarks.size(); ++i) - { - sprintf(buffer, "%04X %s", bookmarks[i].first, bookmarks[i].second.c_str()); - SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_ADDSTRING, 0, (LPARAM)buffer); - } -} - -/** -* Shows the code at the bookmark address in the disassembly window -* -* @param hwnd HWND of the debugger window -**/ -void GoToDebuggerBookmark(HWND hwnd) -{ - int selectedItem = SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_GETCURSEL, 0, 0); - // If no bookmark is selected just return - if (selectedItem == LB_ERR) return; - unsigned int n = getBookmarkAddress(selectedItem); - Disassemble(hwnd, IDC_DEBUGGER_DISASSEMBLY, IDC_DEBUGGER_DISASSEMBLY_VSCR, n); -} - -INT_PTR CALLBACK SymbolicNamingCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch(uMsg) - { - case WM_INITDIALOG: - { - CenterWindow(hwndDlg); - unsigned int newAddress = lParam; - // filename - generateNLFilenameForAddress(newAddress); - SetDlgItemText(hwndDlg, IDC_SYMBOLIC_FILENAME, NLfilename); - // offset - sprintf(temp_chr, "$%04X", newAddress); - SetDlgItemText(hwndDlg, IDC_SYMBOLIC_ADDRESS, temp_chr); - Name* node = findNode(getNamesPointerForAddress(newAddress), newAddress); - if (node) - { - if (node->name && node->name[0]) - SetDlgItemText(hwndDlg, IDC_SYMBOLIC_NAME, node->name); - if (node->comment && node->comment[0]) - SetDlgItemText(hwndDlg, IDC_SYMBOLIC_COMMENT, node->comment); - } - SetDlgItemText(hwndDlg, IDC_EDIT_SYMBOLIC_ARRAY, "10"); - SetDlgItemText(hwndDlg, IDC_EDIT_SYMBOLIC_INIT, "0"); - // set focus to IDC_SYMBOLIC_NAME - SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_SYMBOLIC_NAME), true); - //always set the limits - SendDlgItemMessage(hwndDlg, IDC_SYMBOLIC_NAME, EM_SETLIMITTEXT, NL_MAX_NAME_LEN, 0); - SendDlgItemMessage(hwndDlg, IDC_SYMBOLIC_COMMENT, EM_SETLIMITTEXT, NL_MAX_MULTILINE_COMMENT_LEN, 0); - SendDlgItemMessage(hwndDlg, IDC_EDIT_SYMBOLIC_ARRAY, EM_SETLIMITTEXT, 2, 0); - SendDlgItemMessage(hwndDlg, IDC_EDIT_SYMBOLIC_INIT, EM_SETLIMITTEXT, 2, 0); - DefaultEditCtrlProc = (WNDPROC)SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_EDIT_SYMBOLIC_ARRAY), GWLP_WNDPROC, (LONG_PTR)FilterEditCtrlProc); - DefaultEditCtrlProc = (WNDPROC)SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_EDIT_SYMBOLIC_INIT), GWLP_WNDPROC, (LONG_PTR)FilterEditCtrlProc); - - CheckDlgButton(hwndDlg, IDC_CHECK_SYMBOLIC_NAME_OVERWRITE, BST_CHECKED); - CheckDlgButton(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_ARRAY_HEAD, BST_CHECKED); - break; - } - case WM_CLOSE: - case WM_QUIT: - break; - case WM_COMMAND: - { - switch(HIWORD(wParam)) - { - case BN_CLICKED: - { - switch(LOWORD(wParam)) - { - case IDC_CHECK_SYMBOLIC_DELETE: - { - bool delete_mode = IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_DELETE) == BST_CHECKED; - EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC_SYMBOLIC_NAME), !delete_mode); - EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC_SYMBOLIC_COMMENT), !delete_mode); - EnableWindow(GetDlgItem(hwndDlg, IDC_SYMBOLIC_NAME), !delete_mode); - EnableWindow(GetDlgItem(hwndDlg, IDC_SYMBOLIC_COMMENT), !delete_mode); - EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK_SYMBOLIC_NAME_OVERWRITE), !delete_mode); - if (delete_mode) - { - EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_OVERWRITE), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_ARRAY_HEAD), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC_SYMBOLIC_INIT), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT_SYMBOLIC_INIT), FALSE); - } else - { - bool array_chk = IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_ARRAY) == BST_CHECKED; - bool comment_head_only = IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_ARRAY_HEAD) == BST_CHECKED; - EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_ARRAY_HEAD), array_chk); - EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK_SYMBOLIC_NAME_OVERWRITE), array_chk); - EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_OVERWRITE), array_chk && !comment_head_only); - EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC_SYMBOLIC_INIT), array_chk); - EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT_SYMBOLIC_INIT), array_chk); - } - break; - } - case IDC_CHECK_SYMBOLIC_ARRAY: - { - bool array_chk = IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_ARRAY) == BST_CHECKED; - bool delete_mode = IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_DELETE) == BST_CHECKED; - bool comment_head_only = IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_ARRAY_HEAD) == BST_CHECKED; - EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK_SYMBOLIC_NAME_OVERWRITE), array_chk && !delete_mode); - EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_ARRAY_HEAD), array_chk && !delete_mode); - EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_OVERWRITE), array_chk && !comment_head_only && !delete_mode); - EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT_SYMBOLIC_ARRAY), array_chk); - EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC_SYMBOLIC_BYTE), array_chk); - EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC_SYMBOLIC_INIT), array_chk && !delete_mode); - EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT_SYMBOLIC_INIT), array_chk && !delete_mode); - break; - } - case IDC_CHECK_SYMBOLIC_COMMENT_ARRAY_HEAD: - { - bool comment_head_only = IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_ARRAY_HEAD) == BST_CHECKED; - EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_OVERWRITE), !comment_head_only); - break; - } - case IDOK: - { - unsigned int newAddress = 0; - char newOffset[6] = { 0 }; - GetDlgItemText(hwndDlg, IDC_SYMBOLIC_ADDRESS, newOffset, 6); - if (sscanf(newOffset, "%*[$]%4X", &newAddress) != EOF) - { - - unsigned int arraySize = 0; - unsigned int arrayInit = 0; - if (IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_ARRAY) == BST_CHECKED) - { - char strArraySize[6] = { 0 }; - GetDlgItemText(hwndDlg, IDC_EDIT_SYMBOLIC_ARRAY, strArraySize, 6); - if (*strArraySize) - sscanf(strArraySize, "%4X", &arraySize); - } - - if (IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_DELETE) == BST_UNCHECKED) - { - char newName[NL_MAX_NAME_LEN + 1] = { 0 }; - GetDlgItemText(hwndDlg, IDC_SYMBOLIC_NAME, newName, NL_MAX_NAME_LEN + 1); - char newComment[NL_MAX_MULTILINE_COMMENT_LEN + 1] = { 0 }; - GetDlgItemText(hwndDlg, IDC_SYMBOLIC_COMMENT, newComment, NL_MAX_MULTILINE_COMMENT_LEN + 1); - - char strArrayInit[6] = { 0 }; - GetDlgItemText(hwndDlg, IDC_EDIT_SYMBOLIC_INIT, strArrayInit, 6); - if (*strArrayInit) - sscanf(strArrayInit, "%4X", &arrayInit); - - AddNewSymbolicName(newAddress, newOffset, newName, newComment, arraySize, arrayInit, IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_NAME_OVERWRITE) == BST_CHECKED, IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_ARRAY_HEAD) == BST_CHECKED, IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_OVERWRITE) == BST_CHECKED); - } else - DeleteSymbolicName(newAddress, arraySize); - - WriteNameFileToDisk(generateNLFilenameForAddress(newAddress), getNamesPointerForAddress(newAddress)); - } - EndDialog(hwndDlg, 1); - break; - } - case IDCANCEL: - { - EndDialog(hwndDlg, 0); - break; - } - } - break; - } - } - break; - } - } - return FALSE; -} - -// returns true if user pressed OK, false if Cancel -bool DoSymbolicDebugNaming(int offset, HWND parentHWND) -{ - if (!FCEUI_EmulationPaused()) - FCEUI_ToggleEmulationPause(); - loadNameFiles(); - if (DialogBoxParam(fceu_hInstance, MAKEINTRESOURCE(IDD_SYMBOLIC_DEBUG_NAMING), parentHWND, SymbolicNamingCallB, offset)) - return true; - return false; -} - -void AddNewSymbolicName(uint16 newAddress, char* newOffset, char* newName, char* newComment, int size, int init, bool nameOverwrite, bool commentHeadOnly, bool commentOverwrite) -{ - // remove all delimiterChars from name and comment - char* pos = newName; - while (pos < newName + strlen(newName)) - { - pos = strstr(pos, delimiterChar); - if (pos) - strcpy(pos, pos + 1); - else - break; - } - pos = newComment; - while (pos < newComment + strlen(newComment)) - { - pos = strstr(pos, delimiterChar); - if (pos) - strcpy(pos, pos + 1); - else - break; - } - - if (*newName || *newComment) - { - int i = 0; - char* tmpNewOffset = (char*)malloc(strlen(newOffset) + 1); - strcpy(tmpNewOffset, newOffset); - uint16 tmpNewAddress = newAddress; - int tmpInit = init; - do { - Name* node = getNamesPointerForAddress(tmpNewAddress); - if (!node) - { - node = (Name*)malloc(sizeof(Name)); - node->offset = (char*)malloc(strlen(tmpNewOffset) + 1); - strcpy(node->offset, tmpNewOffset); - node->offsetNumeric = tmpNewAddress; - - // Node name - if (strlen(newName)) - { - if (size) - { - char arr_index[16]; - sprintf(arr_index, "[%X]", tmpInit); - node->name = (char*)malloc(strlen(newName) + strlen(arr_index) + 1); - strcpy(node->name, newName); - strcat(node->name, arr_index); - } - else - { - node->name = (char*)malloc(strlen(newName) + 1); - strcpy(node->name, newName); - } - } - else - node->name = 0; - - if ((i == 0 || !commentHeadOnly) && strlen(newComment)) - { - node->comment = (char*)malloc(strlen(newComment) + 1); - strcpy(node->comment, newComment); - } - else - node->comment = 0; - - node->next = 0; - setNamesPointerForAddress(tmpNewAddress, node); - } - else - { - while (node) - { - if (node->offsetNumeric == tmpNewAddress) - { - // found matching address, proccessing its name and comment based on the configuration - if ((i == 0 || nameOverwrite) && node->name) - { - free(node->name); - node->name = 0; - } - if (!node->name && strlen(newName)) - { - if (size) - { - char arr_index[16]; - sprintf(arr_index, "[%X]", tmpInit); - node->name = (char*)malloc(strlen(newName) + strlen(arr_index) + 1); - strcpy(node->name, newName); - strcat(node->name, arr_index); - } - else - { - node->name = (char*)malloc(strlen(newName) + 1); - strcpy(node->name, newName); - } - } - - if ((i == 0 || !commentHeadOnly && commentOverwrite) && node->comment) - { - free(node->comment); - node->comment = 0; - } - if (!node->comment && strlen(newComment)) - { - node->comment = (char*)malloc(strlen(newComment) + 1); - strcpy(node->comment, newComment); - } - - break; - } - - if (node->next) - node = node->next; - else { - // this is the last node in the list - so just append the address - Name* newNode = (Name*)malloc(sizeof(Name)); - newNode->offset = (char*)malloc(strlen(tmpNewOffset) + 1); - strcpy(newNode->offset, tmpNewOffset); - newNode->offsetNumeric = tmpNewAddress; - - // Node name - if (strlen(newName)) - { - if (size) - { - char arr_index[16]; - sprintf(arr_index, "[%X]", tmpInit); - newNode->name = (char*)malloc(strlen(newName) + strlen(arr_index) + 1); - strcpy(newNode->name, newName); - strcat(newNode->name, arr_index); - } - else - { - newNode->name = (char*)malloc(strlen(newName) + 1); - strcpy(newNode->name, newName); - } - } - else - newNode->name = 0; - - if ((i == 0 || !commentHeadOnly) && strlen(newComment)) - { - newNode->comment = (char*)malloc(strlen(newComment) + 1); - strcpy(newNode->comment, newComment); - } - else - newNode->comment = 0; - - newNode->next = 0; - node->next = newNode; - break; - } - } - } - ++tmpNewAddress; - ++tmpInit; - sprintf(tmpNewOffset, "$%04X", tmpNewAddress); - } while (++i < size); - } -} - -void DeleteSymbolicName(uint16 address, int size) -{ - int i = 0; - uint16 tmpAddress = address; - do { - Name* prev = 0; - Name* initialNode = getNamesPointerForAddress(tmpAddress); - Name* node = initialNode; - while (node) - { - if (node->offsetNumeric == tmpAddress) - { - if (node->offset) - free(node->offset); - if (node->name) - free(node->name); - if (prev) - prev->next = node->next; - if (node == initialNode) - setNamesPointerForAddress(tmpAddress, node->next); - free(node); - break; - } - prev = node; - node = node->next; - } - ++tmpAddress; - } while (++i < size); -} - -void WriteNameFileToDisk(const char* filename, Name* node) -{ - FILE* f = fopen(filename, "wb"); - char tempComment[NL_MAX_MULTILINE_COMMENT_LEN + 10]; - if (f) - { - char tempString[10 + 1 + NL_MAX_NAME_LEN + 1 + NL_MAX_MULTILINE_COMMENT_LEN + 1]; - while (node) - { - strcpy(tempString, node->offset); - strcat(tempString, delimiterChar); - if (node->name) - strcat(tempString, node->name); - strcat(tempString, delimiterChar); - if (node->comment) - { - // dump multiline comment - strcpy(tempComment, node->comment); - char* remainder_pos = tempComment; - char* crlf_pos = strstr(tempComment, "\r\n"); - while (crlf_pos) - { - *crlf_pos = 0; - strcat(tempString, remainder_pos); - strcat(tempString, "\r\n\\"); - crlf_pos = remainder_pos = crlf_pos + 2; - crlf_pos = strstr(crlf_pos, "\r\n"); - } - strcat(tempString, remainder_pos); - strcat(tempString, "\r\n"); - } else - { - strcat(tempString, "\r\n"); - } - // write to the file - fwrite(tempString, 1, strlen(tempString), f); - node = node->next; - } - fclose(f); - } -} - -INT_PTR CALLBACK nameDebuggerBookmarkCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - static std::pair* debuggerBookmark; - - switch (uMsg) - { - case WM_INITDIALOG: - { - // Limit bookmark descriptions to 50 characters - SendDlgItemMessage(hwndDlg, IDC_BOOKMARK_DESCRIPTION, EM_SETLIMITTEXT, 50, 0); - - // Limit the address text - SendDlgItemMessage(hwndDlg, IDC_BOOKMARK_ADDRESS, EM_SETLIMITTEXT, 4, 0); - DefaultEditCtrlProc = (WNDPROC)SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_BOOKMARK_ADDRESS), GWLP_WNDPROC, (LONG_PTR)FilterEditCtrlProc); - - debuggerBookmark = (std::pair*)lParam; - char addr[8]; - sprintf(addr, "%04X", debuggerBookmark->first); - - // Set address and description - sprintf(addr, "%04X", debuggerBookmark->first); - SetDlgItemText(hwndDlg, IDC_BOOKMARK_ADDRESS, addr); - SetDlgItemText(hwndDlg, IDC_BOOKMARK_DESCRIPTION, debuggerBookmark->second.c_str()); - - // Set focus to the edit field. - SetFocus(GetDlgItem(hwndDlg, IDC_BOOKMARK_DESCRIPTION)); - SendDlgItemMessage(hwndDlg, IDC_BOOKMARK_DESCRIPTION, EM_SETSEL, 0, 52); - - break; - } - case WM_QUIT: - case WM_CLOSE: - EndDialog(hwndDlg, 0); - break; - case WM_COMMAND: - switch (HIWORD(wParam)) - { - case BN_CLICKED: - switch (LOWORD(wParam)) - { - case IDOK: - { - char addr_str[8]; - GetDlgItemText(hwndDlg, IDC_BOOKMARK_ADDRESS, addr_str, 8); - sscanf(addr_str, "%X", &debuggerBookmark->first); - - if (debuggerBookmark->first > 0xFFFE) - { - // if the address is out of range - char errmsg[64]; - sprintf(errmsg, "The address must be in range of 0-%X", 0xFFFE); - MessageBox(hwndDlg, errmsg, "Address out of range", MB_OK | MB_ICONERROR); - SetFocus(GetDlgItem(hwndDlg, IDC_BOOKMARK_ADDRESS)); - return FALSE; - } - - /* - extern std::vector> bookmarks; - for (std::vector>::iterator it = bookmarks.begin(); it != bookmarks.end(); ++it) - { - if (it->first == debuggerBookmark->first && it->second == debuggerBookmark->second) - { - // if the address already have a bookmark - MessageBox(hwndDlg, "This address already have a bookmark", "Bookmark duplicated", MB_OK | MB_ICONASTERISK); - return FALSE; - } - } - */ - - // Update the description - char description[51]; - GetDlgItemText(hwndDlg, IDC_BOOKMARK_DESCRIPTION, description, 50); - debuggerBookmark->second = description; - EndDialog(hwndDlg, 1); - break; - } - case IDCANCEL: - EndDialog(hwndDlg, 0); - } - } - } - - return FALSE; -} - +/* FCEUXD SP - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2005 Sebastian Porst + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "common.h" +#include "utils/xstring.h" +#include "debuggersp.h" +#include "debugger.h" +#include "../../fceu.h" +#include "../../debug.h" +#include "../../conddebug.h" + +#include +#include +#include + +int GetNesFileAddress(int A); + +inline int RomPageIndexForAddress(int addr) { return (addr-0x8000)>>(debuggerPageSize); } + +//old +//Name* lastBankNames = 0; +//Name* loadedBankNames = 0; + +//new +Name* pageNames[32] = {0}; //the maximum number of pages we could have is 32, based on 1KB debuggerPageSize + +//old +//int lastBank = -1; +//int loadedBank = -1; + +//new +int pageNumbersLoaded[32] = { + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +}; + +Name* ramBankNames = 0; +bool ramBankNamesLoaded = false; + +extern char LoadedRomFName[2048]; +char NLfilename[2048]; +bool symbDebugEnabled = true; +bool symbRegNames = true; +int debuggerWasActive = 0; +char temp_chr[40] = {0}; +char delimiterChar[2] = "#"; + +INT_PTR CALLBACK nameDebuggerBookmarkCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); +extern WNDPROC DefaultEditCtrlProc; +extern LRESULT APIENTRY FilterEditCtrlProc(HWND hDlg, UINT uMsg, WPARAM wP, LPARAM lP); + +MemoryMappedRegister RegNames[] = { + {"$2000", "PPU_CTRL"}, + {"$2001", "PPU_MASK"}, + {"$2002", "PPU_STATUS"}, + {"$2003", "PPU_OAM_ADDR"}, + {"$2004", "PPU_OAM_DATA"}, + {"$2005", "PPU_SCROLL"}, + {"$2006", "PPU_ADDRESS"}, + {"$2007", "PPU_DATA"}, + {"$4000", "SQ1_VOL"}, + {"$4001", "SQ1_SWEEP"}, + {"$4002", "SQ1_LO"}, + {"$4003", "SQ1_HI"}, + {"$4004", "SQ2_VOL"}, + {"$4005", "SQ2_SWEEP"}, + {"$4006", "SQ2_LO"}, + {"$4007", "SQ2_HI"}, + {"$4008", "TRI_LINEAR"}, +// {"$4009", "UNUSED"}, + {"$400A", "TRI_LO"}, + {"$400B", "TRI_HI"}, + {"$400C", "NOISE_VOL"}, +// {"$400D", "UNUSED"}, + {"$400E", "NOISE_LO"}, + {"$400F", "NOISE_HI"}, + {"$4010", "DMC_FREQ"}, + {"$4011", "DMC_RAW"}, + {"$4012", "DMC_START"}, + {"$4013", "DMC_LEN"}, + {"$4014", "OAM_DMA"}, + {"$4015", "APU_STATUS"}, + {"$4016", "JOY1"}, + {"$4017", "JOY2_FRAME"} +}; + +int RegNameCount = sizeof(RegNames)/sizeof(MemoryMappedRegister); + +/** +* Tests whether a char is a valid hexadecimal character. +* +* @param c The char to test +* @return True or false. +**/ +int isHex(char c) +{ + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); +} + +/** +* Parses a line from a NL file. +* @param line The line to parse +* @param n The name structure to write the information to +* +* @return 0 if everything went OK. Otherwise an error code is returned. +**/ +int parseLine(char* line, Name* n) +{ + char* pos; + int llen; + + // Check parameters + + if (!line) + { + MessageBox(0, "Invalid parameter \"line\" in function parseLine", "Error", MB_OK | MB_ICONERROR); + return 1; + } + + if (!n) + { + MessageBox(0, "Invalid parameter \"n\" in function parseLine", "Error", MB_OK | MB_ICONERROR); + return 2; + } + + // Allow empty lines + if (*line == '\r' || *line == '\n') + { + return -1; + } + + // Attempt to tokenize the given line + + pos = strstr(line, delimiterChar); + + if (!pos) + { + // Found an invalid line + return 3; + } + + // Check if the first tokenized part (the offset) is valid + + *pos = 0; + + llen = (int)strlen(line); + + if (llen == 5) // Offset size of normal lines of the form $XXXX + { + if (line[0] != '$' + || !isHex(line[1]) + || !isHex(line[2]) + || !isHex(line[3]) + || !isHex(line[4]) + ) + { + return 4; + } + } + else if (llen >= 7) // Offset size of array definition lines of the form $XXXX/YY + { + int i; + if (line[0] != '$' + || !isHex(line[1]) + || !isHex(line[2]) + || !isHex(line[3]) + || !isHex(line[4]) + || line[5] != '/' + ) + { + return 5; + } + + for (i=6;line[i];i++) + { + if (!isHex(line[i])) + { + return 6; + } + } + } + else // Lines that have an invalid size + { + return 7; + } + + + // TODO: Validate if the offset is in the correct NL file. + + // After validating the offset it's OK to store it + n->offset = (char*)malloc(strlen(line) + 1); + strcpy(n->offset, line); + line = pos + 1; + + // Attempt to tokenize the string again to find the name of the address + + pos = strstr(line, delimiterChar); + + if (!pos) + { + // Found an invalid line + return 8; + } + + *pos = 0; + + if (*line) + { + if (strlen(line) > NL_MAX_NAME_LEN) + line[NL_MAX_NAME_LEN + 1] = 0; + n->name = (char*)malloc(strlen(line) + 1); + strcpy(n->name, line); + } + else + { + // Properly initialize zero-length names too + n->name = 0; + } + + // Now it's clear that the line was valid. The rest of the line is the comment + // that belongs to that line. Once again a real string is required though. + + line = pos + 1; + + if ((u8)*line > 0x0D) + { + if (strlen(line) > NL_MAX_MULTILINE_COMMENT_LEN) + line[NL_MAX_MULTILINE_COMMENT_LEN + 1] = 0; + // remove all backslashes after \r\n + char* crlf_pos = strstr(line, "\r\n\\"); + while (crlf_pos) + { + strcpy(crlf_pos + 2, crlf_pos + 3); + crlf_pos = strstr(crlf_pos + 2, "\r\n\\"); + } + + n->comment = (char*)malloc(strlen(line) + 1); + strcpy(n->comment, line); + } + else + { + // Properly initialize zero-length comments too + n->comment = 0; + } + + // Everything went fine. + return 0; +} + +/** +* Parses an array of lines read from a NL file. +* @param lines The lines to parse +* @param filename The name of the file the lines were read from. +* +* @return A pointer to the first Name structure built from the parsed lines. +**/ +Name* parse(char* lines, const char* filename) +{ + char* pos, *size; + Name* prev = 0, *cur, *first = 0; + + // Check the parameters + if (!lines) + { + MessageBox(0, "Invalid parameter \"lines\" in function parse", "Error", MB_OK | MB_ICONERROR); + return 0; + } + + if (!filename) + { + MessageBox(0, "Invalid parameter \"filename\" in function parse", "Error", MB_OK | MB_ICONERROR); + return 0; + } + + // Begin the actual parsing + + do + { + int fail; + + // Allocate a name structure to hold the parsed data from the next line + + cur = (Name*)malloc(sizeof(Name)); + cur->offsetNumeric = 0; + cur->offset = 0; + cur->next = 0; + cur->name = 0; + cur->comment = 0; + + pos = lines; + + // This first loop attempts to read potential multi-line comments and add them + // into a single comment. + for(;;) + { + // Get the end of the next line + pos = strstr(pos, "\n"); + + // If there's no end of line or if the next line does not begin with a \ character + // we can stop. + if (!pos || pos[1] != '\\') break; + + // At this point we have the following situation. pos[0] and pos[1] can be overwritten + /* + pos -1 0 1 2 + ? \n \ ? + */ + + // \r\n is needed in text boxes + if (pos[-1] != '\r') + { + pos[0] = '\r'; + pos[1] = '\n'; + } + pos += 2; + } + + if (!pos) + { + // All lines were parsed + break; + } + + if (pos[-1] == '\r') + pos[-1] = 0; + else + *pos = 0; + + // Attempt to parse the current line + fail = parseLine(lines, cur); + + if (fail == -1) + { + continue; + } else if (fail) + { + // Show an error to allow the user to correct the defect line + const char* fmtString = "Error (Code: %d): Invalid line \"%s\" in NL file \"%s\"\n"; + char* msg = (char*)malloc(strlen(fmtString) + 8 + strlen(lines) + strlen(filename) + 1); + sprintf(msg, fmtString, fail, lines, filename); + MessageBox(0, msg, "Error", MB_OK | MB_ICONERROR); + free(msg); + lines = pos + 1; + //MessageBox(0, lines, "Error", MB_OK | MB_ICONERROR); + continue; + } + + lines = pos + 1; + + // Check if the line is an array definition line + + size = strstr(cur->offset, "/"); + + if (size && cur->name) // Array definition line + { + int arrlen, offset; + + *size = 0; + + // Attempt to read the length of the array and the array offset + if (sscanf(size + 1, "%x", &arrlen) > 0 && sscanf(cur->offset + 1, "%x", &offset) > 0) + { + Name* nn = 0; + int i; + + // Create a node for each element of the array + for (i=0;i<=arrlen;i++) + { + char numbuff[10] = {0}; + + nn = (Name*)malloc(sizeof(Name)); + nn->next = 0; + + // The comment is the same for each array element + nn->comment = strdup(cur->comment); + + // The offset of the node + nn->offset = (char*)malloc(6); + sprintf(nn->offset, "$%04X", offset + i); + nn->offsetNumeric = offset + i; + + // The name of an array address is of the form NAME[INDEX] + sprintf(numbuff, "[%X]", i); + + nn->name = (char*)malloc(strlen(cur->name) + strlen(numbuff) + 1); + strcpy(nn->name, cur->name); + strcat(nn->name, numbuff); + + // Add the new node to the list of address nodes + if (prev) + { + prev->next = nn; + prev = prev->next; + } else + { + first = prev = nn; + } + printf("nnoffset: %s, %d\n", nn->offset, nn->offsetNumeric); + } + + // Free the allocated node + free(cur->name); + free(cur->comment); + free(cur->offset); + free(cur); + + cur = nn; + } + else + { + // I don't think it's possible to get here as the validity of + // offset and array size has already been validated in parseLine + continue; + } + } else + { + unsigned int tempOffset; + sscanf(cur->offset, "%*[$]%4X", &(tempOffset)); + cur->offsetNumeric = (uint16)tempOffset; + // Add the node to the list of address nodes + if (prev) + { + prev->next = cur; + prev = prev->next; + } + else + { + first = prev = cur; + } + } + } while (pos); + + // Return the first node in the list of address nodes + return first; +} + +/** +* Load and parse an entire NL file +* @param filename Name of the file to parse +* +* @return A pointer to the first Name structure built from the file data. +**/ +Name* parseNameFile(const char* filename) +{ + char* buffer; + Name* n = 0; + + // Attempt to read the file + FILE* f = fopen(filename, "rb"); + + if (f) + { +// __asm (".byte 0xcc"); + // Get the file size + int lSize; + fseek (f , 0 , SEEK_END); + lSize = ftell(f) + 1; + + // Allocate sufficient buffer space + rewind (f); + + buffer = (char*)malloc(lSize); + + if (buffer) + { + // Read the file and parse it + memset(buffer, 0, lSize); + fread(buffer, 1, lSize - 1, f); + n = parse(buffer, filename); + + fclose(f); + free(buffer); + } + } + + return n; +} + +/** +* Frees an entire list of Name nodes starting with the given node. +* +* @param n The node to start with (0 is a valid value) +**/ +void freeList(Name* n) +{ + Name* next; + + while (n) + { + if (n->offset) free(n->offset); + if (n->name) free(n->name); + if (n->comment) free(n->comment); + + next = n->next; + free(n); + n = next; + } +} + +/** +* Replaces all offsets in a string with the names that were given to those offsets +* The caller needs to make sure that str is large enough. +* +* @param list NL list of address definitions +* @param str The string where replacing takes place. +* @param addressesLog Vector for collecting addresses that were replaced by names +**/ +void replaceNames(Name* list, char* str, std::vector* addressesLog) +{ + static char buff[1001]; + char* pos; + char* src; + + Name* listRoot = list; + + while (list) + { + if (list->name) + { + //don't do replacements if we're not mapped appropriately + if(listRoot != getNamesPointerForAddress(list->offsetNumeric)) + { + list = list->next; + continue; + } + + *buff = 0; + src = str; + + int addrlen = 5; + + //zero 30-nov-2017 - handle zero page differently + char test[64]; + strcpy(test, list->offset); + if(!strncmp(test,"$00",3)) + { + strcpy(test,"$"); + strcat(test,list->offset+3); + addrlen = 3; + } + + for(;;) + { + int myAddrlen = addrlen; + + //first, check for the (potentially) shortened zero page address + pos = strstr(src, test); + if(pos) + { + //next character must not be another part of a longer hex value + if(pos[3] >= '0' && pos[3] <= '9') pos = NULL; + else if(pos[3] >= 'A' && pos[3] <= 'F') pos = NULL; + } + if(!pos) + { + //if we didnt find that, check for the full $ABCD thing + pos = strstr(src, list->offset); + //if we didnt find that, bail + if(!pos) break; + + //otherwise we found a length-5 token to replace + myAddrlen = 5; + } + + //special hack: sometimes we have #$00, and we dont want to replace that. + //(this isn't a problem with $0000 because on the 6502 we ... dont.. have ... #$XXXX ? + //honestly, I don't know anything about this CPU. don't tell anyone. + //I'm just inferring from the fact that the algorithm previously relied on that assumption... + if(pos[-1] == '#') + { + src = pos + myAddrlen; + continue; + } + + //zero 30-nov-2017 - change how this works so we can display the address still + //append beginning part, plus addrlen for the offset + strncat(buff,src,pos-src+myAddrlen); + //append a space + strcat(buff," "); + //append the label + strcat(buff, list->name); + //begin processing after that offset + src = pos + myAddrlen; + + if (addressesLog) + addressesLog->push_back(list->offsetNumeric); + } + // if any offsets were changed, replace str by buff + if (*buff) + { + strcat(buff, src); + // replace whole str + strcpy(str, buff); + } + } + list = list->next; + } + + for (int i = 0; i < RegNameCount; i++) { + if (!symbRegNames) break; + // copypaste, because Name* is too complex to abstract + *buff = 0; + src = str; + + while (pos = strstr(src, RegNames[i].offset)) { + *pos = 0; + strcat(buff, src); + strcat(buff, RegNames[i].name); + src = pos + 5; + } + if (*buff) { + strcat(buff, src); + strcpy(str, buff); + } + } +} + +/** +* Searches an address node in a list of address nodes. The found node +* has the same offset as the passed parameter offs. +* +* @param node The address node list +* @offs The offset to search +* @return The node that has the given offset or 0. +**/ +Name* findNode(Name* node, const char* offset) +{ + while (node) + { + if (!strcmp(node->offset, offset)) + return node; + + node = node->next; + } + return 0; +} +// same, but with offsetNumeric +Name* findNode(Name* node, uint16 offsetNumeric) +{ + while (node) + { + if (node->offsetNumeric == offsetNumeric) + return node; + + node = node->next; + } + return 0; +} + +char* generateNLFilenameForAddress(uint16 address) +{ + if (address < 0x8000) + { + // The NL file for the RAM addresses has the name nesrom.nes.ram.nl + strcpy(NLfilename, mass_replace(LoadedRomFName, "|", ".").c_str()); + strcat(NLfilename, ".ram.nl"); + } else + { + int bank = getBank(address); + #ifdef DW3_NL_0F_1F_HACK + if(bank == 0x0F) + bank = 0x1F; + #endif + sprintf(NLfilename, "%s.%X.nl", mass_replace(LoadedRomFName, "|", ".").c_str(), bank); + } + return NLfilename; +} +Name* getNamesPointerForAddress(uint16 address) +{ + if(address >= 0x8000) + { + return pageNames[RomPageIndexForAddress(address)]; + } + else + { + return ramBankNames; + } +} +void setNamesPointerForAddress(uint16 address, Name* newNode) +{ + if (address >= 0x8000) + { + pageNames[RomPageIndexForAddress(address)] = newNode; + } + else + { + ramBankNames = newNode; + } +} + +/** +* Loads the necessary NL files +**/ +// TODO: instead of loading from disk every time the "loadedBankNames" changes, it's better to cache loaded linkedlists in memory +void loadNameFiles() +{ + int cb; + + if (!ramBankNamesLoaded) + { + ramBankNamesLoaded = true; + // load RAM names + if (ramBankNames) + free(ramBankNames); + + // Load the address descriptions for the RAM addresses + ramBankNames = parseNameFile(generateNLFilenameForAddress(0x0000)); + } + + int nPages = 1<<(15-debuggerPageSize); + + for(int i=0;i> bookmarks; // first:address second:name + +/** +* Returns the bookmark address of a CPU bookmark identified by its index. +* The caller must make sure that the index is valid. +* +* @param hwnd HWND of the debugger window +* @param index Index of the bookmark +**/ +unsigned int getBookmarkAddress(unsigned int index) +{ + if (index < bookmarks.size()) + return bookmarks[index].first; + else + return 0; +} + +/** +* Takes the offset from the debugger bookmark edit field and adds a debugger +* bookmark with that offset to the bookmark list if the offset is valid. +* +* @param hwnd HWMD of the debugger window +**/ +void AddDebuggerBookmark(HWND hwnd) +{ + char buffer[5] = {0}; + GetDlgItemText(hwnd, IDC_DEBUGGER_BOOKMARK, buffer, 5); + int address = offsetStringToInt(BT_C, buffer); + // Make sure the offset is valid + if (address == -1 || address > 0xFFFF) + { + MessageBox(hwnd, "Invalid offset", "Error", MB_OK | MB_ICONERROR); + return; + } +/* + int index = 0; + for (std::vector>::iterator it = bookmarks.begin(); it != bookmarks.end(); ++it) + { + if (it->first == address) + { + // select this bookmark to notify it already have a bookmark + SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_SETCURSEL, index, 0); + return; + } + ++index; + } + */ + + int index = bookmarks.size(); + // try to find Symbolic name for this address + Name* node = findNode(getNamesPointerForAddress(address), address); + std::pair bookmark(address, node && node->name ? node->name : ""); + if (DialogBoxParam(fceu_hInstance, "NAMEBOOKMARKDLGDEBUGGER", hwnd, nameDebuggerBookmarkCallB, (LPARAM)&bookmark)) + { + bookmarks.push_back(bookmark); + // add new item to ListBox + char buffer[256]; + sprintf(buffer, "%04X %s", bookmark.first, bookmark.second.c_str()); + SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_ADDSTRING, 0, (LPARAM)buffer); + // select this item + SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_SETCURSEL, index, 0); + } + +} + +/** +* Removes a debugger bookmark +* +* @param hwnd HWND of the debugger window +**/ +void DeleteDebuggerBookmark(HWND hwnd) +{ + // Get the selected bookmark + unsigned int selectedItem = SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_GETCURSEL, 0, 0); + + if (selectedItem == LB_ERR) + { + MessageBox(hwnd, "Please select a bookmark from the list", "Error", MB_OK | MB_ICONERROR); + return; + } else + { + // Erase the selected bookmark + bookmarks.erase(bookmarks.begin() + selectedItem); + SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_DELETESTRING, selectedItem, 0); + // Select next item + if (selectedItem >= (bookmarks.size() - 1)) + // select last item + SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_SETCURSEL, bookmarks.size() - 1, 0); + else + SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_SETCURSEL, selectedItem, 0); + + } +} + +void EditDebuggerBookmark(HWND hwnd) +{ + // Get the selected bookmark + int selectedItem = SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_GETCURSEL, 0, 0); + if (selectedItem == LB_ERR || selectedItem >= (int)bookmarks.size()) + { + MessageBox(hwnd, "Please select a bookmark from the list", "Error", MB_OK | MB_ICONERROR); + return; + } else + { + std::pair bookmark = bookmarks[selectedItem]; + if (!bookmark.second.size()) + { + // try to find the same address in bookmarks + for (int i = bookmarks.size() - 1; i>= 0; i--) + { + if (i != selectedItem && bookmarks[i].first == bookmarks[selectedItem].first && bookmarks[i].second.size()) + { + bookmark.second = bookmarks[i].second; + break; + } + } + } + // Show the bookmark name dialog + if (DialogBoxParam(fceu_hInstance, "NAMEBOOKMARKDLGDEBUGGER", hwnd, nameDebuggerBookmarkCallB, (LPARAM)&bookmark)) + { + // Rename the selected bookmark + bookmarks[selectedItem] = bookmark; + SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_DELETESTRING, selectedItem, 0); + char buffer[256]; + sprintf(buffer, "%04X %s", bookmarks[selectedItem].first, bookmarks[selectedItem].second.c_str()); + SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_INSERTSTRING, selectedItem, (LPARAM)buffer); + // Reselect the item (selection disappeared when it was deleted) + SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_SETCURSEL, selectedItem, 0); + } + } +} + +void DeleteAllDebuggerBookmarks() +{ + bookmarks.resize(0); +} + +void FillDebuggerBookmarkListbox(HWND hwnd) +{ + SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_RESETCONTENT, 0, 0); + char buffer[256]; + for (unsigned int i = 0; i < bookmarks.size(); ++i) + { + sprintf(buffer, "%04X %s", bookmarks[i].first, bookmarks[i].second.c_str()); + SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_ADDSTRING, 0, (LPARAM)buffer); + } +} + +/** +* Shows the code at the bookmark address in the disassembly window +* +* @param hwnd HWND of the debugger window +**/ +void GoToDebuggerBookmark(HWND hwnd) +{ + int selectedItem = SendDlgItemMessage(hwnd, LIST_DEBUGGER_BOOKMARKS, LB_GETCURSEL, 0, 0); + // If no bookmark is selected just return + if (selectedItem == LB_ERR) return; + unsigned int n = getBookmarkAddress(selectedItem); + Disassemble(hwnd, IDC_DEBUGGER_DISASSEMBLY, IDC_DEBUGGER_DISASSEMBLY_VSCR, n); +} + +INT_PTR CALLBACK SymbolicNamingCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_INITDIALOG: + { + CenterWindow(hwndDlg); + unsigned int newAddress = lParam; + // filename + generateNLFilenameForAddress(newAddress); + SetDlgItemText(hwndDlg, IDC_SYMBOLIC_FILENAME, NLfilename); + // offset + sprintf(temp_chr, "$%04X", newAddress); + SetDlgItemText(hwndDlg, IDC_SYMBOLIC_ADDRESS, temp_chr); + Name* node = findNode(getNamesPointerForAddress(newAddress), newAddress); + if (node) + { + if (node->name && node->name[0]) + SetDlgItemText(hwndDlg, IDC_SYMBOLIC_NAME, node->name); + if (node->comment && node->comment[0]) + SetDlgItemText(hwndDlg, IDC_SYMBOLIC_COMMENT, node->comment); + } + SetDlgItemText(hwndDlg, IDC_EDIT_SYMBOLIC_ARRAY, "10"); + SetDlgItemText(hwndDlg, IDC_EDIT_SYMBOLIC_INIT, "0"); + // set focus to IDC_SYMBOLIC_NAME + SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_SYMBOLIC_NAME), true); + //always set the limits + SendDlgItemMessage(hwndDlg, IDC_SYMBOLIC_NAME, EM_SETLIMITTEXT, NL_MAX_NAME_LEN, 0); + SendDlgItemMessage(hwndDlg, IDC_SYMBOLIC_COMMENT, EM_SETLIMITTEXT, NL_MAX_MULTILINE_COMMENT_LEN, 0); + SendDlgItemMessage(hwndDlg, IDC_EDIT_SYMBOLIC_ARRAY, EM_SETLIMITTEXT, 2, 0); + SendDlgItemMessage(hwndDlg, IDC_EDIT_SYMBOLIC_INIT, EM_SETLIMITTEXT, 2, 0); + DefaultEditCtrlProc = (WNDPROC)SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_EDIT_SYMBOLIC_ARRAY), GWLP_WNDPROC, (LONG_PTR)FilterEditCtrlProc); + DefaultEditCtrlProc = (WNDPROC)SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_EDIT_SYMBOLIC_INIT), GWLP_WNDPROC, (LONG_PTR)FilterEditCtrlProc); + + CheckDlgButton(hwndDlg, IDC_CHECK_SYMBOLIC_NAME_OVERWRITE, BST_CHECKED); + CheckDlgButton(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_ARRAY_HEAD, BST_CHECKED); + break; + } + case WM_CLOSE: + case WM_QUIT: + break; + case WM_COMMAND: + { + switch(HIWORD(wParam)) + { + case BN_CLICKED: + { + switch(LOWORD(wParam)) + { + case IDC_CHECK_SYMBOLIC_DELETE: + { + bool delete_mode = IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_DELETE) == BST_CHECKED; + EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC_SYMBOLIC_NAME), !delete_mode); + EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC_SYMBOLIC_COMMENT), !delete_mode); + EnableWindow(GetDlgItem(hwndDlg, IDC_SYMBOLIC_NAME), !delete_mode); + EnableWindow(GetDlgItem(hwndDlg, IDC_SYMBOLIC_COMMENT), !delete_mode); + EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK_SYMBOLIC_NAME_OVERWRITE), !delete_mode); + if (delete_mode) + { + EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_OVERWRITE), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_ARRAY_HEAD), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC_SYMBOLIC_INIT), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT_SYMBOLIC_INIT), FALSE); + } else + { + bool array_chk = IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_ARRAY) == BST_CHECKED; + bool comment_head_only = IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_ARRAY_HEAD) == BST_CHECKED; + EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_ARRAY_HEAD), array_chk); + EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK_SYMBOLIC_NAME_OVERWRITE), array_chk); + EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_OVERWRITE), array_chk && !comment_head_only); + EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC_SYMBOLIC_INIT), array_chk); + EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT_SYMBOLIC_INIT), array_chk); + } + break; + } + case IDC_CHECK_SYMBOLIC_ARRAY: + { + bool array_chk = IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_ARRAY) == BST_CHECKED; + bool delete_mode = IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_DELETE) == BST_CHECKED; + bool comment_head_only = IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_ARRAY_HEAD) == BST_CHECKED; + EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK_SYMBOLIC_NAME_OVERWRITE), array_chk && !delete_mode); + EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_ARRAY_HEAD), array_chk && !delete_mode); + EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_OVERWRITE), array_chk && !comment_head_only && !delete_mode); + EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT_SYMBOLIC_ARRAY), array_chk); + EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC_SYMBOLIC_BYTE), array_chk); + EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC_SYMBOLIC_INIT), array_chk && !delete_mode); + EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT_SYMBOLIC_INIT), array_chk && !delete_mode); + break; + } + case IDC_CHECK_SYMBOLIC_COMMENT_ARRAY_HEAD: + { + bool comment_head_only = IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_ARRAY_HEAD) == BST_CHECKED; + EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_OVERWRITE), !comment_head_only); + break; + } + case IDOK: + { + unsigned int newAddress = 0; + char newOffset[6] = { 0 }; + GetDlgItemText(hwndDlg, IDC_SYMBOLIC_ADDRESS, newOffset, 6); + if (sscanf(newOffset, "%*[$]%4X", &newAddress) != EOF) + { + + unsigned int arraySize = 0; + unsigned int arrayInit = 0; + if (IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_ARRAY) == BST_CHECKED) + { + char strArraySize[6] = { 0 }; + GetDlgItemText(hwndDlg, IDC_EDIT_SYMBOLIC_ARRAY, strArraySize, 6); + if (*strArraySize) + sscanf(strArraySize, "%4X", &arraySize); + } + + if (IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_DELETE) == BST_UNCHECKED) + { + char newName[NL_MAX_NAME_LEN + 1] = { 0 }; + GetDlgItemText(hwndDlg, IDC_SYMBOLIC_NAME, newName, NL_MAX_NAME_LEN + 1); + char newComment[NL_MAX_MULTILINE_COMMENT_LEN + 1] = { 0 }; + GetDlgItemText(hwndDlg, IDC_SYMBOLIC_COMMENT, newComment, NL_MAX_MULTILINE_COMMENT_LEN + 1); + + char strArrayInit[6] = { 0 }; + GetDlgItemText(hwndDlg, IDC_EDIT_SYMBOLIC_INIT, strArrayInit, 6); + if (*strArrayInit) + sscanf(strArrayInit, "%4X", &arrayInit); + + AddNewSymbolicName(newAddress, newOffset, newName, newComment, arraySize, arrayInit, IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_NAME_OVERWRITE) == BST_CHECKED, IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_ARRAY_HEAD) == BST_CHECKED, IsDlgButtonChecked(hwndDlg, IDC_CHECK_SYMBOLIC_COMMENT_OVERWRITE) == BST_CHECKED); + } else + DeleteSymbolicName(newAddress, arraySize); + + WriteNameFileToDisk(generateNLFilenameForAddress(newAddress), getNamesPointerForAddress(newAddress)); + } + EndDialog(hwndDlg, 1); + break; + } + case IDCANCEL: + { + EndDialog(hwndDlg, 0); + break; + } + } + break; + } + } + break; + } + } + return FALSE; +} + +// returns true if user pressed OK, false if Cancel +bool DoSymbolicDebugNaming(int offset, HWND parentHWND) +{ + if (!FCEUI_EmulationPaused()) + FCEUI_ToggleEmulationPause(); + loadNameFiles(); + if (DialogBoxParam(fceu_hInstance, MAKEINTRESOURCE(IDD_SYMBOLIC_DEBUG_NAMING), parentHWND, SymbolicNamingCallB, offset)) + return true; + return false; +} + +void AddNewSymbolicName(uint16 newAddress, char* newOffset, char* newName, char* newComment, int size, int init, bool nameOverwrite, bool commentHeadOnly, bool commentOverwrite) +{ + // remove all delimiterChars from name and comment + char* pos = newName; + while (pos < newName + strlen(newName)) + { + pos = strstr(pos, delimiterChar); + if (pos) + strcpy(pos, pos + 1); + else + break; + } + pos = newComment; + while (pos < newComment + strlen(newComment)) + { + pos = strstr(pos, delimiterChar); + if (pos) + strcpy(pos, pos + 1); + else + break; + } + + if (*newName || *newComment) + { + int i = 0; + char* tmpNewOffset = (char*)malloc(strlen(newOffset) + 1); + strcpy(tmpNewOffset, newOffset); + uint16 tmpNewAddress = newAddress; + int tmpInit = init; + do { + Name* node = getNamesPointerForAddress(tmpNewAddress); + if (!node) + { + node = (Name*)malloc(sizeof(Name)); + node->offset = (char*)malloc(strlen(tmpNewOffset) + 1); + strcpy(node->offset, tmpNewOffset); + node->offsetNumeric = tmpNewAddress; + + // Node name + if (strlen(newName)) + { + if (size) + { + char arr_index[16]; + sprintf(arr_index, "[%X]", tmpInit); + node->name = (char*)malloc(strlen(newName) + strlen(arr_index) + 1); + strcpy(node->name, newName); + strcat(node->name, arr_index); + } + else + { + node->name = (char*)malloc(strlen(newName) + 1); + strcpy(node->name, newName); + } + } + else + node->name = 0; + + if ((i == 0 || !commentHeadOnly) && strlen(newComment)) + { + node->comment = (char*)malloc(strlen(newComment) + 1); + strcpy(node->comment, newComment); + } + else + node->comment = 0; + + node->next = 0; + setNamesPointerForAddress(tmpNewAddress, node); + } + else + { + while (node) + { + if (node->offsetNumeric == tmpNewAddress) + { + // found matching address, proccessing its name and comment based on the configuration + if ((i == 0 || nameOverwrite) && node->name) + { + free(node->name); + node->name = 0; + } + if (!node->name && strlen(newName)) + { + if (size) + { + char arr_index[16]; + sprintf(arr_index, "[%X]", tmpInit); + node->name = (char*)malloc(strlen(newName) + strlen(arr_index) + 1); + strcpy(node->name, newName); + strcat(node->name, arr_index); + } + else + { + node->name = (char*)malloc(strlen(newName) + 1); + strcpy(node->name, newName); + } + } + + if ((i == 0 || !commentHeadOnly && commentOverwrite) && node->comment) + { + free(node->comment); + node->comment = 0; + } + if (!node->comment && strlen(newComment)) + { + node->comment = (char*)malloc(strlen(newComment) + 1); + strcpy(node->comment, newComment); + } + + break; + } + + if (node->next) + node = node->next; + else { + // this is the last node in the list - so just append the address + Name* newNode = (Name*)malloc(sizeof(Name)); + newNode->offset = (char*)malloc(strlen(tmpNewOffset) + 1); + strcpy(newNode->offset, tmpNewOffset); + newNode->offsetNumeric = tmpNewAddress; + + // Node name + if (strlen(newName)) + { + if (size) + { + char arr_index[16]; + sprintf(arr_index, "[%X]", tmpInit); + newNode->name = (char*)malloc(strlen(newName) + strlen(arr_index) + 1); + strcpy(newNode->name, newName); + strcat(newNode->name, arr_index); + } + else + { + newNode->name = (char*)malloc(strlen(newName) + 1); + strcpy(newNode->name, newName); + } + } + else + newNode->name = 0; + + if ((i == 0 || !commentHeadOnly) && strlen(newComment)) + { + newNode->comment = (char*)malloc(strlen(newComment) + 1); + strcpy(newNode->comment, newComment); + } + else + newNode->comment = 0; + + newNode->next = 0; + node->next = newNode; + break; + } + } + } + ++tmpNewAddress; + ++tmpInit; + sprintf(tmpNewOffset, "$%04X", tmpNewAddress); + } while (++i < size); + } +} + +void DeleteSymbolicName(uint16 address, int size) +{ + int i = 0; + uint16 tmpAddress = address; + do { + Name* prev = 0; + Name* initialNode = getNamesPointerForAddress(tmpAddress); + Name* node = initialNode; + while (node) + { + if (node->offsetNumeric == tmpAddress) + { + if (node->offset) + free(node->offset); + if (node->name) + free(node->name); + if (prev) + prev->next = node->next; + if (node == initialNode) + setNamesPointerForAddress(tmpAddress, node->next); + free(node); + break; + } + prev = node; + node = node->next; + } + ++tmpAddress; + } while (++i < size); +} + +void WriteNameFileToDisk(const char* filename, Name* node) +{ + FILE* f = fopen(filename, "wb"); + char tempComment[NL_MAX_MULTILINE_COMMENT_LEN + 10]; + if (f) + { + char tempString[10 + 1 + NL_MAX_NAME_LEN + 1 + NL_MAX_MULTILINE_COMMENT_LEN + 1]; + while (node) + { + strcpy(tempString, node->offset); + strcat(tempString, delimiterChar); + if (node->name) + strcat(tempString, node->name); + strcat(tempString, delimiterChar); + if (node->comment) + { + // dump multiline comment + strcpy(tempComment, node->comment); + char* remainder_pos = tempComment; + char* crlf_pos = strstr(tempComment, "\r\n"); + while (crlf_pos) + { + *crlf_pos = 0; + strcat(tempString, remainder_pos); + strcat(tempString, "\r\n\\"); + crlf_pos = remainder_pos = crlf_pos + 2; + crlf_pos = strstr(crlf_pos, "\r\n"); + } + strcat(tempString, remainder_pos); + strcat(tempString, "\r\n"); + } else + { + strcat(tempString, "\r\n"); + } + // write to the file + fwrite(tempString, 1, strlen(tempString), f); + node = node->next; + } + fclose(f); + } +} + +INT_PTR CALLBACK nameDebuggerBookmarkCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static std::pair* debuggerBookmark; + + switch (uMsg) + { + case WM_INITDIALOG: + { + // Limit bookmark descriptions to 50 characters + SendDlgItemMessage(hwndDlg, IDC_BOOKMARK_DESCRIPTION, EM_SETLIMITTEXT, 50, 0); + + // Limit the address text + SendDlgItemMessage(hwndDlg, IDC_BOOKMARK_ADDRESS, EM_SETLIMITTEXT, 4, 0); + DefaultEditCtrlProc = (WNDPROC)SetWindowLongPtr(GetDlgItem(hwndDlg, IDC_BOOKMARK_ADDRESS), GWLP_WNDPROC, (LONG_PTR)FilterEditCtrlProc); + + debuggerBookmark = (std::pair*)lParam; + char addr[8]; + sprintf(addr, "%04X", debuggerBookmark->first); + + // Set address and description + sprintf(addr, "%04X", debuggerBookmark->first); + SetDlgItemText(hwndDlg, IDC_BOOKMARK_ADDRESS, addr); + SetDlgItemText(hwndDlg, IDC_BOOKMARK_DESCRIPTION, debuggerBookmark->second.c_str()); + + // Set focus to the edit field. + SetFocus(GetDlgItem(hwndDlg, IDC_BOOKMARK_DESCRIPTION)); + SendDlgItemMessage(hwndDlg, IDC_BOOKMARK_DESCRIPTION, EM_SETSEL, 0, 52); + + break; + } + case WM_QUIT: + case WM_CLOSE: + EndDialog(hwndDlg, 0); + break; + case WM_COMMAND: + switch (HIWORD(wParam)) + { + case BN_CLICKED: + switch (LOWORD(wParam)) + { + case IDOK: + { + char addr_str[8]; + GetDlgItemText(hwndDlg, IDC_BOOKMARK_ADDRESS, addr_str, 8); + sscanf(addr_str, "%X", &debuggerBookmark->first); + + if (debuggerBookmark->first > 0xFFFE) + { + // if the address is out of range + char errmsg[64]; + sprintf(errmsg, "The address must be in range of 0-%X", 0xFFFE); + MessageBox(hwndDlg, errmsg, "Address out of range", MB_OK | MB_ICONERROR); + SetFocus(GetDlgItem(hwndDlg, IDC_BOOKMARK_ADDRESS)); + return FALSE; + } + + /* + extern std::vector> bookmarks; + for (std::vector>::iterator it = bookmarks.begin(); it != bookmarks.end(); ++it) + { + if (it->first == debuggerBookmark->first && it->second == debuggerBookmark->second) + { + // if the address already have a bookmark + MessageBox(hwndDlg, "This address already have a bookmark", "Bookmark duplicated", MB_OK | MB_ICONASTERISK); + return FALSE; + } + } + */ + + // Update the description + char description[51]; + GetDlgItemText(hwndDlg, IDC_BOOKMARK_DESCRIPTION, description, 50); + debuggerBookmark->second = description; + EndDialog(hwndDlg, 1); + break; + } + case IDCANCEL: + EndDialog(hwndDlg, 0); + } + } + } + + return FALSE; +} +