2091 lines
47 KiB
C++
2091 lines
47 KiB
C++
/* FCE Ultra - NES/Famicom Emulator
|
|
*
|
|
* Copyright notice for this file:
|
|
* Copyright (C) 2020 mjbudd77
|
|
*
|
|
* 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
|
|
*/
|
|
//
|
|
// RamSearch.cpp
|
|
//
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <string>
|
|
|
|
#include <SDL.h>
|
|
#include <QMenu>
|
|
#include <QMenuBar>
|
|
#include <QAction>
|
|
#include <QActionGroup>
|
|
#include <QHeaderView>
|
|
#include <QCloseEvent>
|
|
#include <QGroupBox>
|
|
#include <QLineEdit>
|
|
#include <QRadioButton>
|
|
#include <QFileDialog>
|
|
#include <QValidator>
|
|
#include <QPainter>
|
|
#include <QSettings>
|
|
|
|
#include "../../types.h"
|
|
#include "../../fceu.h"
|
|
#include "../../cheat.h"
|
|
#include "../../debug.h"
|
|
#include "../../movie.h"
|
|
|
|
#include "Qt/main.h"
|
|
#include "Qt/dface.h"
|
|
#include "Qt/input.h"
|
|
#include "Qt/config.h"
|
|
#include "Qt/keyscan.h"
|
|
#include "Qt/fceuWrapper.h"
|
|
#include "Qt/RamWatch.h"
|
|
#include "Qt/RamSearch.h"
|
|
#include "Qt/HexEditor.h"
|
|
#include "Qt/CheatsConf.h"
|
|
#include "Qt/ConsoleWindow.h"
|
|
#include "Qt/ConsoleUtilities.h"
|
|
|
|
static bool ShowRAM = true;
|
|
static bool ShowSRAM = false;
|
|
static bool ShowROM = false;
|
|
static RamSearchDialog_t *ramSearchWin = NULL;
|
|
|
|
struct memoryState_t
|
|
{
|
|
union
|
|
{
|
|
int8_t i;
|
|
uint8_t u;
|
|
} v8;
|
|
union
|
|
{
|
|
int16_t i;
|
|
uint16_t u;
|
|
} v16;
|
|
union
|
|
{
|
|
int32_t i;
|
|
uint32_t u;
|
|
} v32;
|
|
};
|
|
|
|
struct memoryLocation_t
|
|
{
|
|
int addr;
|
|
|
|
memoryState_t val;
|
|
|
|
std::vector<memoryState_t> hist;
|
|
|
|
uint32_t chgCount;
|
|
uint64_t elimMask;
|
|
|
|
memoryLocation_t(void)
|
|
{
|
|
addr = 0;
|
|
val.v32.u = 0;
|
|
chgCount = 0;
|
|
elimMask = 0;
|
|
}
|
|
};
|
|
static struct memoryLocation_t memLoc[0x10000];
|
|
static uint8_t lclMemBuf[0x10000];
|
|
|
|
static std::list<struct memoryLocation_t *> actvSrchList;
|
|
static std::list<struct memoryLocation_t *> deactvSrchList;
|
|
static std::vector<int> deactvFrameStack;
|
|
|
|
static int cmpOp = '=';
|
|
static int dpySize = 'b';
|
|
static int dpyType = 's';
|
|
static bool chkMisAligned = false;
|
|
|
|
class ramSearchInputValidator : public QValidator
|
|
{
|
|
public:
|
|
ramSearchInputValidator(QObject *parent)
|
|
: QValidator(parent)
|
|
{
|
|
}
|
|
|
|
QValidator::State validate(QString &input, int &pos) const
|
|
{
|
|
int i;
|
|
//printf("Validate: %i '%s'\n", input.size(), input.toStdString().c_str() );
|
|
|
|
if (input.size() == 0)
|
|
{
|
|
return QValidator::Acceptable;
|
|
}
|
|
std::string s = input.toStdString();
|
|
i = 0;
|
|
|
|
if ((s[i] == '-') || (s[i] == '+'))
|
|
{
|
|
i++;
|
|
}
|
|
if (s[i] == 0)
|
|
{
|
|
return QValidator::Acceptable;
|
|
}
|
|
|
|
if ((s[i] == '$') || ((s[i] == '0') && (tolower(s[i + 1]) == 'x')))
|
|
{
|
|
if (s[i] == '$')
|
|
{
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
i += 2;
|
|
}
|
|
|
|
if (s[i] == 0)
|
|
{
|
|
return QValidator::Acceptable;
|
|
}
|
|
|
|
if (!isxdigit(s[i]))
|
|
{
|
|
return QValidator::Invalid;
|
|
}
|
|
while (isxdigit(s[i]))
|
|
i++;
|
|
|
|
if (s[i] == 0)
|
|
{
|
|
return QValidator::Acceptable;
|
|
}
|
|
}
|
|
else if (isdigit(s[i]))
|
|
{
|
|
while (isdigit(s[i]))
|
|
i++;
|
|
|
|
if (s[i] == 0)
|
|
{
|
|
return QValidator::Acceptable;
|
|
}
|
|
}
|
|
return QValidator::Invalid;
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
void openRamSearchWindow(QWidget *parent)
|
|
{
|
|
if (ramSearchWin != NULL)
|
|
{
|
|
return;
|
|
}
|
|
ramSearchWin = new RamSearchDialog_t(parent);
|
|
|
|
ramSearchWin->show();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
RamSearchDialog_t::RamSearchDialog_t(QWidget *parent)
|
|
: QDialog(parent, Qt::Window)
|
|
{
|
|
QVBoxLayout *mainLayout;
|
|
QHBoxLayout *hbox, *hbox1, *hbox2, *hbox3;
|
|
QVBoxLayout *vbox, *vbox1, *vbox2, *vbox3;
|
|
QGridLayout *grid;
|
|
QGroupBox *frame;
|
|
ramSearchInputValidator *inpValidator;
|
|
QMenuBar *menuBar;
|
|
QMenu *fileMenu;
|
|
QAction *act;
|
|
int useNativeMenuBar;
|
|
QSettings settings;
|
|
|
|
setWindowTitle("RAM Search");
|
|
|
|
menuBar = new QMenuBar(this);
|
|
|
|
// This is needed for menu bar to show up on MacOS
|
|
g_config->getOption( "SDL.UseNativeMenuBar", &useNativeMenuBar );
|
|
|
|
menuBar->setNativeMenuBar( useNativeMenuBar ? true : false );
|
|
|
|
//-----------------------------------------------------------------------
|
|
// Menu Start
|
|
//-----------------------------------------------------------------------
|
|
// File
|
|
fileMenu = menuBar->addMenu(tr("&File"));
|
|
|
|
// File -> Close
|
|
act = new QAction(tr("&Close"), this);
|
|
act->setShortcut(QKeySequence::Close);
|
|
act->setStatusTip(tr("Close Window"));
|
|
connect(act, SIGNAL(triggered()), this, SLOT(closeWindow(void)) );
|
|
|
|
fileMenu->addAction(act);
|
|
|
|
//-----------------------------------------------------------------------
|
|
// Menu End
|
|
//-----------------------------------------------------------------------
|
|
|
|
resize(512, 512);
|
|
|
|
mainLayout = new QVBoxLayout();
|
|
hbox1 = new QHBoxLayout();
|
|
|
|
mainLayout->setMenuBar( menuBar );
|
|
mainLayout->addLayout(hbox1, 100);
|
|
|
|
grid = new QGridLayout();
|
|
ramView = new QRamSearchView(this);
|
|
vbar = new QScrollBar(Qt::Vertical, this);
|
|
hbar = new QScrollBar(Qt::Horizontal, this);
|
|
grid->addWidget(ramView, 0, 0);
|
|
grid->addWidget(vbar, 0, 1);
|
|
grid->addWidget(hbar, 1, 0);
|
|
|
|
connect(hbar, SIGNAL(valueChanged(int)), this, SLOT(hbarChanged(int)));
|
|
connect(vbar, SIGNAL(valueChanged(int)), this, SLOT(vbarChanged(int)));
|
|
|
|
ramView->setScrollBars(hbar, vbar);
|
|
hbar->setMinimum(0);
|
|
hbar->setMaximum(100);
|
|
vbar->setMinimum(0);
|
|
vbar->setMaximum(ShowROM ? 0x10000 : 0x8000);
|
|
vbar->setValue(0);
|
|
|
|
vbox = new QVBoxLayout();
|
|
hbox1->addLayout(grid, 100);
|
|
hbox1->addLayout(vbox, 1);
|
|
|
|
searchButton = new QPushButton(tr("Search"));
|
|
vbox->addWidget(searchButton);
|
|
connect(searchButton, SIGNAL(clicked(void)), this, SLOT(runSearch(void)));
|
|
|
|
resetButton = new QPushButton(tr("Reset"));
|
|
vbox->addWidget(resetButton);
|
|
connect(resetButton, SIGNAL(clicked(void)), this, SLOT(resetSearch(void)));
|
|
|
|
clearChangeButton = new QPushButton(tr("Clear Change"));
|
|
vbox->addWidget(clearChangeButton);
|
|
connect(clearChangeButton, SIGNAL(clicked(void)), this, SLOT(clearChangeCounts(void)));
|
|
|
|
undoButton = new QPushButton(tr("Undo"));
|
|
vbox->addWidget(undoButton);
|
|
connect(undoButton, SIGNAL(clicked(void)), this, SLOT(undoSearch(void)));
|
|
undoButton->setEnabled(false);
|
|
|
|
frame = new QGroupBox( tr("Search Regions") );
|
|
vbox3 = new QVBoxLayout();
|
|
frame->setLayout(vbox3);
|
|
vbox->addWidget(frame);
|
|
|
|
searchRAMCbox = new QCheckBox(tr("RAM"));
|
|
vbox3->addWidget(searchRAMCbox);
|
|
searchRAMCbox->setChecked(ShowRAM);
|
|
searchRAMCbox->setToolTip( tr("Search RAM Address Range: 0x0000 - 0x07FF") );
|
|
connect(searchRAMCbox, SIGNAL(stateChanged(int)), this, SLOT(searchRAMChanged(int)));
|
|
|
|
searchSRAMCbox = new QCheckBox(tr("SRAM"));
|
|
vbox3->addWidget(searchSRAMCbox);
|
|
searchSRAMCbox->setChecked(ShowSRAM);
|
|
searchSRAMCbox->setToolTip( tr("Search SRAM Address Range: 0x6000 - 0x7FFF") );
|
|
connect(searchSRAMCbox, SIGNAL(stateChanged(int)), this, SLOT(searchSRAMChanged(int)));
|
|
|
|
searchROMCbox = new QCheckBox(tr("ROM"));
|
|
vbox3->addWidget(searchROMCbox);
|
|
searchROMCbox->setChecked(ShowROM);
|
|
searchROMCbox->setToolTip( tr("Search ROM Address Range: 0x8000 - 0xFFFF") );
|
|
connect(searchROMCbox, SIGNAL(stateChanged(int)), this, SLOT(searchROMChanged(int)));
|
|
|
|
elimButton = new QPushButton(tr("Eliminate"));
|
|
vbox->addWidget(elimButton);
|
|
connect(elimButton, SIGNAL(clicked(void)), this, SLOT(eliminateSelAddr(void)));
|
|
elimButton->setEnabled(false);
|
|
|
|
watchButton = new QPushButton(tr("Watch"));
|
|
vbox->addWidget(watchButton);
|
|
connect(watchButton, SIGNAL(clicked(void)), this, SLOT(addRamWatchClicked(void)));
|
|
watchButton->setEnabled(false);
|
|
|
|
addCheatButton = new QPushButton(tr("Add Cheat"));
|
|
vbox->addWidget(addCheatButton);
|
|
connect(addCheatButton, SIGNAL(clicked(void)), this, SLOT(addCheatClicked(void)));
|
|
addCheatButton->setEnabled(false);
|
|
|
|
hexEditButton = new QPushButton(tr("Hex Editor"));
|
|
vbox->addWidget(hexEditButton);
|
|
connect(hexEditButton, SIGNAL(clicked(void)), this, SLOT(hexEditSelAddr(void)));
|
|
hexEditButton->setEnabled(false);
|
|
|
|
hbox2 = new QHBoxLayout();
|
|
mainLayout->addLayout(hbox2, 1);
|
|
frame = new QGroupBox(tr("Comparison Operator"));
|
|
vbox = new QVBoxLayout();
|
|
|
|
hbox2->addWidget(frame);
|
|
frame->setLayout(vbox);
|
|
|
|
lt_btn = new QRadioButton(tr("Less Than"));
|
|
gt_btn = new QRadioButton(tr("Greater Than"));
|
|
le_btn = new QRadioButton(tr("Less Than or Equal To"));
|
|
ge_btn = new QRadioButton(tr("Greater Than or Equal To"));
|
|
eq_btn = new QRadioButton(tr("Equal To"));
|
|
ne_btn = new QRadioButton(tr("Not Equal To"));
|
|
df_btn = new QRadioButton(tr("Different By:"));
|
|
md_btn = new QRadioButton(tr("Modulo"));
|
|
|
|
lt_btn->setChecked(cmpOp == '<');
|
|
gt_btn->setChecked(cmpOp == '>');
|
|
le_btn->setChecked(cmpOp == 'l');
|
|
ge_btn->setChecked(cmpOp == 'm');
|
|
eq_btn->setChecked(cmpOp == '=');
|
|
ne_btn->setChecked(cmpOp == '!');
|
|
df_btn->setChecked(cmpOp == 'd');
|
|
md_btn->setChecked(cmpOp == '%');
|
|
|
|
connect(lt_btn, SIGNAL(clicked(void)), this, SLOT(opLtClicked(void)));
|
|
connect(gt_btn, SIGNAL(clicked(void)), this, SLOT(opGtClicked(void)));
|
|
connect(le_btn, SIGNAL(clicked(void)), this, SLOT(opLeClicked(void)));
|
|
connect(ge_btn, SIGNAL(clicked(void)), this, SLOT(opGeClicked(void)));
|
|
connect(eq_btn, SIGNAL(clicked(void)), this, SLOT(opEqClicked(void)));
|
|
connect(ne_btn, SIGNAL(clicked(void)), this, SLOT(opNeClicked(void)));
|
|
connect(df_btn, SIGNAL(clicked(void)), this, SLOT(opDfClicked(void)));
|
|
connect(md_btn, SIGNAL(clicked(void)), this, SLOT(opMdClicked(void)));
|
|
|
|
diffByEdit = new QLineEdit();
|
|
moduloEdit = new QLineEdit();
|
|
|
|
diffByEdit->setEnabled(cmpOp == 'd');
|
|
moduloEdit->setEnabled(cmpOp == '%');
|
|
|
|
inpValidator = new ramSearchInputValidator(this);
|
|
diffByEdit->setMaxLength(16);
|
|
diffByEdit->setCursorPosition(0);
|
|
diffByEdit->setValidator(inpValidator);
|
|
|
|
moduloEdit->setMaxLength(16);
|
|
moduloEdit->setCursorPosition(0);
|
|
moduloEdit->setValidator(inpValidator);
|
|
|
|
vbox->addWidget(lt_btn);
|
|
vbox->addWidget(gt_btn);
|
|
vbox->addWidget(le_btn);
|
|
vbox->addWidget(ge_btn);
|
|
vbox->addWidget(eq_btn);
|
|
vbox->addWidget(ne_btn);
|
|
|
|
hbox = new QHBoxLayout();
|
|
vbox->addLayout(hbox);
|
|
hbox->addWidget(df_btn);
|
|
hbox->addWidget(diffByEdit);
|
|
|
|
hbox = new QHBoxLayout();
|
|
vbox->addLayout(hbox);
|
|
hbox->addWidget(md_btn);
|
|
hbox->addWidget(moduloEdit);
|
|
|
|
vbox1 = new QVBoxLayout();
|
|
grid = new QGridLayout();
|
|
hbox2->addLayout(vbox1);
|
|
frame = new QGroupBox(tr("Compare To/By"));
|
|
frame->setLayout(grid);
|
|
vbox1->addWidget(frame);
|
|
|
|
pv_btn = new QRadioButton(tr("Previous Value"));
|
|
sv_btn = new QRadioButton(tr("Specific Value:"));
|
|
sa_btn = new QRadioButton(tr("Specific Address:"));
|
|
nc_btn = new QRadioButton(tr("Number of Changes:"));
|
|
|
|
pv_btn->setChecked(true);
|
|
|
|
connect(pv_btn, SIGNAL(clicked(void)), this, SLOT(pvBtnClicked(void)));
|
|
connect(sv_btn, SIGNAL(clicked(void)), this, SLOT(svBtnClicked(void)));
|
|
connect(sa_btn, SIGNAL(clicked(void)), this, SLOT(saBtnClicked(void)));
|
|
connect(nc_btn, SIGNAL(clicked(void)), this, SLOT(ncBtnClicked(void)));
|
|
|
|
specValEdit = new QLineEdit();
|
|
specAddrEdit = new QLineEdit();
|
|
numChangeEdit = new QLineEdit();
|
|
|
|
specValEdit->setValidator(inpValidator);
|
|
specAddrEdit->setValidator(inpValidator);
|
|
numChangeEdit->setValidator(inpValidator);
|
|
|
|
specValEdit->setEnabled(false);
|
|
specAddrEdit->setEnabled(false);
|
|
numChangeEdit->setEnabled(false);
|
|
|
|
grid->addWidget(pv_btn, 0, 0, Qt::AlignLeft);
|
|
grid->addWidget(sv_btn, 1, 0, Qt::AlignLeft);
|
|
grid->addWidget(specValEdit, 1, 1, Qt::AlignLeft);
|
|
grid->addWidget(sa_btn, 2, 0, Qt::AlignLeft);
|
|
grid->addWidget(specAddrEdit, 2, 1, Qt::AlignLeft);
|
|
grid->addWidget(nc_btn, 3, 0, Qt::AlignLeft);
|
|
grid->addWidget(numChangeEdit, 3, 1, Qt::AlignLeft);
|
|
|
|
vbox = new QVBoxLayout();
|
|
hbox3 = new QHBoxLayout();
|
|
frame = new QGroupBox(tr("Data Size"));
|
|
frame->setLayout(vbox);
|
|
vbox1->addLayout(hbox3);
|
|
hbox3->addWidget(frame);
|
|
|
|
ds1_btn = new QRadioButton(tr("1 Byte"));
|
|
ds2_btn = new QRadioButton(tr("2 Byte"));
|
|
ds4_btn = new QRadioButton(tr("4 Byte"));
|
|
misalignedCbox = new QCheckBox(tr("Check Misaligned"));
|
|
misalignedCbox->setEnabled(dpySize != 'b');
|
|
misalignedCbox->setChecked(chkMisAligned);
|
|
connect(misalignedCbox, SIGNAL(stateChanged(int)), this, SLOT(misalignedChanged(int)));
|
|
|
|
ds1_btn->setChecked(dpySize == 'b');
|
|
ds2_btn->setChecked(dpySize == 'w');
|
|
ds4_btn->setChecked(dpySize == 'd');
|
|
|
|
connect(ds1_btn, SIGNAL(clicked(void)), this, SLOT(ds1Clicked(void)));
|
|
connect(ds2_btn, SIGNAL(clicked(void)), this, SLOT(ds2Clicked(void)));
|
|
connect(ds4_btn, SIGNAL(clicked(void)), this, SLOT(ds4Clicked(void)));
|
|
|
|
vbox->addWidget(ds1_btn);
|
|
vbox->addWidget(ds2_btn);
|
|
vbox->addWidget(ds4_btn);
|
|
vbox->addWidget(misalignedCbox);
|
|
|
|
vbox = new QVBoxLayout();
|
|
vbox2 = new QVBoxLayout();
|
|
frame = new QGroupBox(tr("Data Type / Display"));
|
|
frame->setLayout(vbox);
|
|
vbox2->addWidget(frame);
|
|
hbox3->addLayout(vbox2);
|
|
|
|
signed_btn = new QRadioButton(tr("Signed"));
|
|
unsigned_btn = new QRadioButton(tr("Unsigned"));
|
|
hex_btn = new QRadioButton(tr("Hexadecimal"));
|
|
|
|
vbox->addWidget(signed_btn);
|
|
vbox->addWidget(unsigned_btn);
|
|
vbox->addWidget(hex_btn);
|
|
|
|
signed_btn->setChecked(dpyType == 's');
|
|
unsigned_btn->setChecked(dpyType == 'u');
|
|
hex_btn->setChecked(dpyType == 'h');
|
|
|
|
connect(signed_btn, SIGNAL(clicked(void)), this, SLOT(signedTypeClicked(void)));
|
|
connect(unsigned_btn, SIGNAL(clicked(void)), this, SLOT(unsignedTypeClicked(void)));
|
|
connect(hex_btn, SIGNAL(clicked(void)), this, SLOT(hexTypeClicked(void)));
|
|
|
|
autoSearchCbox = new QCheckBox(tr("Auto-Search"));
|
|
autoSearchCbox->setEnabled(true);
|
|
vbox2->addWidget(autoSearchCbox);
|
|
|
|
setLayout(mainLayout);
|
|
|
|
cycleCounter = 0;
|
|
frameCounterLastPass = currFrameCounter;
|
|
|
|
resetSearch();
|
|
|
|
updateTimer = new QTimer(this);
|
|
|
|
connect(updateTimer, &QTimer::timeout, this, &RamSearchDialog_t::periodicUpdate);
|
|
|
|
updateTimer->start(8); // ~120hz
|
|
|
|
restoreGeometry(settings.value("ramSearchWindow/geometry").toByteArray());
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
RamSearchDialog_t::~RamSearchDialog_t(void)
|
|
{
|
|
QSettings settings;
|
|
|
|
updateTimer->stop();
|
|
//printf("Destroy RAM Search Window\n");
|
|
ramSearchWin = NULL;
|
|
|
|
actvSrchList.clear();
|
|
deactvSrchList.clear();
|
|
deactvFrameStack.clear();
|
|
|
|
for (unsigned int addr = 0; addr < 0x08000; addr++)
|
|
{
|
|
memLoc[addr].hist.clear();
|
|
}
|
|
settings.setValue("ramSearchWindow/geometry", saveGeometry());
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::closeEvent(QCloseEvent *event)
|
|
{
|
|
//printf("RAM Search Close Window Event\n");
|
|
done(0);
|
|
deleteLater();
|
|
event->accept();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::closeWindow(void)
|
|
{
|
|
//printf("Close Window\n");
|
|
done(0);
|
|
deleteLater();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::periodicUpdate(void)
|
|
{
|
|
int selAddr = -1;
|
|
|
|
if (currFrameCounter != frameCounterLastPass)
|
|
{
|
|
FCEU_WRAPPER_LOCK();
|
|
copyRamToLocalBuffer();
|
|
FCEU_WRAPPER_UNLOCK();
|
|
|
|
//if ( currFrameCounter != (frameCounterLastPass+1) )
|
|
//{
|
|
// printf("Warning: Ram Search Missed Frame: %i \n", currFrameCounter );
|
|
//}
|
|
updateRamValues();
|
|
|
|
if (autoSearchCbox->isChecked())
|
|
{
|
|
runSearch();
|
|
}
|
|
frameCounterLastPass = currFrameCounter;
|
|
}
|
|
|
|
if ((cycleCounter % 10) == 0)
|
|
{
|
|
undoButton->setEnabled(deactvFrameStack.size() > 0);
|
|
|
|
selAddr = ramView->getSelAddr();
|
|
|
|
if (selAddr >= 0)
|
|
{
|
|
elimButton->setEnabled(true);
|
|
watchButton->setEnabled(true);
|
|
addCheatButton->setEnabled(true);
|
|
hexEditButton->setEnabled(true);
|
|
}
|
|
else
|
|
{
|
|
elimButton->setEnabled(false);
|
|
watchButton->setEnabled(false);
|
|
addCheatButton->setEnabled(false);
|
|
hexEditButton->setEnabled(false);
|
|
}
|
|
|
|
ramView->update();
|
|
}
|
|
cycleCounter++;
|
|
}
|
|
//----------------------------------------------------
|
|
void RamSearchDialog_t::hbarChanged(int val)
|
|
{
|
|
ramView->update();
|
|
}
|
|
//----------------------------------------------------
|
|
void RamSearchDialog_t::vbarChanged(int val)
|
|
{
|
|
ramView->update();
|
|
}
|
|
//----------------------------------------------------
|
|
void RamSearchDialog_t::searchRAMChanged(int state)
|
|
{
|
|
ShowRAM = (state != Qt::Unchecked);
|
|
}
|
|
//----------------------------------------------------
|
|
void RamSearchDialog_t::searchSRAMChanged(int state)
|
|
{
|
|
ShowSRAM = (state != Qt::Unchecked);
|
|
}
|
|
//----------------------------------------------------
|
|
void RamSearchDialog_t::searchROMChanged(int state)
|
|
{
|
|
ShowROM = (state != Qt::Unchecked);
|
|
}
|
|
//----------------------------------------------------
|
|
void RamSearchDialog_t::misalignedChanged(int state)
|
|
{
|
|
chkMisAligned = (state != Qt::Unchecked);
|
|
|
|
calcRamList();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
static bool memoryAddrCompare(memoryLocation_t *loc1, memoryLocation_t *loc2)
|
|
{
|
|
return loc1->addr < loc2->addr;
|
|
}
|
|
static void sortActvMemList(void)
|
|
{
|
|
actvSrchList.sort(memoryAddrCompare);
|
|
}
|
|
|
|
// basic comparison functions:
|
|
static bool LessCmp(int64_t x, int64_t y, int64_t i) { return x < y; }
|
|
static bool MoreCmp(int64_t x, int64_t y, int64_t i) { return x > y; }
|
|
static bool LessEqualCmp(int64_t x, int64_t y, int64_t i) { return x <= y; }
|
|
static bool MoreEqualCmp(int64_t x, int64_t y, int64_t i) { return x >= y; }
|
|
static bool EqualCmp(int64_t x, int64_t y, int64_t i) { return x == y; }
|
|
static bool UnequalCmp(int64_t x, int64_t y, int64_t i) { return x != y; }
|
|
static bool DiffByCmp(int64_t x, int64_t y, int64_t p) { return x - y == p || y - x == p; }
|
|
static bool ModIsCmp(int64_t x, int64_t y, int64_t p) { return p && x % p == y; }
|
|
|
|
static int64_t getLineEditValue(QLineEdit *edit, bool forceHex = false)
|
|
{
|
|
int64_t val = 0;
|
|
std::string s;
|
|
|
|
s = edit->text().toStdString();
|
|
|
|
if (s.size() > 0)
|
|
{
|
|
val = strtoll(s.c_str(), NULL, forceHex ? 16 : 0);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::SearchRelative(void)
|
|
{
|
|
int elimCount = 0;
|
|
std::list<struct memoryLocation_t *>::iterator it;
|
|
memoryLocation_t *loc = NULL;
|
|
int64_t x = 0, y = 0, p = 0;
|
|
bool (*cmpFun)(int64_t x, int64_t y, int64_t p) = NULL;
|
|
bool storeHistory = !autoSearchCbox->isChecked();
|
|
|
|
switch (cmpOp)
|
|
{
|
|
case '<':
|
|
cmpFun = LessCmp;
|
|
break;
|
|
case '>':
|
|
cmpFun = MoreCmp;
|
|
break;
|
|
case '=':
|
|
cmpFun = EqualCmp;
|
|
break;
|
|
case '!':
|
|
cmpFun = UnequalCmp;
|
|
break;
|
|
case 'l':
|
|
cmpFun = LessEqualCmp;
|
|
break;
|
|
case 'm':
|
|
cmpFun = MoreEqualCmp;
|
|
break;
|
|
case 'd':
|
|
cmpFun = DiffByCmp;
|
|
p = getLineEditValue(diffByEdit);
|
|
break;
|
|
case '%':
|
|
cmpFun = ModIsCmp;
|
|
p = getLineEditValue(moduloEdit);
|
|
break;
|
|
default:
|
|
cmpFun = NULL;
|
|
break;
|
|
}
|
|
|
|
if (cmpFun == NULL)
|
|
{
|
|
return;
|
|
}
|
|
//printf("Performing Relative Search Operation %zi: '%c' '%lli' '0x%llx' \n", deactvFrameStack.size()+1, cmpOp, (long long int)p, (unsigned long long int)p );
|
|
|
|
it = actvSrchList.begin();
|
|
|
|
while (it != actvSrchList.end())
|
|
{
|
|
loc = *it;
|
|
|
|
switch (dpySize)
|
|
{
|
|
default:
|
|
case 'b':
|
|
{
|
|
if (dpyType == 's')
|
|
{
|
|
x = loc->val.v8.i;
|
|
y = loc->hist.back().v8.i;
|
|
}
|
|
else
|
|
{
|
|
x = loc->val.v8.u;
|
|
y = loc->hist.back().v8.u;
|
|
}
|
|
}
|
|
break;
|
|
case 'w':
|
|
{
|
|
if (dpyType == 's')
|
|
{
|
|
x = loc->val.v16.i;
|
|
y = loc->hist.back().v16.i;
|
|
}
|
|
else
|
|
{
|
|
x = loc->val.v16.u;
|
|
y = loc->hist.back().v16.u;
|
|
}
|
|
}
|
|
break;
|
|
case 'd':
|
|
{
|
|
if (dpyType == 's')
|
|
{
|
|
x = loc->val.v32.i;
|
|
y = loc->hist.back().v32.i;
|
|
}
|
|
else
|
|
{
|
|
x = loc->val.v32.u;
|
|
y = loc->hist.back().v32.u;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (cmpFun(x, y, p) == false)
|
|
{
|
|
//printf("Eliminated Address: $%04X\n", loc->addr );
|
|
it = actvSrchList.erase(it);
|
|
|
|
if (storeHistory)
|
|
{
|
|
deactvSrchList.push_back(loc);
|
|
elimCount++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (storeHistory)
|
|
{
|
|
loc->hist.push_back(loc->val);
|
|
}
|
|
it++;
|
|
}
|
|
}
|
|
|
|
if (storeHistory)
|
|
{
|
|
deactvFrameStack.push_back(elimCount);
|
|
}
|
|
|
|
vbar->setMaximum(actvSrchList.size());
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::SearchSpecificValue(void)
|
|
{
|
|
int elimCount = 0;
|
|
std::list<struct memoryLocation_t *>::iterator it;
|
|
memoryLocation_t *loc = NULL;
|
|
int64_t x = 0, y = 0, p = 0;
|
|
bool (*cmpFun)(int64_t x, int64_t y, int64_t p) = NULL;
|
|
bool storeHistory = !autoSearchCbox->isChecked();
|
|
|
|
switch (cmpOp)
|
|
{
|
|
case '<':
|
|
cmpFun = LessCmp;
|
|
break;
|
|
case '>':
|
|
cmpFun = MoreCmp;
|
|
break;
|
|
case '=':
|
|
cmpFun = EqualCmp;
|
|
break;
|
|
case '!':
|
|
cmpFun = UnequalCmp;
|
|
break;
|
|
case 'l':
|
|
cmpFun = LessEqualCmp;
|
|
break;
|
|
case 'm':
|
|
cmpFun = MoreEqualCmp;
|
|
break;
|
|
case 'd':
|
|
cmpFun = DiffByCmp;
|
|
p = getLineEditValue(diffByEdit);
|
|
break;
|
|
case '%':
|
|
cmpFun = ModIsCmp;
|
|
p = getLineEditValue(moduloEdit);
|
|
break;
|
|
default:
|
|
cmpFun = NULL;
|
|
break;
|
|
}
|
|
|
|
if (cmpFun == NULL)
|
|
{
|
|
return;
|
|
}
|
|
y = getLineEditValue(specValEdit);
|
|
|
|
//printf("Performing Specific Value Search Operation %zi: 'x %c %lli' '%lli' '0x%llx' \n", deactvFrameStack.size()+1, cmpOp,
|
|
// (long long int)y, (long long int)p, (unsigned long long int)p );
|
|
|
|
it = actvSrchList.begin();
|
|
|
|
while (it != actvSrchList.end())
|
|
{
|
|
loc = *it;
|
|
|
|
switch (dpySize)
|
|
{
|
|
default:
|
|
case 'b':
|
|
{
|
|
if (dpyType == 's')
|
|
{
|
|
x = loc->val.v8.i;
|
|
}
|
|
else
|
|
{
|
|
x = loc->val.v8.u;
|
|
}
|
|
}
|
|
break;
|
|
case 'w':
|
|
{
|
|
if (dpyType == 's')
|
|
{
|
|
x = loc->val.v16.i;
|
|
}
|
|
else
|
|
{
|
|
x = loc->val.v16.u;
|
|
}
|
|
}
|
|
break;
|
|
case 'd':
|
|
{
|
|
if (dpyType == 's')
|
|
{
|
|
x = loc->val.v32.i;
|
|
}
|
|
else
|
|
{
|
|
x = loc->val.v32.u;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (cmpFun(x, y, p) == false)
|
|
{
|
|
//printf("Eliminated Address: $%04X\n", loc->addr );
|
|
it = actvSrchList.erase(it);
|
|
|
|
if (storeHistory)
|
|
{
|
|
deactvSrchList.push_back(loc);
|
|
elimCount++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (storeHistory)
|
|
{
|
|
loc->hist.push_back(loc->val);
|
|
}
|
|
it++;
|
|
}
|
|
}
|
|
|
|
if (storeHistory)
|
|
{
|
|
deactvFrameStack.push_back(elimCount);
|
|
}
|
|
|
|
vbar->setMaximum(actvSrchList.size());
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::SearchSpecificAddress(void)
|
|
{
|
|
int elimCount = 0;
|
|
std::list<struct memoryLocation_t *>::iterator it;
|
|
memoryLocation_t *loc = NULL;
|
|
int64_t x = 0, y = 0, p = 0;
|
|
bool (*cmpFun)(int64_t x, int64_t y, int64_t p) = NULL;
|
|
bool storeHistory = !autoSearchCbox->isChecked();
|
|
|
|
switch (cmpOp)
|
|
{
|
|
case '<':
|
|
cmpFun = LessCmp;
|
|
break;
|
|
case '>':
|
|
cmpFun = MoreCmp;
|
|
break;
|
|
case '=':
|
|
cmpFun = EqualCmp;
|
|
break;
|
|
case '!':
|
|
cmpFun = UnequalCmp;
|
|
break;
|
|
case 'l':
|
|
cmpFun = LessEqualCmp;
|
|
break;
|
|
case 'm':
|
|
cmpFun = MoreEqualCmp;
|
|
break;
|
|
case 'd':
|
|
cmpFun = DiffByCmp;
|
|
p = getLineEditValue(diffByEdit);
|
|
break;
|
|
case '%':
|
|
cmpFun = ModIsCmp;
|
|
p = getLineEditValue(moduloEdit);
|
|
break;
|
|
default:
|
|
cmpFun = NULL;
|
|
break;
|
|
}
|
|
|
|
if (cmpFun == NULL)
|
|
{
|
|
return;
|
|
}
|
|
y = getLineEditValue(specAddrEdit);
|
|
|
|
//printf("Performing Specific Address Search Operation %zi: 'x %c 0x%llx' '%lli' '0x%llx' \n", deactvFrameStack.size()+1, cmpOp,
|
|
// (unsigned long long int)y, (long long int)p, (unsigned long long int)p );
|
|
|
|
it = actvSrchList.begin();
|
|
|
|
while (it != actvSrchList.end())
|
|
{
|
|
loc = *it;
|
|
|
|
x = loc->addr;
|
|
|
|
if (cmpFun(x, y, p) == false)
|
|
{
|
|
//printf("Eliminated Address: $%04X\n", loc->addr );
|
|
it = actvSrchList.erase(it);
|
|
|
|
if (storeHistory)
|
|
{
|
|
deactvSrchList.push_back(loc);
|
|
elimCount++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (storeHistory)
|
|
{
|
|
loc->hist.push_back(loc->val);
|
|
}
|
|
it++;
|
|
}
|
|
}
|
|
|
|
if (storeHistory)
|
|
{
|
|
deactvFrameStack.push_back(elimCount);
|
|
}
|
|
|
|
vbar->setMaximum(actvSrchList.size());
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::SearchNumberChanges(void)
|
|
{
|
|
int elimCount = 0;
|
|
std::list<struct memoryLocation_t *>::iterator it;
|
|
memoryLocation_t *loc = NULL;
|
|
int64_t x = 0, y = 0, p = 0;
|
|
bool (*cmpFun)(int64_t x, int64_t y, int64_t p) = NULL;
|
|
bool storeHistory = !autoSearchCbox->isChecked();
|
|
|
|
switch (cmpOp)
|
|
{
|
|
case '<':
|
|
cmpFun = LessCmp;
|
|
break;
|
|
case '>':
|
|
cmpFun = MoreCmp;
|
|
break;
|
|
case '=':
|
|
cmpFun = EqualCmp;
|
|
break;
|
|
case '!':
|
|
cmpFun = UnequalCmp;
|
|
break;
|
|
case 'l':
|
|
cmpFun = LessEqualCmp;
|
|
break;
|
|
case 'm':
|
|
cmpFun = MoreEqualCmp;
|
|
break;
|
|
case 'd':
|
|
cmpFun = DiffByCmp;
|
|
p = getLineEditValue(diffByEdit);
|
|
break;
|
|
case '%':
|
|
cmpFun = ModIsCmp;
|
|
p = getLineEditValue(moduloEdit);
|
|
break;
|
|
default:
|
|
cmpFun = NULL;
|
|
break;
|
|
}
|
|
|
|
if (cmpFun == NULL)
|
|
{
|
|
return;
|
|
}
|
|
y = getLineEditValue(numChangeEdit);
|
|
|
|
//printf("Performing Number of Changes Search Operation %zi: 'x %c 0x%llx' '%lli' '0x%llx' \n", deactvFrameStack.size()+1, cmpOp,
|
|
// (unsigned long long int)y, (long long int)p, (unsigned long long int)p );
|
|
|
|
it = actvSrchList.begin();
|
|
|
|
while (it != actvSrchList.end())
|
|
{
|
|
loc = *it;
|
|
|
|
x = loc->chgCount;
|
|
|
|
if (cmpFun(x, y, p) == false)
|
|
{
|
|
//printf("Eliminated Address: $%04X\n", loc->addr );
|
|
it = actvSrchList.erase(it);
|
|
|
|
if (storeHistory)
|
|
{
|
|
deactvSrchList.push_back(loc);
|
|
elimCount++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (storeHistory)
|
|
{
|
|
loc->hist.push_back(loc->val);
|
|
}
|
|
it++;
|
|
}
|
|
}
|
|
|
|
if (storeHistory)
|
|
{
|
|
deactvFrameStack.push_back(elimCount);
|
|
}
|
|
|
|
vbar->setMaximum(actvSrchList.size());
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
static unsigned int ReadValueAtHardwareAddress(int address, unsigned int size)
|
|
{
|
|
unsigned int value = 0;
|
|
int maxAddr = ShowROM ? 0x10000 : 0x8000;
|
|
|
|
// read as little endian
|
|
for (unsigned int i = 0; i < size; i++)
|
|
{
|
|
if (address < maxAddr)
|
|
{
|
|
value <<= 8;
|
|
value |= lclMemBuf[address];
|
|
address++;
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::runSearch(void)
|
|
{
|
|
if (pv_btn->isChecked())
|
|
{
|
|
// Relative Value
|
|
SearchRelative();
|
|
}
|
|
else if (sv_btn->isChecked())
|
|
{
|
|
// Specific Value
|
|
SearchSpecificValue();
|
|
}
|
|
else if (sa_btn->isChecked())
|
|
{
|
|
// Specific Address
|
|
SearchSpecificAddress();
|
|
}
|
|
else if (nc_btn->isChecked())
|
|
{
|
|
// Number of Changes
|
|
SearchNumberChanges();
|
|
}
|
|
|
|
undoButton->setEnabled(deactvFrameStack.size() > 0);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::copyRamToLocalBuffer(void)
|
|
{
|
|
for (unsigned int addr = 0; addr < 0x10000; addr++)
|
|
{
|
|
lclMemBuf[addr] = GetMem(addr);
|
|
}
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::resetSearch(void)
|
|
{
|
|
memset(lclMemBuf, 0, sizeof(lclMemBuf));
|
|
|
|
FCEU_WRAPPER_LOCK();
|
|
copyRamToLocalBuffer();
|
|
FCEU_WRAPPER_UNLOCK();
|
|
|
|
actvSrchList.clear();
|
|
deactvSrchList.clear();
|
|
deactvFrameStack.clear();
|
|
|
|
for (unsigned int addr = 0; addr < 0x10000; addr++)
|
|
{
|
|
memLoc[addr].hist.clear();
|
|
memLoc[addr].addr = addr;
|
|
memLoc[addr].val.v8.u = lclMemBuf[addr];
|
|
memLoc[addr].val.v16.u = ReadValueAtHardwareAddress(addr, 2);
|
|
memLoc[addr].val.v32.u = ReadValueAtHardwareAddress(addr, 4);
|
|
memLoc[addr].elimMask = 0;
|
|
memLoc[addr].chgCount = 0;
|
|
memLoc[addr].hist.push_back(memLoc[addr].val);
|
|
}
|
|
|
|
calcRamList();
|
|
|
|
undoButton->setEnabled(false);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::undoSearch(void)
|
|
{
|
|
int elimCount = 0;
|
|
memoryLocation_t *loc = NULL;
|
|
std::list<struct memoryLocation_t *>::iterator it;
|
|
|
|
if (deactvFrameStack.empty())
|
|
{
|
|
printf("Error: UNDO Stack is empty\n");
|
|
return;
|
|
}
|
|
printf("UNDO Search Operation: %zi \n", deactvFrameStack.size());
|
|
// To Undo a search operation:
|
|
// 1. Loop through all current active values and revert previous value back to what it was before the search
|
|
// 2. Get the number of eliminated values from the deactivate search stack and tranfer those values back into the active search list.
|
|
|
|
it = actvSrchList.begin();
|
|
|
|
while (it != actvSrchList.end())
|
|
{
|
|
loc = *it;
|
|
|
|
if (!loc->hist.empty())
|
|
{
|
|
loc->hist.pop_back();
|
|
}
|
|
it++;
|
|
}
|
|
|
|
elimCount = deactvFrameStack.back();
|
|
deactvFrameStack.pop_back();
|
|
|
|
while (elimCount > 0)
|
|
{
|
|
if (deactvSrchList.empty())
|
|
{
|
|
printf("Error: Something went wrong with UNDO operation\n");
|
|
break;
|
|
}
|
|
loc = deactvSrchList.back();
|
|
deactvSrchList.pop_back();
|
|
actvSrchList.push_back(loc);
|
|
elimCount--;
|
|
}
|
|
|
|
sortActvMemList();
|
|
|
|
undoButton->setEnabled(deactvFrameStack.size() > 0);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::clearChangeCounts(void)
|
|
{
|
|
for (unsigned int addr = 0; addr < 0x10000; addr++)
|
|
{
|
|
memLoc[addr].chgCount = 0;
|
|
}
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::eliminateSelAddr(void)
|
|
{
|
|
int elimCount = 0, op = '!';
|
|
std::list<struct memoryLocation_t *>::iterator it;
|
|
memoryLocation_t *loc = NULL;
|
|
int64_t x = 0, y = 0, p = 0;
|
|
bool (*cmpFun)(int64_t x, int64_t y, int64_t p) = NULL;
|
|
|
|
switch (op)
|
|
{
|
|
case '<':
|
|
cmpFun = LessCmp;
|
|
break;
|
|
case '>':
|
|
cmpFun = MoreCmp;
|
|
break;
|
|
case '=':
|
|
cmpFun = EqualCmp;
|
|
break;
|
|
case '!':
|
|
cmpFun = UnequalCmp;
|
|
break;
|
|
case 'l':
|
|
cmpFun = LessEqualCmp;
|
|
break;
|
|
case 'm':
|
|
cmpFun = MoreEqualCmp;
|
|
break;
|
|
case 'd':
|
|
cmpFun = DiffByCmp;
|
|
p = getLineEditValue(diffByEdit);
|
|
break;
|
|
case '%':
|
|
cmpFun = ModIsCmp;
|
|
p = getLineEditValue(moduloEdit);
|
|
break;
|
|
default:
|
|
cmpFun = NULL;
|
|
break;
|
|
}
|
|
|
|
if (cmpFun == NULL)
|
|
{
|
|
return;
|
|
}
|
|
y = ramView->getSelAddr();
|
|
|
|
if (y < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
printf("Performing Eliminate Address Operation %zi: 'x %c 0x%llx' '%lli' '0x%llx' \n", deactvFrameStack.size() + 1, cmpOp,
|
|
(unsigned long long int)y, (long long int)p, (unsigned long long int)p);
|
|
|
|
it = actvSrchList.begin();
|
|
|
|
while (it != actvSrchList.end())
|
|
{
|
|
loc = *it;
|
|
|
|
x = loc->addr;
|
|
|
|
if (cmpFun(x, y, p) == false)
|
|
{
|
|
//printf("Eliminated Address: $%04X\n", loc->addr );
|
|
it = actvSrchList.erase(it);
|
|
|
|
deactvSrchList.push_back(loc);
|
|
elimCount++;
|
|
}
|
|
else
|
|
{
|
|
loc->hist.push_back(loc->val);
|
|
it++;
|
|
}
|
|
}
|
|
|
|
deactvFrameStack.push_back(elimCount);
|
|
|
|
vbar->setMaximum(actvSrchList.size());
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::addCheatClicked(void)
|
|
{
|
|
int addr = ramView->getSelAddr();
|
|
char desc[128];
|
|
|
|
if (addr < 0)
|
|
{
|
|
return;
|
|
}
|
|
strcpy(desc, "Quick Cheat Add");
|
|
|
|
FCEU_WRAPPER_LOCK();
|
|
|
|
FCEUI_AddCheat(desc, addr, GetMem(addr), -1, 1);
|
|
|
|
updateCheatDialog();
|
|
|
|
FCEU_WRAPPER_UNLOCK();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::addRamWatchClicked(void)
|
|
{
|
|
int addr = ramView->getSelAddr();
|
|
char desc[128];
|
|
|
|
if (addr < 0)
|
|
{
|
|
return;
|
|
}
|
|
strcpy(desc, "Quick Watch Add");
|
|
|
|
int size = 1;
|
|
switch (dpySize)
|
|
{
|
|
case 'd':
|
|
size = 4;
|
|
break;
|
|
case 'w':
|
|
size = 2;
|
|
break;
|
|
case 'b':
|
|
size = 1;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
ramWatchList.add_entry(desc, addr, dpyType, size, 0);
|
|
|
|
openRamWatchWindow(consoleWindow);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::hexEditSelAddr(void)
|
|
{
|
|
int addr = ramView->getSelAddr();
|
|
|
|
if (addr < 0)
|
|
{
|
|
return;
|
|
}
|
|
hexEditorOpenFromDebugger(QHexEdit::MODE_NES_RAM, addr);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::opLtClicked(void)
|
|
{
|
|
cmpOp = '<';
|
|
diffByEdit->setEnabled(false);
|
|
moduloEdit->setEnabled(false);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::opGtClicked(void)
|
|
{
|
|
cmpOp = '>';
|
|
diffByEdit->setEnabled(false);
|
|
moduloEdit->setEnabled(false);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::opLeClicked(void)
|
|
{
|
|
cmpOp = 'l';
|
|
diffByEdit->setEnabled(false);
|
|
moduloEdit->setEnabled(false);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::opGeClicked(void)
|
|
{
|
|
cmpOp = 'm';
|
|
diffByEdit->setEnabled(false);
|
|
moduloEdit->setEnabled(false);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::opEqClicked(void)
|
|
{
|
|
cmpOp = '=';
|
|
diffByEdit->setEnabled(false);
|
|
moduloEdit->setEnabled(false);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::opNeClicked(void)
|
|
{
|
|
cmpOp = '!';
|
|
diffByEdit->setEnabled(false);
|
|
moduloEdit->setEnabled(false);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::opDfClicked(void)
|
|
{
|
|
cmpOp = 'd';
|
|
diffByEdit->setEnabled(true);
|
|
moduloEdit->setEnabled(false);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::opMdClicked(void)
|
|
{
|
|
cmpOp = '%';
|
|
diffByEdit->setEnabled(false);
|
|
moduloEdit->setEnabled(true);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::pvBtnClicked(void)
|
|
{
|
|
specValEdit->setEnabled(false);
|
|
specAddrEdit->setEnabled(false);
|
|
numChangeEdit->setEnabled(false);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::svBtnClicked(void)
|
|
{
|
|
specValEdit->setEnabled(true);
|
|
specAddrEdit->setEnabled(false);
|
|
numChangeEdit->setEnabled(false);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::saBtnClicked(void)
|
|
{
|
|
specValEdit->setEnabled(false);
|
|
specAddrEdit->setEnabled(true);
|
|
numChangeEdit->setEnabled(false);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::ncBtnClicked(void)
|
|
{
|
|
specValEdit->setEnabled(false);
|
|
specAddrEdit->setEnabled(false);
|
|
numChangeEdit->setEnabled(true);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::ds1Clicked(void)
|
|
{
|
|
dpySize = 'b';
|
|
misalignedCbox->setEnabled(false);
|
|
calcRamList();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::ds2Clicked(void)
|
|
{
|
|
dpySize = 'w';
|
|
misalignedCbox->setEnabled(true);
|
|
calcRamList();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::ds4Clicked(void)
|
|
{
|
|
dpySize = 'd';
|
|
misalignedCbox->setEnabled(true);
|
|
calcRamList();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::signedTypeClicked(void)
|
|
{
|
|
dpyType = 's';
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::unsignedTypeClicked(void)
|
|
{
|
|
dpyType = 'u';
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::hexTypeClicked(void)
|
|
{
|
|
dpyType = 'h';
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::calcRamList(void)
|
|
{
|
|
int i, addr, startAddr, endAddr;
|
|
int numRegions = 0, dataSize = 1;
|
|
int regionStart[5], regionEnd[5];
|
|
|
|
if ( ShowRAM )
|
|
{
|
|
regionStart[ numRegions ] = 0x0000;
|
|
regionEnd[ numRegions ] = 0x0800;
|
|
numRegions++;
|
|
}
|
|
|
|
if ( ShowSRAM )
|
|
{
|
|
regionStart[ numRegions ] = 0x6000;
|
|
regionEnd[ numRegions ] = 0x8000;
|
|
numRegions++;
|
|
}
|
|
|
|
if ( ShowROM )
|
|
{
|
|
regionStart[ numRegions ] = 0x08000;
|
|
regionEnd[ numRegions ] = 0x10000;
|
|
numRegions++;
|
|
}
|
|
|
|
if (chkMisAligned)
|
|
{
|
|
dataSize = 1;
|
|
}
|
|
else if (dpySize == 'd')
|
|
{
|
|
dataSize = 4;
|
|
}
|
|
else if (dpySize == 'w')
|
|
{
|
|
dataSize = 2;
|
|
}
|
|
else
|
|
{
|
|
dataSize = 1;
|
|
}
|
|
|
|
actvSrchList.clear();
|
|
|
|
for (i=0; i<numRegions; i++)
|
|
{
|
|
startAddr = regionStart[i];
|
|
endAddr = regionEnd[i];
|
|
|
|
for (addr = startAddr; addr < endAddr; addr += dataSize)
|
|
{
|
|
switch (dpySize)
|
|
{
|
|
case 'd':
|
|
if ((addr + 3) < endAddr)
|
|
{
|
|
if ((memLoc[addr].elimMask == 0) &&
|
|
(memLoc[addr + 1].elimMask == 0) &&
|
|
(memLoc[addr + 2].elimMask == 0) &&
|
|
(memLoc[addr + 3].elimMask == 0))
|
|
{
|
|
actvSrchList.push_back(&memLoc[addr]);
|
|
}
|
|
}
|
|
break;
|
|
case 'w':
|
|
if ((addr + 1) < endAddr)
|
|
{
|
|
if ((memLoc[addr].elimMask == 0) &&
|
|
(memLoc[addr + 1].elimMask == 0))
|
|
{
|
|
actvSrchList.push_back(&memLoc[addr]);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
case 'b':
|
|
if (memLoc[addr].elimMask == 0)
|
|
{
|
|
actvSrchList.push_back(&memLoc[addr]);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
vbar->setMaximum(actvSrchList.size());
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamSearchDialog_t::updateRamValues(void)
|
|
{
|
|
std::list<struct memoryLocation_t *>::iterator it;
|
|
memoryLocation_t *loc = NULL;
|
|
memoryState_t val;
|
|
|
|
for (it = actvSrchList.begin(); it != actvSrchList.end(); it++)
|
|
{
|
|
loc = *it;
|
|
|
|
val.v8.u = lclMemBuf[loc->addr];
|
|
val.v16.u = ReadValueAtHardwareAddress(loc->addr, 2);
|
|
val.v32.u = ReadValueAtHardwareAddress(loc->addr, 4);
|
|
|
|
if (dpySize == 'd')
|
|
{
|
|
if (memLoc[loc->addr].val.v32.u != val.v32.u)
|
|
{
|
|
memLoc[loc->addr].val = val;
|
|
memLoc[loc->addr].chgCount++;
|
|
}
|
|
}
|
|
else if (dpySize == 'w')
|
|
{
|
|
if (memLoc[loc->addr].val.v16.u != val.v16.u)
|
|
{
|
|
memLoc[loc->addr].val = val;
|
|
memLoc[loc->addr].chgCount++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (memLoc[loc->addr].val.v8.u != val.v8.u)
|
|
{
|
|
memLoc[loc->addr].val = val;
|
|
memLoc[loc->addr].chgCount++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
QRamSearchView::QRamSearchView(QWidget *parent)
|
|
: QWidget(parent)
|
|
{
|
|
QPalette pal;
|
|
QColor c, fg(0, 0, 0), bg(255, 255, 255);
|
|
bool useDarkTheme = false;
|
|
|
|
pal = this->palette();
|
|
|
|
// Figure out if we are using a light or dark theme by checking the
|
|
// default window text grayscale color. If more white, then we will
|
|
// use white text on black background, else we do the opposite.
|
|
c = pal.color(QPalette::WindowText);
|
|
|
|
if (qGray(c.red(), c.green(), c.blue()) > 128)
|
|
{
|
|
useDarkTheme = true;
|
|
}
|
|
|
|
if (useDarkTheme)
|
|
{
|
|
pal.setColor(QPalette::Base, fg);
|
|
pal.setColor(QPalette::Window, fg);
|
|
pal.setColor(QPalette::WindowText, bg);
|
|
}
|
|
else
|
|
{
|
|
pal.setColor(QPalette::Base, bg);
|
|
pal.setColor(QPalette::Window, bg);
|
|
pal.setColor(QPalette::WindowText, fg);
|
|
}
|
|
this->setPalette(pal);
|
|
|
|
font.setFamily("Courier New");
|
|
font.setStyle(QFont::StyleNormal);
|
|
font.setStyleHint(QFont::Monospace);
|
|
|
|
calcFontData();
|
|
|
|
lineOffset = 0;
|
|
maxLineOffset = 0;
|
|
selAddr = -1;
|
|
selLine = -1;
|
|
|
|
wheelPixelCounter = 0;
|
|
wheelAngleCounter = 0;
|
|
|
|
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
|
|
setFocusPolicy(Qt::StrongFocus);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
QRamSearchView::~QRamSearchView(void)
|
|
{
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void QRamSearchView::calcFontData(void)
|
|
{
|
|
this->setFont(font);
|
|
QFontMetrics metrics(font);
|
|
#if QT_VERSION > QT_VERSION_CHECK(5, 11, 0)
|
|
pxCharWidth = metrics.horizontalAdvance(QLatin1Char('2'));
|
|
#else
|
|
pxCharWidth = metrics.width(QLatin1Char('2'));
|
|
#endif
|
|
pxCharHeight = metrics.height();
|
|
pxLineSpacing = metrics.lineSpacing() * 1.25;
|
|
pxLineLead = pxLineSpacing - pxCharHeight;
|
|
pxCursorHeight = pxCharHeight;
|
|
pxColWidth[0] = pxCharWidth * 10;
|
|
pxColWidth[1] = pxCharWidth * 15;
|
|
pxColWidth[2] = pxCharWidth * 15;
|
|
pxColWidth[3] = pxCharWidth * 15;
|
|
pxLineWidth = pxColWidth[0] + pxColWidth[1] + pxColWidth[2] + pxColWidth[3];
|
|
|
|
viewLines = (viewHeight / pxLineSpacing) + 1;
|
|
|
|
setMinimumWidth(pxLineWidth);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void QRamSearchView::setScrollBars(QScrollBar *hbar, QScrollBar *vbar)
|
|
{
|
|
this->hbar = hbar;
|
|
this->vbar = vbar;
|
|
}
|
|
//----------------------------------------------------
|
|
void QRamSearchView::resizeEvent(QResizeEvent *event)
|
|
{
|
|
viewWidth = event->size().width();
|
|
viewHeight = event->size().height();
|
|
|
|
//printf("QAsmView Resize: %ix%i\n", viewWidth, viewHeight );
|
|
|
|
viewLines = (viewHeight / pxLineSpacing) + 1;
|
|
|
|
//maxLineOffset = 0; // mb.numLines() - viewLines + 1;
|
|
|
|
if (viewWidth >= pxLineWidth)
|
|
{
|
|
pxLineXScroll = 0;
|
|
}
|
|
else
|
|
{
|
|
pxLineXScroll = (int)(0.010f * (float)hbar->value() * (float)(pxLineWidth - viewWidth));
|
|
}
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
int QRamSearchView::convPixToLine(QPoint p)
|
|
{
|
|
int lineNum = 0;
|
|
float ly = ((float)pxLineLead / (float)pxLineSpacing);
|
|
float py = ((float)p.y() - (float)pxLineSpacing) / (float)pxLineSpacing;
|
|
float ry = fmod(py, 1.0);
|
|
|
|
if (ry < ly)
|
|
{
|
|
lineNum = (((int)py) - 1);
|
|
}
|
|
else
|
|
{
|
|
lineNum = (((int)py));
|
|
}
|
|
//printf("Pos: %ix%i = %i\n", p.x(), p.y(), lineNum );
|
|
|
|
return lineNum;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void QRamSearchView::keyPressEvent(QKeyEvent *event)
|
|
{
|
|
//printf("Ram Search View Key Press: 0x%x \n", event->key() );
|
|
|
|
if (event->matches(QKeySequence::MoveToPreviousLine))
|
|
{
|
|
selAddr = -1;
|
|
if (selLine > 0)
|
|
{
|
|
selLine--;
|
|
}
|
|
if (selLine < lineOffset)
|
|
{
|
|
lineOffset = selLine;
|
|
}
|
|
if (lineOffset < 0)
|
|
{
|
|
lineOffset = 0;
|
|
}
|
|
vbar->setValue(lineOffset);
|
|
}
|
|
else if (event->matches(QKeySequence::MoveToNextLine))
|
|
{
|
|
selAddr = -1;
|
|
selLine++;
|
|
|
|
if (selLine >= actvSrchList.size())
|
|
{
|
|
selLine = actvSrchList.size() - 1;
|
|
}
|
|
|
|
if (selLine >= (lineOffset + viewLines))
|
|
{
|
|
lineOffset = selLine - viewLines + 1;
|
|
}
|
|
|
|
if (lineOffset >= maxLineOffset)
|
|
{
|
|
lineOffset = maxLineOffset;
|
|
}
|
|
vbar->setValue(lineOffset);
|
|
}
|
|
else if (event->matches(QKeySequence::MoveToNextPage))
|
|
{
|
|
lineOffset += ((3 * viewLines) / 4);
|
|
|
|
if (lineOffset >= maxLineOffset)
|
|
{
|
|
lineOffset = maxLineOffset;
|
|
}
|
|
vbar->setValue(lineOffset);
|
|
}
|
|
else if (event->matches(QKeySequence::MoveToPreviousPage))
|
|
{
|
|
lineOffset -= ((3 * viewLines) / 4);
|
|
|
|
if (lineOffset < 0)
|
|
{
|
|
lineOffset = 0;
|
|
}
|
|
vbar->setValue(lineOffset);
|
|
}
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void QRamSearchView::mousePressEvent(QMouseEvent *event)
|
|
{
|
|
int lineNum = convPixToLine(event->pos());
|
|
|
|
//printf("c: %ix%i \n", c.x(), c.y() );
|
|
|
|
if (event->button() == Qt::LeftButton)
|
|
{
|
|
selLine = lineOffset + lineNum;
|
|
}
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void QRamSearchView::wheelEvent(QWheelEvent *event)
|
|
{
|
|
int zDelta = 0;
|
|
|
|
QPoint numPixels = event->pixelDelta();
|
|
QPoint numDegrees = event->angleDelta();
|
|
|
|
if (!numPixels.isNull())
|
|
{
|
|
wheelPixelCounter -= numPixels.y();
|
|
//printf("numPixels: (%i,%i) \n", numPixels.x(), numPixels.y() );
|
|
|
|
if ( wheelPixelCounter >= pxLineSpacing )
|
|
{
|
|
zDelta = (wheelPixelCounter / pxLineSpacing);
|
|
|
|
wheelPixelCounter = wheelPixelCounter % pxLineSpacing;
|
|
}
|
|
else if ( wheelPixelCounter <= -pxLineSpacing )
|
|
{
|
|
zDelta = (wheelPixelCounter / pxLineSpacing);
|
|
|
|
wheelPixelCounter = wheelPixelCounter % pxLineSpacing;
|
|
}
|
|
}
|
|
else if (!numDegrees.isNull())
|
|
{
|
|
int stepDeg = 120;
|
|
//QPoint numSteps = numDegrees / 15;
|
|
//printf("numSteps: (%i,%i) \n", numSteps.x(), numSteps.y() );
|
|
//printf("numDegrees: (%i,%i) %i\n", numDegrees.x(), numDegrees.y(), pxLineSpacing );
|
|
wheelAngleCounter -= numDegrees.y();
|
|
|
|
if ( wheelAngleCounter <= stepDeg )
|
|
{
|
|
zDelta = wheelAngleCounter / stepDeg;
|
|
|
|
wheelAngleCounter = wheelAngleCounter % stepDeg;
|
|
}
|
|
else if ( wheelAngleCounter >= stepDeg )
|
|
{
|
|
zDelta = wheelAngleCounter / stepDeg;
|
|
|
|
wheelAngleCounter = wheelAngleCounter % stepDeg;
|
|
}
|
|
}
|
|
//printf("Wheel Event: %i\n", wheelPixelCounter);
|
|
|
|
if (zDelta != 0)
|
|
{
|
|
lineOffset += zDelta;
|
|
|
|
if (lineOffset < 0)
|
|
{
|
|
lineOffset = 0;
|
|
}
|
|
else if (lineOffset > maxLineOffset)
|
|
{
|
|
lineOffset = maxLineOffset;
|
|
}
|
|
vbar->setValue(lineOffset);
|
|
}
|
|
|
|
event->accept();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void QRamSearchView::paintEvent(QPaintEvent *event)
|
|
{
|
|
int i, x, y, row, nrow;
|
|
std::list<struct memoryLocation_t *>::iterator it;
|
|
char addrStr[32], valStr[32], prevStr[32], chgStr[32];
|
|
QPainter painter(this);
|
|
memoryLocation_t *loc = NULL;
|
|
int fieldWidth, fieldPad[4], fieldLen[4], fieldStart[4];
|
|
const char *fieldText[4];
|
|
|
|
painter.setFont(font);
|
|
viewWidth = event->rect().width();
|
|
viewHeight = event->rect().height();
|
|
fieldWidth = viewWidth / 4;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
fieldStart[i] = fieldWidth * i;
|
|
}
|
|
|
|
nrow = (viewHeight / pxLineSpacing) - 1;
|
|
|
|
if (nrow < 1)
|
|
nrow = 1;
|
|
|
|
viewLines = nrow;
|
|
|
|
maxLineOffset = actvSrchList.size() - nrow;
|
|
|
|
if (maxLineOffset < 1)
|
|
maxLineOffset = 1;
|
|
|
|
lineOffset = vbar->value();
|
|
|
|
vbar->setMaximum(maxLineOffset);
|
|
|
|
if (lineOffset > maxLineOffset)
|
|
{
|
|
lineOffset = maxLineOffset;
|
|
vbar->setValue(lineOffset);
|
|
}
|
|
if (lineOffset < 0)
|
|
{
|
|
lineOffset = 0;
|
|
vbar->setValue(0);
|
|
}
|
|
|
|
i = 0;
|
|
it = actvSrchList.begin();
|
|
while (it != actvSrchList.end())
|
|
{
|
|
if (i == lineOffset)
|
|
{
|
|
break;
|
|
}
|
|
i++;
|
|
it++;
|
|
}
|
|
|
|
painter.fillRect(0, 0, viewWidth, viewHeight, this->palette().color(QPalette::Window));
|
|
|
|
painter.setPen(this->palette().color(QPalette::WindowText));
|
|
|
|
pxLineXScroll = (int)(0.010f * (float)hbar->value() * (float)(pxLineWidth - viewWidth));
|
|
|
|
x = -pxLineXScroll;
|
|
y = pxLineSpacing;
|
|
|
|
strcpy(addrStr, "Address");
|
|
strcpy(valStr, "Value");
|
|
strcpy(prevStr, "Previous");
|
|
strcpy(chgStr, "Changes");
|
|
|
|
fieldText[0] = addrStr;
|
|
fieldText[1] = valStr;
|
|
fieldText[2] = prevStr;
|
|
fieldText[3] = chgStr;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
fieldLen[i] = strlen(fieldText[i]) * pxCharWidth;
|
|
|
|
fieldPad[i] = (fieldWidth - fieldLen[i]) / 2;
|
|
|
|
painter.drawText(x + fieldStart[i] + fieldPad[i], y, tr(fieldText[i]));
|
|
}
|
|
|
|
y += pxLineSpacing;
|
|
|
|
for (row = 0; row < nrow; row++)
|
|
{
|
|
if (it != actvSrchList.end())
|
|
{
|
|
loc = *it;
|
|
}
|
|
else
|
|
{
|
|
loc = NULL;
|
|
}
|
|
|
|
if (loc == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
if (selLine >= 0)
|
|
{
|
|
if (selLine == (lineOffset + row))
|
|
{
|
|
selAddr = loc->addr;
|
|
}
|
|
}
|
|
it++;
|
|
|
|
if (selAddr == loc->addr)
|
|
{
|
|
painter.fillRect(0, y - pxLineSpacing + pxLineLead, viewWidth, pxLineSpacing, QColor("light blue"));
|
|
}
|
|
|
|
sprintf(addrStr, "$%04X", loc->addr);
|
|
|
|
if (dpySize == 'd')
|
|
{
|
|
if (dpyType == 'h')
|
|
{
|
|
sprintf(valStr, "0x%08X", loc->val.v32.u);
|
|
sprintf(prevStr, "0x%08X", loc->hist.back().v32.u);
|
|
}
|
|
else if (dpyType == 'u')
|
|
{
|
|
sprintf(valStr, "%u", loc->val.v32.u);
|
|
sprintf(prevStr, "%u", loc->hist.back().v32.u);
|
|
}
|
|
else
|
|
{
|
|
sprintf(valStr, "%i", loc->val.v32.i);
|
|
sprintf(prevStr, "%i", loc->hist.back().v32.i);
|
|
}
|
|
}
|
|
else if (dpySize == 'w')
|
|
{
|
|
if (dpyType == 'h')
|
|
{
|
|
sprintf(valStr, "0x%04X", loc->val.v16.u);
|
|
sprintf(prevStr, "0x%04X", loc->hist.back().v16.u);
|
|
}
|
|
else if (dpyType == 'u')
|
|
{
|
|
sprintf(valStr, "%u", loc->val.v16.u);
|
|
sprintf(prevStr, "%u", loc->hist.back().v16.u);
|
|
}
|
|
else
|
|
{
|
|
sprintf(valStr, "%i", loc->val.v16.i);
|
|
sprintf(prevStr, "%i", loc->hist.back().v16.i);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (dpyType == 'h')
|
|
{
|
|
sprintf(valStr, "0x%02X", loc->val.v8.u);
|
|
sprintf(prevStr, "0x%02X", loc->hist.back().v8.u);
|
|
}
|
|
else if (dpyType == 'u')
|
|
{
|
|
sprintf(valStr, "%u", loc->val.v8.u);
|
|
sprintf(prevStr, "%u", loc->hist.back().v8.u);
|
|
}
|
|
else
|
|
{
|
|
sprintf(valStr, "%i", loc->val.v8.i);
|
|
sprintf(prevStr, "%i", loc->hist.back().v8.i);
|
|
}
|
|
}
|
|
sprintf(chgStr, "%u", loc->chgCount);
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
fieldLen[i] = strlen(fieldText[i]) * pxCharWidth;
|
|
|
|
fieldPad[i] = (fieldWidth - fieldLen[i]) / 2;
|
|
|
|
painter.drawText(x + fieldStart[i] + fieldPad[i], y, tr(fieldText[i]));
|
|
}
|
|
|
|
y += pxLineSpacing;
|
|
}
|
|
|
|
painter.drawLine(0, pxLineSpacing + pxLineLead, viewWidth, pxLineSpacing + pxLineLead);
|
|
painter.drawLine(x + fieldStart[1], 0, x + fieldStart[1], viewHeight);
|
|
painter.drawLine(x + fieldStart[2], 0, x + fieldStart[2], viewHeight);
|
|
painter.drawLine(x + fieldStart[3], 0, x + fieldStart[3], viewHeight);
|
|
}
|
|
//----------------------------------------------------------------------------
|