diff --git a/TODO-SDL b/TODO-SDL index 6fc0b1bb..2a81dfdc 100644 --- a/TODO-SDL +++ b/TODO-SDL @@ -39,7 +39,7 @@ Movie record/save/play functionality | YES | YES Cheat search window | YES | YES | Active Cheat window | YES | YES | RAM Search Window | NO | NO | -RAM Watch Window | NO | YES | +RAM Watch Window | YES | YES | Memory Watch Window | NO | NO | TAS Editor | NO | NO | 6502 Debugger Window | YES | YES | diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 344a4ce2..a21663ab 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -444,6 +444,7 @@ set(SRC_DRIVERS_SDL ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/fceuWrapper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ppuViewer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/NameTableViewer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/RamWatch.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/config.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/input.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/nes_shm.cpp diff --git a/src/drivers/Qt/CheatsConf.cpp b/src/drivers/Qt/CheatsConf.cpp index 18af6258..0dbfd5ee 100644 --- a/src/drivers/Qt/CheatsConf.cpp +++ b/src/drivers/Qt/CheatsConf.cpp @@ -36,14 +36,6 @@ GuiCheatsDialog_t::GuiCheatsDialog_t(QWidget *parent) QLabel *lbl; QGroupBox *groupBox; QFrame *frame; - QScreen *screen = QGuiApplication::primaryScreen(); - double devPixRatio = 1.0f; - - if ( screen != NULL ) - { - devPixRatio = (int)( screen->devicePixelRatio() + 0.50f); - //printf("Pix Ratio: %f \n", devPixRatio ); - } font.setFamily("Courier New"); font.setStyle( QFont::StyleNormal ); @@ -51,8 +43,11 @@ GuiCheatsDialog_t::GuiCheatsDialog_t(QWidget *parent) QFontMetrics fm(font); - //fontCharWidth = fm.boundingRect('X').width() * devPixRatio; - fontCharWidth = 2.00 * fm.averageCharWidth() * devPixRatio; +#if QT_VERSION > QT_VERSION_CHECK(5, 11, 0) + fontCharWidth = 2 * fm.horizontalAdvance(QLatin1Char('2')); +#else + fontCharWidth = 2 * fm.width(QLatin1Char('2')); +#endif setWindowTitle("Cheat Search"); @@ -644,6 +639,7 @@ int GuiCheatsDialog_t::activeCheatListCB (char *name, uint32 a, uint8 v, int c, item->setTextAlignment( 0, Qt::AlignLeft); item->setTextAlignment( 1, Qt::AlignLeft); + item->setTextAlignment( 2, Qt::AlignLeft); actvCheatIdx++; diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp index 71e206a9..a785e037 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -35,6 +35,7 @@ #include "Qt/fceuWrapper.h" #include "Qt/ppuViewer.h" #include "Qt/NameTableViewer.h" +#include "Qt/RamWatch.h" #include "Qt/keyscan.h" #include "Qt/nes_shm.h" @@ -483,6 +484,14 @@ void consoleWin_t::createMainMenu(void) toolsMenu->addAction(cheatsAct); + // Tools -> RAM Watch + ramWatchAct = new QAction(tr("RAM Watch..."), this); + //ramWatchAct->setShortcut( QKeySequence(tr("Shift+F7"))); + ramWatchAct->setStatusTip(tr("Open RAM Watch Window")); + connect(ramWatchAct, SIGNAL(triggered()), this, SLOT(openRamWatch(void)) ); + + toolsMenu->addAction(ramWatchAct); + //----------------------------------------------------------------------- // Debug debugMenu = menuBar()->addMenu(tr("Debug")); @@ -1017,6 +1026,17 @@ void consoleWin_t::openCheats(void) cheatWin->show(); } +void consoleWin_t::openRamWatch(void) +{ + RamWatchDialog_t *ramWatchWin; + + //printf("Open GUI RAM Watch Window\n"); + + ramWatchWin = new RamWatchDialog_t(this); + + ramWatchWin->show(); +} + void consoleWin_t::openDebugWindow(void) { ConsoleDebugger *debugWin; diff --git a/src/drivers/Qt/ConsoleWindow.h b/src/drivers/Qt/ConsoleWindow.h index cf082440..08ea48dd 100644 --- a/src/drivers/Qt/ConsoleWindow.h +++ b/src/drivers/Qt/ConsoleWindow.h @@ -89,6 +89,7 @@ class consoleWin_t : public QMainWindow QAction *fdsEjectAct; QAction *fdsLoadBiosAct; QAction *cheatsAct; + QAction *ramWatchAct; QAction *debuggerAct; QAction *codeDataLogAct; QAction *traceLogAct; @@ -167,6 +168,7 @@ class consoleWin_t : public QMainWindow void openPPUViewer(void); void openNTViewer(void); void openCheats(void); + void openRamWatch(void); void openMovie(void); void stopMovie(void); void recordMovie(void); diff --git a/src/drivers/Qt/NameTableViewer.cpp b/src/drivers/Qt/NameTableViewer.cpp index c64b5b16..a2ed46f3 100644 --- a/src/drivers/Qt/NameTableViewer.cpp +++ b/src/drivers/Qt/NameTableViewer.cpp @@ -44,7 +44,9 @@ static class NTCache public: NTCache(void) : curr_vnapage(0) - {} + { + memset( cache, 0, sizeof(cache) ); + } uint8_t* curr_vnapage; uint8_t cache[0x400]; diff --git a/src/drivers/Qt/RamWatch.cpp b/src/drivers/Qt/RamWatch.cpp new file mode 100644 index 00000000..63b6b5ab --- /dev/null +++ b/src/drivers/Qt/RamWatch.cpp @@ -0,0 +1,1334 @@ +// HotKeyConf.cpp +// +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../types.h" +#include "../../fceu.h" +#include "../../cheat.h" +#include "../../debug.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/ConsoleUtilities.h" + +ramWatchList_t ramWatchList; +//---------------------------------------------------------------------------- +RamWatchDialog_t::RamWatchDialog_t(QWidget *parent) + : QDialog( parent ) +{ + QMenuBar *menuBar; + QHBoxLayout *mainLayout; + QVBoxLayout *vbox, *vbox1; + QTreeWidgetItem *item; + QMenu *fileMenu, *watchMenu; + QAction *menuAct; + QGroupBox *frame; + int useNativeMenuBar; + + font.setFamily("Courier New"); + font.setStyle( QFont::StyleNormal ); + font.setStyleHint( QFont::Monospace ); + + QFontMetrics fm(font); + +#if QT_VERSION > QT_VERSION_CHECK(5, 11, 0) + fontCharWidth = 2 * fm.horizontalAdvance(QLatin1Char('2')); +#else + fontCharWidth = 2 * fm.width(QLatin1Char('2')); +#endif + + setWindowTitle("RAM Watch"); + + resize( 512, 512 ); + + 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 + //----------------------------------------------------------------------- + // File + fileMenu = menuBar->addMenu(tr("File")); + + // File -> New List + menuAct = new QAction(tr("New List"), this); + menuAct->setShortcut( QKeySequence(tr("Ctrl+N")) ); + menuAct->setStatusTip(tr("New List")); + connect(menuAct, SIGNAL(triggered()), this, SLOT(newListCB(void)) ); + + fileMenu->addAction(menuAct); + + // File -> Open + menuAct = new QAction(tr("Open"), this); + menuAct->setShortcut( QKeySequence(tr("Ctrl+O")) ); + menuAct->setStatusTip(tr("Open Watch File")); + connect(menuAct, SIGNAL(triggered()), this, SLOT(openListCB(void)) ); + + fileMenu->addAction(menuAct); + + // File -> Save + menuAct = new QAction(tr("Save"), this); + menuAct->setShortcut( QKeySequence(tr("Ctrl+S")) ); + menuAct->setStatusTip(tr("Save Watch File")); + connect(menuAct, SIGNAL(triggered()), this, SLOT(saveListCB(void)) ); + + fileMenu->addAction(menuAct); + + // File -> Save As + menuAct = new QAction(tr("Save As"), this); + menuAct->setShortcut( QKeySequence(tr("Ctrl+Shift+S")) ); + menuAct->setStatusTip(tr("Save As Watch File")); + connect(menuAct, SIGNAL(triggered()), this, SLOT(saveListAs(void)) ); + + fileMenu->addAction(menuAct); + + // File -> Append + menuAct = new QAction(tr("Append from File"), this); + //menuAct->setShortcut( QKeySequence(tr("Ctrl+A")) ); + menuAct->setStatusTip(tr("Append from File")); + connect(menuAct, SIGNAL(triggered()), this, SLOT(appendListCB(void)) ); + + fileMenu->addAction(menuAct); + + fileMenu->addSeparator(); + + // File -> Append + menuAct = new QAction(tr("Close"), this); + menuAct->setShortcut( QKeySequence(tr("Alt+F4")) ); + menuAct->setStatusTip(tr("Close Window")); + connect(menuAct, SIGNAL(triggered()), this, SLOT(closeWindow(void)) ); + + fileMenu->addAction(menuAct); + + // Watch + watchMenu = menuBar->addMenu(tr("Watch")); + + // Watch -> New Watch + menuAct = new QAction(tr("New Watch"), this); + menuAct->setShortcut( QKeySequence(tr("N")) ); + menuAct->setStatusTip(tr("New Watch")); + connect(menuAct, SIGNAL(triggered()), this, SLOT(newWatchClicked(void)) ); + + watchMenu->addAction(menuAct); + + // Watch -> Edit Watch + menuAct = new QAction(tr("Edit Watch"), this); + menuAct->setShortcut( QKeySequence(tr("E")) ); + menuAct->setStatusTip(tr("Edit Watch")); + connect(menuAct, SIGNAL(triggered()), this, SLOT(editWatchClicked(void)) ); + + watchMenu->addAction(menuAct); + + // Watch -> Remove Watch + menuAct = new QAction(tr("Remove Watch"), this); + menuAct->setShortcut( QKeySequence(tr("R")) ); + menuAct->setStatusTip(tr("Remove Watch")); + connect(menuAct, SIGNAL(triggered()), this, SLOT(removeWatchClicked(void)) ); + + watchMenu->addAction(menuAct); + + // Watch -> Duplicate Watch + menuAct = new QAction(tr("Duplicate Watch"), this); + menuAct->setShortcut( QKeySequence(tr("A")) ); + menuAct->setStatusTip(tr("Duplicate Watch")); + connect(menuAct, SIGNAL(triggered()), this, SLOT(dupWatchClicked(void)) ); + + watchMenu->addAction(menuAct); + + // Watch -> Add Separator + menuAct = new QAction(tr("Add Separator"), this); + menuAct->setShortcut( QKeySequence(tr("S")) ); + menuAct->setStatusTip(tr("Add Separator")); + //connect(menuAct, SIGNAL(triggered()), this, SLOT(newListCB(void)) ); + + watchMenu->addAction(menuAct); + + watchMenu->addSeparator(); + + // Watch -> Move Up + menuAct = new QAction(tr("Move Up"), this); + menuAct->setShortcut( QKeySequence(tr("U")) ); + menuAct->setStatusTip(tr("Move Up")); + connect(menuAct, SIGNAL(triggered()), this, SLOT(moveWatchUpClicked(void)) ); + + watchMenu->addAction(menuAct); + + // Watch -> Move Down + menuAct = new QAction(tr("Move Down"), this); + menuAct->setShortcut( QKeySequence(tr("D")) ); + menuAct->setStatusTip(tr("Move Down")); + connect(menuAct, SIGNAL(triggered()), this, SLOT(moveWatchDownClicked(void)) ); + + watchMenu->addAction(menuAct); + + //----------------------------------------------------------------------- + // End Menu + //----------------------------------------------------------------------- + + mainLayout = new QHBoxLayout(); + + tree = new QTreeWidget(); + + tree->setColumnCount(4); + + item = new QTreeWidgetItem(); + item->setText( 0, QString::fromStdString( "Address" ) ); + item->setText( 1, QString::fromStdString( "Value Dec" ) ); + item->setText( 2, QString::fromStdString( "Value Hex" ) ); + item->setText( 3, QString::fromStdString( "Notes" ) ); + item->setTextAlignment( 0, Qt::AlignLeft); + item->setTextAlignment( 1, Qt::AlignLeft); + item->setTextAlignment( 2, Qt::AlignLeft); + item->setTextAlignment( 3, Qt::AlignLeft); + + connect( tree, SIGNAL(itemClicked(QTreeWidgetItem*, int)), + this, SLOT(watchClicked( QTreeWidgetItem*, int)) ); + + tree->setHeaderItem( item ); + + //tree->header()->setSectionResizeMode( QHeaderView::ResizeToContents ); + + vbox1 = new QVBoxLayout(); + vbox = new QVBoxLayout(); + frame = new QGroupBox( tr("Watches") ); + vbox1->addWidget( frame ); + frame->setLayout( vbox ); + + up_btn = new QPushButton( tr("Up") ); + vbox->addWidget( up_btn ); + connect( up_btn, SIGNAL(clicked(void)), this, SLOT(moveWatchUpClicked(void))); + up_btn->setEnabled(false); + + down_btn = new QPushButton( tr("Down") ); + vbox->addWidget( down_btn ); + connect( down_btn, SIGNAL(clicked(void)), this, SLOT(moveWatchDownClicked(void))); + down_btn->setEnabled(false); + + edit_btn = new QPushButton( tr("Edit") ); + vbox->addWidget( edit_btn ); + connect( edit_btn, SIGNAL(clicked(void)), this, SLOT(editWatchClicked(void))); + edit_btn->setEnabled(false); + + del_btn = new QPushButton( tr("Remove") ); + vbox->addWidget( del_btn ); + connect( del_btn, SIGNAL(clicked(void)), this, SLOT(removeWatchClicked(void))); + del_btn->setEnabled(false); + + new_btn = new QPushButton( tr("New") ); + vbox->addWidget( new_btn ); + connect( new_btn, SIGNAL(clicked(void)), this, SLOT(newWatchClicked(void))); + new_btn->setEnabled(true); + + dup_btn = new QPushButton( tr("Duplicate") ); + vbox->addWidget( dup_btn ); + connect( dup_btn, SIGNAL(clicked(void)), this, SLOT(dupWatchClicked(void))); + dup_btn->setEnabled(false); + + sep_btn = new QPushButton( tr("Separator") ); + vbox->addWidget( sep_btn ); + sep_btn->setEnabled(true); + connect( sep_btn, SIGNAL(clicked(void)), this, SLOT(sepWatchClicked(void))); + + mainLayout->addWidget( tree ); + mainLayout->addLayout( vbox1 ); + mainLayout->setMenuBar( menuBar ); + + cht_btn = new QPushButton( tr("Add Cheat") ); + vbox1->addWidget( cht_btn ); + cht_btn->setEnabled(false); + connect( cht_btn, SIGNAL(clicked(void)), this, SLOT(addCheatClicked(void))); + + setLayout( mainLayout ); + + updateTimer = new QTimer( this ); + + connect( updateTimer, &QTimer::timeout, this, &RamWatchDialog_t::periodicUpdate ); + + updateTimer->start( 100 ); // 10hz +} +//---------------------------------------------------------------------------- +RamWatchDialog_t::~RamWatchDialog_t(void) +{ + updateTimer->stop(); + printf("Destroy RAM Watch Config Window\n"); +} +//---------------------------------------------------------------------------- +void RamWatchDialog_t::closeEvent(QCloseEvent *event) +{ + printf("RAM Watch Close Window Event\n"); + done(0); + deleteLater(); + event->accept(); +} +//---------------------------------------------------------------------------- +void RamWatchDialog_t::closeWindow(void) +{ + //printf("Close Window\n"); + done(0); + deleteLater(); +} +//---------------------------------------------------------------------------- +void RamWatchDialog_t::periodicUpdate(void) +{ + bool buttonEnable; + QTreeWidgetItem *item; + + item = tree->currentItem(); + + if ( item == NULL ) + { + buttonEnable = false; + } + else + { + buttonEnable = true; + } + up_btn->setEnabled(buttonEnable); + down_btn->setEnabled(buttonEnable); + edit_btn->setEnabled(buttonEnable); + del_btn->setEnabled(buttonEnable); + dup_btn->setEnabled(buttonEnable); + cht_btn->setEnabled(buttonEnable); + + + updateRamWatchDisplay(); + +} +//---------------------------------------------------------------------------- +void RamWatchDialog_t::updateRamWatchDisplay(void) +{ + int idx=0; + QTreeWidgetItem *item; + std::list < ramWatch_t * >::iterator it; + char addrStr[32], valStr1[16], valStr2[16]; + ramWatch_t *rw; + + for (it = ramWatchList.ls.begin (); it != ramWatchList.ls.end (); it++) + { + rw = *it; + + item = tree->topLevelItem(idx); + + if ( item == NULL ) + { + item = new QTreeWidgetItem(); + + tree->addTopLevelItem( item ); + + item->setFont( 0, font); + item->setFont( 1, font); + item->setFont( 2, font); + item->setFont( 3, font); + } + if ( rw->isSep || (rw->addr < 0) ) + { + strcpy (addrStr, "--------"); + } + else + { + if ( rw->size > 1 ) + { + sprintf (addrStr, "$%04X-$%04X", rw->addr, rw->addr + rw->size - 1); + } + else + { + sprintf (addrStr, "$%04X", rw->addr); + } + } + + rw->updateMem (); + + if ( rw->isSep || (rw->addr < 0) ) + { + strcpy( valStr1, "--------"); + strcpy( valStr2, "--------"); + } + else + { + if (rw->size == 4) + { + if (rw->type == 's') + { + sprintf (valStr1, "%i", rw->val.i32); + } + else + { + sprintf (valStr1, "%u", rw->val.u32); + } + sprintf (valStr2, "0x%08X", rw->val.u32); + } + else if (rw->size == 2) + { + if (rw->type == 's') + { + sprintf (valStr1, "%6i", rw->val.i16); + } + else + { + sprintf (valStr1, "%6u", rw->val.u16); + } + sprintf (valStr2, "0x%04X", rw->val.u16); + } + else + { + if (rw->type == 's') + { + sprintf (valStr1, "%6i", rw->val.i8); + } + else + { + sprintf (valStr1, "%6u", rw->val.u8); + } + sprintf (valStr2, "0x%02X", rw->val.u8); + } + } + + item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren ); + + if ( rw->isSep ) + { + int i,j; + char stmp[256]; + const char *c; + + j=0; + + for (i=0; i<3; i++) + { + stmp[j] = '-'; j++; + } + stmp[j] = ' '; j++; + + c = rw->name.c_str(); i = 0; + + while ( c[i] != 0 ) + { + stmp[j] = c[i]; j++; i++; + } + stmp[j] = ' '; j++; + + while ( j < 64 ) + { + stmp[j] = '-'; j++; + } + stmp[j] = 0; + + item->setFirstColumnSpanned(true); + item->setText( 0, tr(stmp) ); + } + else + { + item->setFirstColumnSpanned(false); + item->setText( 0, tr(addrStr) ); + item->setText( 1, tr(valStr1) ); + item->setText( 2, tr(valStr2) ); + item->setText( 3, tr(rw->name.c_str()) ); + } + + item->setTextAlignment( 0, Qt::AlignLeft); + item->setTextAlignment( 1, Qt::AlignCenter); + item->setTextAlignment( 2, Qt::AlignCenter); + item->setTextAlignment( 3, Qt::AlignLeft); + + idx++; + } + tree->viewport()->update(); +} +//---------------------------------------------------------------------------- +void RamWatchDialog_t::watchClicked( QTreeWidgetItem *item, int column) +{ +// int row = tree->indexOfTopLevelItem(item); + +} +//---------------------------------------------------------------------------- +void RamWatchDialog_t::newListCB(void) +{ + ramWatchList.clear(); + tree->clear(); + tree->viewport()->update(); +} +//---------------------------------------------------------------------------- +void RamWatchDialog_t::openListCB(void) +{ + int ret, useNativeFileDialogVal; + QString filename; + const char *romFile = NULL; + QFileDialog dialog(this, tr("Open Watch File") ); + + dialog.setFileMode(QFileDialog::ExistingFile); + + dialog.setNameFilter(tr("Watch files (*.wch *.WCH) ;; All files (*)")); + + dialog.setViewMode(QFileDialog::List); + dialog.setFilter( QDir::AllEntries | QDir::Hidden ); + dialog.setLabelText( QFileDialog::Accept, tr("Open") ); + + //g_config->getOption ("SDL.LastOpenFile", &last ); + + romFile = getRomFile(); + + if ( romFile != NULL ) + { + char dir[512], base[256]; + + parseFilepath( romFile, dir, base ); + + strcat( base, ".wch"); + + dialog.setDirectory( tr(dir) ); + + dialog.selectFile( tr(base) ); + } + + // Check config option to use native file dialog or not + g_config->getOption ("SDL.UseNativeFileDialog", &useNativeFileDialogVal); + + dialog.setOption(QFileDialog::DontUseNativeDialog, !useNativeFileDialogVal); + + dialog.show(); + ret = dialog.exec(); + + if ( ret ) + { + QStringList fileList; + fileList = dialog.selectedFiles(); + + if ( fileList.size() > 0 ) + { + filename = fileList[0]; + } + } + + if ( filename.isNull() ) + { + return; + } + //qDebug() << "selected file path : " << filename.toUtf8(); + + loadWatchFile ( filename.toStdString().c_str() ); + + return; +} +//---------------------------------------------------------------------------- +void RamWatchDialog_t::appendListCB(void) +{ + int ret, useNativeFileDialogVal; + QString filename; + const char *romFile = NULL; + QFileDialog dialog(this, tr("Append from Watch File") ); + + dialog.setFileMode(QFileDialog::ExistingFile); + + dialog.setNameFilter(tr("Watch Files (*.wch *.WCH) ;; All files (*)")); + + dialog.setViewMode(QFileDialog::List); + dialog.setFilter( QDir::AllEntries | QDir::Hidden ); + dialog.setLabelText( QFileDialog::Accept, tr("Load") ); + dialog.setDefaultSuffix( tr(".wch") ); + + romFile = getRomFile(); + + if ( romFile != NULL ) + { + char dir[512], base[256]; + + parseFilepath( romFile, dir, base ); + + strcat( base, ".wch"); + + dialog.setDirectory( tr(dir) ); + + dialog.selectFile( tr(base) ); + } + + // Check config option to use native file dialog or not + g_config->getOption ("SDL.UseNativeFileDialog", &useNativeFileDialogVal); + + dialog.setOption(QFileDialog::DontUseNativeDialog, !useNativeFileDialogVal); + + dialog.show(); + ret = dialog.exec(); + + if ( ret ) + { + QStringList fileList; + fileList = dialog.selectedFiles(); + + if ( fileList.size() > 0 ) + { + filename = fileList[0]; + } + } + + if ( filename.isNull() ) + { + return; + } + //qDebug() << "selected file path : " << filename.toUtf8(); + + loadWatchFile( filename.toStdString().c_str(), 1 ); +} +//---------------------------------------------------------------------------- +void RamWatchDialog_t::saveListCB(void) +{ + if ( ramWatchList.size() == 0 ) + { + return; + } + + if ( saveFileName.size() > 0 ) + { + char file[512]; + + strcpy( file, saveFileName.c_str() ); + + saveWatchFile( file ); + } + else + { + saveListAs(); + } +} +//---------------------------------------------------------------------------- +void RamWatchDialog_t::saveListAs(void) +{ + int ret, useNativeFileDialogVal; + QString filename; + const char *romFile = NULL; + QFileDialog dialog(this, tr("Save Watch List To File") ); + + if ( ramWatchList.size() == 0 ) + { + return; + } + dialog.setFileMode(QFileDialog::AnyFile); + + dialog.setNameFilter(tr("Watch Files (*.wch *.WCH) ;; All files (*)")); + + dialog.setViewMode(QFileDialog::List); + dialog.setFilter( QDir::AllEntries | QDir::Hidden ); + dialog.setLabelText( QFileDialog::Accept, tr("Save") ); + dialog.setDefaultSuffix( tr(".wch") ); + + romFile = getRomFile(); + + if ( romFile != NULL ) + { + char dir[512], base[256]; + + parseFilepath( romFile, dir, base ); + + strcat( base, ".wch"); + + dialog.setDirectory( tr(dir) ); + + dialog.selectFile( tr(base) ); + } + + // Check config option to use native file dialog or not + g_config->getOption ("SDL.UseNativeFileDialog", &useNativeFileDialogVal); + + dialog.setOption(QFileDialog::DontUseNativeDialog, !useNativeFileDialogVal); + + dialog.show(); + ret = dialog.exec(); + + if ( ret ) + { + QStringList fileList; + fileList = dialog.selectedFiles(); + + if ( fileList.size() > 0 ) + { + filename = fileList[0]; + } + } + + if ( filename.isNull() ) + { + return; + } + //qDebug() << "selected file path : " << filename.toUtf8(); + + saveWatchFile( filename.toStdString().c_str() ); +} +//---------------------------------------------------------------------------- +void ramWatch_t::updateMem (void) +{ + if ( addr < 0 ) + { + return; + } + if (size == 1) + { + val.u8 = GetMem (addr); + } + else if (size == 2) + { + val.u16 = GetMem (addr) | (GetMem (addr + 1) << 8); + } + else + { + val.u8 = GetMem (addr); + } +} +//---------------------------------------------------------------------------- +void RamWatchDialog_t::openWatchEditWindow( ramWatch_t *rw, int mode) +{ + int ret, isSep = 0; + QDialog dialog(this); + QVBoxLayout *mainLayout, *vbox; + QHBoxLayout *hbox; + QLabel *lbl; + QLineEdit *addrEntry, *notesEntry; + QGroupBox *frame; + QRadioButton *signedTypeBtn, *unsignedTypeBtn; + QRadioButton *dataSize1Btn, *dataSize2Btn, *dataSize4Btn; + QPushButton *cancelButton, *okButton; + + if ( rw == NULL ) + { + dialog.setWindowTitle("Add Watch"); + } + else + { + if ( rw->isSep ) + { + if ( mode ) + { + dialog.setWindowTitle("Add Separator"); + } + else + { + dialog.setWindowTitle("Edit Separator"); + } + } + else + { + dialog.setWindowTitle("Edit Watch"); + } + } + + mainLayout = new QVBoxLayout(); + + dialog.setLayout( mainLayout ); + + hbox = new QHBoxLayout(); + lbl = new QLabel( tr("Address") ); + addrEntry = new QLineEdit(); + + addrEntry->setMaxLength(4); + addrEntry->setInputMask( ">HHHH;" ); + addrEntry->setCursorPosition(0); + + mainLayout->addLayout( hbox ); + hbox->addWidget( lbl ); + hbox->addWidget( addrEntry ); + + hbox = new QHBoxLayout(); + lbl = new QLabel( tr("Notes") ); + notesEntry = new QLineEdit(); + + mainLayout->addLayout( hbox ); + hbox->addWidget( lbl ); + hbox->addWidget( notesEntry ); + + hbox = new QHBoxLayout(); + mainLayout->addLayout( hbox ); + + vbox = new QVBoxLayout(); + frame = new QGroupBox( tr("Data Type") ); + hbox->addWidget( frame ); + frame->setLayout( vbox ); + + signedTypeBtn = new QRadioButton( tr("Signed") ); + unsignedTypeBtn = new QRadioButton( tr("Unsigned") ); + + vbox->addWidget( signedTypeBtn ); + vbox->addWidget( unsignedTypeBtn ); + + vbox = new QVBoxLayout(); + frame = new QGroupBox( tr("Data Size") ); + hbox->addWidget( frame ); + frame->setLayout( vbox ); + + dataSize1Btn = new QRadioButton( tr("1 Byte") ); + dataSize2Btn = new QRadioButton( tr("2 Bytes") ); + dataSize4Btn = new QRadioButton( tr("4 Bytes") ); + + vbox->addWidget( dataSize1Btn ); + vbox->addWidget( dataSize2Btn ); + vbox->addWidget( dataSize4Btn ); + + hbox = new QHBoxLayout(); + mainLayout->addLayout( hbox ); + + okButton = new QPushButton( tr("OK") ); + cancelButton = new QPushButton( tr("Cancel") ); + hbox->addWidget( cancelButton ); + hbox->addWidget( okButton ); + okButton->setDefault(true); + + connect( okButton, SIGNAL(clicked(void)), &dialog, SLOT(accept(void)) ); + connect( cancelButton, SIGNAL(clicked(void)), &dialog, SLOT(reject(void)) ); + + if ( rw != NULL ) + { + char stmp[64]; + + isSep = rw->isSep; + + if ( (rw->addr >= 0) && !rw->isSep ) + { + sprintf( stmp, "%04X", rw->addr ); + addrEntry->setText( tr(stmp) ); + } + else + { + addrEntry->setEnabled(false); + } + + notesEntry->setText( tr(rw->name.c_str()) ); + + if ( rw->isSep ) + { + signedTypeBtn->setEnabled(false); + unsignedTypeBtn->setEnabled(false); + dataSize1Btn->setEnabled(false); + dataSize2Btn->setEnabled(false); + dataSize4Btn->setEnabled(false); + } + else + { + signedTypeBtn->setChecked( rw->type == 's' ); + unsignedTypeBtn->setChecked( rw->type != 's' ); + dataSize1Btn->setChecked( rw->size == 1 ); + dataSize2Btn->setChecked( rw->size == 2 ); + dataSize4Btn->setChecked( rw->size == 4 ); + } + } + else + { + signedTypeBtn->setChecked( true ); + unsignedTypeBtn->setChecked( false ); + dataSize1Btn->setChecked( true ); + dataSize2Btn->setChecked( false ); + dataSize4Btn->setChecked( false ); + } + + ret = dialog.exec(); + + if ( ret == QDialog::Accepted ) + { + int addr = -1, size = 1; + + addr = ::strtol( addrEntry->text().toStdString().c_str(), NULL, 16 ); + + if ( dataSize4Btn->isChecked() ) + { + size = 4; + } + else if ( dataSize2Btn->isChecked() ) + { + size = 2; + } + else + { + size = 1; + } + + if ( (rw == NULL) || mode ) + { + ramWatchList.add_entry( notesEntry->text().toStdString().c_str(), + addr, unsignedTypeBtn->isChecked(), size, isSep); + } + else + { + rw->name = notesEntry->text().toStdString(); + rw->type = unsignedTypeBtn->isChecked() ? 'u' : 's'; + rw->addr = addr; + rw->size = size; + rw->isSep = isSep; + } + } +} +//---------------------------------------------------------------------------- +void RamWatchDialog_t::addCheatClicked(void) +{ + ramWatch_t *rw = NULL; + QTreeWidgetItem *item; + + item = tree->currentItem(); + + if ( item == NULL ) + { + printf( "No Item Selected\n"); + return; + } + int row = tree->indexOfTopLevelItem(item); + + if ( row >= 0 ) + { + rw = ramWatchList.getIndex(row); + } + + if ( rw != NULL ) + { + FCEUI_AddCheat( rw->name.c_str(), rw->addr, GetMem(rw->addr), -1, 1 ); + } +} +//---------------------------------------------------------------------------- +void RamWatchDialog_t::newWatchClicked(void) +{ + openWatchEditWindow(); +} +//---------------------------------------------------------------------------- +void RamWatchDialog_t::sepWatchClicked(void) +{ + ramWatch_t rw; + + rw.addr = -1; + rw.isSep = 1; + + openWatchEditWindow( &rw, 1 ); +} +//---------------------------------------------------------------------------- +void RamWatchDialog_t::editWatchClicked(void) +{ + ramWatch_t *rw = NULL; + QTreeWidgetItem *item; + + item = tree->currentItem(); + + if ( item == NULL ) + { + printf( "No Item Selected\n"); + return; + } + int row = tree->indexOfTopLevelItem(item); + + if ( row >= 0 ) + { + rw = ramWatchList.getIndex(row); + } + openWatchEditWindow(rw, 0); +} +//---------------------------------------------------------------------------- +void RamWatchDialog_t::dupWatchClicked(void) +{ + ramWatch_t *rw = NULL; + QTreeWidgetItem *item; + + item = tree->currentItem(); + + if ( item == NULL ) + { + printf( "No Item Selected\n"); + return; + } + int row = tree->indexOfTopLevelItem(item); + + if ( row >= 0 ) + { + rw = ramWatchList.getIndex(row); + } + openWatchEditWindow(rw, 1); +} +//---------------------------------------------------------------------------- +void RamWatchDialog_t::removeWatchClicked(void) +{ + QTreeWidgetItem *item; + + item = tree->currentItem(); + + if ( item == NULL ) + { + printf( "No Item Selected\n"); + return; + } + int row = tree->indexOfTopLevelItem(item); + + ramWatchList.deleteIndex(row); + + tree->clear(); + + updateRamWatchDisplay(); + + if ( row > 0 ) + { + if ( row >= tree->topLevelItemCount() ) + { + item = tree->topLevelItem( tree->topLevelItemCount()-1 ); + } + else + { + item = tree->topLevelItem( row ); + } + } + else + { + item = tree->topLevelItem( 0 ); + } + + if ( item ) + { + tree->setCurrentItem(item); + tree->viewport()->update(); + } +} +//---------------------------------------------------------------------------- +void RamWatchDialog_t::moveWatchUpClicked(void) +{ + QTreeWidgetItem *item; + + item = tree->currentItem(); + + if ( item == NULL ) + { + printf( "No Item Selected\n"); + return; + } + int row = tree->indexOfTopLevelItem(item); + + if ( row > 0 ) + { + ramWatchList.moveIndexUp(row); + item = tree->topLevelItem( row-1 ); + + if ( item ) + { + tree->setCurrentItem(item); + tree->viewport()->update(); + } + } +} +//---------------------------------------------------------------------------- +void RamWatchDialog_t::moveWatchDownClicked(void) +{ + QTreeWidgetItem *item; + + item = tree->currentItem(); + + if ( item == NULL ) + { + printf( "No Item Selected\n"); + return; + } + int row = tree->indexOfTopLevelItem(item); + + if ( row < (tree->topLevelItemCount()-1) ) + { + ramWatchList.moveIndexDown(row); + + item = tree->topLevelItem( row+1 ); + + if ( item ) + { + tree->setCurrentItem(item); + tree->viewport()->update(); + } + } +} +//---------------------------------------------------------------------------- +void RamWatchDialog_t::saveWatchFile (const char *filename, int append ) +{ + int i, lineCount = 0, sizeChar; + FILE *fp; + const char *c, *mode; + std::list < ramWatch_t * >::iterator it; + ramWatch_t *rw; + + mode = (append) ? "a" : "w"; + + fp = fopen (filename, mode); + + if (fp == NULL) + { + printf ("Error: Failed to open file: %s\n", filename); + return; + } + saveFileName.assign( filename ); + + fprintf( fp, "\n%zi\n", ramWatchList.size() ); + + for (it = ramWatchList.ls.begin (); it != ramWatchList.ls.end (); it++) + { + rw = *it; + + c = rw->name.c_str (); + + if ( rw->isSep ) + { + fprintf (fp, "%05i %04X %c %c 0 ", lineCount, rw->addr, 'S', 'S' ); + } + else + { + if ( rw->size == 4 ) + { + sizeChar = 'd'; + } + else if ( rw->size == 2 ) + { + sizeChar = 'w'; + } + else + { + sizeChar = 'b'; + } + fprintf (fp, "%05i %04X %c %c 0 ", lineCount, rw->addr, sizeChar, rw->type); + } + + i = 0; + while (c[i]) + { + if (c[i] == '"') + { + fprintf (fp, "\\%c", c[i]); + } + else + { + fprintf (fp, "%c", c[i]); + } + i++; + } + fprintf (fp, "\n"); + + lineCount++; + } + fclose (fp); + +} +//---------------------------------------------------------------------------- +void RamWatchDialog_t::loadWatchFile (const char *filename, int append ) +{ + FILE *fp; + int i, j, a, t, s, isSep, literal; + char line[512], stmp[512]; + ramWatch_t *rw; + + fp = fopen (filename, "r"); + + if (fp == NULL) + { + printf ("Error: Failed to open file: %s\n", filename); + return; + } + saveFileName.assign( filename ); + + if ( !append ) + { + ramWatchList.clear(); + tree->clear(); + tree->viewport()->update(); + } + + while (fgets (line, sizeof (line) - 1, fp) != NULL) + { + a = -1; + t = -1; + s = -1; + isSep = 0; + // Check for Comments + i = 0; + while (line[i] != 0) + { + if (literal) + { + literal = 0; + } + else + { + if (line[i] == '#') + { + line[i] = 0; + break; + } + else if (line[i] == '\\') + { + literal = 1; + } + } + i++; + } + + i = 0; + j = 0; + while (isspace (line[i])) i++; + + while ( isdigit(line[i]) ) i++; + + while (isspace (line[i])) i++; + + if ((line[i] == '0') && (tolower (line[i + 1]) == 'x')) + { + i += 2; + + while (isxdigit (line[i])) + { + stmp[j] = line[i]; + i++; + j++; + } + } + else + { + while (isxdigit (line[i])) + { + stmp[j] = line[i]; + i++; + j++; + } + } + stmp[j] = 0; + + if (j == 0) continue; + + a = strtol (stmp, NULL, 16); + + while (isspace (line[i])) i++; + + s = tolower(line[i]); + i++; + + while (isspace (line[i])) i++; + + t = tolower(line[i]); + i++; + + if ((t != 'u') && (t != 's') && (t != 'h') && (t != 'b') ) + { + printf ("Error: Invalid RAM Watch Byte Type: %c", t); + continue; + } + if (!isdigit (s) && (s != 's') && (s != 'b') && (s != 'w') && (s != 'd') ) + { + printf ("Error: Invalid RAM Watch Byte Size: %c", s); + continue; + } + if (s == 's') + { + isSep = 1; s = 1; + } + else if ( s == 'b' ) + { + s = 1; + } + else if ( s == 'w' ) + { + s = 2; + } + else if ( s == 'd' ) + { + s = 4; + } + else if ( isdigit(s) ) + { + s = s - '0'; + } + + if ((s != 1) && (s != 2) && (s != 4)) + { + printf ("Error: Invalid RAM Watch Byte Size: %i", s); + continue; + } + while (isspace (line[i])) i++; + + while (isdigit(line[i])) i++; + + while (isspace (line[i])) i++; + + if (line[i] == '"') + { + i++; + j = 0; + literal = 0; + while ((line[i] != 0)) + { + if (literal) + { + literal = 0; + } + else + { + if (line[i] == '"') + { + break; + } + else if (line[i] == '\\') + { + literal = 1; + } + } + if (!literal) + { + stmp[j] = line[i]; + j++; + } + i++; + } + stmp[j] = 0; + } + else + { + j=0; + while (line[i] != 0) + { + if ( line[i] == '\n') + { + break; + } + stmp[j] = line[i]; i++; j++; + } + stmp[j] = 0; + } + + j--; + while ( j >= 0 ) + { + if ( isspace(stmp[j]) ) + { + stmp[j] = 0; + } + else + { + break; + } + j--; + } + rw = new ramWatch_t; + + rw->addr = a; + rw->type = t; + rw->size = s; + rw->isSep = isSep; + rw->name.assign (stmp); + + ramWatchList.ls.push_back (rw); + } + + fclose (fp); +} +//---------------------------------------------------------------------------- diff --git a/src/drivers/Qt/RamWatch.h b/src/drivers/Qt/RamWatch.h new file mode 100644 index 00000000..b15b1b4d --- /dev/null +++ b/src/drivers/Qt/RamWatch.h @@ -0,0 +1,256 @@ +// RamWatch.h +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Qt/main.h" + +struct ramWatch_t +{ + std::string name; + int addr; + int type; + int size; + int isSep; + + union + { + int8_t i8; + uint8_t u8; + int16_t i16; + uint16_t u16; + int32_t i32; + uint32_t u32; + } val; + + ramWatch_t (void) + { + addr = 0; + type = 's'; + size = 0; + isSep = 0; + val.u32 = 0; + }; + + void updateMem (void); +}; + +struct ramWatchList_t +{ + + std::list ls; + + ramWatchList_t (void) + { + + } + + ~ramWatchList_t (void) + { + ramWatch_t *rw; + + while (!ls.empty ()) + { + rw = ls.front (); + + delete rw; + + ls.pop_front (); + } + } + + size_t size (void) + { + return ls.size (); + }; + + void clear(void) + { + ramWatch_t *rw; + + while (!ls.empty ()) + { + rw = ls.front (); + + delete rw; + + ls.pop_front (); + } + } + + void add_entry (const char *name, int addr, int type, int size, int isSep = 0) + { + ramWatch_t *rw = new ramWatch_t; + + rw->name.assign (name); + rw->addr = addr; + rw->type = type; + rw->size = size; + rw->isSep = isSep; + ls.push_back (rw); + } + + void updateMemoryValues (void) + { + ramWatch_t *rw; + std::list < ramWatch_t * >::iterator it; + + for (it = ls.begin (); it != ls.end (); it++) + { + rw = *it; + + rw->updateMem (); + } + } + + ramWatch_t *getIndex (size_t idx) + { + size_t i = 0; + std::list < ramWatch_t * >::iterator it; + + for (it = ls.begin (); it != ls.end (); it++) + { + if (i == idx) + { + return *it; + } + i++; + } + return NULL; + } + + int deleteIndex (size_t idx) + { + size_t i = 0; + std::list < ramWatch_t * >::iterator it; + + for (it = ls.begin (); it != ls.end (); it++) + { + if (i == idx) + { + delete *it; + ls.erase (it); + return 0; + } + i++; + } + return -1; + } + + int moveIndexUp(size_t idx) + { + size_t i = 0; + std::list < ramWatch_t * >::iterator it, lp; + + lp = ls.begin(); + + for (it = ls.begin (); it != ls.end (); it++) + { + if (i == idx) + { + ls.insert( lp, *it ); + ls.erase (it); + return 0; + } + lp = it; + + i++; + } + return -1; + } + + int moveIndexDown(size_t idx) + { + size_t i = 0; + std::list < ramWatch_t * >::iterator it, next; + + for (it = ls.begin (); it != ls.end (); it++) + { + if (i == idx) + { + next = it; next++; + + if ( next != ls.end() ) + { + ls.insert( it, *next ); + ls.erase (next); + } + return 0; + } + i++; + } + return -1; + } +}; + +class RamWatchDialog_t : public QDialog +{ + Q_OBJECT + + public: + RamWatchDialog_t(QWidget *parent = 0); + ~RamWatchDialog_t(void); + + protected: + void closeEvent(QCloseEvent *event); + void loadWatchFile (const char *filename, int append = 0); + void saveWatchFile (const char *filename, int append = 0); + + QFont font; + QTreeWidget *tree; + QPushButton *up_btn; + QPushButton *down_btn; + QPushButton *edit_btn; + QPushButton *del_btn; + QPushButton *new_btn; + QPushButton *dup_btn; + QPushButton *sep_btn; + QPushButton *cht_btn; + QTimer *updateTimer; + + std::string saveFileName; + + //ramWatchList_t ramWatchList; + + int fontCharWidth; + + private: + void updateRamWatchDisplay(void); + void openWatchEditWindow( ramWatch_t *rw = NULL, int mode = 0); + + public slots: + void closeWindow(void); + private slots: + void newListCB(void); + void openListCB(void); + void saveListCB(void); + void saveListAs(void); + void appendListCB(void); + void periodicUpdate(void); + void addCheatClicked(void); + void newWatchClicked(void); + void sepWatchClicked(void); + void dupWatchClicked(void); + void editWatchClicked(void); + void removeWatchClicked(void); + void moveWatchUpClicked(void); + void moveWatchDownClicked(void); + void watchClicked( QTreeWidgetItem *item, int column); + +}; + +extern ramWatchList_t ramWatchList; diff --git a/src/drivers/Qt/TraceLogger.cpp b/src/drivers/Qt/TraceLogger.cpp index d8e098ff..120e316c 100644 --- a/src/drivers/Qt/TraceLogger.cpp +++ b/src/drivers/Qt/TraceLogger.cpp @@ -659,6 +659,9 @@ int traceRecord_t::convToText( char *txt ) char stmp[128]; char str_axystate[32], str_procstatus[32]; + str_axystate[0] = 0; + str_procstatus[0] = 0; + txt[0] = 0; if ( opSize == 0 ) {