fceux/src/drivers/Qt/HexEditor.cpp

2769 lines
64 KiB
C++

// HotKeyConf.cpp
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <SDL.h>
#include <QHeaderView>
#include <QScrollBar>
#include <QPainter>
#include <QMenuBar>
#include <QFileDialog>
#include <QColorDialog>
#include <QInputDialog>
#include <QMessageBox>
#include "../../types.h"
#include "../../fceu.h"
#include "../../cheat.h"
#include "../../debug.h"
#include "../../driver.h"
#include "../../version.h"
#include "../../movie.h"
#include "../../palette.h"
#include "../../fds.h"
#include "../../ppu.h"
#include "../../cart.h"
#include "../../ines.h"
#include "../common/configSys.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/HexEditor.h"
#include "Qt/CheatsConf.h"
#include "Qt/SymbolicDebug.h"
#include "Qt/ConsoleDebugger.h"
#include "Qt/ConsoleUtilities.h"
#include "Qt/ConsoleWindow.h"
static bool memNeedsCheck = false;
static HexBookMarkManager_t hbm;
static std::list <HexEditorDialog_t*> winList;
static const char *memViewNames[] = { "RAM", "PPU", "OAM", "ROM", NULL };
//----------------------------------------------------------------------------
static int getRAM( unsigned int i )
{
return GetMem(i);
}
//----------------------------------------------------------------------------
static int getPPU( unsigned int i )
{
i &= 0x3FFF;
if (i < 0x2000)return VPage[(i) >> 10][(i)];
//NSF PPU Viewer crash here (UGETAB) (Also disabled by 'MaxSize = 0x2000')
if (GameInfo->type == GIT_NSF)
return 0;
else
{
if (i < 0x3F00)
return vnapage[(i >> 10) & 0x3][i & 0x3FF];
return READPAL_MOTHEROFALL(i & 0x1F);
}
return 0;
}
//----------------------------------------------------------------------------
static int getOAM( unsigned int i )
{
return SPRAM[i & 0xFF];
}
//----------------------------------------------------------------------------
static int getROM( unsigned int offset)
{
if (offset < 16)
{
return *((unsigned char *)&head+offset);
}
else if (offset < (16+PRGsize[0]) )
{
return PRGptr[0][offset-16];
}
else if (offset < (16+PRGsize[0]+CHRsize[0]) )
{
return CHRptr[0][offset-16-PRGsize[0]];
}
return -1;
}
//----------------------------------------------------------------------------
static void PalettePoke(uint32 addr, uint8 data)
{
data = data & 0x3F;
addr = addr & 0x1F;
if ((addr & 3) == 0)
{
addr = (addr & 0xC) >> 2;
if (addr == 0)
{
PALRAM[0x00] = PALRAM[0x04] = PALRAM[0x08] = PALRAM[0x0C] = data;
}
else
{
UPALRAM[addr-1] = UPALRAM[0x10|(addr-1)] = data;
}
}
else
{
PALRAM[addr] = data;
}
}
//----------------------------------------------------------------------------
static int writeMem( int mode, unsigned int addr, int value )
{
value = value & 0x000000ff;
switch ( mode )
{
default:
case QHexEdit::MODE_NES_RAM:
{
if ( addr < 0x8000 )
{
writefunc wfunc;
wfunc = GetWriteHandler (addr);
if (wfunc)
{
wfunc ((uint32) addr,
(uint8) (value & 0x000000ff));
}
}
else
{
fprintf( stdout, "Error: Writing into RAM addresses >= 0x8000 is unsafe. Operation Denied.\n");
}
}
break;
case QHexEdit::MODE_NES_PPU:
{
addr &= 0x3FFF;
if (addr < 0x2000)
{
VPage[addr >> 10][addr] = value; //todo: detect if this is vrom and turn it red if so
}
if ((addr >= 0x2000) && (addr < 0x3F00))
{
vnapage[(addr >> 10) & 0x3][addr & 0x3FF] = value; //todo: this causes 0x3000-0x3f00 to mirror 0x2000-0x2f00, is this correct?
}
if ((addr >= 0x3F00) && (addr < 0x3FFF))
{
PalettePoke(addr, value);
}
}
break;
case QHexEdit::MODE_NES_OAM:
{
addr &= 0xFF;
SPRAM[addr] = value;
}
break;
case QHexEdit::MODE_NES_ROM:
{
if (addr < 16)
{
fprintf( stdout, "You can't edit ROM header here, however you can use iNES Header Editor to edit the header if it's an iNES format file.");
}
else if ( (addr >= 16) && (addr < PRGsize[0]+16) )
{
*(uint8 *)(GetNesPRGPointer(addr-16)) = value;
}
else if ( (addr >= PRGsize[0]+16) && (addr < CHRsize[0]+PRGsize[0]+16) )
{
*(uint8 *)(GetNesCHRPointer(addr-16-PRGsize[0])) = value;
}
}
break;
}
return 0;
}
//----------------------------------------------------------------------------
static int convToXchar( int i )
{
int c = 0;
if ( (i >= 0) && (i < 10) )
{
c = i + '0';
}
else if ( i < 16 )
{
c = (i - 10) + 'A';
}
return c;
}
//----------------------------------------------------------------------------
static int convFromXchar( int i )
{
int c = 0;
i = ::toupper(i);
if ( (i >= '0') && (i <= '9') )
{
c = i - '0';
}
else if ( (i >= 'A') && (i <= 'F') )
{
c = (i - 'A') + 10;
}
return c;
}
//----------------------------------------------------------------------------
memBlock_t::memBlock_t( void )
{
buf = NULL;
_size = 0;
_maxLines = 0;
memAccessFunc = NULL;
}
//----------------------------------------------------------------------------
memBlock_t::~memBlock_t(void)
{
if ( buf != NULL )
{
::free( buf ); buf = NULL;
}
_size = 0;
_maxLines = 0;
}
//----------------------------------------------------------------------------
int memBlock_t::reAlloc( int newSize )
{
if ( newSize == 0 )
{
return 0;
}
if ( _size == newSize )
{
return 0;
}
if ( buf != NULL )
{
::free( buf ); buf = NULL;
}
_size = 0;
_maxLines = 0;
buf = (struct memByte_t *)malloc( newSize * sizeof(struct memByte_t) );
if ( buf != NULL )
{
_size = newSize;
init();
if ( (_size % 16) )
{
_maxLines = (_size / 16) + 1;
}
else
{
_maxLines = (_size / 16);
}
}
return (buf == NULL);
}
//----------------------------------------------------------------------------
void memBlock_t::setAccessFunc( int (*newMemAccessFunc)( unsigned int offset) )
{
memAccessFunc = newMemAccessFunc;
}
//----------------------------------------------------------------------------
void memBlock_t::init(void)
{
for (int i=0; i<_size; i++)
{
buf[i].data = memAccessFunc(i);
buf[i].color = 0;
buf[i].actv = 0;
//buf[i].draw = 1;
}
}
//----------------------------------------------------------------------------
HexBookMark::HexBookMark(void)
{
addr = 0;
mode = 0;
desc[0] = 0;
}
//----------------------------------------------------------------------------
HexBookMark::~HexBookMark(void)
{
}
//----------------------------------------------------------------------------
HexBookMarkManager_t::HexBookMarkManager_t(void)
{
}
//----------------------------------------------------------------------------
HexBookMarkManager_t::~HexBookMarkManager_t(void)
{
removeAll();
}
//----------------------------------------------------------------------------
void HexBookMarkManager_t::removeAll(void)
{
HexBookMark *b;
while ( !ls.empty() )
{
b = ls.front();
delete b;
ls.pop_front();
}
v.clear();
}
//----------------------------------------------------------------------------
int HexBookMarkManager_t::addBookMark( int addr, int mode, const char *desc )
{
HexBookMark *b;
b = new HexBookMark();
b->addr = addr;
b->mode = mode;
if ( desc )
{
strncpy( b->desc, desc, 63 );
}
b->desc[63] = 0;
ls.push_back(b);
updateVector();
return 0;
}
//----------------------------------------------------------------------------
void HexBookMarkManager_t::updateVector(void)
{
std::list <HexBookMark*>::iterator it;
v.clear();
for (it=ls.begin(); it!=ls.end(); it++)
{
v.push_back( *it );
}
return;
}
//----------------------------------------------------------------------------
int HexBookMarkManager_t::size(void)
{
return ls.size();
}
//----------------------------------------------------------------------------
HexBookMark *HexBookMarkManager_t::getBookMark( int index )
{
if ( index < 0 )
{
return NULL;
}
else if ( index >= (int)v.size() )
{
return NULL;
}
return v[index];
}
//----------------------------------------------------------------------------
int HexBookMarkManager_t::loadFromFile(void)
{
int i,j,mode,addr;
FILE *fp;
QDir dir;
char line[256], fd[256], baseFile[512];
const char *romFile = getRomFile();
const char *baseDir = FCEUI_GetBaseDirectory();
std::string path;
if ( romFile == NULL )
{
return -1;
}
path = std::string(baseDir) + "/bookmarks/";
dir.mkpath( QString::fromStdString(path) );
getFileBaseName( romFile, baseFile );
path += std::string(baseFile) + ".txt";
fp = ::fopen( path.c_str(), "r");
if ( fp == NULL )
{
return -1;
}
while ( ::fgets( line, sizeof(line), fp ) != NULL )
{
i=0; j=0;
//printf("%s\n", line );
//
while ( isspace(line[i]) ) i++;
while ( isalpha(line[i]) )
{
fd[j] = line[i]; i++; j++;
}
fd[j] = 0;
mode = -1;
for (j=0; j<4; j++)
{
if ( strcmp( fd, memViewNames[j] ) == 0 )
{
mode = j; break;
}
}
if ( mode < 0 ) continue;
while ( isspace(line[i]) ) i++;
if ( line[i] == ':' ) i++;
while ( isspace(line[i]) ) i++;
j=0;
while ( isxdigit(line[i]) )
{
fd[j] = line[i]; i++; j++;
}
fd[j] = 0;
addr = strtol( fd, NULL, 16 );
while ( isspace(line[i]) ) i++;
if ( line[i] == ':' ) i++;
while ( isspace(line[i]) ) i++;
j=0;
while ( line[i] )
{
fd[j] = line[i]; i++; j++;
}
fd[j] = 0;
j--;
while ( j >= 0 )
{
if ( isspace( fd[j] ) )
{
fd[j] = 0;
}
else
{
break;
}
j--;
}
addBookMark( addr, mode, fd );
}
::fclose(fp);
return 0;
}
//----------------------------------------------------------------------------
int HexBookMarkManager_t::saveToFile(void)
{
FILE *fp;
QDir dir;
char baseFile[512];
const char *romFile = getRomFile();
const char *baseDir = FCEUI_GetBaseDirectory();
std::string path;
if ( romFile == NULL )
{
return -1;
}
path = std::string(baseDir) + "/bookmarks/";
dir.mkpath( QString::fromStdString(path) );
getFileBaseName( romFile, baseFile );
path += std::string(baseFile) + ".txt";
fp = ::fopen( path.c_str(), "w");
if ( fp == NULL )
{
return -1;
}
for (int i=0; i<v.size(); i++)
{
fprintf( fp, "%s:%08X:%s\n",
memViewNames[ v[i]->mode ], v[i]->addr, v[i]->desc );
}
::fclose(fp);
return 0;
}
//----------------------------------------------------------------------------
HexBookMarkMenuAction::HexBookMarkMenuAction(QString desc, QWidget *parent)
: QAction( desc, parent )
{
bm = NULL; qedit = NULL;
}
//----------------------------------------------------------------------------
HexBookMarkMenuAction::~HexBookMarkMenuAction(void)
{
//printf("Hex Bookmark Menu Action Deleted\n");
}
//----------------------------------------------------------------------------
void HexBookMarkMenuAction::activateCB(void)
{
//printf("Activate Bookmark: %p \n", bm );
qedit->setMode( bm->mode );
qedit->setAddr( bm->addr );
}
//----------------------------------------------------------------------------
HexEditorDialog_t::HexEditorDialog_t(QWidget *parent)
: QDialog( parent, Qt::Window )
{
//QVBoxLayout *mainLayout;
QGridLayout *grid;
QMenuBar *menuBar;
QMenu *fileMenu, *viewMenu, *colorMenu;
QAction *saveROM, *closeAct;
QAction *actHlgt, *actHlgtRV, *actColorFG, *actColorBG;
QActionGroup *group;
int useNativeMenuBar;
QDialog::setWindowTitle( tr("Hex Editor") );
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 -> Save ROM
saveROM = new QAction(tr("Save ROM"), this);
//saveROM->setShortcut(QKeySequence::Open);
saveROM->setStatusTip(tr("Save ROM File"));
connect(saveROM, SIGNAL(triggered()), this, SLOT(saveRomFile(void)) );
fileMenu->addAction(saveROM);
// File -> Save ROM As
saveROM = new QAction(tr("Save ROM As"), this);
//saveROM->setShortcut(QKeySequence::Open);
saveROM->setStatusTip(tr("Save ROM File As"));
connect(saveROM, SIGNAL(triggered()), this, SLOT(saveRomFileAs(void)) );
fileMenu->addAction(saveROM);
// File -> Goto Address
gotoAddrAct = new QAction(tr("Goto Addresss"), this);
gotoAddrAct->setShortcut(QKeySequence(tr("Ctrl+A")));
gotoAddrAct->setStatusTip(tr("Goto Address"));
connect(gotoAddrAct, SIGNAL(triggered()), this, SLOT(openGotoAddrDialog(void)) );
fileMenu->addAction(gotoAddrAct);
fileMenu->addSeparator();
// File -> Close
closeAct = new QAction(tr("Close"), this);
//closeAct->setShortcuts(QKeySequence::Open);
closeAct->setStatusTip(tr("Close Window"));
connect(closeAct, SIGNAL(triggered()), this, SLOT(closeWindow(void)) );
fileMenu->addAction(closeAct);
// View
viewMenu = menuBar->addMenu(tr("View"));
group = new QActionGroup(this);
group->setExclusive(true);
// View -> RAM
viewRAM = new QAction(tr("RAM"), this);
//viewRAM->setShortcuts(QKeySequence::Open);
viewRAM->setStatusTip(tr("View RAM"));
viewRAM->setCheckable(true);
connect(viewRAM, SIGNAL(triggered()), this, SLOT(setViewRAM(void)) );
group->addAction(viewRAM);
viewMenu->addAction(viewRAM);
// View -> PPU
viewPPU = new QAction(tr("PPU"), this);
//viewPPU->setShortcuts(QKeySequence::Open);
viewPPU->setStatusTip(tr("View PPU"));
viewPPU->setCheckable(true);
connect(viewPPU, SIGNAL(triggered()), this, SLOT(setViewPPU(void)) );
group->addAction(viewPPU);
viewMenu->addAction(viewPPU);
// View -> OAM
viewOAM = new QAction(tr("OAM"), this);
//viewOAM->setShortcuts(QKeySequence::Open);
viewOAM->setStatusTip(tr("View OAM"));
viewOAM->setCheckable(true);
connect(viewOAM, SIGNAL(triggered()), this, SLOT(setViewOAM(void)) );
group->addAction(viewOAM);
viewMenu->addAction(viewOAM);
// View -> ROM
viewROM = new QAction(tr("ROM"), this);
//viewROM->setShortcuts(QKeySequence::Open);
viewROM->setStatusTip(tr("View ROM"));
viewROM->setCheckable(true);
connect(viewROM, SIGNAL(triggered()), this, SLOT(setViewROM(void)) );
group->addAction(viewROM);
viewMenu->addAction(viewROM);
viewRAM->setChecked(true); // Set default view
// Color Menu
colorMenu = menuBar->addMenu(tr("Color"));
// Color -> Highlight Activity
actHlgt = new QAction(tr("Highlight Activity"), this);
//actHlgt->setShortcuts(QKeySequence::Open);
actHlgt->setStatusTip(tr("Highlight Activity"));
actHlgt->setCheckable(true);
actHlgt->setChecked(true);
connect(actHlgt, SIGNAL(triggered(bool)), this, SLOT(actvHighlightCB(bool)) );
colorMenu->addAction(actHlgt);
// Color -> Highlight Reverse Video
actHlgtRV = new QAction(tr("Highlight Reverse Video"), this);
//actHlgtRV->setShortcuts(QKeySequence::Open);
actHlgtRV->setStatusTip(tr("Highlight Reverse Video"));
actHlgtRV->setCheckable(true);
actHlgtRV->setChecked(true);
connect(actHlgtRV, SIGNAL(triggered(bool)), this, SLOT(actvHighlightRVCB(bool)) );
colorMenu->addAction(actHlgtRV);
// Color -> ForeGround Color
actColorFG = new QAction(tr("ForeGround Color"), this);
//actColorFG->setShortcuts(QKeySequence::Open);
actColorFG->setStatusTip(tr("ForeGround Color"));
connect(actColorFG, SIGNAL(triggered(void)), this, SLOT(pickForeGroundColor(void)) );
colorMenu->addAction(actColorFG);
// Color -> BackGround Color
actColorBG = new QAction(tr("BackGround Color"), this);
//actColorBG->setShortcuts(QKeySequence::Open);
actColorBG->setStatusTip(tr("BackGround Color"));
connect(actColorBG, SIGNAL(triggered(void)), this, SLOT(pickBackGroundColor(void)) );
colorMenu->addAction(actColorBG);
// Bookmarks Menu
bookmarkMenu = menuBar->addMenu(tr("Bookmarks"));
//-----------------------------------------------------------------------
// Menu End
//-----------------------------------------------------------------------
//mainLayout = new QVBoxLayout();
grid = new QGridLayout(this);
editor = new QHexEdit(this);
vbar = new QScrollBar( Qt::Vertical, this );
hbar = new QScrollBar( Qt::Horizontal, this );
grid->setMenuBar( menuBar );
grid->addWidget( editor, 0, 0 );
grid->addWidget( vbar , 0, 1 );
grid->addWidget( hbar , 1, 0 );
//mainLayout->addLayout( grid );
setLayout( grid );
hbar->setMinimum(0);
hbar->setMaximum(100);
vbar->setMinimum(0);
vbar->setMaximum( 0x10000 / 16 );
editor->setScrollBars( hbar, vbar );
//connect( vbar, SIGNAL(sliderMoved(int)), this, SLOT(vbarMoved(int)) );
connect( hbar, SIGNAL(valueChanged(int)), this, SLOT(hbarChanged(int)) );
connect( vbar, SIGNAL(valueChanged(int)), this, SLOT(vbarChanged(int)) );
editor->memModeUpdate();
periodicTimer = new QTimer( this );
connect( periodicTimer, &QTimer::timeout, this, &HexEditorDialog_t::updatePeriodic );
periodicTimer->start( 100 ); // 10hz
// Lock the mutex before adding a new window to the list,
// we want to be sure that the emulator is not iterating the list
// when we change it.
fceuWrapperLock();
winList.push_back(this);
fceuWrapperUnLock();
populateBookmarkMenu();
FCEUI_CreateCheatMap();
}
//----------------------------------------------------------------------------
HexEditorDialog_t::~HexEditorDialog_t(void)
{
std::list <HexEditorDialog_t*>::iterator it;
printf("Hex Editor Deleted\n");
periodicTimer->stop();
// Lock the emulation thread mutex to ensure
// that the emulator is not attempting to update memory values
// for window while we are destroying it or editing the window list.
fceuWrapperLock();
for (it = winList.begin(); it != winList.end(); it++)
{
if ( (*it) == this )
{
winList.erase(it);
//printf("Removing Hex Edit Window\n");
break;
}
}
fceuWrapperUnLock();
}
//----------------------------------------------------------------------------
void HexEditorDialog_t::setWindowTitle(void)
{
const char *modeString;
char stmp[128];
modeString = memViewNames[ editor->getMode() ];
sprintf( stmp, "Hex Editor - %s: 0x%04X", modeString, editor->getAddr() );
QDialog::setWindowTitle( tr(stmp) );
}
//----------------------------------------------------------------------------
void HexEditorDialog_t::removeAllBookmarks(void)
{
int ret;
QMessageBox mbox(this);
mbox.setWindowTitle( tr("Bookmarks") );
mbox.setText( tr("Remove All Bookmarks?") );
mbox.setIcon( QMessageBox::Question );
mbox.setStandardButtons( QMessageBox::Cancel | QMessageBox::Ok );
ret = mbox.exec();
//printf("Ret: %i \n", ret );
if ( ret == QMessageBox::Ok )
{
hbm.removeAll();
populateBookmarkMenu();
}
}
//----------------------------------------------------------------------------
void HexEditorDialog_t::populateBookmarkMenu(void)
{
QAction *act;
HexBookMarkMenuAction *hAct;
bookmarkMenu->clear();
// Bookmarks -> Remove All Bookmarks
act = new QAction(tr("Remove All Bookmarks"), bookmarkMenu);
//act->setShortcuts(QKeySequence::Open);
act->setStatusTip(tr("Remove All Bookmarks"));
connect(act, SIGNAL(triggered(void)), this, SLOT(removeAllBookmarks(void)) );
bookmarkMenu->addAction(act);
bookmarkMenu->addSeparator();
for (int i=0; i<hbm.size(); i++)
{
HexBookMark *b = hbm.getBookMark(i);
if ( b )
{
//printf("%p %p \n", b, editor );
hAct = new HexBookMarkMenuAction(tr(b->desc), bookmarkMenu);
bookmarkMenu->addAction(hAct);
hAct->bm = b; hAct->qedit = editor;
connect(hAct, SIGNAL(triggered(void)), hAct, SLOT(activateCB(void)) );
}
}
}
//----------------------------------------------------------------------------
void HexEditorDialog_t::closeEvent(QCloseEvent *event)
{
printf("Hex Editor Close Window Event\n");
done(0);
deleteLater();
event->accept();
}
//----------------------------------------------------------------------------
void HexEditorDialog_t::closeWindow(void)
{
//printf("Close Window\n");
done(0);
deleteLater();
}
//----------------------------------------------------------------------------
void HexEditorDialog_t::pickForeGroundColor(void)
{
int ret;
QColorDialog dialog( this );
dialog.setOption( QColorDialog::DontUseNativeDialog, true );
dialog.show();
ret = dialog.exec();
if ( ret == QDialog::Accepted )
{
QString colorText;
colorText = dialog.selectedColor().name();
//printf("FG Color string '%s'\n", colorText.toStdString().c_str() );
g_config->setOption("SDL.HexEditFgColor", colorText.toStdString().c_str() );
editor->setForeGroundColor( dialog.selectedColor() );
}
}
//----------------------------------------------------------------------------
void HexEditorDialog_t::pickBackGroundColor(void)
{
int ret;
QColorDialog dialog( this );
dialog.setOption( QColorDialog::DontUseNativeDialog, true );
dialog.show();
ret = dialog.exec();
if ( ret == QDialog::Accepted )
{
QString colorText;
colorText = dialog.selectedColor().name();
//printf("BG Color string '%s'\n", colorText.toStdString().c_str() );
g_config->setOption("SDL.HexEditBgColor", colorText.toStdString().c_str() );
editor->setBackGroundColor( dialog.selectedColor() );
}
}
//----------------------------------------------------------------------------
void HexEditorDialog_t::vbarMoved(int value)
{
//printf("VBar Moved: %i\n", value);
editor->setLine( value );
}
//----------------------------------------------------------------------------
void HexEditorDialog_t::vbarChanged(int value)
{
//printf("VBar Changed: %i\n", value);
editor->setLine( value );
}
//----------------------------------------------------------------------------
void HexEditorDialog_t::hbarChanged(int value)
{
//printf("HBar Changed: %i\n", value);
editor->setHorzScroll( value );
}
//----------------------------------------------------------------------------
void HexEditorDialog_t::gotoAddress( int newAddr )
{
editor->setAddr( newAddr );
}
//----------------------------------------------------------------------------
void HexEditorDialog_t::saveRomFile(void)
{
//FlushUndoBuffer();
iNesSave();
//UpdateColorTable();
}
//----------------------------------------------------------------------------
void HexEditorDialog_t::saveRomFileAs(void)
{
int ret, useNativeFileDialogVal;
QString filename;
QFileDialog dialog(this, tr("Save ROM To File") );
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setNameFilter(tr("NES Files (*.nes *.NES) ;; All files (*)"));
dialog.setViewMode(QFileDialog::List);
dialog.setFilter( QDir::AllEntries | QDir::AllDirs | QDir::Hidden );
dialog.setLabelText( QFileDialog::Accept, tr("Save") );
dialog.setDefaultSuffix( tr(".nes") );
// 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();
iNesSaveAs( filename.toStdString().c_str() );
}
//----------------------------------------------------------------------------
void HexEditorDialog_t::setViewRAM(void)
{
editor->setMode( QHexEdit::MODE_NES_RAM );
}
//----------------------------------------------------------------------------
void HexEditorDialog_t::setViewPPU(void)
{
editor->setMode( QHexEdit::MODE_NES_PPU );
}
//----------------------------------------------------------------------------
void HexEditorDialog_t::setViewOAM(void)
{
editor->setMode( QHexEdit::MODE_NES_OAM );
}
//----------------------------------------------------------------------------
void HexEditorDialog_t::setViewROM(void)
{
editor->setMode( QHexEdit::MODE_NES_ROM );
}
//----------------------------------------------------------------------------
void HexEditorDialog_t::actvHighlightCB(bool enable)
{
//printf("Highlight: %i \n", enable );
editor->setHighlightActivity( enable );
}
//----------------------------------------------------------------------------
void HexEditorDialog_t::actvHighlightRVCB(bool enable)
{
//printf("Highlight: %i \n", enable );
editor->setHighlightReverseVideo( enable );
}
//----------------------------------------------------------------------------
void HexEditorDialog_t::openDebugSymbolEditWindow( int addr )
{
int ret, bank, charWidth;
QDialog dialog(this);
QHBoxLayout *hbox;
QVBoxLayout *mainLayout;
QLabel *lbl;
QLineEdit *filepath, *addrEntry, *nameEntry, *commentEntry;
QPushButton *okButton, *cancelButton;
char stmp[512];
debugSymbol_t *sym;
QFont font;
font.setFamily("Courier New");
font.setStyle( QFont::StyleNormal );
font.setStyleHint( QFont::Monospace );
QFontMetrics fm(font);
#if QT_VERSION > QT_VERSION_CHECK(5, 11, 0)
charWidth = fm.horizontalAdvance(QLatin1Char('2'));
#else
charWidth = fm.width(QLatin1Char('2'));
#endif
if ( addr < 0x8000 )
{
bank = -1;
}
else
{
bank = getBank( addr );
}
sym = debugSymbolTable.getSymbolAtBankOffset( bank, addr );
generateNLFilenameForAddress( addr, stmp );
dialog.setWindowTitle( tr("Symbolic Debug Naming") );
hbox = new QHBoxLayout();
mainLayout = new QVBoxLayout();
lbl = new QLabel( tr("File") );
filepath = new QLineEdit();
filepath->setFont( font );
filepath->setText( tr(stmp) );
filepath->setReadOnly( true );
filepath->setMinimumWidth( charWidth * (filepath->text().size() + 4) );
hbox->addWidget( lbl );
hbox->addWidget( filepath );
mainLayout->addLayout( hbox );
sprintf( stmp, "%04X", addr );
hbox = new QHBoxLayout();
lbl = new QLabel( tr("Address") );
addrEntry = new QLineEdit();
addrEntry->setFont( font );
addrEntry->setText( tr(stmp) );
addrEntry->setReadOnly( true );
addrEntry->setAlignment(Qt::AlignCenter);
addrEntry->setMaximumWidth( charWidth * 6 );
hbox->addWidget( lbl );
hbox->addWidget( addrEntry );
lbl = new QLabel( tr("Name") );
nameEntry = new QLineEdit();
hbox->addWidget( lbl );
hbox->addWidget( nameEntry );
mainLayout->addLayout( hbox );
hbox = new QHBoxLayout();
lbl = new QLabel( tr("Comment") );
commentEntry = new QLineEdit();
hbox->addWidget( lbl );
hbox->addWidget( commentEntry );
mainLayout->addLayout( hbox );
hbox = new QHBoxLayout();
okButton = new QPushButton( tr("OK") );
cancelButton = new QPushButton( tr("Cancel") );
mainLayout->addLayout( hbox );
hbox->addWidget( cancelButton );
hbox->addWidget( okButton );
connect( okButton, SIGNAL(clicked(void)), &dialog, SLOT(accept(void)) );
connect( cancelButton, SIGNAL(clicked(void)), &dialog, SLOT(reject(void)) );
okButton->setDefault(true);
if ( sym != NULL )
{
nameEntry->setText( tr(sym->name.c_str()) );
commentEntry->setText( tr(sym->comment.c_str()) );
}
dialog.setLayout( mainLayout );
ret = dialog.exec();
if ( ret == QDialog::Accepted )
{
if ( sym == NULL )
{
sym = new debugSymbol_t();
sym->ofs = addr;
sym->name = nameEntry->text().toStdString();
sym->comment = commentEntry->text().toStdString();
debugSymbolTable.addSymbolAtBankOffset( bank, addr, sym );
}
else
{
sym->name = nameEntry->text().toStdString();
sym->comment = commentEntry->text().toStdString();
}
sym->trimTrailingSpaces();
//fceuWrapperLock();
updateAllDebuggerWindows();
//fceuWrapperUnLock();
}
}
//----------------------------------------------------------------------------
void HexEditorDialog_t::updatePeriodic(void)
{
//printf("Update Periodic\n");
if ( fceuWrapperTryLock(0) )
{
memNeedsCheck = false;
editor->checkMemActivity();
fceuWrapperUnLock();
}
else
{
memNeedsCheck = true;
}
editor->memModeUpdate();
editor->update();
setWindowTitle();
switch ( editor->getMode() )
{
case QHexEdit::MODE_NES_RAM:
if ( !viewRAM->isChecked() )
{
viewRAM->setChecked(true);
}
break;
case QHexEdit::MODE_NES_PPU:
if ( !viewPPU->isChecked() )
{
viewPPU->setChecked(true);
}
break;
case QHexEdit::MODE_NES_OAM:
if ( !viewOAM->isChecked() )
{
viewOAM->setChecked(true);
}
break;
case QHexEdit::MODE_NES_ROM:
if ( !viewROM->isChecked() )
{
viewROM->setChecked(true);
}
break;
}
}
//----------------------------------------------------------------------------
void HexEditorDialog_t::openGotoAddrDialog(void)
{
editor->openGotoAddrDialog();
}
//----------------------------------------------------------------------------
QHexEdit::QHexEdit(QWidget *parent)
: QWidget( parent )
{
QPalette pal;
QColor bg, fg;
std::string colorString;
this->parent = (HexEditorDialog_t*)parent;
this->setFocusPolicy(Qt::StrongFocus);
font.setFamily("Courier New");
font.setStyle( QFont::StyleNormal );
font.setStyleHint( QFont::Monospace );
g_config->getOption("SDL.HexEditBgColor", &colorString);
bg.setNamedColor( colorString.c_str() );
g_config->getOption("SDL.HexEditFgColor", &colorString);
fg.setNamedColor( colorString.c_str() );
pal = this->palette();
pal.setColor(QPalette::Base , bg );
pal.setColor(QPalette::Background, bg );
pal.setColor(QPalette::WindowText, fg );
//editor->setAutoFillBackground(true);
this->setPalette(pal);
calcFontData();
memAccessFunc = getRAM;
viewMode = MODE_NES_RAM;
lineOffset = 0;
cursorPosX = 0;
cursorPosY = 0;
cursorAddr = 0;
cursorBlink = true;
cursorBlinkCount = 0;
maxLineOffset = 0;
editAddr = -1;
editValue = 0;
editMask = 0;
reverseVideo = true;
actvHighlightEnable = true;
total_instructions_lp = 0;
pxLineXScroll = 0;
frzRamAddr = -1;
frzRamVal = 0;
frzRamMode = 0;
frzIdx = 0;
wheelPixelCounter = 0;
highLightColor[ 0].setRgb( 0x00, 0x00, 0x00 );
highLightColor[ 1].setRgb( 0x35, 0x40, 0x00 );
highLightColor[ 2].setRgb( 0x18, 0x52, 0x18 );
highLightColor[ 3].setRgb( 0x34, 0x5C, 0x5E );
highLightColor[ 4].setRgb( 0x00, 0x4C, 0x80 );
highLightColor[ 5].setRgb( 0x00, 0x03, 0xBA );
highLightColor[ 6].setRgb( 0x38, 0x00, 0xD1 );
highLightColor[ 7].setRgb( 0x72, 0x12, 0xB2 );
highLightColor[ 8].setRgb( 0xAB, 0x00, 0xBA );
highLightColor[ 9].setRgb( 0xB0, 0x00, 0x6F );
highLightColor[10].setRgb( 0xC2, 0x00, 0x37 );
highLightColor[11].setRgb( 0xBA, 0x0C, 0x00 );
highLightColor[12].setRgb( 0xC9, 0x2C, 0x00 );
highLightColor[13].setRgb( 0xBF, 0x53, 0x00 );
highLightColor[14].setRgb( 0xCF, 0x72, 0x00 );
highLightColor[15].setRgb( 0xC7, 0x8B, 0x3C );
for (int i=0; i<HIGHLIGHT_ACTIVITY_NUM_COLORS; i++)
{
float red, green, blue, avg, grayScale;
red = highLightColor[i].redF();
green = highLightColor[i].greenF();
blue = highLightColor[i].blueF();
avg = (red + green + blue) / 3.0;
if ( avg >= 0.5 )
{
grayScale = 0.0;
}
else
{
grayScale = 1.0;
}
rvActvTextColor[i].setRgbF( grayScale, grayScale, grayScale );
}
}
//----------------------------------------------------------------------------
QHexEdit::~QHexEdit(void)
{
}
//----------------------------------------------------------------------------
void QHexEdit::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;
pxXoffset = pxCharWidth;
pxYoffset = pxLineSpacing * 2.0;
pxHexOffset = pxXoffset + (7*pxCharWidth);
pxHexAscii = pxHexOffset + (16*3*pxCharWidth) + (pxCharWidth);
pxLineWidth = pxHexAscii + (17*pxCharWidth);
//_pxGapAdr = _pxCharWidth / 2;
//_pxGapAdrHex = _pxCharWidth;
//_pxGapHexAscii = 2 * _pxCharWidth;
pxCursorHeight = pxCharHeight;
//_pxSelectionSub = _pxCharHeight / 5;
viewLines = (viewHeight - pxLineSpacing) / pxLineSpacing;
}
//----------------------------------------------------------------------------
void QHexEdit::setHighlightActivity( int enable )
{
actvHighlightEnable = enable;
}
//----------------------------------------------------------------------------
void QHexEdit::setHighlightReverseVideo( int enable )
{
reverseVideo = enable;
}
//----------------------------------------------------------------------------
void QHexEdit::setForeGroundColor( QColor fg )
{
QPalette pal;
pal = this->palette();
//pal.setColor(QPalette::Base , Qt::black);
//pal.setColor(QPalette::Background, Qt::black);
pal.setColor(QPalette::WindowText, fg );
this->setPalette(pal);
}
//----------------------------------------------------------------------------
void QHexEdit::setBackGroundColor( QColor bg )
{
QPalette pal;
pal = this->palette();
//pal.setColor(QPalette::Base , Qt::black);
pal.setColor(QPalette::Background, bg );
//pal.setColor(QPalette::WindowText, fg );
this->setPalette(pal);
}
//----------------------------------------------------------------------------
void QHexEdit::setMode( int mode )
{
if ( viewMode != mode )
{
viewMode = mode;
memModeUpdate();
}
}
//----------------------------------------------------------------------------
void QHexEdit::setLine( int newLineOffset )
{
lineOffset = newLineOffset;
}
//----------------------------------------------------------------------------
void QHexEdit::setAddr( int newAddr )
{
int addr;
lineOffset = newAddr / 16;
if ( lineOffset < 0 )
{
lineOffset = 0;
}
else if ( lineOffset >= maxLineOffset )
{
lineOffset = maxLineOffset;
}
addr = 16*lineOffset;
cursorPosX = 2*((newAddr - addr)%16);
cursorPosY = (newAddr - addr)/16;
vbar->setValue( lineOffset );
}
//----------------------------------------------------------------------------
void QHexEdit::setHorzScroll( int value )
{
float f;
//printf("Value: %i \n", value);
if ( viewWidth >= pxLineWidth )
{
f = 0.0;
}
else
{
f = 0.010f * (float)value * (float)(pxLineWidth - viewWidth);
}
pxLineXScroll = (int)f;
}
//----------------------------------------------------------------------------
void QHexEdit::setScrollBars( QScrollBar *h, QScrollBar *v )
{
hbar = h; vbar = v;
}
//----------------------------------------------------------------------------
void QHexEdit::resizeEvent(QResizeEvent *event)
{
viewWidth = event->size().width();
viewHeight = event->size().height();
//printf("QHexEdit Resize: %ix%i\n", viewWidth, viewHeight );
viewLines = (viewHeight - pxLineSpacing) / pxLineSpacing;
maxLineOffset = mb.numLines() - viewLines + 1;
if ( viewWidth >= pxLineWidth )
{
pxLineXScroll = 0;
}
else
{
pxLineXScroll = (int)(0.010f * (float)hbar->value() * (float)(pxLineWidth - viewWidth) );
}
}
//----------------------------------------------------------------------------
void QHexEdit::openGotoAddrDialog(void)
{
int ret;
char stmp[128];
QInputDialog dialog(this);
sprintf( stmp, "Specify Address [ 0x0 -> 0x%X ]", mb.size()-1 );
dialog.setWindowTitle( tr("Goto Address") );
dialog.setLabelText( tr(stmp) );
dialog.setOkButtonText( tr("Go") );
//dialog.setTextValue( tr("0") );
dialog.show();
ret = dialog.exec();
if ( QDialog::Accepted == ret )
{
int addr;
std::string s = dialog.textValue().toStdString();
addr = strtol( s.c_str(), NULL, 16 );
parent->gotoAddress(addr);
}
}
//----------------------------------------------------------------------------
void QHexEdit::resetCursor(void)
{
cursorBlink = true;
cursorBlinkCount = 0;
editAddr = -1;
editValue = 0;
editMask = 0;
}
//----------------------------------------------------------------------------
QPoint QHexEdit::convPixToCursor( QPoint p )
{
QPoint c(0,0);
//printf("Pos: %ix%i \n", p.x(), p.y() );
p.setX( p.x() + pxLineXScroll );
if ( p.x() < pxHexOffset )
{
c.setX(0);
}
else if ( (p.x() >= pxHexOffset) && (p.x() < pxHexAscii) )
{
float px = ( (float)p.x() - (float)pxHexOffset) / (float)(pxCharWidth);
float ox = (px/3.0);
float rx = fmodf(px,3.0);
if ( rx >= 2.50 )
{
c.setX( 2*( (int)ox + 1 ) );
}
else
{
//if ( rx >= 1.0 )
//{
// c.setX( 2*( (int)ox ) + 1 );
//}
//else
//{
c.setX( 2*( (int)ox ) );
//}
}
}
else
{
c.setX( 32 + (p.x() - pxHexAscii) / pxCharWidth );
}
if ( c.x() >= 48 )
{
c.setX( 47 );
}
if ( p.y() < pxYoffset )
{
c.setY( 0 );
}
else
{
float ly = ( (float)pxLineLead / (float)pxLineSpacing );
float py = ( (float)p.y() - (float)pxLineSpacing) / (float)pxLineSpacing;
float ry = fmod( py, 1.0 );
if ( ry < ly )
{
c.setY( ((int)py) - 1 );
}
else
{
c.setY( (int)py );
}
}
if ( c.y() < 0 )
{
c.setY(0);
}
else if ( c.y() >= viewLines )
{
c.setY( viewLines - 1 );
}
//printf("c: %ix%i \n", cx, cy );
//
return c;
}
//----------------------------------------------------------------------------
int QHexEdit::convPixToAddr( QPoint p )
{
int a,addr;
QPoint c = convPixToCursor(p);
//printf("Cursor: %ix%i\n", c.x(), c.y() );
if ( c.x() < 32 )
{
a = (c.x() / 2);
addr = 16*(lineOffset + c.y()) + a;
}
else
{
a = (c.x()-32);
addr = 16*(lineOffset + c.y()) + a;
}
return addr;
}
//----------------------------------------------------------------------------
void QHexEdit::keyPressEvent(QKeyEvent *event)
{
//printf("Hex Window Key Press: 0x%x \n", event->key() );
if (event->matches(QKeySequence::MoveToNextChar))
{
if ( cursorPosX < 32 )
{
if ( cursorPosX % 2 )
{
cursorPosX++;
}
else
{
cursorPosX += 2;
}
}
else
{
cursorPosX++;
}
if ( cursorPosX >= 48 )
{
cursorPosX = 47;
}
resetCursor();
}
else if (event->matches(QKeySequence::MoveToPreviousChar))
{
if ( cursorPosX < 33 )
{
if ( cursorPosX % 2 )
{
cursorPosX -= 3;
}
else
{
cursorPosX -= 2;
}
}
else
{
cursorPosX--;
}
if ( cursorPosX < 0 )
{
cursorPosX = 0;
}
resetCursor();
}
else if (event->matches(QKeySequence::MoveToEndOfLine))
{
cursorPosX = 47;
resetCursor();
}
else if (event->matches(QKeySequence::MoveToStartOfLine))
{
cursorPosX = 0;
resetCursor();
}
else if (event->matches(QKeySequence::MoveToPreviousLine))
{
cursorPosY--;
if ( cursorPosY < 0 )
{
lineOffset--;
if ( lineOffset < 0 )
{
lineOffset = 0;
}
cursorPosY = 0;
vbar->setValue( lineOffset );
}
resetCursor();
}
else if (event->matches(QKeySequence::MoveToNextLine))
{
cursorPosY++;
if ( cursorPosY >= viewLines )
{
lineOffset++;
if ( lineOffset >= maxLineOffset )
{
lineOffset = maxLineOffset;
}
cursorPosY = viewLines-1;
vbar->setValue( lineOffset );
}
resetCursor();
}
else if (event->matches(QKeySequence::MoveToNextPage))
{
lineOffset += ( (3 * viewLines) / 4);
if ( lineOffset >= maxLineOffset )
{
lineOffset = maxLineOffset;
}
vbar->setValue( lineOffset );
resetCursor();
}
else if (event->matches(QKeySequence::MoveToPreviousPage))
{
lineOffset -= ( (3 * viewLines) / 4);
if ( lineOffset < 0 )
{
lineOffset = 0;
}
vbar->setValue( lineOffset );
resetCursor();
}
else if (event->matches(QKeySequence::MoveToEndOfDocument))
{
lineOffset = maxLineOffset;
vbar->setValue( lineOffset );
resetCursor();
}
else if (event->matches(QKeySequence::MoveToStartOfDocument))
{
lineOffset = 0;
vbar->setValue( lineOffset );
resetCursor();
}
else if (Qt::ControlModifier == event->modifiers())
{
if ( event->key() == Qt::Key_A )
{
openGotoAddrDialog();
}
else if ( event->key() == Qt::Key_F )
{
frzRamAddr = ctxAddr = cursorAddr;
frzRamToggle();
}
}
else if (event->key() == Qt::Key_Tab && (cursorPosX < 32) )
{ // switch from hex to ascii edit
cursorPosX = 32 + (cursorPosX / 2);
}
else if (event->key() == Qt::Key_Backtab && (cursorPosX >= 32) )
{ // switch from ascii to hex edit
cursorPosX = 2 * (cursorPosX - 32);
}
else
{
int key;
if ( cursorPosX >= 32 )
{ // Edit Area is ASCII
key = (uchar)event->text()[0].toLatin1();
if ( ::isascii( key ) )
{
int offs = (cursorPosX-32);
int addr = 16*(lineOffset+cursorPosY) + offs;
fceuWrapperLock();
writeMem( viewMode, addr, key );
fceuWrapperUnLock();
editAddr = -1;
editValue = 0;
editMask = 0;
}
}
else
{ // Edit Area is Hex
key = int(event->text()[0].toUpper().toLatin1());
if ( ::isxdigit( key ) )
{
int offs, nibbleValue, nibbleIndex;
offs = (cursorPosX / 2);
nibbleIndex = (cursorPosX % 2);
editAddr = 16*(lineOffset+cursorPosY) + offs;
nibbleValue = convFromXchar( key );
if ( nibbleIndex )
{
nibbleValue = editValue | nibbleValue;
fceuWrapperLock();
writeMem( viewMode, editAddr, nibbleValue );
fceuWrapperUnLock();
editAddr = -1;
editValue = 0;
editMask = 0;
}
else
{
editValue = (nibbleValue << 4);
editMask = 0x00f0;
}
cursorPosX++;
if ( cursorPosX >= 32 )
{
cursorPosX = 0;
}
}
}
//printf("Key: %c %i \n", key, key);
}
}
//----------------------------------------------------------------------------
void QHexEdit::keyReleaseEvent(QKeyEvent *event)
{
//printf("Hex Window Key Release: 0x%x \n", event->key() );
}
//----------------------------------------------------------------------------
void QHexEdit::mousePressEvent(QMouseEvent * event)
{
QPoint c = convPixToCursor( event->pos() );
//printf("c: %ix%i \n", c.x(), c.y() );
if ( event->button() == Qt::LeftButton )
{
cursorPosX = c.x();
cursorPosY = c.y();
resetCursor();
}
}
//----------------------------------------------------------------------------
void QHexEdit::wheelEvent(QWheelEvent *event)
{
QPoint numPixels = event->pixelDelta();
QPoint numDegrees = event->angleDelta();
if (!numPixels.isNull())
{
wheelPixelCounter += numPixels.y();
//printf("numPixels: (%i,%i) \n", numPixels.x(), numPixels.y() );
}
else if (!numDegrees.isNull())
{
//QPoint numSteps = numDegrees / 15;
//printf("numSteps: (%i,%i) \n", numSteps.x(), numSteps.y() );
//printf("numDegrees: (%i,%i) %i\n", numDegrees.x(), numDegrees.y(), pxLineSpacing );
wheelPixelCounter += (pxLineSpacing * numDegrees.y()) / (15*8);
}
//printf("Wheel Event: %i\n", wheelPixelCounter);
if ( wheelPixelCounter >= pxLineSpacing )
{
lineOffset += (wheelPixelCounter / pxLineSpacing);
if ( lineOffset > maxLineOffset )
{
lineOffset = maxLineOffset;
}
vbar->setValue( lineOffset );
wheelPixelCounter = wheelPixelCounter % pxLineSpacing;
}
else if ( wheelPixelCounter <= -pxLineSpacing )
{
lineOffset += (wheelPixelCounter / pxLineSpacing);
if ( lineOffset < 0 )
{
lineOffset = 0;
}
vbar->setValue( lineOffset );
wheelPixelCounter = wheelPixelCounter % pxLineSpacing;
}
event->accept();
}
//----------------------------------------------------------------------------
void QHexEdit::contextMenuEvent(QContextMenuEvent *event)
{
QAction *act;
QMenu menu(this);
int addr;
char stmp[128];
QPoint c = convPixToCursor( event->pos() );
cursorPosX = c.x();
cursorPosY = c.y();
resetCursor();
ctxAddr = addr = convPixToAddr( event->pos() );
//printf("contextMenuEvent\n");
switch ( viewMode )
{
case MODE_NES_RAM:
{
QMenu *subMenu;
act = new QAction(tr("Add Symbolic Debug Name"), &menu);
menu.addAction(act);
connect( act, SIGNAL(triggered(void)), this, SLOT(addDebugSym(void)) );
subMenu = menu.addMenu(tr("Freeze/Unfreeze Address"));
act = new QAction(tr("Toggle State"), &menu);
act->setShortcut( QKeySequence(tr("Ctrl+F")));
subMenu->addAction(act);
connect( act, SIGNAL(triggered(void)), this, SLOT(frzRamToggle(void)) );
act = new QAction(tr("Freeze"), &menu);
subMenu->addAction(act);
connect( act, SIGNAL(triggered(void)), this, SLOT(frzRamSet(void)) );
act = new QAction(tr("Unfreeze"), &menu);
subMenu->addAction(act);
connect( act, SIGNAL(triggered(void)), this, SLOT(frzRamUnset(void)) );
subMenu->addSeparator();
act = new QAction(tr("Unfreeze All"), &menu);
subMenu->addAction(act);
connect( act, SIGNAL(triggered(void)), this, SLOT(frzRamUnsetAll(void)) );
sprintf( stmp, "Add Read Breakpoint for Address $%04X", addr );
act = new QAction(tr(stmp), &menu);
menu.addAction(act);
connect( act, SIGNAL(triggered(void)), this, SLOT(addRamReadBP(void)) );
sprintf( stmp, "Add Write Breakpoint for Address $%04X", addr );
act = new QAction(tr(stmp), &menu);
menu.addAction(act);
connect( act, SIGNAL(triggered(void)), this, SLOT(addRamWriteBP(void)) );
sprintf( stmp, "Add Execute Breakpoint for Address $%04X", addr );
act = new QAction(tr(stmp), &menu);
menu.addAction(act);
connect( act, SIGNAL(triggered(void)), this, SLOT(addRamExecuteBP(void)) );
if ( addr > 0x6000 )
{
int romAddr = GetNesFileAddress(addr);
if ( romAddr >= 0 )
{
jumpToRomValue = romAddr;
sprintf( stmp, "Go Here in ROM File: (%08X)", romAddr );
act = new QAction(tr(stmp), &menu);
menu.addAction(act);
connect( act, SIGNAL(triggered(void)), this, SLOT(jumpToROM(void)) );
}
}
act = new QAction(tr("Add Bookmark"), &menu);
menu.addAction(act);
connect( act, SIGNAL(triggered(void)), this, SLOT(addBookMarkCB(void)) );
}
break;
case MODE_NES_PPU:
{
sprintf( stmp, "Add Read Breakpoint for Address $%04X", addr );
act = new QAction(tr(stmp), &menu);
menu.addAction(act);
connect( act, SIGNAL(triggered(void)), this, SLOT(addPpuReadBP(void)) );
sprintf( stmp, "Add Write Breakpoint for Address $%04X", addr );
act = new QAction(tr(stmp), &menu);
menu.addAction(act);
connect( act, SIGNAL(triggered(void)), this, SLOT(addPpuWriteBP(void)) );
act = new QAction(tr("Add Bookmark"), &menu);
menu.addAction(act);
connect( act, SIGNAL(triggered(void)), this, SLOT(addBookMarkCB(void)) );
}
break;
case MODE_NES_OAM:
{
act = new QAction(tr("Add Bookmark"), &menu);
menu.addAction(act);
connect( act, SIGNAL(triggered(void)), this, SLOT(addBookMarkCB(void)) );
}
break;
case MODE_NES_ROM:
{
act = new QAction(tr("Add Bookmark"), &menu);
menu.addAction(act);
connect( act, SIGNAL(triggered(void)), this, SLOT(addBookMarkCB(void)) );
}
break;
}
menu.exec(event->globalPos());
}
//----------------------------------------------------------------------------
void QHexEdit::addBookMarkCB(void)
{
int ret;
char stmp[64];
QInputDialog dialog(this);
switch ( viewMode )
{
default:
case MODE_NES_RAM:
sprintf( stmp, "RAM %04X", ctxAddr );
break;
case MODE_NES_PPU:
sprintf( stmp, "PPU %04X", ctxAddr );
break;
case MODE_NES_OAM:
sprintf( stmp, "OAM %04X", ctxAddr );
break;
case MODE_NES_ROM:
sprintf( stmp, "ROM %04X", ctxAddr );
break;
}
dialog.setWindowTitle( tr("Add Bookmark") );
dialog.setLabelText( tr("Specify New Bookmark Description") );
dialog.setOkButtonText( tr("Add") );
dialog.setTextValue( tr(stmp) );
dialog.show();
ret = dialog.exec();
if ( QDialog::Accepted == ret )
{
hbm.addBookMark( ctxAddr, viewMode, dialog.textValue().toStdString().c_str() );
parent->populateBookmarkMenu();
}
}
//----------------------------------------------------------------------------
static int RamFreezeCB(char *name, uint32 a, uint8 v, int compare,int s,int type, void *data)
{
return ((QHexEdit*)data)->FreezeRam( name, a, v, compare, s, type );
}
//----------------------------------------------------------------------------
int QHexEdit::FreezeRam( const char *name, uint32_t a, uint8_t v, int c, int s, int type )
{
//if ( c >= 0 )
//{
// printf("$%04X?%02X:%02X %i: %s\n", a, c, v, s, name );
//}
//else
//{
// printf("$%04X:%02X %i: %s\n", a, v, s, name );
//}
if ( a == frzRamAddr )
{
switch ( frzRamMode )
{
case 0: // Toggle
if ( s )
{
FCEUI_DelCheat( frzIdx );
frzRamAddr = -1;
return 0;
}
break;
case 1: // Freeze
if ( s )
{
// Already Set so there is nothing further to do
frzRamAddr = -1;
return 0;
}
break;
case 2: // Unfreeze
if ( s )
{
FCEUI_DelCheat( frzIdx );
}
break;
default:
case 3: // Unfreeze All Handled Below
// Nothing to do
break;
}
}
if ( frzRamMode == 3 )
{
if ( s )
{
FCEUI_DelCheat( frzIdx );
}
}
frzIdx++;
return 1;
}
//----------------------------------------------------------------------------
bool QHexEdit::frzRamAddrValid( int addr )
{
if ( addr < 0 )
{
return false;
}
if ( (addr < 0x2000) || ( (addr >= 0x6000) && (addr <= 0x7FFF) ) )
{
return true;
}
return false;
}
//----------------------------------------------------------------------------
void QHexEdit::frzRamSet(void)
{
frzIdx = 0;
frzRamMode = 1;
frzRamAddr = ctxAddr;
if ( !frzRamAddrValid( frzRamAddr ) )
{
return;
}
fceuWrapperLock();
FCEUI_ListCheats( RamFreezeCB, this);
if ( (frzRamAddr >= 0) && (FrozenAddressCount < 256) )
{
FCEUI_AddCheat("", frzRamAddr, GetMem(frzRamAddr), -1, 1);
}
updateCheatDialog();
fceuWrapperUnLock();
}
//----------------------------------------------------------------------------
void QHexEdit::frzRamUnset(void)
{
frzIdx = 0;
frzRamMode = 2;
frzRamAddr = ctxAddr;
if ( !frzRamAddrValid( frzRamAddr ) )
{
return;
}
fceuWrapperLock();
FCEUI_ListCheats( RamFreezeCB, this);
updateCheatDialog();
fceuWrapperUnLock();
}
//----------------------------------------------------------------------------
void QHexEdit::frzRamUnsetAll(void)
{
frzIdx = 0;
frzRamMode = 3;
frzRamAddr = ctxAddr;
if ( !frzRamAddrValid( frzRamAddr ) )
{
return;
}
fceuWrapperLock();
FCEUI_ListCheats( RamFreezeCB, this);
updateCheatDialog();
fceuWrapperUnLock();
}
//----------------------------------------------------------------------------
void QHexEdit::frzRamToggle(void)
{
frzIdx = 0;
frzRamMode = 0;
frzRamAddr = ctxAddr;
if ( !frzRamAddrValid( frzRamAddr ) )
{
return;
}
fceuWrapperLock();
FCEUI_ListCheats( RamFreezeCB, this);
if ( (frzRamAddr >= 0) && (FrozenAddressCount < 256) )
{
FCEUI_AddCheat("", frzRamAddr, GetMem(frzRamAddr), -1, 1);
}
updateCheatDialog();
fceuWrapperUnLock();
}
//----------------------------------------------------------------------------
void QHexEdit::addDebugSym(void)
{
parent->openDebugSymbolEditWindow( ctxAddr );
}
//----------------------------------------------------------------------------
void QHexEdit::addRamReadBP(void)
{
int retval, type;
char cond[64], name[64];
type = BT_C | WP_R;
cond[0] = 0;
name[0] = 0;
if ( ctxAddr >= 0x8000 )
{
sprintf(cond, "K==#%02X", getBank(ctxAddr));
}
retval = NewBreak( name, ctxAddr, -1, type, cond, numWPs, true);
if ( (retval == 1) || (retval == 2) )
{
printf("Breakpoint Add Failed\n");
}
else
{
numWPs++;
}
}
//----------------------------------------------------------------------------
void QHexEdit::addRamWriteBP(void)
{
int retval, type;
char cond[64], name[64];
type = BT_C | WP_W;
cond[0] = 0;
name[0] = 0;
if ( ctxAddr >= 0x8000 )
{
sprintf(cond, "K==#%02X", getBank(ctxAddr));
}
retval = NewBreak( name, ctxAddr, -1, type, cond, numWPs, true);
if ( (retval == 1) || (retval == 2) )
{
printf("Breakpoint Add Failed\n");
}
else
{
numWPs++;
}
}
//----------------------------------------------------------------------------
void QHexEdit::addRamExecuteBP(void)
{
int retval, type;
char cond[64], name[64];
type = BT_C | WP_X;
cond[0] = 0;
name[0] = 0;
if ( ctxAddr >= 0x8000 )
{
sprintf(cond, "K==#%02X", getBank(ctxAddr));
}
retval = NewBreak( name, ctxAddr, -1, type, cond, numWPs, true);
if ( (retval == 1) || (retval == 2) )
{
printf("Breakpoint Add Failed\n");
}
else
{
numWPs++;
}
}
//----------------------------------------------------------------------------
void QHexEdit::addPpuReadBP(void)
{
int retval, type;
char cond[64], name[64];
type = BT_P | WP_R;
cond[0] = 0;
name[0] = 0;
retval = NewBreak( name, ctxAddr, -1, type, cond, numWPs, true);
if ( (retval == 1) || (retval == 2) )
{
printf("Breakpoint Add Failed\n");
}
else
{
numWPs++;
}
}
//----------------------------------------------------------------------------
void QHexEdit::addPpuWriteBP(void)
{
int retval, type;
char cond[64], name[64];
type = BT_P | WP_W;
cond[0] = 0;
name[0] = 0;
retval = NewBreak( name, ctxAddr, -1, type, cond, numWPs, true);
if ( (retval == 1) || (retval == 2) )
{
printf("Breakpoint Add Failed\n");
}
else
{
numWPs++;
}
}
//----------------------------------------------------------------------------
void QHexEdit::jumpToROM(void)
{
setMode( MODE_NES_ROM );
maxLineOffset = mb.numLines() - viewLines + 1;
if ( lineOffset > maxLineOffset )
{
lineOffset = maxLineOffset;
}
setAddr( jumpToRomValue );
}
//----------------------------------------------------------------------------
// Calling of checkMemActivity must always be synchronized with the emulation
// thread as calling GetMem while the emulation is executing can mess up certain
// registers (especially controller registers $4016 and $4017)
int QHexEdit::checkMemActivity(void)
{
int c;
// Don't perform memory activity checks when:
// 1. In ROM View Mode
// 2. The simulation is not cycling (paused)
if ( ( viewMode == MODE_NES_ROM ) ||
( total_instructions_lp == total_instructions ) )
{
return -1;
}
for (int i=0; i<mb.size(); i++)
{
c = memAccessFunc(i);
if ( c != mb.buf[i].data )
{
mb.buf[i].actv = 15;
mb.buf[i].data = c;
//mb.buf[i].draw = 1;
}
else
{
if ( mb.buf[i].actv > 0 )
{
//mb.buf[i].draw = 1;
mb.buf[i].actv--;
}
}
}
total_instructions_lp = total_instructions;
return 0;
}
//----------------------------------------------------------------------------
int QHexEdit::getRomAddrColor( int addr, QColor &fg, QColor &bg )
{
int temp_offset;
QColor color, oppColor;
fg = this->palette().color(QPalette::WindowText);
bg = this->palette().color(QPalette::Background);
if ( reverseVideo )
{
color = this->palette().color(QPalette::Background);
oppColor = this->palette().color(QPalette::WindowText);
}
else
{
color = this->palette().color(QPalette::WindowText);
oppColor = this->palette().color(QPalette::Background);
}
if ( viewMode != MODE_NES_ROM )
{
return -1;
}
if (cdloggerdataSize == 0)
{
return -1;
}
temp_offset = addr - 16;
if (temp_offset >= 0)
{
if ((unsigned int)temp_offset < cdloggerdataSize)
{
// PRG
if ((cdloggerdata[temp_offset] & 3) == 3)
{
// the byte is both Code and Data - green
color.setRgb(0, 190, 0);
}
else if ((cdloggerdata[temp_offset] & 3) == 1)
{
// the byte is Code - dark-yellow
color.setRgb(160, 140, 0);
oppColor.setRgb( 0, 0, 0 );
}
else if ((cdloggerdata[temp_offset] & 3) == 2)
{
// the byte is Data - blue/cyan
if (cdloggerdata[temp_offset] & 0x40)
{
// PCM data - cyan
color.setRgb(0, 130, 160);
}
else
{
// non-PCM data - blue
color.setRgb(0, 0, 210);
}
}
}
else
{
temp_offset -= cdloggerdataSize;
if (((unsigned int)temp_offset < cdloggerVideoDataSize))
{
// CHR
if ((cdloggervdata[temp_offset] & 3) == 3)
{
// the byte was both rendered and read programmatically - light-green
color.setRgb(5, 255, 5);
}
else if ((cdloggervdata[temp_offset] & 3) == 1)
{
// the byte was rendered - yellow
color.setRgb(210, 190, 0);
oppColor.setRgb( 0, 0, 0 );
}
else if ((cdloggervdata[temp_offset] & 3) == 2)
{
// the byte was read programmatically - light-blue
color.setRgb(15, 15, 255);
}
}
}
}
if ( reverseVideo )
{
bg = color;
fg = oppColor;
}
else
{
fg = color;
bg = oppColor;
}
return 0;
}
//----------------------------------------------------------------------------
void QHexEdit::memModeUpdate(void)
{
int memSize;
switch ( getMode() )
{
default:
case MODE_NES_RAM:
memAccessFunc = getRAM;
memSize = 0x10000;
break;
case MODE_NES_PPU:
memAccessFunc = getPPU;
if ( GameInfo )
{
memSize = (GameInfo->type == GIT_NSF ? 0x2000 : 0x4000);
}
else
{
memSize = 0x4000;
}
break;
case MODE_NES_OAM:
memAccessFunc = getOAM;
memSize = 0x100;
break;
case MODE_NES_ROM:
if ( GameInfo != NULL )
{
memAccessFunc = getROM;
memSize = 16 + CHRsize[0] + PRGsize[0];
}
else
{ // No Game Loaded!!! Get out of Function
memAccessFunc = NULL;
memSize = 0;
return;
}
break;
}
if ( memSize != mb.size() )
{
mb.setAccessFunc( memAccessFunc );
if ( mb.reAlloc( memSize ) )
{
printf("Error: Failed to allocate memview buffer size\n");
return;
}
maxLineOffset = mb.numLines() - viewLines + 1;
vbar->setMaximum( memSize / 16 );
}
}
//----------------------------------------------------------------------------
void QHexEdit::paintEvent(QPaintEvent *event)
{
int x, y, w, h, row, col, nrow, addr;
int c, cx, cy, ca;
char txt[32], asciiTxt[4];
QPainter painter(this);
QColor white("white"), black("black"), blue("blue");
painter.setFont(font);
w = event->rect().width();
h = event->rect().height();
viewWidth = w;
viewHeight = h;
//painter.fillRect( 0, 0, w, h, QColor("white") );
nrow = (h - pxLineSpacing) / pxLineSpacing;
if ( nrow < 1 ) nrow = 1;
viewLines = nrow;
if ( cursorPosY >= viewLines )
{
cursorPosY = viewLines-1;
}
//printf("Draw Area: %ix%i \n", event->rect().width(), event->rect().height() );
//
maxLineOffset = mb.numLines() - nrow + 1;
if ( maxLineOffset < 0 )
{
maxLineOffset = 0;
}
if ( lineOffset < 0 )
{
lineOffset = 0;
}
if ( lineOffset > maxLineOffset )
{
lineOffset = maxLineOffset;
}
painter.fillRect( 0, 0, w, h, this->palette().color(QPalette::Background) );
if ( cursorBlinkCount >= 5 )
{
cursorBlink = !cursorBlink;
cursorBlinkCount = 0;
}
else
{
cursorBlinkCount++;
}
cy = pxYoffset + (pxLineSpacing*cursorPosY) - pxCursorHeight + pxLineLead;
if ( cursorPosX < 32 )
{
int a = (cursorPosX / 2);
int r = (cursorPosX % 2);
cx = pxHexOffset + (a*3*pxCharWidth) + (r*pxCharWidth) - pxLineXScroll;
ca = 16*(lineOffset + cursorPosY) + a;
}
else
{
int a = (cursorPosX-32);
cx = pxHexAscii + (a*pxCharWidth) - pxLineXScroll;
ca = 16*(lineOffset + cursorPosY) + a;
}
cursorAddr = ca;
if ( cursorBlink )
{
painter.fillRect( cx , cy, pxCharWidth, pxCursorHeight, QColor("gray") );
}
painter.setPen( this->palette().color(QPalette::WindowText));
//painter.setPen( QColor("white") );
addr = lineOffset * 16;
y = pxYoffset;
for ( row=0; row < nrow; row++)
{
x = pxXoffset - pxLineXScroll;
painter.setPen( this->palette().color(QPalette::WindowText));
sprintf( txt, "%06X", addr );
painter.drawText( x, y, tr(txt) );
x = pxHexOffset - pxLineXScroll;
for (col=0; col<16; col++)
{
if ( addr < mb.size() )
{
c = mb.buf[addr].data;
if ( ::isprint(c) )
{
asciiTxt[0] = c;
}
else
{
asciiTxt[0] = '.';
}
asciiTxt[1] = 0;
if ( addr == editAddr )
{ // Set a cell currently being editting to red text
painter.setPen( QColor("red") );
txt[0] = convToXchar( (editValue >> 4) & 0x0F );
txt[1] = convToXchar( c & 0x0F );
txt[2] = 0;
painter.drawText( x, y, tr(txt) );
painter.setPen( this->palette().color(QPalette::WindowText));
}
else
{
if ( viewMode == MODE_NES_ROM )
{
QColor romBgColor, romFgColor;
getRomAddrColor( addr, romFgColor, romBgColor );
if ( reverseVideo )
{
painter.setPen( romFgColor );
painter.fillRect( x - (0.5*pxCharWidth) , y-pxLineSpacing+pxLineLead, 3*pxCharWidth, pxLineSpacing, romBgColor );
painter.fillRect( pxHexAscii + (col*pxCharWidth) - pxLineXScroll, y-pxLineSpacing+pxLineLead, pxCharWidth, pxLineSpacing, romBgColor );
}
else
{
painter.setPen( romFgColor );
}
}
else if ( viewMode == MODE_NES_RAM )
{
if ( FCEUI_FindCheatMapByte( addr ) )
{
if ( reverseVideo )
{
painter.setPen( white );
painter.fillRect( x - (0.5*pxCharWidth) , y-pxLineSpacing+pxLineLead, 3*pxCharWidth, pxLineSpacing, blue );
painter.fillRect( pxHexAscii + (col*pxCharWidth) - pxLineXScroll, y-pxLineSpacing+pxLineLead, pxCharWidth, pxLineSpacing, blue );
}
else
{
painter.setPen( blue );
}
}
else if ( actvHighlightEnable && (mb.buf[addr].actv > 0) )
{
if ( reverseVideo )
{
painter.setPen( rvActvTextColor[ mb.buf[addr].actv ] );
painter.fillRect( x - (0.5*pxCharWidth) , y-pxLineSpacing+pxLineLead, 3*pxCharWidth, pxLineSpacing, highLightColor[ mb.buf[addr].actv ] );
painter.fillRect( pxHexAscii + (col*pxCharWidth) - pxLineXScroll, y-pxLineSpacing+pxLineLead, pxCharWidth, pxLineSpacing, highLightColor[ mb.buf[addr].actv ] );
}
else
{
painter.setPen( highLightColor[ mb.buf[addr].actv ] );
}
}
else
{
painter.setPen( this->palette().color(QPalette::WindowText));
}
}
else if ( actvHighlightEnable && (mb.buf[addr].actv > 0) )
{
if ( reverseVideo )
{
painter.setPen( rvActvTextColor[ mb.buf[addr].actv ] );
painter.fillRect( x - (0.5*pxCharWidth) , y-pxLineSpacing+pxLineLead, 3*pxCharWidth, pxLineSpacing, highLightColor[ mb.buf[addr].actv ] );
painter.fillRect( pxHexAscii + (col*pxCharWidth) - pxLineXScroll, y-pxLineSpacing+pxLineLead, pxCharWidth, pxLineSpacing, highLightColor[ mb.buf[addr].actv ] );
}
else
{
painter.setPen( highLightColor[ mb.buf[addr].actv ] );
}
}
else
{
painter.setPen( this->palette().color(QPalette::WindowText));
}
txt[0] = convToXchar( (c >> 4) & 0x0F );
txt[1] = convToXchar( c & 0x0F );
txt[2] = 0;
if ( cursorBlink && (ca == addr) )
{
painter.fillRect( cx , cy, pxCharWidth, pxCursorHeight, QColor("gray") );
}
painter.drawText( x, y, tr(txt) );
painter.drawText( pxHexAscii + (col*pxCharWidth) - pxLineXScroll, y, tr(asciiTxt) );
}
}
x += (3*pxCharWidth);
addr++;
}
//addr += 16;
y += pxLineSpacing;
}
painter.setPen( this->palette().color(QPalette::WindowText));
painter.drawText( pxHexOffset - pxLineXScroll, pxLineSpacing, "00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F" );
painter.drawLine( pxHexOffset - (pxCharWidth/2) - pxLineXScroll, 0, pxHexOffset - (pxCharWidth/2) - pxLineXScroll, h );
painter.drawLine( pxHexAscii - (pxCharWidth/2) - pxLineXScroll, 0, pxHexAscii - (pxCharWidth/2) - pxLineXScroll, h );
painter.drawLine( 0, pxLineSpacing + (pxLineLead), w, pxLineSpacing + (pxLineLead) );
}
//----------------------------------------------------------------------------
void hexEditorLoadBookmarks(void)
{
std::list <HexEditorDialog_t*>::iterator it;
hbm.removeAll();
hbm.loadFromFile();
for (it = winList.begin(); it != winList.end(); it++)
{
(*it)->populateBookmarkMenu();
}
}
//----------------------------------------------------------------------------
void hexEditorSaveBookmarks(void)
{
std::list <HexEditorDialog_t*>::iterator it;
printf("Save Bookmarks\n");
hbm.saveToFile();
hbm.removeAll();
for (it = winList.begin(); it != winList.end(); it++)
{
(*it)->populateBookmarkMenu();
}
}
//----------------------------------------------------------------------------
int hexEditorNumWindows(void)
{
return winList.size();
}
//----------------------------------------------------------------------------
int hexEditorOpenFromDebugger( int mode, int addr )
{
HexEditorDialog_t *win = NULL;
if ( winList.size() > 0 )
{
win = winList.front();
}
if ( win == NULL )
{
win = new HexEditorDialog_t(consoleWindow);
win->show();
}
win->editor->setMode( mode );
win->editor->setAddr( addr );
return 0;
}
//----------------------------------------------------------------------------
// This function must be called from within the emulation thread
void hexEditorUpdateMemoryValues(void)
{
std::list <HexEditorDialog_t*>::iterator it;
if ( !memNeedsCheck )
{
return;
}
for (it = winList.begin(); it != winList.end(); it++)
{
(*it)->editor->checkMemActivity();
}
memNeedsCheck = false;
}
//----------------------------------------------------------------------------