FLTK: Cheats reimplemented

This commit is contained in:
rdanbrook 2024-05-19 08:00:13 -06:00
parent 9b2f86dbb8
commit 0ad4790576
13 changed files with 570 additions and 487 deletions

View file

@ -710,8 +710,8 @@ nestopia_SOURCES = \
source/nes_ntsc/demo_impl.h
nestopia_SOURCES += \
source/fltkui/cheats.cpp \
source/fltkui/cheats.h \
source/fltkui/chtmanager.cpp \
source/fltkui/chtmanager.h \
source/fltkui/cli.cpp \
source/fltkui/cli.h \
source/fltkui/video.cpp \

View file

@ -1,255 +0,0 @@
/*
* Nestopia UE
*
* Copyright (C) 2012-2018 R. Danbrook
*
* 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.
*
*/
#include <cstdlib>
#include <iostream>
#include <fstream>
#include "cheats.h"
static Xml savexml;
static Xml::Node saveroot;
std::vector<NstCheat> chtlist;
//extern Emulator emulator;
void nst_cheats_init(const char *cheatpath) {
// Initialize cheat engine
/*Cheats cheats(emulator);
Xml xml;
cheats.ClearCodes();
std::ifstream cheatfile(cheatpath, std::ifstream::in|std::ifstream::binary);
if (cheatfile.is_open()) {
xml.Read(cheatfile);
if (xml.GetRoot().IsType(L"cheats")) {
Xml::Node root(xml.GetRoot());
Xml::Node node(root.GetFirstChild());
for (int i = 0; i < root.NumChildren(L"cheat"); i++) {
if (node.GetAttribute(L"enabled").IsValue(L"1")) {
if (node.GetChild(L"genie")) { // Game Genie
nst_cheats_code_gg_add(node.GetChild(L"genie").GetValue());
}
else if (node.GetChild(L"rocky")) { // Pro Action Rocky
nst_cheats_code_par_add(node.GetChild(L"rocky").GetValue());
}
else if (node.GetChild(L"address")) { // Raw
Cheats::Code code;
code.useCompare = false;
code.address = node.GetChild(L"address").GetUnsignedValue();
if (node.GetChild(L"value")) {
code.value = node.GetChild(L"value").GetUnsignedValue();
}
if (node.GetChild(L"compare")) {
code.compare = node.GetChild(L"compare").GetUnsignedValue();
code.useCompare = true;
}
cheats.SetCode(code);
}
//fprintf(stderr, "Cheat: %ls\n", node.GetChild(L"description").GetValue());
}
NstCheat cht = {
node.GetAttribute(L"enabled").IsValue(L"1"),
node.GetChild(L"genie").GetValue(),
node.GetChild(L"rocky").GetValue(),
node.GetChild(L"address").GetUnsignedValue(),
node.GetChild(L"value").GetUnsignedValue(),
node.GetChild(L"compare").GetUnsignedValue(),
node.GetChild(L"description").GetValue()
};
chtlist.push_back(cht);
node = node.GetNextSibling();
}
}
cheatfile.close();
}*/
}
void nst_cheats_save(const char *cheatpath) {
// Save the cheat list
/*std::ofstream cheatfile(cheatpath, std::ifstream::out|std::ifstream::binary);
if (cheatfile.is_open()) {
saveroot = (savexml.GetRoot());
saveroot = savexml.Create( L"cheats" );
saveroot.AddAttribute( L"version", L"1.0" );
char buf[9];
wchar_t wbuf[9];
for (int i = 0; i < chtlist.size(); i++) {
Xml::Node node(saveroot.AddChild(L"cheat"));
node.AddAttribute(L"enabled", chtlist[i].enabled ? L"1" : L"0");
if (chtlist[i].gg.size() > 0) {
node.AddChild(L"genie", chtlist[i].gg.c_str());
}
if (chtlist[i].par.size() > 0) {
node.AddChild(L"rocky", chtlist[i].par.c_str());
}
if (chtlist[i].address != 0) {
snprintf(buf, sizeof(buf), "0x%04X", chtlist[i].address);
mbstowcs(wbuf, buf, 9);
node.AddChild(L"address", wbuf);
snprintf(buf, sizeof(buf), "0x%02x", chtlist[i].value);
mbstowcs(wbuf, buf, 9);
node.AddChild(L"value", wbuf);
snprintf(buf, sizeof(buf), "0x%02x", chtlist[i].compare);
mbstowcs(wbuf, buf, 9);
node.AddChild(L"compare", wbuf);
}
if (chtlist[i].description.size() > 0) {
node.AddChild(L"description", chtlist[i].description.c_str());
}
}
savexml.Write(saveroot, cheatfile);
cheatfile.close();
}*/
}
void nst_cheats_code_gg_add(const std::wstring data) {
// Add a Game Genie code
/*Cheats cheats(emulator);
Cheats::Code code;
char gg[9];
snprintf(gg, sizeof(gg), "%ls", data.c_str());
cheats.GameGenieDecode(gg, code);
cheats.SetCode(code);*/
}
void nst_cheats_code_par_add(const std::wstring data) {
// Add a Pro Action Rocky code
/*Cheats cheats(emulator);
Cheats::Code code;
char par[9];
snprintf(par, sizeof(par), "%ls", data.c_str());
cheats.ProActionRockyDecode(par, code);
cheats.SetCode(code);*/
}
void nst_cheats_refresh() {
/*Cheats cheats(emulator);
cheats.ClearCodes();
for (int i = 0; i < chtlist.size(); i++) {
if (chtlist[i].enabled) {
if (chtlist[i].gg.size()) {
nst_cheats_code_gg_add(chtlist[i].gg);
}
else if (chtlist[i].par.size()) {
nst_cheats_code_par_add(chtlist[i].par);
}
else if (chtlist[i].address) {
Cheats::Code code;
code.useCompare = false;
code.address = chtlist[i].address;
code.value = chtlist[i].value;
code.compare = chtlist[i].compare;
code.useCompare = code.compare != 0;
cheats.SetCode(code);
}
}
}*/
}
// DIP Switches
void nst_dip_handle(const char *dippath) {
// Handle the DIP switch file
/*DipSwitches dipswitches(emulator);
Xml xml;
std::ifstream dipfile(dippath, std::ifstream::in|std::ifstream::binary);
if (dipfile.is_open()) {
xml.Read(dipfile);
if (xml.GetRoot().IsType(L"dipswitches")) {
Xml::Node root(xml.GetRoot());
Xml::Node node(root.GetFirstChild());
for (int i = 0; i < root.NumChildren(L"dip"); i++) {
if (node.GetChild(L"value")) {
dipswitches.SetValue(i, node.GetChild(L"value").GetUnsignedValue());
}
node = node.GetNextSibling();
}
}
dipfile.close();
}
else {
Xml::Node root(xml.GetRoot());
root = xml.Create(L"dipswitches");
root.AddAttribute(L"version", L"1.0");
wchar_t wbuf[32];
char buf[32];
int numdips = dipswitches.NumDips();
if (numdips > 0) {
for (int i = 0; i < numdips; i++) {
Xml::Node node(root.AddChild(L"dip"));
snprintf(buf, sizeof(buf), "%s", dipswitches.GetDipName(i));
mbstowcs(wbuf, buf, sizeof(buf));
node.AddChild(L"description", wbuf);
snprintf(buf, sizeof(buf), "%d", dipswitches.GetValue(i));
mbstowcs(wbuf, buf, sizeof(buf));
node.AddChild(L"value", wbuf);
}
}
std::ofstream dipout(dippath, std::ifstream::out|std::ifstream::binary);
if (dipout.is_open()) {
xml.Write(root, dipout);
}
dipout.close();
}*/
}

