483 lines
12 KiB
C++
483 lines
12 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
|
|
*/
|
|
// GameGenie.cpp
|
|
//
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <string>
|
|
|
|
#include <QHeaderView>
|
|
#include <QCloseEvent>
|
|
#include <QSettings>
|
|
|
|
#include "../../types.h"
|
|
#include "../../fceu.h"
|
|
#include "../../cart.h"
|
|
#include "../../cheat.h"
|
|
#include "../../debug.h"
|
|
#include "../../driver.h"
|
|
|
|
#include "Qt/main.h"
|
|
#include "Qt/dface.h"
|
|
#include "Qt/dface.h"
|
|
#include "Qt/input.h"
|
|
#include "Qt/config.h"
|
|
#include "Qt/keyscan.h"
|
|
#include "Qt/fceuWrapper.h"
|
|
#include "Qt/CheatsConf.h"
|
|
#include "Qt/HexEditor.h"
|
|
#include "Qt/GameGenie.h"
|
|
|
|
static const char *GameGenieLetters = "APZLGITYEOXUKSVN";
|
|
|
|
class fceuGGCodeValidtor : public QValidator
|
|
{
|
|
public:
|
|
fceuGGCodeValidtor( QObject *parent)
|
|
: QValidator(parent)
|
|
{
|
|
|
|
}
|
|
|
|
QValidator::State validate(QString &input, int &pos) const
|
|
{
|
|
int i,j, ok;
|
|
//printf("Validate: %i '%s'\n", input.size(), input.toStdString().c_str() );
|
|
|
|
if ( input.size() == 0 )
|
|
{
|
|
return QValidator::Acceptable;
|
|
}
|
|
input = input.toUpper();
|
|
std::string s = input.toStdString();
|
|
i=0;
|
|
|
|
while ( s[i] != 0 )
|
|
{
|
|
j=0; ok=0;
|
|
while ( GameGenieLetters[j] != 0 )
|
|
{
|
|
if ( s[i] == GameGenieLetters[j] )
|
|
{
|
|
ok = 1; break;
|
|
}
|
|
j++;
|
|
}
|
|
|
|
if ( !ok )
|
|
{
|
|
return QValidator::Invalid;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
return QValidator::Acceptable;
|
|
}
|
|
|
|
private:
|
|
};
|
|
//----------------------------------------------------------------------------
|
|
GameGenieDialog_t::GameGenieDialog_t(QWidget *parent)
|
|
: QDialog( parent )
|
|
{
|
|
int charWidth;
|
|
QVBoxLayout *mainLayout, *vbox1, *vbox;
|
|
QHBoxLayout *hbox1, *hbox;
|
|
QTreeWidgetItem *item;
|
|
QGroupBox *frame;
|
|
QFont font;
|
|
QPushButton *closeButton;
|
|
fceuGGCodeValidtor *ggCodeValidator;
|
|
QSettings settings;
|
|
|
|
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
|
|
|
|
setWindowTitle("Game Genie Encoder/Decoder Tool");
|
|
|
|
mainLayout = new QVBoxLayout();
|
|
vbox = new QVBoxLayout();
|
|
vbox1 = new QVBoxLayout();
|
|
hbox1 = new QHBoxLayout();
|
|
|
|
frame = new QGroupBox( tr("Address/Compare/Value") );
|
|
mainLayout->addLayout( hbox1 );
|
|
frame->setLayout( vbox );
|
|
|
|
hbox1->addWidget( frame );
|
|
hbox1->addLayout( vbox1 );
|
|
addr = new QLineEdit();
|
|
cmp = new QLineEdit();
|
|
val = new QLineEdit();
|
|
|
|
hbox = new QHBoxLayout();
|
|
vbox->addLayout( hbox );
|
|
hbox->addWidget( new QLabel( tr("Address:") ), 0, Qt::AlignRight );
|
|
hbox->addWidget( addr, 0, Qt::AlignLeft );
|
|
|
|
hbox = new QHBoxLayout();
|
|
vbox->addLayout( hbox );
|
|
hbox->addWidget( new QLabel( tr("Compare:") ), 0, Qt::AlignRight );
|
|
hbox->addWidget( cmp, 0, Qt::AlignLeft );
|
|
|
|
hbox = new QHBoxLayout();
|
|
vbox->addLayout( hbox );
|
|
hbox->addWidget( new QLabel( tr("Value:") ), 0, Qt::AlignRight );
|
|
hbox->addWidget( val, 0, Qt::AlignLeft );
|
|
|
|
frame = new QGroupBox( tr("Game Genie Code") );
|
|
vbox = new QVBoxLayout();
|
|
vbox1->addWidget( frame );
|
|
frame->setLayout( vbox );
|
|
|
|
ggCode = new QLineEdit();
|
|
vbox->addWidget( ggCode );
|
|
|
|
addCheatBtn = new QPushButton( tr("Add To Cheat List") );
|
|
vbox1->addWidget( addCheatBtn );
|
|
|
|
tree = new QTreeWidget();
|
|
|
|
tree->setColumnCount(1);
|
|
|
|
item = new QTreeWidgetItem();
|
|
item->setText( 0, QString::fromStdString( "Possible Affected ROM File Addresses" ) );
|
|
item->setTextAlignment( 0, Qt::AlignLeft);
|
|
|
|
tree->setHeaderItem( item );
|
|
|
|
tree->header()->setSectionResizeMode( QHeaderView::ResizeToContents );
|
|
|
|
mainLayout->addWidget( tree );
|
|
|
|
closeButton = new QPushButton( tr("Close") );
|
|
closeButton->setIcon(style()->standardIcon(QStyle::SP_DialogCloseButton));
|
|
connect(closeButton, SIGNAL(clicked(void)), this, SLOT(closeWindow(void)));
|
|
|
|
hbox = new QHBoxLayout();
|
|
hbox->addStretch(5);
|
|
hbox->addWidget( closeButton, 1 );
|
|
|
|
mainLayout->addLayout( hbox );
|
|
|
|
setLayout( mainLayout );
|
|
|
|
addrValidator = new fceuHexIntValidtor( 0, 0xFFFF, this );
|
|
cmpValidator = new fceuHexIntValidtor( 0, 0x00FF, this );
|
|
valValidator = new fceuHexIntValidtor( 0, 0x00FF, this );
|
|
|
|
ggCodeValidator = new fceuGGCodeValidtor( this );
|
|
|
|
addr->setValidator( addrValidator );
|
|
cmp->setValidator( cmpValidator );
|
|
val->setValidator( valValidator );
|
|
ggCode->setValidator( ggCodeValidator );
|
|
|
|
addr->setMaxLength( 4 );
|
|
cmp->setMaxLength( 2 );
|
|
val->setMaxLength( 2 );
|
|
ggCode->setMaxLength( 8 );
|
|
|
|
addr->setMinimumWidth( 6 * charWidth );
|
|
cmp->setMinimumWidth( 4 * charWidth );
|
|
val->setMinimumWidth( 4 * charWidth );
|
|
addr->setMaximumWidth( 6 * charWidth );
|
|
cmp->setMaximumWidth( 4 * charWidth );
|
|
val->setMaximumWidth( 4 * charWidth );
|
|
|
|
addr->setAlignment(Qt::AlignCenter);
|
|
cmp->setAlignment(Qt::AlignCenter);
|
|
val->setAlignment(Qt::AlignCenter);
|
|
|
|
addr->setFont( font );
|
|
cmp->setFont( font );
|
|
val->setFont( font );
|
|
ggCode->setFont( font );
|
|
|
|
connect( addCheatBtn, SIGNAL(clicked(void)), this, SLOT(addCheatClicked(void)));
|
|
|
|
connect( addr , SIGNAL(textEdited(const QString &)), this, SLOT(addrChanged(const QString &)));
|
|
connect( cmp , SIGNAL(textEdited(const QString &)), this, SLOT(cmpChanged(const QString &)));
|
|
connect( val , SIGNAL(textEdited(const QString &)), this, SLOT(valChanged(const QString &)));
|
|
connect( ggCode, SIGNAL(textEdited(const QString &)), this, SLOT(ggChanged(const QString &)));
|
|
|
|
connect( tree, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(romAddrDoubleClicked(QTreeWidgetItem*, int)) );
|
|
|
|
addCheatBtn->setEnabled( false );
|
|
|
|
restoreGeometry(settings.value("GameGenieWindow/geometry").toByteArray());
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
GameGenieDialog_t::~GameGenieDialog_t(void)
|
|
{
|
|
QSettings settings;
|
|
//printf("Destroy Game Genie Window\n");
|
|
settings.setValue("GameGenieWindow/geometry", saveGeometry());
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void GameGenieDialog_t::closeEvent(QCloseEvent *event)
|
|
{
|
|
//printf("Game Genie Close Window Event\n");
|
|
done(0);
|
|
deleteLater();
|
|
event->accept();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void GameGenieDialog_t::closeWindow(void)
|
|
{
|
|
//printf("Close Window\n");
|
|
done(0);
|
|
deleteLater();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void GameGenieDialog_t::addCheatClicked(void)
|
|
{
|
|
int a = -1, v = -1, c = -1;
|
|
std::string name;
|
|
|
|
name = ggCode->text().toStdString();
|
|
|
|
if ( addr->text().size() > 0 )
|
|
{
|
|
a = strtol( addr->text().toStdString().c_str(), NULL, 16 );
|
|
}
|
|
if ( val->text().size() > 0 )
|
|
{
|
|
v = strtol( val->text().toStdString().c_str(), NULL, 16 );
|
|
}
|
|
if ( cmp->text().size() > 0 )
|
|
{
|
|
c = strtol( cmp->text().toStdString().c_str(), NULL, 16 );
|
|
}
|
|
|
|
FCEU_WRAPPER_LOCK();
|
|
FCEUI_AddCheat( name.c_str(), a, v, c, 1 );
|
|
updateCheatDialog();
|
|
FCEU_WRAPPER_UNLOCK();
|
|
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void GameGenieDialog_t::romAddrDoubleClicked(QTreeWidgetItem *item, int column)
|
|
{
|
|
int addr;
|
|
|
|
addr = strtol( item->text(0).toStdString().c_str(), NULL, 16 );
|
|
|
|
printf("ROM Addr: %06X \n", addr );
|
|
|
|
hexEditorOpenFromDebugger( QHexEdit::MODE_NES_ROM, addr );
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void GameGenieDialog_t::addrChanged(const QString &s)
|
|
{
|
|
int a, v, c = -1;
|
|
char gg[12];
|
|
|
|
a = strtol( s.toStdString().c_str(), NULL, 16 );
|
|
v = strtol( val->text().toStdString().c_str(), NULL, 16 );
|
|
|
|
if ( cmp->text().size() > 0 )
|
|
{
|
|
c = strtol( cmp->text().toStdString().c_str(), NULL, 16 );
|
|
}
|
|
|
|
EncodeGG( gg, a, v, c );
|
|
|
|
ggCode->setText( tr(gg) );
|
|
|
|
ListGGAddresses();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void GameGenieDialog_t::cmpChanged(const QString &s)
|
|
{
|
|
int a, v, c = -1;
|
|
char gg[12];
|
|
|
|
a = strtol( addr->text().toStdString().c_str(), NULL, 16 );
|
|
v = strtol( val->text().toStdString().c_str(), NULL, 16 );
|
|
|
|
if ( s.size() > 0 )
|
|
{
|
|
c = strtol( s.toStdString().c_str(), NULL, 16 );
|
|
}
|
|
|
|
EncodeGG( gg, a, v, c );
|
|
|
|
ggCode->setText( tr(gg) );
|
|
|
|
ListGGAddresses();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void GameGenieDialog_t::valChanged(const QString &s)
|
|
{
|
|
int a, v, c = -1;
|
|
char gg[12];
|
|
|
|
a = strtol( addr->text().toStdString().c_str(), NULL, 16 );
|
|
v = strtol( s.toStdString().c_str(), NULL, 16 );
|
|
|
|
if ( cmp->text().size() > 0 )
|
|
{
|
|
c = strtol( cmp->text().toStdString().c_str(), NULL, 16 );
|
|
}
|
|
EncodeGG( gg, a, v, c );
|
|
|
|
ggCode->setText( tr(gg) );
|
|
|
|
ListGGAddresses();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void GameGenieDialog_t::ggChanged(const QString &s)
|
|
{
|
|
int a = -1, c = -1, v = -1;
|
|
char gg[12];
|
|
char stmp[32];
|
|
|
|
memset( gg, 0, sizeof(gg) );
|
|
|
|
strncpy( gg, ggCode->text().toStdString().c_str(), 8 );
|
|
|
|
FCEUI_DecodeGG( gg, &a, &v, &c);
|
|
|
|
if ( a >= 0 )
|
|
{
|
|
sprintf( stmp, "%04X", a );
|
|
|
|
addr->setText( tr(stmp) );
|
|
}
|
|
else
|
|
{
|
|
addr->clear();
|
|
}
|
|
|
|
if ( v >= 0 )
|
|
{
|
|
sprintf( stmp, "%02X", v );
|
|
|
|
val->setText( tr(stmp) );
|
|
}
|
|
else
|
|
{
|
|
val->clear();
|
|
}
|
|
|
|
if ( c >= 0 )
|
|
{
|
|
sprintf( stmp, "%02X", c );
|
|
|
|
cmp->setText( tr(stmp) );
|
|
}
|
|
else
|
|
{
|
|
cmp->clear();
|
|
}
|
|
|
|
ListGGAddresses();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
//The code in this function is a modified version
|
|
//of Chris Covell's work - I'd just like to point that out
|
|
void EncodeGG(char *str, int a, int v, int c)
|
|
{
|
|
uint8 num[8];
|
|
int i;
|
|
|
|
a&=0x7fff;
|
|
|
|
num[0]=(v&7)+((v>>4)&8);
|
|
num[1]=((v>>4)&7)+((a>>4)&8);
|
|
num[2]=((a>>4)&7);
|
|
num[3]=(a>>12)+(a&8);
|
|
num[4]=(a&7)+((a>>8)&8);
|
|
num[5]=((a>>8)&7);
|
|
|
|
if (c == -1){
|
|
num[5]+=v&8;
|
|
for(i = 0;i < 6;i++)str[i] = GameGenieLetters[num[i]];
|
|
str[6] = 0;
|
|
} else {
|
|
num[2]+=8;
|
|
num[5]+=c&8;
|
|
num[6]=(c&7)+((c>>4)&8);
|
|
num[7]=((c>>4)&7)+(v&8);
|
|
for(i = 0;i < 8;i++)str[i] = GameGenieLetters[num[i]];
|
|
str[8] = 0;
|
|
}
|
|
return;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void GameGenieDialog_t::ListGGAddresses(void)
|
|
{
|
|
int a = -1; int v = -1; int c = -1;
|
|
QTreeWidgetItem *item;
|
|
char str[32];
|
|
bool addCheatEmable;
|
|
|
|
if ( addr->text().size() > 0 )
|
|
{
|
|
a = strtol( addr->text().toStdString().c_str(), NULL, 16 );
|
|
}
|
|
if ( val->text().size() > 0 )
|
|
{
|
|
v = strtol( val->text().toStdString().c_str(), NULL, 16 );
|
|
}
|
|
if ( cmp->text().size() > 0 )
|
|
{
|
|
c = strtol( cmp->text().toStdString().c_str(), NULL, 16 );
|
|
}
|
|
// also enable/disable the add GG button here
|
|
addCheatEmable = (a >= 0) && ( (ggCode->text().size() == 6) || (ggCode->text().size() == 8) );
|
|
|
|
addCheatBtn->setEnabled( addCheatEmable );
|
|
|
|
tree->clear();
|
|
|
|
if (a != -1 && v != -1)
|
|
{
|
|
for (unsigned int i = 0; i < PRGsize[0]; i += 0x2000)
|
|
{
|
|
if (c == -1 || PRGptr[0][i + (a & 0x1FFF)] == c)
|
|
{
|
|
item = new QTreeWidgetItem();
|
|
|
|
sprintf(str, "%06X", i + (a & 0x1FFF) + 0x10);
|
|
|
|
//printf("Added ROM ADDR: %s\n", str );
|
|
item->setText( 0, tr(str) );
|
|
|
|
item->setTextAlignment( 0, Qt::AlignCenter);
|
|
tree->addTopLevelItem( item );
|
|
}
|
|
}
|
|
}
|
|
tree->viewport()->update();
|
|
}
|
|
//----------------------------------------------------------------------------
|