1385 lines
30 KiB
C++
1385 lines
30 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
|
|
*/
|
|
//
|
|
// RamWatch.cpp
|
|
//
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <string>
|
|
|
|
#include <SDL.h>
|
|
#include <QMenuBar>
|
|
#include <QAction>
|
|
#include <QSettings>
|
|
#include <QHeaderView>
|
|
#include <QCloseEvent>
|
|
#include <QGroupBox>
|
|
#include <QLineEdit>
|
|
#include <QRadioButton>
|
|
#include <QFileDialog>
|
|
|
|
#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/CheatsConf.h"
|
|
#include "Qt/ConsoleUtilities.h"
|
|
|
|
ramWatchList_t ramWatchList;
|
|
static RamWatchDialog_t *ramWatchMainWin = NULL;
|
|
//----------------------------------------------------------------------------
|
|
void openRamWatchWindow( QWidget *parent, int force )
|
|
{
|
|
if ( !force )
|
|
{
|
|
if ( ramWatchMainWin != NULL ) return;
|
|
}
|
|
ramWatchMainWin = new RamWatchDialog_t(parent);
|
|
|
|
ramWatchMainWin->show();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
RamWatchDialog_t::RamWatchDialog_t(QWidget *parent)
|
|
: QDialog( parent, Qt::Window )
|
|
{
|
|
QSettings settings;
|
|
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 -> Close
|
|
menuAct = new QAction(tr("&Close"), this);
|
|
menuAct->setShortcut( QKeySequence::Close );
|
|
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("Duplic&ate 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 );
|
|
|
|
ramWatchMainWin = this;
|
|
|
|
updateTimer = new QTimer( this );
|
|
|
|
connect( updateTimer, &QTimer::timeout, this, &RamWatchDialog_t::periodicUpdate );
|
|
|
|
updateTimer->start( 100 ); // 10hz
|
|
|
|
restoreGeometry(settings.value("ramWatch/geometry").toByteArray());
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
RamWatchDialog_t::~RamWatchDialog_t(void)
|
|
{
|
|
QSettings settings;
|
|
|
|
updateTimer->stop();
|
|
|
|
if ( ramWatchMainWin == this )
|
|
{
|
|
ramWatchMainWin = NULL;
|
|
}
|
|
settings.setValue("ramWatch/geometry", saveGeometry());
|
|
|
|
//printf("Destroy RAM Watch Config Window\n");
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamWatchDialog_t::closeEvent(QCloseEvent *event)
|
|
{
|
|
QSettings settings;
|
|
//printf("RAM Watch Close Window Event\n");
|
|
settings.setValue("ramWatch/geometry", saveGeometry());
|
|
done(0);
|
|
deleteLater();
|
|
event->accept();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void RamWatchDialog_t::closeWindow(void)
|
|
{
|
|
QSettings settings;
|
|
//printf("Close Window\n");
|
|
settings.setValue("ramWatch/geometry", saveGeometry());
|
|
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::AllDirs | QDir::Hidden );
|
|
dialog.setLabelText( QFileDialog::Accept, tr("Open") );
|
|
|
|
//g_config->getOption ("SDL.LastOpenFile", &last );
|
|
|
|
romFile = getRomFile();
|
|
|
|
if ( romFile != NULL )
|
|
{
|
|
std::string dir, base;
|
|
|
|
parseFilepath( romFile, &dir, &base );
|
|
|
|
base.append(".wch");
|
|
|
|
dialog.setDirectory( tr(dir.c_str()) );
|
|
|
|
dialog.selectFile( tr(base.c_str()) );
|
|
}
|
|
|
|
// Check config option to use native file dialog or not
|
|
g_config->getOption ("SDL.UseNativeFileDialog", &useNativeFileDialogVal);
|
|
|
|
dialog.setOption(QFileDialog::DontUseNativeDialog, !useNativeFileDialogVal);
|
|
|
|
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::AllDirs | QDir::Hidden );
|
|
dialog.setLabelText( QFileDialog::Accept, tr("Load") );
|
|
dialog.setDefaultSuffix( tr(".wch") );
|
|
|
|
romFile = getRomFile();
|
|
|
|
if ( romFile != NULL )
|
|
{
|
|
std::string dir, base;
|
|
|
|
parseFilepath( romFile, &dir, &base );
|
|
|
|
base.append(".wch");
|
|
|
|
dialog.setDirectory( tr(dir.c_str()) );
|
|
|
|
dialog.selectFile( tr(base.c_str()) );
|
|
}
|
|
|
|
// Check config option to use native file dialog or not
|
|
g_config->getOption ("SDL.UseNativeFileDialog", &useNativeFileDialogVal);
|
|
|
|
dialog.setOption(QFileDialog::DontUseNativeDialog, !useNativeFileDialogVal);
|
|
|
|
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 )
|
|
{
|
|
saveWatchFile( saveFileName.c_str() );
|
|
}
|
|
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::AllDirs | QDir::Hidden );
|
|
dialog.setLabelText( QFileDialog::Accept, tr("Save") );
|
|
dialog.setDefaultSuffix( tr(".wch") );
|
|
|
|
romFile = getRomFile();
|
|
|
|
if ( romFile != NULL )
|
|
{
|
|
std::string dir, base;
|
|
|
|
parseFilepath( romFile, &dir, &base );
|
|
|
|
base.append(".wch");
|
|
|
|
dialog.setDirectory( tr(dir.c_str()) );
|
|
|
|
dialog.selectFile( tr(base.c_str()) );
|
|
}
|
|
|
|
// Check config option to use native file dialog or not
|
|
g_config->getOption ("SDL.UseNativeFileDialog", &useNativeFileDialogVal);
|
|
|
|
dialog.setOption(QFileDialog::DontUseNativeDialog, !useNativeFileDialogVal);
|
|
|
|
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) << 8) | GetMem (addr + 1);
|
|
}
|
|
else if (size == 4)
|
|
{
|
|
val.u32 = GetMem (addr + 3);
|
|
val.u32 |= GetMem (addr + 2) << 8;
|
|
val.u32 |= GetMem (addr + 1) << 16;
|
|
val.u32 |= GetMem (addr ) << 24;
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------.----
|
|
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() ? 'u' : 's', 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 );
|
|
|
|
updateCheatDialog();
|
|
}
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
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=0;
|
|
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);
|
|
}
|
|
//----------------------------------------------------------------------------
|