#include "cheatfinder.moc" CheatFinderWindow *cheatFinderWindow; CheatFinderWindow::CheatFinderWindow() { layout = new QVBoxLayout; layout->setMargin(Style::WindowMargin); layout->setSpacing(Style::WidgetSpacing); setLayout(layout); list = new QTreeWidget; list->setColumnCount(3); list->setHeaderLabels(QStringList() << "Address" << "Current Value" << "Previous Value"); list->setAllColumnsShowFocus(true); list->sortByColumn(0, Qt::AscendingOrder); list->setRootIsDecorated(false); layout->addWidget(list); controlLayout = new QGridLayout; controlLayout->setVerticalSpacing(0); layout->addLayout(controlLayout); sizeLabel = new QLabel("Data size:"); controlLayout->addWidget(sizeLabel, 0, 0); sizeGroup = new QButtonGroup(this); size8bit = new QRadioButton("8-bit"); size8bit->setChecked(true); sizeGroup->addButton(size8bit); controlLayout->addWidget(size8bit, 0, 1); size16bit = new QRadioButton("16-bit"); sizeGroup->addButton(size16bit); controlLayout->addWidget(size16bit, 0, 2); size24bit = new QRadioButton("24-bit"); sizeGroup->addButton(size24bit); controlLayout->addWidget(size24bit, 0, 3); size32bit = new QRadioButton("32-bit"); sizeGroup->addButton(size32bit); controlLayout->addWidget(size32bit, 0, 4); compareLabel = new QLabel("Compare mode:"); controlLayout->addWidget(compareLabel, 1, 0); compareGroup = new QButtonGroup(this); compareEqual = new QRadioButton("Equal to"); compareEqual->setChecked(true); compareGroup->addButton(compareEqual); controlLayout->addWidget(compareEqual, 1, 1); compareNotEqual = new QRadioButton("Not equal to"); compareGroup->addButton(compareNotEqual); controlLayout->addWidget(compareNotEqual, 1, 2); compareLessThan = new QRadioButton("Less than"); compareGroup->addButton(compareLessThan); controlLayout->addWidget(compareLessThan, 1, 3); compareGreaterThan = new QRadioButton("Greater than"); compareGroup->addButton(compareGreaterThan); controlLayout->addWidget(compareGreaterThan, 1, 4); valueLabel = new QLabel("Search value:"); controlLayout->addWidget(valueLabel, 2, 0); actionLayout = new QHBoxLayout; actionLayout->setSpacing(Style::WidgetSpacing); controlLayout->addLayout(actionLayout, 2, 1, 1, 4); valueEdit = new QLineEdit; actionLayout->addWidget(valueEdit); searchButton = new QPushButton("Search"); actionLayout->addWidget(searchButton); resetButton = new QPushButton("Reset"); actionLayout->addWidget(resetButton); connect(valueEdit, SIGNAL(returnPressed()), this, SLOT(searchMemory())); connect(searchButton, SIGNAL(released()), this, SLOT(searchMemory())); connect(resetButton, SIGNAL(released()), this, SLOT(resetSearch())); synchronize(); } void CheatFinderWindow::synchronize() { if(SNES::cartridge.loaded() == false || application.power == false) { list->clear(); for(unsigned n = 0; n < 3; n++) list->resizeColumnToContents(n); valueEdit->setEnabled(false); valueEdit->setText(""); searchButton->setEnabled(false); resetButton->setEnabled(false); } else { valueEdit->setEnabled(true); searchButton->setEnabled(true); resetButton->setEnabled(true); } } void CheatFinderWindow::refreshList() { list->clear(); list->setSortingEnabled(false); unsigned size = 0; if(size16bit->isChecked()) size = 1; if(size24bit->isChecked()) size = 2; if(size32bit->isChecked()) size = 3; for(unsigned i = 0; i < addrList.size() && i < 256; i++) { QTreeWidgetItem *item = new QTreeWidgetItem(list); unsigned addr = addrList[i]; unsigned data = read(addr, size); unsigned prev = dataList[i]; char temp[256]; sprintf(temp, "%.6x", 0x7e0000 + addr); item->setText(0, temp); sprintf(temp, "%u (0x%x)", data, data); item->setText(1, temp); sprintf(temp, "%u (0x%x)", prev, prev); item->setText(2, temp); } list->setSortingEnabled(true); list->header()->setSortIndicatorShown(false); for(unsigned n = 0; n < 3; n++) list->resizeColumnToContents(n); } void CheatFinderWindow::searchMemory() { unsigned size = 0; if(size16bit->isChecked()) size = 1; if(size24bit->isChecked()) size = 2; if(size32bit->isChecked()) size = 3; unsigned data; string text = valueEdit->text().toUtf8().constData(); //auto-detect input data type if(strbegin(text, "0x")) data = strhex((const char*)text + 2); else if(strbegin(text, "-")) data = strsigned(text); else data = strunsigned(text); if(addrList.size() == 0) { //search for the first time: enqueue all possible values so they are all searched for(unsigned addr = 0; addr < SNES::memory::wram.size(); addr++) { addrList.append(addr); dataList.append(read(addr, size)); } } array newAddrList, newDataList, oldDataList; for(unsigned i = 0; i < addrList.size(); i++) { unsigned thisAddr = addrList[i]; unsigned thisData = read(thisAddr, size); if((compareEqual->isChecked() && thisData == data) || (compareNotEqual->isChecked() && thisData != data) || (compareLessThan->isChecked() && thisData < data) || (compareGreaterThan->isChecked() && thisData > data) ) { newAddrList.append(thisAddr); newDataList.append(thisData); oldDataList.append(dataList[i]); } } //first refresh the list with the old data values (for the previous value column) addrList = newAddrList; dataList = oldDataList; refreshList(); //and now update the list with the new data values (for the next search) dataList = newDataList; } void CheatFinderWindow::resetSearch() { addrList.reset(); dataList.reset(); refreshList(); } //size = 0 (8-bit), 1 (16-bit), 2 (24-bit), 3 (32-bit) unsigned CheatFinderWindow::read(unsigned addr, unsigned size) { unsigned data = 0; for(unsigned n = 0; n <= size; n++) { if(addr + n >= SNES::memory::wram.size()) break; data |= SNES::memory::wram[addr + n] << (n << 3); } return data; }