Allow `AREngine` to be used independently of `ARCodeFile` (#2108)

* Make `EmuInstance::cheatFile` use a `unique_ptr`

- Fixes a memory leak, as the cheat file wasn't cleaned up in the destructor

* Split `AREngine` and `ARCodeFile` apart

- Suitable for frontends that have their own way of storing cheats
- Store the cheats in `AREngine` in a `std::vector`
- Apparently cheats are _supposed_ to be executed each frame; I didn't understand this until recently
This commit is contained in:
Jesse Talavera 2024-08-01 16:01:00 -04:00 committed by GitHub
parent f3f6a6a194
commit c6bf5d5181
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 45 additions and 37 deletions

View File

@ -33,17 +33,26 @@ ARCodeFile::ARCodeFile(const std::string& filename)
{
Filename = filename;
Error = false;
Categories.clear();
if (!Load())
Error = true;
}
ARCodeFile::~ARCodeFile()
std::vector<ARCode> ARCodeFile::GetCodes() const noexcept
{
Categories.clear();
if (Error)
return {};
std::vector<ARCode> codes;
for (const ARCodeCat& cat : Categories)
{
for (const ARCode& code : cat.Codes)
{
codes.push_back(code);
}
}
return codes;
}
bool ARCodeFile::Load()

View File

@ -48,14 +48,16 @@ class ARCodeFile
{
public:
ARCodeFile(const std::string& filename);
~ARCodeFile();
~ARCodeFile() noexcept = default;
bool Error;
[[nodiscard]] std::vector<ARCode> GetCodes() const noexcept;
bool Error = false;
bool Load();
bool Save();
ARCodeCatList Categories;
ARCodeCatList Categories {};
private:
std::string Filename;

View File

@ -31,7 +31,6 @@ using Platform::LogLevel;
AREngine::AREngine(melonDS::NDS& nds) : NDS(nds)
{
CodeFile = nullptr;
}
#define case16(x) \
@ -388,19 +387,12 @@ void AREngine::RunCheat(const ARCode& arcode)
void AREngine::RunCheats()
{
if (!CodeFile) return;
if (Cheats.empty()) return;
for (ARCodeCatList::iterator i = CodeFile->Categories.begin(); i != CodeFile->Categories.end(); i++)
for (const ARCode& code : Cheats)
{
ARCodeCat& cat = *i;
for (ARCodeList::iterator j = cat.Codes.begin(); j != cat.Codes.end(); j++)
{
ARCode& code = *j;
if (code.Enabled)
RunCheat(code);
}
}
}
}

View File

@ -19,6 +19,7 @@
#ifndef ARENGINE_H
#define ARENGINE_H
#include <vector>
#include "ARCodeFile.h"
namespace melonDS
@ -29,14 +30,13 @@ class AREngine
public:
AREngine(melonDS::NDS& nds);
ARCodeFile* GetCodeFile() { return CodeFile; }
void SetCodeFile(ARCodeFile* file) { CodeFile = file; }
std::vector<ARCode> Cheats {};
private:
friend class ARM;
void RunCheats();
void RunCheat(const ARCode& arcode);
private:
melonDS::NDS& NDS;
ARCodeFile* CodeFile; // AR code file - frontend is responsible for managing this
};
}

View File

@ -686,12 +686,8 @@ void EmuInstance::undoStateLoad()
void EmuInstance::unloadCheats()
{
if (cheatFile)
{
delete cheatFile;
cheatFile = nullptr;
nds->AREngine.SetCodeFile(nullptr);
}
cheatFile = nullptr; // cleaned up by unique_ptr
nds->AREngine.Cheats.clear();
}
void EmuInstance::loadCheats()
@ -701,9 +697,16 @@ void EmuInstance::loadCheats()
std::string filename = getAssetPath(false, globalCfg.GetString("CheatFilePath"), ".mch");
// TODO: check for error (malformed cheat file, ...)
cheatFile = new ARCodeFile(filename);
cheatFile = std::make_unique<ARCodeFile>(filename);
nds->AREngine.SetCodeFile(cheatsOn ? cheatFile : nullptr);
if (cheatsOn)
{
nds->AREngine.Cheats = cheatFile->GetCodes();
}
else
{
nds->AREngine.Cheats.clear();
}
}
std::unique_ptr<ARM9BIOSImage> EmuInstance::loadARM9BIOS() noexcept
@ -1013,12 +1016,14 @@ void EmuInstance::enableCheats(bool enable)
{
cheatsOn = enable;
if (cheatFile)
nds->AREngine.SetCodeFile(cheatsOn ? cheatFile : nullptr);
nds->AREngine.Cheats = cheatFile->GetCodes();
else
nds->AREngine.Cheats.clear();
}
ARCodeFile* EmuInstance::getCheatFile()
{
return cheatFile;
return cheatFile.get();
}
void EmuInstance::setBatteryLevels()

View File

@ -256,7 +256,7 @@ private:
bool savestateLoaded;
std::string previousSaveFile;
melonDS::ARCodeFile* cheatFile;
std::unique_ptr<melonDS::ARCodeFile> cheatFile;
bool cheatsOn;
SDL_AudioDeviceID audioDevice;