Skip to content

Commit

Permalink
Add support for multiple codes per cheat entry and misc tweaks
Browse files Browse the repository at this point in the history
* Imagine: Update bundled libc++ to LLVM 19-rc2
* Imagine: Replace usage of contains() with std::ranges::contains()
* Imagine: Fix ZArray to satisfy range concept and add toIterator() utility function
* EmuFramework: Add a generic cheat API in EmuSystem to support setting multiple codes per cheat
* EmuFramework: Rewrite all cheat GUI views to share more common code
* EmuFramework: Add confirmation before deleting a cheat
* GBA.emu, GBC.emu, MD.emu, NES.emu, Snes9x: Remove cheat entry limits and update to new cheat API
  • Loading branch information
Robert Broglia committed Sep 4, 2024
1 parent 754b955 commit e730318
Show file tree
Hide file tree
Showing 58 changed files with 1,953 additions and 1,770 deletions.
1 change: 0 additions & 1 deletion EmuFramework/build.mk
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ gui/AudioOptionView.cc \
gui/AutosaveSlotView.cc \
gui/BundledGamesView.cc \
gui/ButtonConfigView.cc \
gui/Cheats.cc \
gui/CPUAffinityView.cc \
gui/CreditsView.cc \
gui/EmuInputView.cc \
Expand Down
215 changes: 188 additions & 27 deletions EmuFramework/include/emuframework/Cheats.hh
Original file line number Diff line number Diff line change
Expand Up @@ -15,89 +15,250 @@
You should have received a copy of the GNU General Public License
along with EmuFramework. If not, see <http://www.gnu.org/licenses/> */

#include <emuframework/EmuApp.hh>
#include <emuframework/EmuAppHelper.hh>
#include <emuframework/viewUtils.hh>
#include <imagine/gui/TableView.hh>
#include <imagine/gui/AlertView.hh>
#include <imagine/gui/MenuItem.hh>
#include <vector>