View file

@ -1,36 +0,0 @@
#ifndef _CHEATS_H_
#define _CHEATS_H_
#include <string>
#include <vector>
#include "core/api/NstApiEmulator.hpp"
#include "core/api/NstApiCheats.hpp"
#include "core/api/NstApiDipSwitches.hpp"
#include "core/NstStream.hpp"
#include "core/NstXml.hpp"
using namespace Nes::Api;
typedef Nes::Core::Xml Xml;
typedef struct NstCheat {
bool enabled;
std::wstring gg;
std::wstring par;
unsigned short address;
unsigned char value;
unsigned char compare;
std::wstring description;
} NstCheat;
void nst_cheats_init(const char *cheatpath);
void nst_cheats_save(const char *cheatpath);
void nst_cheats_refresh();
void nst_cheats_code_gg_add(const std::wstring data);
void nst_cheats_code_par_add(const std::wstring data);
// DIP Switches
void nst_dip_handle(const char *dippath);
#endif

View file

@ -0,0 +1,242 @@
/*
* Nestopia UE
*
* Copyright (C) 2012-2024 R. Danbrook
*
* 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.
*
*/
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <sstream>
#include "jg/jg.h"
#include "chtmanager.h"
CheatManager::CheatManager(JGManager& jgm) : jgm(jgm) {
}
std::vector<NstCheat>& CheatManager::get_list() {
return chtlist;
}
void CheatManager::clear() {
chtlist.clear();
jgm.cheat_clear();
}
void CheatManager::refresh() {
// First clear the active cheats
jgm.cheat_clear();
// Apply any enabled cheats
for (auto& cht : chtlist) {
if (cht.enabled) {
if (cht.gg.size()) {
jgm.cheat_set(cht.gg.c_str());
}
else if (cht.par.size()) {
jgm.cheat_set(cht.par.c_str());
}
else if (cht.raw.size()) {
jgm.cheat_set(cht.raw.c_str());
}
}
}
}
void CheatManager::load(const char *cheatpath) {
Xml xml;
clear();
std::ifstream cheatfile(cheatpath, std::ifstream::in|std::ifstream::binary);
if (cheatfile.is_open()) {
xml.Read(cheatfile);
if (xml.GetRoot().IsType(L"cheats")) {
Xml::Node root(xml.GetRoot());
Xml::Node node(root.GetFirstChild());
for (int i = 0; i < root.NumChildren(L"cheat"); i++) {
std::string ggcode{};
std::string parcode{};
std::string rawcode{};
if (node.GetChild(L"genie")) { // Game Genie
std::wstring ws = node.GetChild(L"genie").GetValue();
ggcode = std::string(ws.begin(), ws.end());
}
else if (node.GetChild(L"rocky")) { // Pro Action Rocky
std::wstring ws = node.GetChild(L"rocky").GetValue();
parcode = std::string(ws.begin(), ws.end());
}
else if (node.GetChild(L"address")) { // Raw
std::wstring ws = node.GetChild(L"address").GetValue();
rawcode = std::string(ws.begin(), ws.end());
if (node.GetChild(L"value")) {
std::wstring vws = node.GetChild(L"value").GetValue();
rawcode += ' ' + std::string(vws.begin(), vws.end());
}
if (node.GetChild(L"compare")) {
std::wstring cws = node.GetChild(L"compare").GetValue();
rawcode += ' ' + std::string(cws.begin(), cws.end());
}
}
NstCheat cht = {
node.GetAttribute(L"enabled").IsValue(L"1"),
ggcode,
parcode,
rawcode,
node.GetChild(L"description").GetValue()
};
chtlist.push_back(cht);
node = node.GetNextSibling();
}
}
cheatfile.close();
refresh();
}
}
void CheatManager::save(const char *cheatpath) {
// Save the cheat list
std::ofstream cheatfile(cheatpath, std::ifstream::out|std::ifstream::binary);
if (cheatfile.is_open()) {
saveroot = (savexml.GetRoot());
saveroot = savexml.Create( L"cheats" );
saveroot.AddAttribute( L"version", L"1.0" );
for (auto& cht : chtlist) {
Xml::Node node(saveroot.AddChild(L"cheat"));
node.AddAttribute(L"enabled", cht.enabled ? L"1" : L"0");
if (cht.gg.size()) {
std::wstring ggcode = std::wstring(cht.gg.begin(), cht.gg.end());
node.AddChild(L"genie", ggcode.c_str());
}
if (cht.par.size()) {
std::wstring parcode = std::wstring(cht.par.begin(), cht.par.end());
node.AddChild(L"rocky", parcode.c_str());
}
if (cht.raw.size()) {
std::vector<std::string> tokens;
std::stringstream rawcode(cht.raw);
std::string token;
while (std::getline(rawcode, token, ' ')) {
tokens.push_back(token);
}
if ((tokens.size() == 2) || (tokens.size() == 3)) {
std::wstring ws = std::wstring(tokens[0].begin(), tokens[0].end());
node.AddChild(L"address", ws.c_str());
ws = std::wstring(tokens[1].begin(), tokens[1].end());
node.AddChild(L"value", ws.c_str());
if (tokens.size() == 3) {
ws = std::wstring(tokens[2].begin(), tokens[2].end());
node.AddChild(L"compare", ws.c_str());
}
}
}
if (cht.description.size() > 0) {
node.AddChild(L"description", cht.description.c_str());
}
}
savexml.Write(saveroot, cheatfile);
cheatfile.close();
}
}
// DIP Switches
void nst_dip_handle(const char *dippath) {
// Handle the DIP switch file
/*DipSwitches dipswitches(emulator);
Xml xml;
std::ifstream dipfile(dippath, std::ifstream::in|std::ifstream::binary);
if (dipfile.is_open()) {
xml.Read(dipfile);
if (xml.GetRoot().IsType(L"dipswitches")) {
Xml::Node root(xml.GetRoot());
Xml::Node node(root.GetFirstChild());
for (int i = 0; i < root.NumChildren(L"dip"); i++) {
if (node.GetChild(L"value")) {
dipswitches.SetValue(i, node.GetChild(L"value").GetUnsignedValue());
}
node = node.GetNextSibling();
}
}
dipfile.close();
}
else {
Xml::Node root(xml.GetRoot());
root = xml.Create(L"dipswitches");
root.AddAttribute(L"version", L"1.0");
wchar_t wbuf[32];
char buf[32];
int numdips = dipswitches.NumDips();
if (numdips > 0) {
for (int i = 0; i < numdips; i++) {
Xml::Node node(root.AddChild(L"dip"));
snprintf(buf, sizeof(buf), "%s", dipswitches.GetDipName(i));
mbstowcs(wbuf, buf, sizeof(buf));
node.AddChild(L"description", wbuf);
snprintf(buf, sizeof(buf), "%d", dipswitches.GetValue(i));
mbstowcs(wbuf, buf, sizeof(buf));
node.AddChild(L"value", wbuf);
}
}
std::ofstream dipout(dippath, std::ifstream::out|std::ifstream::binary);
if (dipout.is_open()) {
xml.Write(root, dipout);
}
dipout.close();
}*/
}

