mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2025-07-21 05:09:46 -06:00
(finally) build the goddamn cheat interface
This commit is contained in:
@ -35,8 +35,21 @@ ARCodeFile::ARCodeFile(const char* filename)
|
|||||||
|
|
||||||
Categories.clear();
|
Categories.clear();
|
||||||
|
|
||||||
|
if (!Load())
|
||||||
|
Error = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ARCodeFile::~ARCodeFile()
|
||||||
|
{
|
||||||
|
Categories.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ARCodeFile::Load()
|
||||||
|
{
|
||||||
FILE* f = Platform::OpenFile(Filename, "r");
|
FILE* f = Platform::OpenFile(Filename, "r");
|
||||||
if (!f) return;
|
if (!f) return true;
|
||||||
|
|
||||||
|
Categories.clear();
|
||||||
|
|
||||||
bool isincat = false;
|
bool isincat = false;
|
||||||
ARCodeCat curcat;
|
ARCodeCat curcat;
|
||||||
@ -60,15 +73,14 @@ ARCodeFile::ARCodeFile(const char* filename)
|
|||||||
if (!strncasecmp(start, "CAT", 3))
|
if (!strncasecmp(start, "CAT", 3))
|
||||||
{
|
{
|
||||||
char catname[128];
|
char catname[128];
|
||||||
int ret = sscanf(start, "CAT %127[^\n]", catname);
|
int ret = sscanf(start, "CAT %127[^\r\n]", catname);
|
||||||
catname[127] = '\0';
|
catname[127] = '\0';
|
||||||
|
|
||||||
if (ret < 1)
|
if (ret < 1)
|
||||||
{
|
{
|
||||||
printf("AR: malformed CAT line: %s\n", start);
|
printf("AR: malformed CAT line: %s\n", start);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
Error = true;
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isincode) curcat.Codes.push_back(curcode);
|
if (isincode) curcat.Codes.push_back(curcode);
|
||||||
@ -84,23 +96,21 @@ ARCodeFile::ARCodeFile(const char* filename)
|
|||||||
{
|
{
|
||||||
int enable;
|
int enable;
|
||||||
char codename[128];
|
char codename[128];
|
||||||
int ret = sscanf(start, "CODE %d %127[^\n]", &enable, codename);
|
int ret = sscanf(start, "CODE %d %127[^\r\n]", &enable, codename);
|
||||||
codename[127] = '\0';
|
codename[127] = '\0';
|
||||||
|
|
||||||
if (ret < 2)
|
if (ret < 2)
|
||||||
{
|
{
|
||||||
printf("AR: malformed CODE line: %s\n", start);
|
printf("AR: malformed CODE line: %s\n", start);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
Error = true;
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isincat)
|
if (!isincat)
|
||||||
{
|
{
|
||||||
printf("AR: encountered CODE line with no category started\n");
|
printf("AR: encountered CODE line with no category started\n");
|
||||||
fclose(f);
|
fclose(f);
|
||||||
Error = true;
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isincode) curcat.Codes.push_back(curcode);
|
if (isincode) curcat.Codes.push_back(curcode);
|
||||||
@ -119,24 +129,21 @@ ARCodeFile::ARCodeFile(const char* filename)
|
|||||||
{
|
{
|
||||||
printf("AR: malformed data line: %s\n", start);
|
printf("AR: malformed data line: %s\n", start);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
Error = true;
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isincode)
|
if (!isincode)
|
||||||
{
|
{
|
||||||
printf("AR: encountered data line with no code started\n");
|
printf("AR: encountered data line with no code started\n");
|
||||||
fclose(f);
|
fclose(f);
|
||||||
Error = true;
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (curcode.CodeLen >= 2*64)
|
if (curcode.CodeLen >= 2*64)
|
||||||
{
|
{
|
||||||
printf("AR: code too long!\n");
|
printf("AR: code too long!\n");
|
||||||
fclose(f);
|
fclose(f);
|
||||||
Error = true;
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 idx = curcode.CodeLen;
|
u32 idx = curcode.CodeLen;
|
||||||
@ -150,27 +157,35 @@ ARCodeFile::ARCodeFile(const char* filename)
|
|||||||
if (isincat) Categories.push_back(curcat);
|
if (isincat) Categories.push_back(curcat);
|
||||||
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ARCodeFile::Save()
|
||||||
|
{
|
||||||
|
FILE* f = Platform::OpenFile(Filename, "w");
|
||||||
|
if (!f) return false;
|
||||||
|
|
||||||
printf("PARSED OUTPUT\n");
|
|
||||||
for (ARCodeCatList::iterator it = Categories.begin(); it != Categories.end(); it++)
|
for (ARCodeCatList::iterator it = Categories.begin(); it != Categories.end(); it++)
|
||||||
{
|
{
|
||||||
ARCodeCat& cat = *it;
|
ARCodeCat& cat = *it;
|
||||||
printf("CAT %s\n", cat.Name);
|
|
||||||
|
if (it != Categories.begin()) fprintf(f, "\n");
|
||||||
|
fprintf(f, "CAT %s\n\n", cat.Name);
|
||||||
|
|
||||||
for (ARCodeList::iterator jt = cat.Codes.begin(); jt != cat.Codes.end(); jt++)
|
for (ARCodeList::iterator jt = cat.Codes.begin(); jt != cat.Codes.end(); jt++)
|
||||||
{
|
{
|
||||||
ARCode& code = *jt;
|
ARCode& code = *jt;
|
||||||
printf("CODE %d %s\n", code.Enabled, code.Name);
|
fprintf(f, "CODE %d %s\n", code.Enabled, code.Name);
|
||||||
|
|
||||||
for (u32 i = 0; i < code.CodeLen; i+=2)
|
for (u32 i = 0; i < code.CodeLen; i+=2)
|
||||||
{
|
{
|
||||||
printf("%08X %08X\n", code.Code[i], code.Code[i+1]);
|
fprintf(f, "%08X %08X\n", code.Code[i], code.Code[i+1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fprintf(f, "\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ARCodeFile::~ARCodeFile()
|
fclose(f);
|
||||||
{
|
return true;
|
||||||
//
|
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
#ifndef ARCODEFILE_H
|
#ifndef ARCODEFILE_H
|
||||||
#define ARCODEFILE_H
|
#define ARCODEFILE_H
|
||||||
|
|
||||||
#include <vector>
|
#include <list>
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ typedef struct
|
|||||||
|
|
||||||
} ARCode;
|
} ARCode;
|
||||||
|
|
||||||
typedef std::vector<ARCode> ARCodeList;
|
typedef std::list<ARCode> ARCodeList;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@ -41,7 +41,7 @@ typedef struct
|
|||||||
|
|
||||||
} ARCodeCat;
|
} ARCodeCat;
|
||||||
|
|
||||||
typedef std::vector<ARCodeCat> ARCodeCatList;
|
typedef std::list<ARCodeCat> ARCodeCatList;
|
||||||
|
|
||||||
|
|
||||||
class ARCodeFile
|
class ARCodeFile
|
||||||
@ -52,6 +52,7 @@ public:
|
|||||||
|
|
||||||
bool Error;
|
bool Error;
|
||||||
|
|
||||||
|
bool Load();
|
||||||
bool Save();
|
bool Save();
|
||||||
|
|
||||||
ARCodeCatList Categories;
|
ARCodeCatList Categories;
|
||||||
|
@ -104,7 +104,7 @@ void Load()
|
|||||||
while (!feof(f))
|
while (!feof(f))
|
||||||
{
|
{
|
||||||
fgets(linebuf, 1024, f);
|
fgets(linebuf, 1024, f);
|
||||||
int ret = sscanf(linebuf, "%31[A-Za-z_0-9]=%[^\t\n]", entryname, entryval);
|
int ret = sscanf(linebuf, "%31[A-Za-z_0-9]=%[^\t\r\n]", entryname, entryval);
|
||||||
entryname[31] = '\0';
|
entryname[31] = '\0';
|
||||||
if (ret < 2) continue;
|
if (ret < 2) continue;
|
||||||
|
|
||||||
|
@ -67,6 +67,9 @@ extern bool SavestateLoaded;
|
|||||||
// initialize the ROM handling utility
|
// initialize the ROM handling utility
|
||||||
void Init_ROM();
|
void Init_ROM();
|
||||||
|
|
||||||
|
// deinitialize the ROM handling utility
|
||||||
|
void DeInit_ROM();
|
||||||
|
|
||||||
// load the BIOS/firmware and boot from it
|
// load the BIOS/firmware and boot from it
|
||||||
int LoadBIOS();
|
int LoadBIOS();
|
||||||
|
|
||||||
@ -97,6 +100,9 @@ bool SaveState(const char* filename);
|
|||||||
// undo the latest savestate load
|
// undo the latest savestate load
|
||||||
void UndoStateLoad();
|
void UndoStateLoad();
|
||||||
|
|
||||||
|
// enable or disable cheats
|
||||||
|
void EnableCheats(bool enable);
|
||||||
|
|
||||||
|
|
||||||
// setup the display layout based on the provided display size and parameters
|
// setup the display layout based on the provided display size and parameters
|
||||||
// * screenWidth/screenHeight: size of the host display
|
// * screenWidth/screenHeight: size of the host display
|
||||||
|
@ -27,6 +27,8 @@
|
|||||||
#include "NDS.h"
|
#include "NDS.h"
|
||||||
#include "GBACart.h"
|
#include "GBACart.h"
|
||||||
|
|
||||||
|
#include "AREngine.h"
|
||||||
|
|
||||||
|
|
||||||
namespace Frontend
|
namespace Frontend
|
||||||
{
|
{
|
||||||
@ -37,6 +39,9 @@ char PrevSRAMPath[ROMSlot_MAX][1024]; // for savestate 'undo load'
|
|||||||
|
|
||||||
bool SavestateLoaded;
|
bool SavestateLoaded;
|
||||||
|
|
||||||
|
ARCodeFile* CheatFile;
|
||||||
|
bool CheatsOn;
|
||||||
|
|
||||||
|
|
||||||
void Init_ROM()
|
void Init_ROM()
|
||||||
{
|
{
|
||||||
@ -48,6 +53,18 @@ void Init_ROM()
|
|||||||
memset(SRAMPath[ROMSlot_GBA], 0, 1024);
|
memset(SRAMPath[ROMSlot_GBA], 0, 1024);
|
||||||
memset(PrevSRAMPath[ROMSlot_NDS], 0, 1024);
|
memset(PrevSRAMPath[ROMSlot_NDS], 0, 1024);
|
||||||
memset(PrevSRAMPath[ROMSlot_GBA], 0, 1024);
|
memset(PrevSRAMPath[ROMSlot_GBA], 0, 1024);
|
||||||
|
|
||||||
|
CheatFile = nullptr;
|
||||||
|
CheatsOn = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeInit_ROM()
|
||||||
|
{
|
||||||
|
if (CheatFile)
|
||||||
|
{
|
||||||
|
delete CheatFile;
|
||||||
|
CheatFile = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: currently, when failing to load a ROM for whatever reason, we attempt
|
// TODO: currently, when failing to load a ROM for whatever reason, we attempt
|
||||||
@ -198,6 +215,25 @@ int VerifyDSiNAND()
|
|||||||
return Load_OK;
|
return Load_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LoadCheats()
|
||||||
|
{
|
||||||
|
if (CheatFile)
|
||||||
|
{
|
||||||
|
delete CheatFile;
|
||||||
|
CheatFile = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
char filename[1024];
|
||||||
|
strncpy(filename, ROMPath[ROMSlot_NDS], 1023);
|
||||||
|
filename[1023] = '\0';
|
||||||
|
strncpy(filename + strlen(ROMPath[ROMSlot_NDS]) - 3, "mch", 3);
|
||||||
|
|
||||||
|
// TODO: check for error (malformed cheat file, ...)
|
||||||
|
CheatFile = new ARCodeFile(filename);
|
||||||
|
|
||||||
|
AREngine::SetCodeFile(CheatsOn ? CheatFile : nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
int LoadBIOS()
|
int LoadBIOS()
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
@ -235,6 +271,8 @@ int LoadBIOS()
|
|||||||
|
|
||||||
SavestateLoaded = false;
|
SavestateLoaded = false;
|
||||||
|
|
||||||
|
LoadCheats();
|
||||||
|
|
||||||
return Load_OK;
|
return Load_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,6 +333,8 @@ int LoadROM(const char* file, int slot)
|
|||||||
{
|
{
|
||||||
SavestateLoaded = false;
|
SavestateLoaded = false;
|
||||||
|
|
||||||
|
LoadCheats();
|
||||||
|
|
||||||
// Reload the inserted GBA cartridge (if any)
|
// Reload the inserted GBA cartridge (if any)
|
||||||
// TODO: report failure there??
|
// TODO: report failure there??
|
||||||
if (ROMPath[ROMSlot_GBA][0] != '\0') NDS::LoadGBAROM(ROMPath[ROMSlot_GBA], SRAMPath[ROMSlot_GBA]);
|
if (ROMPath[ROMSlot_GBA][0] != '\0') NDS::LoadGBAROM(ROMPath[ROMSlot_GBA], SRAMPath[ROMSlot_GBA]);
|
||||||
@ -387,6 +427,8 @@ int Reset()
|
|||||||
return Load_ROMLoadError;
|
return Load_ROMLoadError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LoadCheats();
|
||||||
|
|
||||||
return Load_OK;
|
return Load_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -539,4 +581,11 @@ void UndoStateLoad()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EnableCheats(bool enable)
|
||||||
|
{
|
||||||
|
CheatsOn = enable;
|
||||||
|
if (CheatFile)
|
||||||
|
AREngine::SetCodeFile(CheatsOn ? CheatFile : nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
@ -32,30 +33,380 @@ CheatsDialog* CheatsDialog::currentDlg = nullptr;
|
|||||||
|
|
||||||
extern char* EmuDirectory;
|
extern char* EmuDirectory;
|
||||||
|
|
||||||
|
namespace Frontend { extern ARCodeFile* CheatFile; }
|
||||||
|
|
||||||
|
|
||||||
CheatsDialog::CheatsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::CheatsDialog)
|
CheatsDialog::CheatsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::CheatsDialog)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
setAttribute(Qt::WA_DeleteOnClose);
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
|
||||||
// setup UI here
|
codeFile = Frontend::CheatFile;
|
||||||
|
|
||||||
|
QStandardItemModel* model = new QStandardItemModel();
|
||||||
|
ui->tvCodeList->setModel(model);
|
||||||
|
connect(model, &QStandardItemModel::itemChanged, this, &CheatsDialog::onCheatEntryModified);
|
||||||
|
connect(ui->tvCodeList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &CheatsDialog::onCheatSelectionChanged);
|
||||||
|
|
||||||
|
{
|
||||||
|
QStandardItem* root = model->invisibleRootItem();
|
||||||
|
|
||||||
|
for (ARCodeCatList::iterator i = codeFile->Categories.begin(); i != codeFile->Categories.end(); i++)
|
||||||
|
{
|
||||||
|
ARCodeCat& cat = *i;
|
||||||
|
|
||||||
|
QStandardItem* catitem = new QStandardItem(cat.Name);
|
||||||
|
catitem->setEditable(true);
|
||||||
|
catitem->setData(QVariant::fromValue(i));
|
||||||
|
root->appendRow(catitem);
|
||||||
|
|
||||||
|
for (ARCodeList::iterator j = cat.Codes.begin(); j != cat.Codes.end(); j++)
|
||||||
|
{
|
||||||
|
ARCode& code = *j;
|
||||||
|
|
||||||
|
QStandardItem* codeitem = new QStandardItem(code.Name);
|
||||||
|
codeitem->setEditable(true);
|
||||||
|
codeitem->setCheckable(true);
|
||||||
|
codeitem->setCheckState(code.Enabled ? Qt::Checked : Qt::Unchecked);
|
||||||
|
codeitem->setData(QVariant::fromValue(j));
|
||||||
|
catitem->appendRow(codeitem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->txtCode->setPlaceholderText("");
|
||||||
|
codeChecker = new ARCodeChecker(ui->txtCode->document());
|
||||||
|
|
||||||
|
ui->btnNewARCode->setEnabled(false);
|
||||||
|
ui->btnDeleteCode->setEnabled(false);
|
||||||
|
ui->txtCode->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
CheatsDialog::~CheatsDialog()
|
CheatsDialog::~CheatsDialog()
|
||||||
{
|
{
|
||||||
|
QAbstractItemModel* model = ui->tvCodeList->model();
|
||||||
|
ui->tvCodeList->setModel(nullptr);
|
||||||
|
delete model;
|
||||||
|
|
||||||
|
delete codeChecker;
|
||||||
|
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheatsDialog::on_CheatsDialog_accepted()
|
void CheatsDialog::on_CheatsDialog_accepted()
|
||||||
{
|
{
|
||||||
// save shit here
|
codeFile->Save();
|
||||||
|
|
||||||
closeDlg();
|
closeDlg();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheatsDialog::on_CheatsDialog_rejected()
|
void CheatsDialog::on_CheatsDialog_rejected()
|
||||||
{
|
{
|
||||||
// don't save shit here
|
codeFile->Load();
|
||||||
|
|
||||||
closeDlg();
|
closeDlg();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheatsDialog::on_btnNewCat_clicked()
|
||||||
|
{
|
||||||
|
QStandardItem* root = ((QStandardItemModel*)ui->tvCodeList->model())->invisibleRootItem();
|
||||||
|
|
||||||
|
ARCodeCat cat;
|
||||||
|
cat.Codes.clear();
|
||||||
|
memset(cat.Name, 0, 128);
|
||||||
|
strncpy(cat.Name, "(new category)", 127);
|
||||||
|
|
||||||
|
codeFile->Categories.push_back(cat);
|
||||||
|
ARCodeCatList::iterator id = codeFile->Categories.end(); id--;
|
||||||
|
|
||||||
|
QStandardItem* catitem = new QStandardItem(cat.Name);
|
||||||
|
catitem->setEditable(true);
|
||||||
|
catitem->setData(QVariant::fromValue(id));
|
||||||
|
root->appendRow(catitem);
|
||||||
|
|
||||||
|
ui->tvCodeList->selectionModel()->select(catitem->index(), QItemSelectionModel::ClearAndSelect);
|
||||||
|
ui->tvCodeList->edit(catitem->index());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheatsDialog::on_btnNewARCode_clicked()
|
||||||
|
{
|
||||||
|
QModelIndexList indices = ui->tvCodeList->selectionModel()->selectedIndexes();
|
||||||
|
if (indices.isEmpty())
|
||||||
|
{
|
||||||
|
// ????
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStandardItemModel* model = (QStandardItemModel*)ui->tvCodeList->model();
|
||||||
|
QStandardItem* item = model->itemFromIndex(indices.first());
|
||||||
|
QStandardItem* parentitem;
|
||||||
|
|
||||||
|
QVariant data = item->data();
|
||||||
|
if (data.canConvert<ARCodeCatList::iterator>())
|
||||||
|
{
|
||||||
|
parentitem = item;
|
||||||
|
}
|
||||||
|
else if (data.canConvert<ARCodeList::iterator>())
|
||||||
|
{
|
||||||
|
parentitem = item->parent();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("what?? :(\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ARCodeCatList::iterator it_cat = parentitem->data().value<ARCodeCatList::iterator>();
|
||||||
|
ARCodeCat& cat = *it_cat;
|
||||||
|
|
||||||
|
ARCode code;
|
||||||
|
memset(code.Name, 0, 128);
|
||||||
|
strncpy(code.Name, "(new AR code)", 127);
|
||||||
|
code.Enabled = true;
|
||||||
|
code.CodeLen = 0;
|
||||||
|
memset(code.Code, 0, sizeof(code.Code));
|
||||||
|
|
||||||
|
cat.Codes.push_back(code);
|
||||||
|
ARCodeList::iterator id = cat.Codes.end(); id--;
|
||||||
|
|
||||||
|
QStandardItem* codeitem = new QStandardItem(code.Name);
|
||||||
|
codeitem->setEditable(true);
|
||||||
|
codeitem->setCheckable(true);
|
||||||
|
codeitem->setCheckState(code.Enabled ? Qt::Checked : Qt::Unchecked);
|
||||||
|
codeitem->setData(QVariant::fromValue(id));
|
||||||
|
parentitem->appendRow(codeitem);
|
||||||
|
|
||||||
|
ui->tvCodeList->selectionModel()->select(codeitem->index(), QItemSelectionModel::ClearAndSelect);
|
||||||
|
ui->tvCodeList->edit(codeitem->index());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheatsDialog::on_btnDeleteCode_clicked()
|
||||||
|
{
|
||||||
|
QModelIndexList indices = ui->tvCodeList->selectionModel()->selectedIndexes();
|
||||||
|
if (indices.isEmpty())
|
||||||
|
{
|
||||||
|
// ????
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMessageBox::StandardButton res = QMessageBox::question(this,
|
||||||
|
"Confirm deletion",
|
||||||
|
"Really delete the selected item?",
|
||||||
|
QMessageBox::Yes|QMessageBox::No,
|
||||||
|
QMessageBox::No);
|
||||||
|
if (res != QMessageBox::Yes) return;
|
||||||
|
|
||||||
|
QStandardItemModel* model = (QStandardItemModel*)ui->tvCodeList->model();
|
||||||
|
QStandardItem* item = model->itemFromIndex(indices.first());
|
||||||
|
|
||||||
|
QVariant data = item->data();
|
||||||
|
if (data.canConvert<ARCodeCatList::iterator>())
|
||||||
|
{
|
||||||
|
ARCodeCatList::iterator it_cat = data.value<ARCodeCatList::iterator>();
|
||||||
|
|
||||||
|
(*it_cat).Codes.clear();
|
||||||
|
codeFile->Categories.erase(it_cat);
|
||||||
|
|
||||||
|
model->invisibleRootItem()->removeRow(item->row());
|
||||||
|
}
|
||||||
|
else if (data.canConvert<ARCodeList::iterator>())
|
||||||
|
{
|
||||||
|
ARCodeList::iterator it_code = data.value<ARCodeList::iterator>();
|
||||||
|
ARCodeCatList::iterator it_cat = item->parent()->data().value<ARCodeCatList::iterator>();
|
||||||
|
|
||||||
|
(*it_cat).Codes.erase(it_code);
|
||||||
|
|
||||||
|
item->parent()->removeRow(item->row());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheatsDialog::onCheatSelectionChanged(const QItemSelection& sel, const QItemSelection& desel)
|
||||||
|
{
|
||||||
|
QModelIndexList indices = sel.indexes();
|
||||||
|
if (indices.isEmpty())
|
||||||
|
{
|
||||||
|
ui->btnNewARCode->setEnabled(false);
|
||||||
|
ui->btnDeleteCode->setEnabled(false);
|
||||||
|
ui->txtCode->setEnabled(false);
|
||||||
|
ui->txtCode->setPlaceholderText("");
|
||||||
|
ui->txtCode->clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QStandardItem* item = ((QStandardItemModel*)ui->tvCodeList->model())->itemFromIndex(indices.first());
|
||||||
|
|
||||||
|
QVariant data = item->data();
|
||||||
|
if (data.canConvert<ARCodeCatList::iterator>())
|
||||||
|
{
|
||||||
|
ui->btnDeleteCode->setEnabled(true);
|
||||||
|
ui->txtCode->setEnabled(false);
|
||||||
|
ui->txtCode->setPlaceholderText("");
|
||||||
|
ui->txtCode->clear();
|
||||||
|
}
|
||||||
|
else if (data.canConvert<ARCodeList::iterator>())
|
||||||
|
{
|
||||||
|
ARCode& code = *(data.value<ARCodeList::iterator>());
|
||||||
|
|
||||||
|
ui->btnDeleteCode->setEnabled(true);
|
||||||
|
ui->txtCode->setEnabled(true);
|
||||||
|
ui->txtCode->setPlaceholderText("(enter AR code here)");
|
||||||
|
|
||||||
|
QString codestr = "";
|
||||||
|
for (u32 i = 0; i < code.CodeLen; i += 2)
|
||||||
|
{
|
||||||
|
u32 c0 = code.Code[i+0];
|
||||||
|
u32 c1 = code.Code[i+1];
|
||||||
|
//codestr += QString("%1 %2\n").arg(c0, 8, 16, '0').arg(c1, 8, 16, '0').toUpper();
|
||||||
|
codestr += QString::asprintf("%08X %08X\n", c0, c1);
|
||||||
|
}
|
||||||
|
ui->txtCode->setPlainText(codestr);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->btnNewARCode->setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheatsDialog::onCheatEntryModified(QStandardItem* item)
|
||||||
|
{
|
||||||
|
QVariant data = item->data();
|
||||||
|
if (data.canConvert<ARCodeCatList::iterator>())
|
||||||
|
{
|
||||||
|
ARCodeCat& cat = *(data.value<ARCodeCatList::iterator>());
|
||||||
|
|
||||||
|
if (item->text().isEmpty())
|
||||||
|
{
|
||||||
|
QString oldname = QString(cat.Name);
|
||||||
|
item->setText(oldname.isEmpty() ? "(blank category name??)" : oldname);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
strncpy(cat.Name, item->text().toStdString().c_str(), 127);
|
||||||
|
cat.Name[127] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (data.canConvert<ARCodeList::iterator>())
|
||||||
|
{
|
||||||
|
ARCode& code = *(data.value<ARCodeList::iterator>());
|
||||||
|
|
||||||
|
if (item->text().isEmpty())
|
||||||
|
{
|
||||||
|
QString oldname = QString(code.Name);
|
||||||
|
item->setText(oldname.isEmpty() ? "(blank code name??)" : oldname);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
strncpy(code.Name, item->text().toStdString().c_str(), 127);
|
||||||
|
code.Name[127] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
code.Enabled = (item->checkState() == Qt::Checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheatsDialog::on_txtCode_textChanged()
|
||||||
|
{
|
||||||
|
QModelIndexList indices = ui->tvCodeList->selectionModel()->selectedIndexes();
|
||||||
|
if (indices.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QStandardItem* item = ((QStandardItemModel*)ui->tvCodeList->model())->itemFromIndex(indices.first());
|
||||||
|
QVariant data = item->data();
|
||||||
|
if (!data.canConvert<ARCodeList::iterator>())
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool error = false;
|
||||||
|
u32 codeout[2*64];
|
||||||
|
u32 codelen = 0;
|
||||||
|
|
||||||
|
QString text = ui->txtCode->document()->toPlainText();
|
||||||
|
QStringList lines = text.split('\n', QString::SkipEmptyParts);
|
||||||
|
for (QStringList::iterator it = lines.begin(); it != lines.end(); it++)
|
||||||
|
{
|
||||||
|
QString line = *it;
|
||||||
|
line = line.trimmed();
|
||||||
|
if (line.isEmpty()) continue;
|
||||||
|
|
||||||
|
if (line.length() > 17)
|
||||||
|
{
|
||||||
|
error = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList numbers = line.split(' ');
|
||||||
|
if (numbers.length() != 2)
|
||||||
|
{
|
||||||
|
error = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList::iterator jt = numbers.begin();
|
||||||
|
QString s0 = *jt++;
|
||||||
|
QString s1 = *jt++;
|
||||||
|
|
||||||
|
bool c0good, c1good;
|
||||||
|
u32 c0, c1;
|
||||||
|
|
||||||
|
c0 = s0.toUInt(&c0good, 16);
|
||||||
|
c1 = s1.toUInt(&c1good, 16);
|
||||||
|
|
||||||
|
if (!c0good || !c1good)
|
||||||
|
{
|
||||||
|
error = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codelen >= 2*64)
|
||||||
|
{
|
||||||
|
error = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
codeout[codelen++] = c0;
|
||||||
|
codeout[codelen++] = c1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->btnNewCat->setEnabled(!error);
|
||||||
|
ui->btnNewARCode->setEnabled(!error);
|
||||||
|
ui->btnDeleteCode->setEnabled(!error);
|
||||||
|
ui->tvCodeList->setEnabled(!error);
|
||||||
|
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!error);
|
||||||
|
|
||||||
|
if (error) return;
|
||||||
|
|
||||||
|
ARCode& code = *(data.value<ARCodeList::iterator>());
|
||||||
|
memcpy(code.Code, codeout, codelen*sizeof(u32));
|
||||||
|
code.CodeLen = codelen;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARCodeChecker::highlightBlock(const QString& text)
|
||||||
|
{
|
||||||
|
QTextCharFormat errformat; errformat.setForeground(Qt::red);
|
||||||
|
|
||||||
|
{
|
||||||
|
QRegularExpression expr("^\\s*[0-9A-Fa-f]{1,8} [0-9A-Fa-f]{1,8}\\s*$");
|
||||||
|
QRegularExpressionMatchIterator it = expr.globalMatch(text);
|
||||||
|
if (!it.hasNext())
|
||||||
|
{
|
||||||
|
setFormat(0, text.length(), errformat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*{
|
||||||
|
QRegularExpression expr("[^0-9A-Fa-f\\s]+");
|
||||||
|
QRegularExpressionMatchIterator it = expr.globalMatch(text);
|
||||||
|
while (it.hasNext())
|
||||||
|
{
|
||||||
|
QRegularExpressionMatch match = it.next();
|
||||||
|
setFormat(match.capturedStart(), match.capturedLength(), errformat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
QRegularExpression expr("[0-9A-Fa-f]{9,}");
|
||||||
|
QRegularExpressionMatchIterator it = expr.globalMatch(text);
|
||||||
|
while (it.hasNext())
|
||||||
|
{
|
||||||
|
QRegularExpressionMatch match = it.next();
|
||||||
|
setFormat(match.capturedStart(), match.capturedLength(), errformat);
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
@ -20,10 +20,31 @@
|
|||||||
#define CHEATSDIALOG_H
|
#define CHEATSDIALOG_H
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
#include <QStandardItemModel>
|
||||||
|
#include <QItemSelection>
|
||||||
|
#include <QSyntaxHighlighter>
|
||||||
|
|
||||||
|
#include "ARCodeFile.h"
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(ARCodeList::iterator)
|
||||||
|
Q_DECLARE_METATYPE(ARCodeCatList::iterator)
|
||||||
|
|
||||||
namespace Ui { class CheatsDialog; }
|
namespace Ui { class CheatsDialog; }
|
||||||
class CheatsDialog;
|
class CheatsDialog;
|
||||||
|
|
||||||
|
class ARCodeChecker : public QSyntaxHighlighter
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
ARCodeChecker(QTextDocument* parent) : QSyntaxHighlighter(parent) {}
|
||||||
|
~ARCodeChecker() {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void highlightBlock(const QString& text) override;
|
||||||
|
};
|
||||||
|
|
||||||
class CheatsDialog : public QDialog
|
class CheatsDialog : public QDialog
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -54,12 +75,20 @@ private slots:
|
|||||||
void on_CheatsDialog_accepted();
|
void on_CheatsDialog_accepted();
|
||||||
void on_CheatsDialog_rejected();
|
void on_CheatsDialog_rejected();
|
||||||
|
|
||||||
//
|
void on_btnNewCat_clicked();
|
||||||
|
void on_btnNewARCode_clicked();
|
||||||
|
void on_btnDeleteCode_clicked();
|
||||||
|
|
||||||
|
void onCheatSelectionChanged(const QItemSelection& sel, const QItemSelection& desel);
|
||||||
|
void onCheatEntryModified(QStandardItem* item);
|
||||||
|
|
||||||
|
void on_txtCode_textChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::CheatsDialog* ui;
|
Ui::CheatsDialog* ui;
|
||||||
|
|
||||||
//
|
ARCodeFile* codeFile;
|
||||||
|
ARCodeChecker* codeChecker;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CHEATSDIALOG_H
|
#endif // CHEATSDIALOG_H
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>AR cheat code editor - melonDS</string>
|
<string>Cheat code editor - melonDS</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
<item>
|
<item>
|
||||||
@ -36,7 +36,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="btnDeleteCode">
|
<widget class="QPushButton" name="btnDeleteCode">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Delete code</string>
|
<string>Delete</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -72,6 +72,8 @@ char MicWavPath[1024];
|
|||||||
|
|
||||||
char LastROMFolder[1024];
|
char LastROMFolder[1024];
|
||||||
|
|
||||||
|
int EnableCheats;
|
||||||
|
|
||||||
bool EnableJIT;
|
bool EnableJIT;
|
||||||
|
|
||||||
ConfigEntry PlatformConfigFile[] =
|
ConfigEntry PlatformConfigFile[] =
|
||||||
@ -162,6 +164,8 @@ ConfigEntry PlatformConfigFile[] =
|
|||||||
|
|
||||||
{"LastROMFolder", 1, LastROMFolder, 0, "", 1023},
|
{"LastROMFolder", 1, LastROMFolder, 0, "", 1023},
|
||||||
|
|
||||||
|
{"EnableCheats", 0, &EnableCheats, 0, NULL, 0},
|
||||||
|
|
||||||
{"", -1, NULL, 0, NULL, 0}
|
{"", -1, NULL, 0, NULL, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -85,6 +85,8 @@ extern char MicWavPath[1024];
|
|||||||
|
|
||||||
extern char LastROMFolder[1024];
|
extern char LastROMFolder[1024];
|
||||||
|
|
||||||
|
extern int EnableCheats;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // PLATFORMCONFIG_H
|
#endif // PLATFORMCONFIG_H
|
||||||
|
@ -1067,6 +1067,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
|
|||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
|
|
||||||
actEnableCheats = menu->addAction("Enable cheats");
|
actEnableCheats = menu->addAction("Enable cheats");
|
||||||
|
actEnableCheats->setCheckable(true);
|
||||||
connect(actEnableCheats, &QAction::triggered, this, &MainWindow::onEnableCheats);
|
connect(actEnableCheats, &QAction::triggered, this, &MainWindow::onEnableCheats);
|
||||||
|
|
||||||
actSetupCheats = menu->addAction("Setup cheat codes");
|
actSetupCheats = menu->addAction("Setup cheat codes");
|
||||||
@ -1218,6 +1219,10 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
|
|||||||
actReset->setEnabled(false);
|
actReset->setEnabled(false);
|
||||||
actStop->setEnabled(false);
|
actStop->setEnabled(false);
|
||||||
|
|
||||||
|
actSetupCheats->setEnabled(false);
|
||||||
|
|
||||||
|
|
||||||
|
actEnableCheats->setChecked(Config::EnableCheats != 0);
|
||||||
|
|
||||||
actSavestateSRAMReloc->setChecked(Config::SavestateRelocSRAM != 0);
|
actSavestateSRAMReloc->setChecked(Config::SavestateRelocSRAM != 0);
|
||||||
|
|
||||||
@ -1662,7 +1667,8 @@ void MainWindow::onStop()
|
|||||||
|
|
||||||
void MainWindow::onEnableCheats(bool checked)
|
void MainWindow::onEnableCheats(bool checked)
|
||||||
{
|
{
|
||||||
//
|
Config::EnableCheats = checked?1:0;
|
||||||
|
Frontend::EnableCheats(Config::EnableCheats != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onSetupCheats()
|
void MainWindow::onSetupCheats()
|
||||||
@ -1881,6 +1887,8 @@ void MainWindow::onEmuStart()
|
|||||||
actPause->setChecked(false);
|
actPause->setChecked(false);
|
||||||
actReset->setEnabled(true);
|
actReset->setEnabled(true);
|
||||||
actStop->setEnabled(true);
|
actStop->setEnabled(true);
|
||||||
|
|
||||||
|
actSetupCheats->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onEmuStop()
|
void MainWindow::onEmuStop()
|
||||||
@ -1897,6 +1905,8 @@ void MainWindow::onEmuStop()
|
|||||||
actPause->setEnabled(false);
|
actPause->setEnabled(false);
|
||||||
actReset->setEnabled(false);
|
actReset->setEnabled(false);
|
||||||
actStop->setEnabled(false);
|
actStop->setEnabled(false);
|
||||||
|
|
||||||
|
actSetupCheats->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onUpdateVideoSettings(bool glchange)
|
void MainWindow::onUpdateVideoSettings(bool glchange)
|
||||||
@ -2028,6 +2038,8 @@ int main(int argc, char** argv)
|
|||||||
micWavBuffer = nullptr;
|
micWavBuffer = nullptr;
|
||||||
|
|
||||||
Frontend::Init_ROM();
|
Frontend::Init_ROM();
|
||||||
|
Frontend::EnableCheats(Config::EnableCheats != 0);
|
||||||
|
|
||||||
Frontend::Init_Audio(audioFreq);
|
Frontend::Init_Audio(audioFreq);
|
||||||
|
|
||||||
if (Config::MicInputType == 1)
|
if (Config::MicInputType == 1)
|
||||||
@ -2084,6 +2096,8 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
Input::CloseJoystick();
|
Input::CloseJoystick();
|
||||||
|
|
||||||
|
Frontend::DeInit_ROM();
|
||||||
|
|
||||||
if (audioDevice) SDL_CloseAudioDevice(audioDevice);
|
if (audioDevice) SDL_CloseAudioDevice(audioDevice);
|
||||||
if (micDevice) SDL_CloseAudioDevice(micDevice);
|
if (micDevice) SDL_CloseAudioDevice(micDevice);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user