namespace EmuEx
{

using namespace IG;
using RefreshCheatsDelegate = DelegateFunc<void ()>;

class BaseCheatsView : public TableView, public EmuAppHelper
class CheatsView : public TableView, public EmuAppHelper
{
public:
BaseCheatsView(ViewAttachParams attach);
CheatsView(ViewAttachParams attach):
TableView
{
"Cheats",
attach,
[this](ItemMessage msg) -> ItemReply
{
return msg.visit(overloaded
{
[&](const ItemsMessage&) -> ItemReply { return 1 + cheats.size(); },
[&](const GetItemMessage& m) -> ItemReply
{
if(m.idx == 0)
return &edit;
else
return &cheats[m.idx - 1];
},
});
}
},
edit
{
"Add/Edit", attach,
[this](const Input::Event &e)
{
auto editCheatsView = app().makeEditCheatsView(attachParams(), *this);
pushAndShow(std::move(editCheatsView), e);
}
}
{
loadCheatItems();
}

void onCheatsChanged()
{
auto selectedCell = selected;
loadCheatItems();
highlightCell(selectedCell);
place();
}

protected:
TextMenuItem edit;
std::vector<BoolMenuItem> cheat;
std::vector<BoolMenuItem> cheats;

virtual void loadCheatItems() = 0;
void loadCheatItems()
{
cheats.clear();
system().forEachCheat([this](auto& c, std::string_view name)
{
cheats.emplace_back(name, attachParams(), system().isCheatEnabled(c), [this, &c](BoolMenuItem& item)
{
system().setCheatEnabled(c, item.flipBoolValue(*this));
});
return true;
});
}
};

class BaseEditCheatListView : public TableView, public EmuAppHelper
class BaseEditCheatsView : public TableView, public EmuAppHelper
{
public:
BaseEditCheatListView(ViewAttachParams attach, TableView::ItemSourceDelegate);
void setOnCheatListChanged(RefreshCheatsDelegate del);
BaseEditCheatsView(ViewAttachParams attach, CheatsView& cheatsView, TableView::ItemSourceDelegate itemSrc):
TableView
{
"Edit Cheats",
attach,
itemSrc
},
cheatsViewPtr{&cheatsView}
{
loadCheatItems();
}

void onCheatsChanged()
{
auto selectedCell = selected;
loadCheatItems();
highlightCell(selectedCell);
place();
cheatsViewPtr->onCheatsChanged();
}

protected:
std::vector<TextMenuItem> cheat;
RefreshCheatsDelegate onCheatListChanged_;
std::vector<TextMenuItem> cheats;
CheatsView* cheatsViewPtr;

void loadCheatItems()
{
cheats.clear();
system().forEachCheat([this](Cheat& c, std::string_view name)
{
cheats.emplace_back(name, attachParams(), [this, &c](const Input::Event& e)
{
pushAndShow(app().makeEditCheatView(attachParams(), c, *this), e);
});
return true;
});
}

void onCheatListChanged();
virtual void loadCheatItems() = 0;
void addNewCheat(const char* promptStr, const Input::Event& e, unsigned flags = 0)
{
pushAndShowNewCollectTextInputView(attachParams(), e, promptStr, "",
[this, flags](CollectTextInputView& view, const char* str)
{
auto cheatPtr = system().newCheat(app(), "", {str, flags});
if(!cheatPtr)
return true;
onCheatsChanged();
view.dismiss();
pushAndShowNewCollectTextInputView(attachParams(), {}, "Input description", "",
[this, &cheat = *cheatPtr](CollectTextInputView &view, const char *str)
{
if(!system().setCheatName(cheat, str))
{
app().postMessage(true, "A cheat with name already exists");
return true;
}
onCheatsChanged();
view.dismiss();
return false;
});
return false;
});
}
};

template <class CheatViewImpl>
class BaseEditCheatView : public TableView, public EmuAppHelper
{
public:
BaseEditCheatView(UTF16Convertible auto &&viewName, ViewAttachParams attach, UTF16Convertible auto &&cheatName,
TableView::ItemSourceDelegate itemSrc, TextMenuItem::SelectDelegate removed,
RefreshCheatsDelegate onCheatListChanged_):
BaseEditCheatView(UTF16Convertible auto &&viewName, ViewAttachParams attach, Cheat& cheat,
BaseEditCheatsView& editCheatsView_, TableView::ItemSourceDelegate itemSrc):
TableView
{
IG_forward(viewName),
attach,
itemSrc
},
cheatPtr{&cheat},
editCheatsView{editCheatsView_},
name
{
IG_forward(cheatName), attach,
system().cheatName(cheat), attach,
[this](const Input::Event &e)
{
pushAndShowNewCollectValueInputView<const char*>(attachParams(), e,
"Input description", static_cast<CheatViewImpl*>(this)->cheatNameString(),
"Input description", system().cheatName(*cheatPtr),
[this](CollectTextInputView&, auto str)
{
if(!system().setCheatName(*cheatPtr, str))
{
app().postMessage(true, "A cheat with name already exists");
return false;
}
name.compile(str);
static_cast<CheatViewImpl*>(this)->renamed(str);
onCheatListChanged();
onCheatsChanged();
postDraw();
return true;
});
}
},
remove
{
"Delete Cheat", attach,
removed
},
onCheatListChanged_{onCheatListChanged_} {}
"Delete", attach,
[this](const Input::Event &e)
{
pushAndShowModal(makeView<YesNoAlertView>("Really delete this cheat?",
YesNoAlertView::Delegates{.onYes = [this]{ removeCheat(); }}), e);
}
} {}

void onCheatsChanged() { editCheatsView.onCheatsChanged(); }

void removeCheat()
{
system().removeCheat(*cheatPtr);
onCheatsChanged();
dismiss();
}

void removeCheatCode(this auto&& self, CheatCode& c)
{
self.cheatPtr = self.system().removeCheatCode(*self.cheatPtr, c);
self.onCheatsChanged();
if(!self.cheatPtr)
self.dismiss();
else
self.loadItems();
}

bool modifyCheatCode(this auto&& self, CheatCode& c, CheatCodeDesc desc)
{
if(!strlen(desc.str))
{
self.removeCheatCode(c);
return true;
}
if(!self.system().modifyCheatCode(self.app(), *self.cheatPtr, c, desc))
{
self.postDraw();
return false;
}
self.onCheatsChanged();
self.loadItems();
self.place();
self.postDraw();
return true;
}

protected:
Cheat* cheatPtr;
BaseEditCheatsView& editCheatsView;
std::vector<MenuItem*> items;
std::vector<DualTextMenuItem> codes;
TextMenuItem name, remove;
RefreshCheatsDelegate onCheatListChanged_;

void onCheatListChanged()
void addNewCheatCode(this auto&& self, const char* promptStr, const Input::Event& e, unsigned flags = 0)
{
onCheatListChanged_.callSafe();
pushAndShowNewCollectTextInputView(self.attachParams(), e, promptStr, "",
[&self, flags](CollectTextInputView& view, const char* str)
{
if(!self.system().addCheatCode(self.app(), self.cheatPtr, {str, flags}))
return true;
self.loadItems();
self.onCheatsChanged();
view.dismiss();
return false;
});
}
};