View file

@ -0,0 +1,44 @@
#pragma once
#include <string>
#include <vector>
#include "core/api/NstApiDipSwitches.hpp"
#include "core/NstStream.hpp"
#include "core/NstXml.hpp"
#include "jgmanager.h"
typedef Nes::Core::Xml Xml;
typedef struct NstCheat {
bool enabled{false};
std::string gg;
std::string par;
std::string raw;
std::wstring description;
} NstCheat;
// DIP Switches
void nst_dip_handle(const char *dippath);
class CheatManager {
public:
CheatManager() = delete;
CheatManager(JGManager& jgm);
~CheatManager() {}
void clear();
void refresh();
void load(const char *cheatpath);
void save(const char *cheatpath);
std::vector<NstCheat>& get_list();
private:
std::vector<NstCheat> chtlist;
Xml savexml;
Xml::Node saveroot;
JGManager& jgm;
};

View file

@ -43,12 +43,11 @@
#include "jgmanager.h"
#include "setmanager.h"
#include "inputmanager.h"
#include "chtmanager.h"
#include "audio.h"
#include "video.h"
#include "cheats.h"
#include "fltkui.h"
#include "fltkui_archive.h"
#include "fltkui_cheats.h"
@ -67,6 +66,7 @@ static NstSettingsWindow *setwin;
static JGManager *jgm = nullptr;
static SettingManager *setmgr = nullptr;
static InputManager *inputmgr = nullptr;
static CheatManager *chtmgr = nullptr;
static std::vector<uint8_t> game;
@ -153,6 +153,8 @@ static void fltkui_rom_open(Fl_Widget* w, void* userdata) {
fltkui_load_file(fc.filename());
if (jgm->is_loaded()) {
chtmgr->clear();
chtwin->refresh();
fltkui_enable_menu();
nstwin->label(jgm->get_gamename().c_str());
jg_setup_audio();
@ -538,7 +540,7 @@ void makenstwin(const char *name) {
Fl::add_handler(handle);
// Cheats Window
chtwin = new NstChtWindow(660, 500, "Cheat Manager");
chtwin = new NstChtWindow(720, 500, "Cheat Manager", *chtmgr);
chtwin->populate();
// Settings Window
@ -601,6 +603,7 @@ int main(int argc, char *argv[]) {
//nst_archive_select = &fltkui_archive_select;
inputmgr = new InputManager(*jgm, *setmgr);
chtmgr = new CheatManager(*jgm);
makenstwin(argv[0]);
nstwin->label("Nestopia UE");
@ -683,6 +686,10 @@ int main(int argc, char *argv[]) {
delete setmgr;
}
if (chtmgr) {
delete chtmgr;
}
delete chtwin;
delete setwin;
delete glarea;

View file

@ -30,247 +30,299 @@
#include <FL/Fl_Gl_Window.H>
#include <FL/fl_draw.H>
#include "cheats.h"
#include "video.h"
#include "fltkui.h"
#include "fltkui_cheats.h"
static Fl_Input *input_desc;
static Fl_Input *input_gg;
static Fl_Input *input_par;
static int rsel = 0;
namespace {
extern Emulator emulator;
extern std::vector<NstCheat> chtlist;
NstChtWindow *chtwin = nullptr;
Fl_Input *input_desc;
Fl_Input *input_gg;
Fl_Input *input_par;
}
class ChtTable : public Fl_Table_Row
{
protected:
void draw_cell(TableContext context, int R=0, int C=0, int X=0, int Y=0, int W=0, int H=0);
public:
ChtTable(int x, int y, int w, int h, const char *l=0) : Fl_Table_Row(x,y,w,h,l) { end(); }
ChtTable(int x, int y, int w, int h, const char *l, CheatManager& chtmgr) :
Fl_Table_Row(x,y,w,h,l), chtmgr(chtmgr) { end(); }
private:
CheatManager& chtmgr;
protected:
void draw_cell(TableContext context, int r = 0, int c = 0,
int x = 0, int y = 0, int w = 0, int h = 0);
};
static ChtTable *ctable;
NstChtWindow::NstChtWindow(int w, int h, const char* t, CheatManager& chtmgr) :
Fl_Double_Window(w, h, t), chtmgr(chtmgr) {
chtwin = this;
}
static void cb_ok(Fl_Widget *w, long) {
w->parent()->hide();
w->parent()->hide();
}
static void cb_table(Fl_Widget* w, long rn) {
Fl_Table *table = (Fl_Table*)w;
void NstChtWindow::cb_table(Fl_Widget* w, long rn) {
Fl_Table *table = (Fl_Table*)w;
if (!table->rows()) { return; }
if (!table->rows()) {
return;
}
rn = table->callback_row();
rsel = rn;
rn = table->callback_row();
rsel = rn;
if (Fl::event_clicks() > 0) {
chtlist[rn].enabled = !chtlist[rn].enabled;
nst_cheats_refresh();
}
if (Fl::event_clicks() > 0) {
Fl::event_clicks(0);
std::vector<NstCheat>& chtlist = chtmgr.get_list();
chtlist[rn].enabled = !chtlist[rn].enabled;
chtmgr.refresh();
}
ctable->redraw();
ctable->redraw();
}
void cb_toggle(Fl_Widget* w, long) {
if (!chtlist.size()) { return; }
chtlist[rsel].enabled = !chtlist[rsel].enabled;
nst_cheats_refresh();
ctable->redraw();
void NstChtWindow::cb_table_s(Fl_Widget *w, long rn) {
chtwin->cb_table(w, rn);
}
void cb_add(Fl_Widget* w, long) {
NstCheat cht;
bool addgg = false;
bool addpar = false;
cht.enabled = true;
void NstChtWindow::cb_toggle(Fl_Widget *w, void*) {
std::vector<NstCheat>& chtlist = chtmgr.get_list();
if (!chtlist.size()) {
return;
}
wchar_t wtmp[256];
chtlist[rsel].enabled = !chtlist[rsel].enabled;
mbstowcs(wtmp, input_desc->value(), 256);
cht.description = std::wstring(wtmp);
if (strlen(input_gg->value())) {
mbstowcs(wtmp, input_gg->value(), 256);
cht.gg = std::wstring(wtmp);
addgg = true;
}
if (strlen(input_par->value())) {
mbstowcs(wtmp, input_par->value(), 256);
cht.par = std::wstring(wtmp);
addpar = true;
}
cht.address = cht.value = cht.compare = 0;
if (addgg || addpar) {
chtlist.push_back(cht);
nst_cheats_refresh();
ctable->rows(chtlist.size());
input_desc->value("");
input_gg->value("");
input_par->value("");
return;
}
chtmgr.refresh();
ctable->redraw();
}
void cb_del(Fl_Widget* w, long) {
if (chtlist.size()) {
chtlist.erase(chtlist.begin() + rsel);
nst_cheats_refresh();
ctable->rows(chtlist.size());
}
void NstChtWindow::cb_toggle_s(Fl_Widget *w, void*) {
chtwin->cb_toggle(w, nullptr);
}
void cb_clr(Fl_Widget* w, long) {
chtlist.clear();
nst_cheats_refresh();
ctable->rows(chtlist.size());
void NstChtWindow::cb_add(Fl_Widget *w, void*) {
NstCheat cht;
bool addgg = false;
bool addpar = false;
cht.enabled = true;
wchar_t wtmp[256];
mbstowcs(wtmp, input_desc->value(), 256);
cht.description = std::wstring(wtmp);
if (strlen(input_gg->value())) {
cht.gg = std::string(input_gg->value());
addgg = true;
}
if (strlen(input_par->value())) {
cht.par = std::string(input_par->value());
addpar = true;
}
cht.raw = std::string{};
if (addgg || addpar) {
std::vector<NstCheat>& chtlist = chtmgr.get_list();
chtlist.push_back(cht);
chtmgr.refresh();
ctable->rows(chtlist.size());
input_desc->value("");
input_gg->value("");
input_par->value("");
return;
}
}
void cb_load(Fl_Widget* w, long) {
Fl_Native_File_Chooser fc;
fc.title("Select a Cheat List");
fc.type(Fl_Native_File_Chooser::BROWSE_FILE);
//fc.directory((const char*)nstpaths.cheatpath); // FIXME
fc.filter("Nestopia Cheats\t*.xml");
// Show file chooser
switch (fc.show()) {
case -1: fprintf(stderr, "Error: %s\n", fc.errmsg()); break;
case 1: break; // Cancel
default:
if (fc.filename()) {
chtlist.clear();
nst_cheats_init(fc.filename());
ctable->rows(chtlist.size());
}
break;
}
void NstChtWindow::cb_add_s(Fl_Widget *w, void*) {
chtwin->cb_add(w, nullptr);
}
void cb_save(Fl_Widget* w, long) {
Fl_Native_File_Chooser fc;
fc.title("Save Cheat List");
fc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE);
//fc.directory((const char*)nstpaths.cheatpath); // FIXME
//std::string preset = std::string(nstpaths.cheatpath) + "/" + std::string(nstpaths.gamename) + ".xml";
//fc.preset_file(preset.c_str());
fc.filter("Nestopia Cheats\t*.xml");
fc.options(Fl_Native_File_Chooser::SAVEAS_CONFIRM | Fl_Native_File_Chooser::USE_FILTER_EXT);
void NstChtWindow::cb_del(Fl_Widget *w, void*) {
std::vector<NstCheat>& chtlist = chtmgr.get_list();
if (chtlist.size()) {
chtlist.erase(chtlist.begin() + rsel);
chtmgr.refresh();
ctable->rows(chtlist.size());
}
}
// Show file chooser
if (fc.show()) { return; }
void NstChtWindow::cb_del_s(Fl_Widget *w, void*) {
chtwin->cb_del(w, nullptr);
}
nst_cheats_save(fc.filename());
void NstChtWindow::cb_clear(Fl_Widget *w, void*) {
chtmgr.clear();
ctable->rows(0);
}
void NstChtWindow::cb_clear_s(Fl_Widget *w, void*) {
chtwin->cb_clear(w, nullptr);
}
void NstChtWindow::cb_load(Fl_Widget*, void*) {
Fl_Native_File_Chooser fc;
fc.title("Select a Cheat List");
fc.type(Fl_Native_File_Chooser::BROWSE_FILE);
//fc.directory((const char*)nstpaths.cheatpath); // FIXME
fc.filter("Nestopia Cheats\t*.xml");
// Show file chooser
switch (fc.show()) {
case -1: fprintf(stderr, "Error: %s\n", fc.errmsg()); break;
case 1: break; // Cancel
default:
if (fc.filename()) {
chtmgr.load(fc.filename());
std::vector<NstCheat>& chtlist = chtmgr.get_list();
ctable->rows(chtlist.size());
refresh();
}
break;
}
}
void NstChtWindow::cb_load_s(Fl_Widget *w, void*) {
chtwin->cb_load(w, nullptr);
}
void NstChtWindow::cb_save(Fl_Widget *w, void*) {
Fl_Native_File_Chooser fc;
fc.title("Save Cheat List");
fc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE);
//fc.directory((const char*)nstpaths.cheatpath); // FIXME
//std::string preset = std::string(nstpaths.cheatpath) + "/" + std::string(nstpaths.gamename) + ".xml";
//fc.preset_file(preset.c_str());
fc.filter("Nestopia Cheats\t*.xml");
fc.options(Fl_Native_File_Chooser::SAVEAS_CONFIRM | Fl_Native_File_Chooser::USE_FILTER_EXT);
// Show file chooser
if (fc.show()) {
return;
}
chtmgr.save(fc.filename());
}
void NstChtWindow::cb_save_s(Fl_Widget *w, void*) {
chtwin->cb_save(w, nullptr);
}
// Handle drawing all cells in table
void ChtTable::draw_cell(TableContext context, int r, int c, int X, int Y, int W, int H) {
static char s[128];
static char s[128];
switch (context) {
case CONTEXT_COL_HEADER:
switch (c) {
case 0: s[0] = '\0'; break;
case 1: snprintf(s, sizeof(s), "Game Genie"); break;
case 2: snprintf(s, sizeof(s), "PAR"); break;
case 3: snprintf(s, sizeof(s), "Raw"); break;
case 4: snprintf(s, sizeof(s), "Description"); break;
}
std::vector<NstCheat>& chtlist = chtmgr.get_list();
fl_push_clip(X, Y, W, H);
switch (context) {
case CONTEXT_COL_HEADER:
switch (c) {
case 0: s[0] = '\0'; break;
case 1: snprintf(s, sizeof(s), "Game Genie"); break;
case 2: snprintf(s, sizeof(s), "PAR"); break;
case 3: snprintf(s, sizeof(s), "Raw"); break;
case 4: snprintf(s, sizeof(s), "Description"); break;
}
fl_draw_box(FL_THIN_UP_BOX, X, Y, W, H, color());
fl_color(FL_BLACK);
fl_draw(s, X, Y, W, H, FL_ALIGN_CENTER);
fl_push_clip(X, Y, W, H);
fl_pop_clip();
return;
fl_draw_box(FL_THIN_UP_BOX, X, Y, W, H, color());
fl_color(FL_BLACK);
fl_draw(s, X, Y, W, H, FL_ALIGN_CENTER);
case CONTEXT_CELL:
fl_push_clip(X, Y, W, H);
fl_pop_clip();
return;
switch (c) {
case 0: snprintf(s, sizeof(s), "%s", chtlist[r].enabled ? "On" : "Off"); break;
case 1: snprintf(s, sizeof(s), "%ls", chtlist[r].gg.c_str()); break;
case 2: snprintf(s, sizeof(s), "%ls", chtlist[r].par.c_str()); break;
case 3:
if (chtlist[r].address) {
snprintf(s, sizeof(s), "%04X %02X %02X", chtlist[r].address, chtlist[r].value, chtlist[r].compare);
}
else {
s[0] = '\0';
}
break;
case 4: snprintf(s, sizeof(s), "%ls", chtlist[r].description.c_str()); break;
default: break;
}
case CONTEXT_CELL:
fl_push_clip(X, Y, W, H);
// Background
fl_color( row_selected(r) ? selection_color() : FL_WHITE);
fl_rectf(X, Y, W, H);
switch (c) {
case 0: snprintf(s, sizeof(s), "%s", chtlist[r].enabled ? "On" : "Off"); break;
case 1: snprintf(s, sizeof(s), "%s", chtlist[r].gg.c_str()); break;
case 2: snprintf(s, sizeof(s), "%s", chtlist[r].par.c_str()); break;
case 3:
if (!chtlist[r].raw.empty()) {
snprintf(s, sizeof(s), "%s", chtlist[r].raw.c_str());
}
else {
s[0] = '\0';
}
break;
case 4: snprintf(s, sizeof(s), "%ls", chtlist[r].description.c_str()); break;
default: break;
}
// Text
fl_color(FL_BLACK);
fl_draw(s, X, Y, W, H, c ? FL_ALIGN_LEFT : FL_ALIGN_CENTER);
// Background
fl_color( row_selected(r) ? selection_color() : FL_WHITE);
fl_rectf(X, Y, W, H);
fl_pop_clip();
// Text
fl_color(row_selected(r) ? NstWhite : FL_BLACK);
fl_draw(s, X, Y, W, H, c ? FL_ALIGN_LEFT : FL_ALIGN_CENTER);
return;
fl_pop_clip();
default: return;
}
return;
default: return;
}
}
void NstChtWindow::refresh() {
ctable->rows(chtlist.size());
ctable->row_header(0);
std::vector<NstCheat>& chtlist = chtmgr.get_list();
ctable->rows(chtlist.size());
ctable->row_header(0);
}
void NstChtWindow::populate() {
ctable = new ChtTable(19, 20, 642, 270);
ctable->selection_color(FL_YELLOW);
ctable->cols(5);
ctable->col_header(1); // enable col header
ctable->col_width(0, 80);
ctable->col_width(1, 100);
ctable->col_width(2, 90);
ctable->col_width(3, 90);
ctable->col_width(4, 260);
ctable = new ChtTable(19, 20, 682, 270, "", chtmgr);
ctable->selection_color(NstGreen);
ctable->cols(5);
ctable->col_header(1); // enable col header
ctable->col_width(0, 40);
ctable->col_width(1, 100);
ctable->col_width(2, 90);
ctable->col_width(3, 150);
ctable->col_width(4, 280);
ctable->col_resize(1);
ctable->callback(cb_table, 0);
ctable->when(FL_WHEN_CHANGED);
ctable->end();
ctable->callback(cb_table_s, 0);
ctable->when(FL_WHEN_CHANGED);
ctable->end();
input_desc = new Fl_Input(380, 310, 260, UI_ELEMHEIGHT, "Description:");
input_gg = new Fl_Input(380, 340, 260, UI_ELEMHEIGHT, "Game Genie:");
input_par = new Fl_Input(380, 370, 260, UI_ELEMHEIGHT, "Pro Action Rocky:");
input_desc = new Fl_Input(400, 310, 300, UI_ELEMHEIGHT, "Description:");
input_gg = new Fl_Input(400, 340, 300, UI_ELEMHEIGHT, "Game Genie:");
input_par = new Fl_Input(400, 370, 300, UI_ELEMHEIGHT, "Pro Action Rocky:");
Fl_Button *btnadd = new Fl_Button(380, 400, UI_ELEMWIDTH / 2, UI_ELEMHEIGHT, "Add");
btnadd->callback(cb_add, 0);
Fl_Button *btnadd = new Fl_Button(400, 400, UI_ELEMWIDTH / 2, UI_ELEMHEIGHT, "Add");
btnadd->callback(cb_add_s, 0);
Fl_Button *btntog = new Fl_Button(20, 300, UI_ELEMWIDTH / 2, UI_ELEMHEIGHT, "Toggle");
btntog->callback(cb_toggle, 0);
Fl_Button *btntog = new Fl_Button(20, 300, UI_ELEMWIDTH / 2, UI_ELEMHEIGHT, "Toggle");
btntog->callback(cb_toggle_s, 0);
Fl_Button *btndel = new Fl_Button(110, 300, UI_ELEMWIDTH / 2, UI_ELEMHEIGHT, "Delete");
btndel->callback(cb_del, 0);
Fl_Button *btndel = new Fl_Button(110, 300, UI_ELEMWIDTH / 2, UI_ELEMHEIGHT, "Delete");
btndel->callback(cb_del_s, 0);
Fl_Button *btnclr = new Fl_Button(200, 300, UI_ELEMWIDTH / 2, UI_ELEMHEIGHT, "Clear");
btnclr->callback(cb_clr, 0);
Fl_Button *btnclr = new Fl_Button(200, 300, UI_ELEMWIDTH / 2, UI_ELEMHEIGHT, "Clear");
btnclr->callback(cb_clear_s, 0);
Fl_Button *btnload = new Fl_Button(20, 350, UI_ELEMWIDTH / 2, UI_ELEMHEIGHT, "Load...");
btnload->callback(cb_load, 0);
Fl_Button *btnload = new Fl_Button(20, 350, UI_ELEMWIDTH / 2, UI_ELEMHEIGHT, "Load...");
btnload->callback(cb_load_s, 0);
Fl_Button *btnsave = new Fl_Button(20, 380, UI_ELEMWIDTH / 2, UI_ELEMHEIGHT, "Save...");
btnsave->callback(cb_save, 0);
Fl_Button *btnsave = new Fl_Button(20, 380, UI_ELEMWIDTH / 2, UI_ELEMHEIGHT, "Save...");
btnsave->callback(cb_save_s, 0);
Fl_Button *btnok = new Fl_Button(560, 460, UI_ELEMWIDTH / 2, UI_ELEMHEIGHT, "&OK");
btnok->callback(cb_ok, 0);
Fl_Button *btnok = new Fl_Button(620, 460, UI_ELEMWIDTH / 2, UI_ELEMHEIGHT, "OK");
btnok->callback(cb_ok, 0);
this->end();
this->end();
}

View file

@ -1,14 +1,37 @@
#ifndef _FLTKUI_CHEATS_H_
#define _FLTKUI_CHEATS_H_
#pragma once
#include "chtmanager.h"
class NstChtWindow : public Fl_Double_Window {
public:
NstChtWindow(int w, int h, const char* t) : Fl_Double_Window(w, h, t) { }
virtual ~NstChtWindow() { }
NstChtWindow(int w, int h, const char* t, CheatManager& chtmgr);
void refresh();
void populate();
void refresh();
void populate();
private:
void cb_del(Fl_Widget *w, void *data);
static void cb_del_s(Fl_Widget *w, void *data);
void cb_toggle(Fl_Widget *w, void *data);
static void cb_toggle_s(Fl_Widget *w, void *data);
void cb_add(Fl_Widget *w, void *data);
static void cb_add_s(Fl_Widget *w, void *data);
void cb_clear(Fl_Widget *w, void *data);
static void cb_clear_s(Fl_Widget *w, void *data);
void cb_load(Fl_Widget *w, void *data);
static void cb_load_s(Fl_Widget *w, void *data);
void cb_save(Fl_Widget *w, void *data);
static void cb_save_s(Fl_Widget *w, void *data);
void cb_table(Fl_Widget *w, long rn);
static void cb_table_s(Fl_Widget *w, long rn);
int rsel{0};
CheatManager& chtmgr;
};
#endif

View file

@ -40,7 +40,6 @@ public:
void show_msgbox(bool show) { if (show) msgbox->show(); else msgbox->hide(); }
private:
void cb_chooser(Fl_Widget *w, void *data);
static void cb_chooser_s(Fl_Widget *w, void *data);

View file

@ -266,6 +266,14 @@ void JGManager::media_insert() {
jg_media_insert();
}
void JGManager::cheat_clear() {
jg_cheat_clear();
}
void JGManager::cheat_set(const char *code) {
jg_cheat_set(code);
}
int JGManager::get_frametime() {
return frametime;
}

View file

@ -29,6 +29,9 @@ public:
void media_select();
void media_insert();
void cheat_clear();
void cheat_set(const char *code);
int get_frametime();
std::string& get_basepath();

View file

@ -95,10 +95,6 @@ SettingManager::SettingManager() {
std::filesystem::create_directories(confpath);
}
SettingManager::~SettingManager() {
// Save frontend and emulator settings to the config file
}
void SettingManager::read(JGManager& jgm) {
// Read in any settings
mINI::INIFile file(confpath + "/nestopia.conf");

View file

@ -12,7 +12,7 @@ constexpr unsigned FLAG_FRONTEND = 0x8000000;
class SettingManager {
public:
SettingManager();
~SettingManager();
~SettingManager() {}
void read(JGManager& jgm);
void write(JGManager& jgm);