Expand Down
7 changes: 5 additions & 2 deletions EmuFramework/include/emuframework/EmuApp.hh
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ namespace EmuEx
struct MainWindowData;
class EmuMainMenuView;
class EmuViewController;
class Cheat;
class CheatsView;
class BaseEditCheatsView;

WISE_ENUM_CLASS((AssetFileID, size_t),
ui,
Expand Down Expand Up @@ -148,8 +151,6 @@ public:
SYSTEM_OPTIONS,
FILE_PATH_OPTIONS,
GUI_OPTIONS,
EDIT_CHEATS,
LIST_CHEATS,
};

// Static app configuration
Expand All @@ -167,6 +168,8 @@ public:
// optional sub-class API functions
bool willCreateSystem(ViewAttachParams, const Input::Event &);
static bool allowsTurboModifier(KeyCode);
std::unique_ptr<View> makeEditCheatsView(ViewAttachParams, CheatsView&);
std::unique_ptr<View> makeEditCheatView(ViewAttachParams, Cheat&, BaseEditCheatsView&);

void mainInitCommon(IG::ApplicationInitParams, IG::ApplicationContext);
static void onCustomizeNavView(NavView &v);
Expand Down
14 changes: 14 additions & 0 deletions EmuFramework/include/emuframework/EmuAppInlines.hh
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,20 @@ bool EmuApp::allowsTurboModifier(KeyCode c)
return true;
}

std::unique_ptr<View> EmuApp::makeEditCheatsView(ViewAttachParams attach, CheatsView& view)
{
if(&MainApp::makeEditCheatsView != &EmuApp::makeEditCheatsView)
return static_cast<MainApp*>(this)->makeEditCheatsView(attach, view);
return {};
}

std::unique_ptr<View> EmuApp::makeEditCheatView(ViewAttachParams attach, Cheat& c, BaseEditCheatsView& baseView)
{
if(&MainApp::makeEditCheatView != &EmuApp::makeEditCheatView)
return static_cast<MainApp*>(this)->makeEditCheatView(attach, c, baseView);
return {};
}

AssetDesc EmuApp::vControllerAssetDesc(KeyInfo key) const
{
return static_cast<const MainApp*>(this)->vControllerAssetDesc(key);
Expand Down
19 changes: 19 additions & 0 deletions EmuFramework/include/emuframework/EmuSystem.hh
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ class EmuVideo;
class EmuApp;
struct EmuFrameTimeInfo;
class VControllerKeyboard;
class Cheat;
class CheatCode;

struct CheatCodeDesc
{
const char* str{};
unsigned flags{};
};

struct AspectRatioInfo
{
Expand Down Expand Up @@ -247,6 +255,17 @@ public:
FS::FileString contentDisplayNameForPath(CStringView path) const;
IG::Rotation contentRotation() const;
void addThreadGroupIds(std::vector<ThreadId> &) const;
Cheat* newCheat(EmuApp&, const char* name, CheatCodeDesc);
bool setCheatName(Cheat&, const char* name);
std::string_view cheatName(const Cheat&) const;
void setCheatEnabled(Cheat&, bool on);
bool isCheatEnabled(const Cheat&) const;
bool addCheatCode(EmuApp&, Cheat*&, CheatCodeDesc);
bool modifyCheatCode(EmuApp&, Cheat&, CheatCode&, CheatCodeDesc);
Cheat* removeCheatCode(Cheat&, CheatCode&);
bool removeCheat(Cheat&);
void forEachCheat(DelegateFunc<bool(Cheat&, std::string_view)>);
void forEachCheatCode(Cheat&, DelegateFunc<bool(CheatCode&, std::string_view)>);

ApplicationContext appContext() const { return appCtx; }
bool isActive() const { return state == State::ACTIVE; }
Expand Down
Loading

0 comments on commit e730318

Please sign in to comment.