ActionReplay: UI Consistency and Cleanup

Cleanup code style.

Move ActionReplay code->INI saving into ActionReplay namespace.

Threadsafety Cleanup: ActionReplay is accessed from the Host, Emu
and CPU Threads so the internal storage needs to be protected by a
lock to prevent vectors/strings being deleted/moved while in use by
the CPU Thread.

UI Consistency: Make ARCodes behave like Gecko Codes - only apply
changes when Apply is pressed. Save changes to INI from CheatsWindow.

ISOProperties/CheatsWindow now synchronize with each other.
This commit is contained in:
EmptyChaos 2016-04-22 10:42:16 +00:00
parent 25b072ff2b
commit 6ab1b27477
11 changed files with 328 additions and 374 deletions

View File

@ -19,9 +19,12 @@
// Zero Codes: any code with no address. These codes are used to do special operations like memory copy, etc
// -------------------------------------------------------------------------------------------------------------
#include <list>
#include <algorithm>
#include <atomic>
#include <iterator>
#include <mutex>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>
@ -75,17 +78,14 @@ enum
SUB_MASTER_CODE = 0x03,
};
// General lock. Protects codes list and internal log.
static std::mutex s_lock;
static std::vector<ARCode> s_active_codes;
static std::vector<std::string> s_internal_log;
static std::atomic<bool> s_use_internal_log{ false };
// pointer to the code currently being run, (used by log messages that include the code name)
static ARCode const* current_code = nullptr;
static bool b_RanOnce = false;
static std::vector<ARCode> arCodes;
static std::vector<ARCode> activeCodes;
static bool logSelf = false;
static std::vector<std::string> arLog;
static std::mutex s_callbacks_lock;
static std::list<std::function<void()>> s_callbacks;
static const ARCode* s_current_code = nullptr;
static bool s_disable_logging = false;
struct ARAddr
{
@ -106,13 +106,6 @@ struct ARAddr
operator u32() const { return address; }
};
static void RunCodeChangeCallbacks()
{
std::lock_guard<std::mutex> guard(s_callbacks_lock);
for (const auto& cb : s_callbacks)
cb();
}
// ----------------------
// AR Remote Functions
void ApplyCodes(const std::vector<ARCode>& codes)
@ -120,73 +113,59 @@ void ApplyCodes(const std::vector<ARCode>& codes)
if (!SConfig::GetInstance().bEnableCheats)
return;
arCodes = codes;
UpdateActiveList();
RunCodeChangeCallbacks();
std::lock_guard<std::mutex> guard(s_lock);
s_disable_logging = false;
s_active_codes.clear();
std::copy_if(codes.begin(), codes.end(), std::back_inserter(s_active_codes), [](const ARCode& code)
{
return code.active;
});
s_active_codes.shrink_to_fit();
}
void AddCode(const ARCode& code)
void AddCode(ARCode code)
{
if (!SConfig::GetInstance().bEnableCheats)
return;
arCodes.push_back(code);
if (code.active)
UpdateActiveList();
RunCodeChangeCallbacks();
}
void* RegisterCodeChangeCallback(std::function<void()> callback)
{
if (!callback)
return nullptr;
std::lock_guard<std::mutex> guard(s_callbacks_lock);
s_callbacks.emplace_back(std::move(callback));
return &s_callbacks.back();
}
void UnregisterCodeChangeCallback(void* token)
{
std::lock_guard<std::mutex> guard(s_callbacks_lock);
for (auto i = s_callbacks.begin(); i != s_callbacks.end(); ++i)
{
if (&*i == token)
{
s_callbacks.erase(i);
break;
}
std::lock_guard<std::mutex> guard(s_lock);
s_disable_logging = false;
s_active_codes.emplace_back(std::move(code));
}
}
void LoadAndApplyCodes(const IniFile& globalIni, const IniFile& localIni)
void LoadAndApplyCodes(const IniFile& global_ini, const IniFile& local_ini)
{
ApplyCodes(LoadCodes(globalIni, localIni));
ApplyCodes(LoadCodes(global_ini, local_ini));
}
// Parses the Action Replay section of a game ini file.
std::vector<ARCode> LoadCodes(const IniFile& globalIni, const IniFile& localIni)
std::vector<ARCode> LoadCodes(const IniFile& global_ini, const IniFile& local_ini)
{
std::vector<ARCode> codes;
std::vector<std::string> enabledLines;
std::set<std::string> enabledNames;
localIni.GetLines("ActionReplay_Enabled", &enabledLines);
for (const std::string& line : enabledLines)
std::unordered_set<std::string> enabled_names;
{
if (line.size() != 0 && line[0] == '$')
std::vector<std::string> enabled_lines;
local_ini.GetLines("ActionReplay_Enabled", &enabled_lines);
for (const std::string& line : enabled_lines)
{
std::string name = line.substr(1, line.size() - 1);
enabledNames.insert(name);
if (line.size() != 0 && line[0] == '$')
{
std::string name = line.substr(1, line.size() - 1);
enabled_names.insert(name);
}
}
}
const IniFile* inis[2] = {&globalIni, &localIni};
const IniFile* inis[2] = {&global_ini, &local_ini};
for (const IniFile* ini : inis)
{
std::vector<std::string> lines;
std::vector<std::string> encryptedLines;
ARCode currentCode;
std::vector<std::string> encrypted_lines;
ARCode current_code;
ini->GetLines("ActionReplay", &lines);
@ -202,22 +181,22 @@ std::vector<ARCode> LoadCodes(const IniFile& globalIni, const IniFile& localIni)
// Check if the line is a name of the code
if (line[0] == '$')
{
if (currentCode.ops.size())
if (current_code.ops.size())
{
codes.push_back(currentCode);
currentCode.ops.clear();
codes.push_back(current_code);
current_code.ops.clear();
}
if (encryptedLines.size())
if (encrypted_lines.size())
{
DecryptARCode(encryptedLines, currentCode.ops);
codes.push_back(currentCode);
currentCode.ops.clear();
encryptedLines.clear();
DecryptARCode(encrypted_lines, current_code.ops);
codes.push_back(current_code);
current_code.ops.clear();
encrypted_lines.clear();
}
currentCode.name = line.substr(1, line.size() - 1);
currentCode.active = enabledNames.find(currentCode.name) != enabledNames.end();
currentCode.user_defined = (ini == &localIni);
current_code.name = line.substr(1, line.size() - 1);
current_code.active = enabled_names.find(current_code.name) != enabled_names.end();
current_code.user_defined = (ini == &local_ini);
}
else
{
@ -232,7 +211,7 @@ std::vector<ARCode> LoadCodes(const IniFile& globalIni, const IniFile& localIni)
if (success_addr && success_val)
{
currentCode.ops.push_back(op);
current_code.ops.push_back(op);
}
else
{
@ -253,105 +232,91 @@ std::vector<ARCode> LoadCodes(const IniFile& globalIni, const IniFile& localIni)
// Encrypted AR code
// Decryption is done in "blocks", so we must push blocks into a vector,
// then send to decrypt when a new block is encountered, or if it's the last block.
encryptedLines.push_back(pieces[0]+pieces[1]+pieces[2]);
encrypted_lines.emplace_back(pieces[0] + pieces[1] + pieces[2]);
}
}
}
}
// Handle the last code correctly.
if (currentCode.ops.size())
if (current_code.ops.size())
{
codes.push_back(currentCode);
codes.push_back(current_code);
}
if (encryptedLines.size())
if (encrypted_lines.size())
{
DecryptARCode(encryptedLines, currentCode.ops);
codes.push_back(currentCode);
DecryptARCode(encrypted_lines, current_code.ops);
codes.push_back(current_code);
}
}
return codes;
}
static void LogInfo(const char *format, ...)
void SaveCodes(IniFile* local_ini, const std::vector<ARCode>& codes)
{
if (!b_RanOnce)
std::vector<std::string> lines;
std::vector<std::string> enabled_lines;
for (const ActionReplay::ARCode& code : codes)
{
if (LogManager::GetMaxLevel() >= LogTypes::LINFO || logSelf)
{
va_list args;
va_start(args, format);
std::string text = StringFromFormatV(format, args);
va_end(args);
INFO_LOG(ACTIONREPLAY, "%s", text.c_str());
if (code.active)
enabled_lines.emplace_back("$" + code.name);
if (logSelf)
if (code.user_defined)
{
lines.emplace_back("$" + code.name);
for (const ActionReplay::AREntry& op : code.ops)
{
text += '\n';
arLog.push_back(text);
lines.emplace_back(StringFromFormat("%08X %08X", op.cmd_addr, op.value));
}
}
}
local_ini->SetLines("ActionReplay_Enabled", enabled_lines);
local_ini->SetLines("ActionReplay", lines);
}
size_t GetCodeListSize()
{
return arCodes.size();
}
ARCode GetARCode(size_t index)
static void LogInfo(const char* format, ...)
{
if (index > arCodes.size())
{
PanicAlertT("GetARCode: Index is greater than "
"ar code list size %zu", index);
return ARCode();
}
return arCodes[index];
}
void SetARCode_IsActive(bool active, size_t index)
{
if (index > arCodes.size())
{
PanicAlertT("SetARCode_IsActive: Index is greater than "
"ar code list size %zu", index);
if (s_disable_logging)
return;
bool use_internal_log = s_use_internal_log.load(std::memory_order_relaxed);
if (LogManager::GetMaxLevel() < LogTypes::LINFO && !use_internal_log)
return;
}
arCodes[index].active = active;
UpdateActiveList();
RunCodeChangeCallbacks();
}
void UpdateActiveList()
{
bool old_value = SConfig::GetInstance().bEnableCheats;
SConfig::GetInstance().bEnableCheats = false;
b_RanOnce = false;
activeCodes.clear();
for (auto& arCode : arCodes)
va_list args;
va_start(args, format);
std::string text = StringFromFormatV(format, args);
va_end(args);
INFO_LOG(ACTIONREPLAY, "%s", text.c_str());
if (use_internal_log)
{
if (arCode.active)
activeCodes.push_back(arCode);
text += '\n';
s_internal_log.emplace_back(std::move(text));
}
SConfig::GetInstance().bEnableCheats = old_value;
}
void EnableSelfLogging(bool enable)
{
logSelf = enable;
s_use_internal_log.store(enable, std::memory_order_relaxed);
}
const std::vector<std::string> &GetSelfLog()
std::vector<std::string> GetSelfLog()
{
return arLog;
std::lock_guard<std::mutex> guard(s_lock);
return s_internal_log;
}
void ClearSelfLog()
{
std::lock_guard<std::mutex> guard(s_lock);
s_internal_log.clear();
}
bool IsSelfLogging()
{
return logSelf;
return s_use_internal_log.load(std::memory_order_relaxed);
}
// ----------------------
@ -405,8 +370,8 @@ static bool Subtype_RamWriteAndFill(const ARAddr& addr, const u32 data)
default:
LogInfo("Bad Size");
PanicAlertT("Action Replay Error: Invalid size "
"(%08x : address = %08x) in Ram Write And Fill (%s)",
addr.size, addr.gcaddr, current_code->name.c_str());
"(%08x : address = %08x) in Ram Write And Fill (%s)",
addr.size, addr.gcaddr, s_current_code->name.c_str());
return false;
}
@ -443,7 +408,7 @@ static bool Subtype_WriteToPointer(const ARAddr& addr, const u32 data)
LogInfo("Write 16-bit to pointer");
LogInfo("--------");
const u16 theshort = data & 0xFFFF;
const u32 offset = (data >> 16) << 1;
const u32 offset = (data >> 16) << 1;
LogInfo("Pointer: %08x", ptr);
LogInfo("Byte: %08x", theshort);
LogInfo("Offset: %08x", offset);
@ -465,8 +430,8 @@ static bool Subtype_WriteToPointer(const ARAddr& addr, const u32 data)
default:
LogInfo("Bad Size");
PanicAlertT("Action Replay Error: Invalid size "
"(%08x : address = %08x) in Write To Pointer (%s)",
addr.size, addr.gcaddr, current_code->name.c_str());
"(%08x : address = %08x) in Write To Pointer (%s)",
addr.size, addr.gcaddr, s_current_code->name.c_str());
return false;
}
return true;
@ -512,8 +477,10 @@ static bool Subtype_AddCode(const ARAddr& addr, const u32 data)
LogInfo("--------");
const u32 read = PowerPC::HostRead_U32(new_addr);
const float fread = *((float*)&read) + (float)data; // data contains an integer value
const u32 newval = *((u32*)&fread);
const float read_float = reinterpret_cast<const float&>(read);
// data contains an (unsigned?) integer value
const float fread = read_float + static_cast<float>(data);
const u32 newval = reinterpret_cast<const u32&>(fread);
PowerPC::HostWrite_U32(newval, new_addr);
LogInfo("Old Value %08x", read);
LogInfo("Increment %08x", data);
@ -525,8 +492,8 @@ static bool Subtype_AddCode(const ARAddr& addr, const u32 data)
default:
LogInfo("Bad Size");
PanicAlertT("Action Replay Error: Invalid size "
"(%08x : address = %08x) in Add Code (%s)",
addr.size, addr.gcaddr, current_code->name.c_str());
"(%08x : address = %08x) in Add Code (%s)",
addr.size, addr.gcaddr, s_current_code->name.c_str());
return false;
}
return true;
@ -540,18 +507,20 @@ static bool Subtype_MasterCodeAndWriteToCCXXXXXX(const ARAddr& addr, const u32 d
// u8 mcode_count = (data & 0xFF00) >> 8;
// u8 mcode_number = data & 0xFF;
PanicAlertT("Action Replay Error: Master Code and Write To CCXXXXXX not implemented (%s)\n"
"Master codes are not needed. Do not use master codes.", current_code->name.c_str());
"Master codes are not needed. Do not use master codes.",
s_current_code->name.c_str());
return false;
}
static bool ZeroCode_FillAndSlide(const u32 val_last, const ARAddr& addr, const u32 data) // This needs more testing
// This needs more testing
static bool ZeroCode_FillAndSlide(const u32 val_last, const ARAddr& addr, const u32 data)
{
const u32 new_addr = ((ARAddr*)&val_last)->GCAddress();
const u8 size = ((ARAddr*)&val_last)->size;
const u32 new_addr = ARAddr(val_last).GCAddress();
const u8 size = ARAddr(val_last).size;
const s16 addr_incr = (s16)(data & 0xFFFF);
const s8 val_incr = (s8)(data >> 24);
const u8 write_num = (data & 0xFF0000) >> 16;
const s16 addr_incr = static_cast<s16>(data & 0xFFFF);
const s8 val_incr = static_cast<s8>(data >> 24);
const u8 write_num = static_cast<u8>((data & 0xFF0000) >> 16);
u32 val = addr;
u32 curr_addr = new_addr;
@ -612,7 +581,8 @@ static bool ZeroCode_FillAndSlide(const u32 val_last, const ARAddr& addr, const
default:
LogInfo("Bad Size");
PanicAlertT("Action Replay Error: Invalid size (%08x : address = %08x) in Fill and Slide (%s)", size, new_addr, current_code->name.c_str());
PanicAlertT("Action Replay Error: Invalid size (%08x : address = %08x) in Fill and Slide (%s)",
size, new_addr, s_current_code->name.c_str());
return false;
}
return true;
@ -659,7 +629,8 @@ static bool ZeroCode_MemoryCopy(const u32 val_last, const ARAddr& addr, const u3
else
{
LogInfo("Bad Value");
PanicAlertT("Action Replay Error: Invalid value (%08x) in Memory Copy (%s)", (data & ~0x7FFF), current_code->name.c_str());
PanicAlertT("Action Replay Error: Invalid value (%08x) in Memory Copy (%s)",
(data & ~0x7FFF), s_current_code->name.c_str());
return false;
}
return true;
@ -695,9 +666,9 @@ static bool NormalCode(const ARAddr& addr, const u32 data)
default:
LogInfo("Bad Subtype");
PanicAlertT("Action Replay: Normal Code 0: Invalid Subtype %08x (%s)", addr.subtype, current_code->name.c_str());
PanicAlertT("Action Replay: Normal Code 0: Invalid Subtype %08x (%s)", addr.subtype,
s_current_code->name.c_str());
return false;
break;
}
return true;
@ -709,43 +680,36 @@ static bool CompareValues(const u32 val1, const u32 val2, const int type)
{
case CONDTIONAL_EQUAL:
LogInfo("Type 1: If Equal");
return (val1 == val2);
break;
return val1 == val2;
case CONDTIONAL_NOT_EQUAL:
LogInfo("Type 2: If Not Equal");
return (val1 != val2);
break;
return val1 != val2;
case CONDTIONAL_LESS_THAN_SIGNED:
LogInfo("Type 3: If Less Than (Signed)");
return ((int)val1 < (int)val2);
break;
return static_cast<s32>(val1) < static_cast<s32>(val2);
case CONDTIONAL_GREATER_THAN_SIGNED:
LogInfo("Type 4: If Greater Than (Signed)");
return ((int)val1 >(int)val2);
break;
return static_cast<s32>(val1) > static_cast<s32>(val2);
case CONDTIONAL_LESS_THAN_UNSIGNED:
LogInfo("Type 5: If Less Than (Unsigned)");
return (val1 < val2);
break;
return val1 < val2;
case CONDTIONAL_GREATER_THAN_UNSIGNED:
LogInfo("Type 6: If Greater Than (Unsigned)");
return (val1 > val2);
break;
return val1 > val2;
case CONDTIONAL_AND:
LogInfo("Type 7: If And");
return !!(val1 & val2); // bitwise AND
break;
default: LogInfo("Unknown Compare type");
PanicAlertT("Action Replay: Invalid Normal Code Type %08x (%s)", type, current_code->name.c_str());
PanicAlertT("Action Replay: Invalid Normal Code Type %08x (%s)",
type, s_current_code->name.c_str());
return false;
break;
}
}
@ -761,11 +725,11 @@ static bool ConditionalCode(const ARAddr& addr, const u32 data, int* const pSkip
switch (addr.size)
{
case DATATYPE_8BIT:
result = CompareValues((u32)PowerPC::HostRead_U8(new_addr), (data & 0xFF), addr.type);
result = CompareValues(PowerPC::HostRead_U8(new_addr), (data & 0xFF), addr.type);
break;
case DATATYPE_16BIT:
result = CompareValues((u32)PowerPC::HostRead_U16(new_addr), (data & 0xFFFF), addr.type);
result = CompareValues(PowerPC::HostRead_U16(new_addr), (data & 0xFFFF), addr.type);
break;
case DATATYPE_32BIT_FLOAT:
@ -775,9 +739,9 @@ static bool ConditionalCode(const ARAddr& addr, const u32 data, int* const pSkip
default:
LogInfo("Bad Size");
PanicAlertT("Action Replay: Conditional Code: Invalid Size %08x (%s)", addr.size, current_code->name.c_str());
PanicAlertT("Action Replay: Conditional Code: Invalid Size %08x (%s)", addr.size,
s_current_code->name.c_str());
return false;
break;
}
// if the comparison failed we need to skip some lines
@ -794,58 +758,41 @@ static bool ConditionalCode(const ARAddr& addr, const u32 data, int* const pSkip
// Skip lines until a "00000000 40000000" line is reached
case CONDTIONAL_ALL_LINES:
case CONDTIONAL_ALL_LINES_UNTIL:
*pSkipCount = -(int) addr.subtype;
*pSkipCount = -static_cast<int>(addr.subtype);
break;
default:
LogInfo("Bad Subtype");
PanicAlertT("Action Replay: Normal Code %i: Invalid subtype %08x (%s)", 1, addr.subtype, current_code->name.c_str());
PanicAlertT("Action Replay: Normal Code %i: Invalid subtype %08x (%s)",
1, addr.subtype, s_current_code->name.c_str());
return false;
break;
}
}
return true;
}
void RunAllActive()
{
if (SConfig::GetInstance().bEnableCheats)
{
for (auto& activeCode : activeCodes)
{
if (activeCode.active)
{
activeCode.active = RunCode(activeCode);
LogInfo("\n");
}
}
b_RanOnce = true;
}
}
bool RunCode(const ARCode &arcode)
// NOTE: Lock needed to give mutual exclusion to s_current_code and LogInfo
static bool RunCodeLocked(const ARCode& arcode)
{
// The mechanism is different than what the real AR uses, so there may be compatibility problems.
bool doFillNSlide = false;
bool doMemoryCopy = false;
bool do_fill_and_slide = false;
bool do_memory_copy = false;
// used for conditional codes
int skip_count = 0;
u32 val_last = 0;
current_code = &arcode;
s_current_code = &arcode;
LogInfo("Code Name: %s", arcode.name.c_str());
LogInfo("Number of codes: %zu", arcode.ops.size());
for (const AREntry& entry : arcode.ops)
{
const ARAddr& addr = *(ARAddr*)&entry.cmd_addr;
const ARAddr addr(entry.cmd_addr);
const u32 data = entry.value;
// after a conditional code, skip lines if needed
@ -877,9 +824,9 @@ bool RunCode(const ARCode &arcode)
//LogInfo("Command: %08x", cmd);
// Do Fill & Slide
if (doFillNSlide)
if (do_fill_and_slide)
{
doFillNSlide = false;
do_fill_and_slide = false;
LogInfo("Doing Fill And Slide");
if (false == ZeroCode_FillAndSlide(val_last, addr, data))
return false;
@ -887,9 +834,9 @@ bool RunCode(const ARCode &arcode)
}
// Memory Copy
if (doMemoryCopy)
if (do_memory_copy)
{
doMemoryCopy = false;
do_memory_copy = false;
LogInfo("Doing Memory Copy");
if (false == ZeroCode_MemoryCopy(val_last, addr, data))
return false;
@ -912,7 +859,7 @@ bool RunCode(const ARCode &arcode)
// Zero codes
if (0x0 == addr) // Check if the code is a zero code
{
const u8 zcode = (data >> 29);
const u8 zcode = data >> 29;
LogInfo("Doing Zero Code %08x", zcode);
@ -921,7 +868,6 @@ bool RunCode(const ARCode &arcode)
case ZCODE_END: // END OF CODES
LogInfo("ZCode: End Of Codes");
return true;
break;
// TODO: the "00000000 40000000"(end if) codes fall into this case, I don't think that is correct
case ZCODE_NORM: // Normal execution of codes
@ -934,19 +880,18 @@ bool RunCode(const ARCode &arcode)
LogInfo("ZCode: Executes all codes in the same row, Set register 1BB4 to 1 (zcode not supported)");
PanicAlertT("Zero 3 code not supported");
return false;
break;
case ZCODE_04: // Fill & Slide or Memory Copy
if (0x3 == ((data >> 25) & 0x03))
{
LogInfo("ZCode: Memory Copy");
doMemoryCopy = true;
do_memory_copy = true;
val_last = data;
}
else
{
LogInfo("ZCode: Fill And Slide");
doFillNSlide = true;
do_fill_and_slide = true;
val_last = data;
}
break;
@ -955,7 +900,6 @@ bool RunCode(const ARCode &arcode)
LogInfo("ZCode: Unknown");
PanicAlertT("Zero code unknown to Dolphin: %08x", zcode);
return false;
break;
}
// done handling zero codes
@ -981,9 +925,25 @@ bool RunCode(const ARCode &arcode)
}
}
b_RanOnce = true;
return true;
}
void RunAllActive()
{
if (!SConfig::GetInstance().bEnableCheats)
return;
// If the mutex is idle then acquiring it should be cheap, fast mutexes
// are only atomic ops unless contested. It should be rare for this to
// be contested.
std::lock_guard<std::mutex> guard(s_lock);
s_active_codes.erase(std::remove_if(s_active_codes.begin(), s_active_codes.end(), [](const ARCode& code)
{
bool success = RunCodeLocked(code);
LogInfo("\n");
return !success;
}), s_active_codes.end());
s_disable_logging = true;
}
} // namespace ActionReplay

View File

@ -4,7 +4,6 @@
#pragma once
#include <functional>
#include <string>
#include <vector>
#include "Common/CommonTypes.h"
@ -31,18 +30,16 @@ struct ARCode
};
void RunAllActive();
bool RunCode(const ARCode &arcode);
void ApplyCodes(const std::vector<ARCode>& codes);
void AddCode(const ARCode& new_code);
void* RegisterCodeChangeCallback(std::function<void()> callback);
void UnregisterCodeChangeCallback(void* token);
void LoadAndApplyCodes(const IniFile& globalini, const IniFile& localIni);
std::vector<ARCode> LoadCodes(const IniFile& globalini, const IniFile& localIni);
size_t GetCodeListSize();
ARCode GetARCode(size_t index);
void SetARCode_IsActive(bool active, size_t index);
void UpdateActiveList();
void AddCode(ARCode new_code);
void LoadAndApplyCodes(const IniFile& global_ini, const IniFile& local_ini);
std::vector<ARCode> LoadCodes(const IniFile& global_ini, const IniFile& local_ini);
void SaveCodes(IniFile* local_ini, const std::vector<ARCode>& codes);
void EnableSelfLogging(bool enable);
const std::vector<std::string> &GetSelfLog();
std::vector<std::string> GetSelfLog();
void ClearSelfLog();
bool IsSelfLogging();
} // namespace

View File

@ -160,6 +160,7 @@ void CARCodeAddEdit::SaveCheatData(wxCommandEvent& WXUNUSED(event))
newCheat.name = WxStrToStr(EditCheatName->GetValue());
newCheat.ops = decryptedLines;
newCheat.active = true;
newCheat.user_defined = true;
arCodes->push_back(newCheat);
}

View File

@ -174,7 +174,6 @@ void CheatSearchTab::OnCreateARCodeClicked(wxCommandEvent&)
const u32 address = m_search_results[idx].address | ((m_search_type_size & ~1) << 24);
CreateCodeDialog arcode_dlg(this, address);
arcode_dlg.SetExtraStyle(arcode_dlg.GetExtraStyle() & ~wxWS_EX_BLOCK_EVENTS);
arcode_dlg.ShowModal();
}

View File

@ -6,9 +6,10 @@
#include <cstddef>
#include <cstdio>
#include <cstring>
#include <functional>
#include <string>
#include <utility>
#include <vector>
#include <wx/app.h>
#include <wx/button.h>
#include <wx/checkbox.h>
#include <wx/checklst.h>
@ -32,6 +33,7 @@
#include "Core/GeckoCode.h"
#include "Core/GeckoCodeConfig.h"
#include "DolphinWX/Frame.h"
#include "DolphinWX/Globals.h"
#include "DolphinWX/Main.h"
#include "DolphinWX/WxUtils.h"
#include "DolphinWX/Cheats/CheatSearchTab.h"
@ -39,10 +41,12 @@
#include "DolphinWX/Cheats/CreateCodeDialog.h"
#include "DolphinWX/Cheats/GeckoCodeDiag.h"
namespace
wxDEFINE_EVENT(DOLPHIN_EVT_ADD_NEW_ACTION_REPLAY_CODE, wxCommandEvent);
struct wxCheatsWindow::CodeData : public wxClientData
{
wxDEFINE_EVENT(DOLPHIN_EVT_UPDATE_CHEAT_LIST, wxThreadEvent);
}
ActionReplay::ARCode code;
};
wxCheatsWindow::wxCheatsWindow(wxWindow* const parent)
: wxDialog(parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX | wxMINIMIZE_BOX | wxDIALOG_NO_PARENT)
@ -50,10 +54,9 @@ wxCheatsWindow::wxCheatsWindow(wxWindow* const parent)
// Create the GUI controls
Init_ChildControls();
m_ar_callback_token = ActionReplay::RegisterCodeChangeCallback(std::bind(&wxCheatsWindow::OnActionReplayModified, this));
// load codes
UpdateGUI();
wxTheApp->Bind(DOLPHIN_EVT_LOCAL_INI_CHANGED, &wxCheatsWindow::OnEvent_CheatsList_Update, this);
SetSize(wxSize(-1, 600));
Center();
@ -62,7 +65,6 @@ wxCheatsWindow::wxCheatsWindow(wxWindow* const parent)
wxCheatsWindow::~wxCheatsWindow()
{
ActionReplay::UnregisterCodeChangeCallback(m_ar_callback_token);
main_frame->g_CheatsWindow = nullptr;
}
@ -75,18 +77,17 @@ void wxCheatsWindow::Init_ChildControls()
// Cheats List Tab
m_tab_cheats = new wxPanel(m_notebook_main, wxID_ANY);
m_checklistbox_cheats_list = new wxCheckListBox(m_tab_cheats, wxID_ANY, wxDefaultPosition, wxSize(300, 0), m_cheat_string_list, wxLB_HSCROLL, wxDefaultValidator);
m_checklistbox_cheats_list = new wxCheckListBox(m_tab_cheats, wxID_ANY, wxDefaultPosition, wxSize(300, 0), 0, nullptr, wxLB_HSCROLL);
m_checklistbox_cheats_list->Bind(wxEVT_LISTBOX, &wxCheatsWindow::OnEvent_CheatsList_ItemSelected, this);
m_checklistbox_cheats_list->Bind(wxEVT_CHECKLISTBOX, &wxCheatsWindow::OnEvent_CheatsList_ItemToggled, this);
m_label_code_name = new wxStaticText(m_tab_cheats, wxID_ANY, _("Name: "));
m_label_code_name = new wxStaticText(m_tab_cheats, wxID_ANY, _("Name: "), wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE);
m_groupbox_info = new wxStaticBox(m_tab_cheats, wxID_ANY, _("Code Info"));
m_label_num_codes = new wxStaticText(m_tab_cheats, wxID_ANY, _("Number Of Codes: "));
m_listbox_codes_list = new wxListBox(m_tab_cheats, wxID_ANY, wxDefaultPosition, wxSize(120, 150), 0, nullptr, wxLB_HSCROLL);
wxStaticBoxSizer* sGroupBoxInfo = new wxStaticBoxSizer(m_groupbox_info, wxVERTICAL);
sGroupBoxInfo->Add(m_label_code_name, 0, wxALL, 5);
sGroupBoxInfo->Add(m_label_code_name, 0, wxEXPAND | wxALL, 5);
sGroupBoxInfo->Add(m_label_num_codes, 0, wxALL, 5);
sGroupBoxInfo->Add(m_listbox_codes_list, 1, wxALL, 5);
@ -104,6 +105,8 @@ void wxCheatsWindow::Init_ChildControls()
wxButton* const button_updatelog = new wxButton(m_tab_log, wxID_ANY, _("Update"));
button_updatelog->Bind(wxEVT_BUTTON, &wxCheatsWindow::OnEvent_ButtonUpdateLog_Press, this);
wxButton* const button_clearlog = new wxButton(m_tab_log, wxID_ANY, _("Clear"));
button_clearlog->Bind(wxEVT_BUTTON, &wxCheatsWindow::OnClearActionReplayLog, this);
m_checkbox_log_ar = new wxCheckBox(m_tab_log, wxID_ANY, _("Enable AR Logging"));
m_checkbox_log_ar->Bind(wxEVT_CHECKBOX, &wxCheatsWindow::OnEvent_CheckBoxEnableLogging_StateChange, this);
@ -114,6 +117,7 @@ void wxCheatsWindow::Init_ChildControls()
wxBoxSizer *HStrip1 = new wxBoxSizer(wxHORIZONTAL);
HStrip1->Add(m_checkbox_log_ar, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
HStrip1->Add(button_updatelog, 0, wxALL, 5);
HStrip1->Add(button_clearlog, 0, wxALL, 5);
wxBoxSizer *sTabLog = new wxBoxSizer(wxVERTICAL);
sTabLog->Add(HStrip1, 0, wxALL, 5);
@ -128,19 +132,15 @@ void wxCheatsWindow::Init_ChildControls()
m_notebook_main->AddPage(tab_cheat_search, _("Cheat Search"));
m_notebook_main->AddPage(m_tab_log, _("Logging"));
// Button Strip
m_button_apply = new wxButton(this, wxID_APPLY, _("Apply"));
m_button_apply->Bind(wxEVT_BUTTON, &wxCheatsWindow::OnEvent_ApplyChanges_Press, this);
wxButton* const button_cancel = new wxButton(this, wxID_CANCEL, _("Cancel"));
button_cancel->Bind(wxEVT_BUTTON, &wxCheatsWindow::OnEvent_ButtonClose_Press, this);
Bind(wxEVT_BUTTON, &wxCheatsWindow::OnEvent_ApplyChanges_Press, this, wxID_APPLY);
Bind(wxEVT_BUTTON, &wxCheatsWindow::OnEvent_ButtonClose_Press, this, wxID_CANCEL);
Bind(wxEVT_CLOSE_WINDOW, &wxCheatsWindow::OnEvent_Close, this);
Bind(DOLPHIN_EVT_UPDATE_CHEAT_LIST, &wxCheatsWindow::OnEvent_CheatsList_Update, this);
Bind(DOLPHIN_EVT_ADD_NEW_ACTION_REPLAY_CODE, &wxCheatsWindow::OnNewARCodeCreated, this);
wxStdDialogButtonSizer* const sButtons = new wxStdDialogButtonSizer();
sButtons->AddButton(m_button_apply);
sButtons->AddButton(button_cancel);
sButtons->Realize();
wxStdDialogButtonSizer* const sButtons = CreateStdDialogButtonSizer(wxAPPLY | wxCANCEL);
m_button_apply = sButtons->GetApplyButton();
SetEscapeId(wxID_CANCEL);
SetAffirmativeId(wxID_CANCEL);
wxBoxSizer* const sMain = new wxBoxSizer(wxVERTICAL);
sMain->Add(m_notebook_main, 1, wxEXPAND | wxALL, 5);
@ -165,7 +165,9 @@ void wxCheatsWindow::UpdateGUI()
const SConfig& parameters = SConfig::GetInstance();
m_gameini_default = parameters.LoadDefaultGameIni();
m_gameini_local = parameters.LoadLocalGameIni();
m_gameini_local_path = File::GetUserPath(D_GAMESETTINGS_IDX) + parameters.GetUniqueID() + ".ini";
m_game_id = parameters.GetUniqueID();
m_game_revision = parameters.m_revision;
m_gameini_local_path = File::GetUserPath(D_GAMESETTINGS_IDX) + m_game_id + ".ini";
Load_ARCodes();
Load_GeckoCodes();
@ -176,32 +178,28 @@ void wxCheatsWindow::UpdateGUI()
// write the ISO name in the title
if (Core::IsRunning())
SetTitle(title + ": " + parameters.GetUniqueID() + " - " + parameters.m_strName);
SetTitle(title + StrToWxStr(": " + m_game_id + " - " + parameters.m_strName));
else
SetTitle(title);
}
void wxCheatsWindow::Load_ARCodes()
{
using namespace ActionReplay;
m_checklistbox_cheats_list->Clear();
if (!Core::IsRunning())
return;
m_index_list.clear();
size_t size = GetCodeListSize();
for (size_t i = 0; i < size; i++)
m_checklistbox_cheats_list->Freeze();
for (auto& code : ActionReplay::LoadCodes(m_gameini_default, m_gameini_local))
{
ARCode code = GetARCode(i);
ARCodeIndex ind;
u32 index = m_checklistbox_cheats_list->Append(StrToWxStr(code.name));
m_checklistbox_cheats_list->Check(index, code.active);
ind.index = i;
ind.uiIndex = index;
m_index_list.push_back(ind);
CodeData* cd = new CodeData();
cd->code = std::move(code);
int index = m_checklistbox_cheats_list->Append(wxCheckListBox::EscapeMnemonics(StrToWxStr(cd->code.name)),
cd);
m_checklistbox_cheats_list->Check(index, cd->code.active);
}
m_checklistbox_cheats_list->Thaw();
}
void wxCheatsWindow::Load_GeckoCodes()
@ -209,70 +207,81 @@ void wxCheatsWindow::Load_GeckoCodes()
m_geckocode_panel->LoadCodes(m_gameini_default, m_gameini_local, SConfig::GetInstance().GetUniqueID(), true);
}
void wxCheatsWindow::OnEvent_CheatsList_ItemSelected(wxCommandEvent& WXUNUSED(event))
void wxCheatsWindow::OnNewARCodeCreated(wxCommandEvent& ev)
{
using namespace ActionReplay;
auto code = static_cast<ActionReplay::ARCode*>(ev.GetClientData());
ActionReplay::AddCode(*code);
int index = m_checklistbox_cheats_list->GetSelection();
for (size_t i = 0; i < m_index_list.size(); i++)
{
if ((int)m_index_list[i].uiIndex == index)
{
ARCode code = GetARCode(i);
m_label_code_name->SetLabel(_("Name: ") + StrToWxStr(code.name));
std::string numcodes = StringFromFormat("Number of Codes: %zu", code.ops.size());
m_label_num_codes->SetLabel(StrToWxStr(numcodes));
m_listbox_codes_list->Clear();
for (const AREntry& entry : code.ops)
{
std::string ops = StringFromFormat("%08x %08x", entry.cmd_addr, entry.value);
m_listbox_codes_list->Append(StrToWxStr(ops));
}
}
}
CodeData* cd = new CodeData();
cd->code = *code;
int idx = m_checklistbox_cheats_list->Append(wxCheckListBox::EscapeMnemonics(StrToWxStr(code->name)),
cd);
m_checklistbox_cheats_list->Check(idx, code->active);
}
void wxCheatsWindow::OnEvent_CheatsList_ItemToggled(wxCommandEvent& WXUNUSED(event))
void wxCheatsWindow::OnEvent_CheatsList_ItemSelected(wxCommandEvent& event)
{
int index = m_checklistbox_cheats_list->GetSelection();
for (const ARCodeIndex& code_index : m_index_list)
CodeData* cd = static_cast<CodeData*>(event.GetClientObject());
m_label_code_name->SetLabelText(_("Name: ") + StrToWxStr(cd->code.name));
m_label_code_name->Wrap(m_label_code_name->GetSize().GetWidth());
m_label_code_name->InvalidateBestSize();
m_label_num_codes->SetLabelText(wxString::Format("%s%zu", _("Number Of Codes: "), cd->code.ops.size()));
m_listbox_codes_list->Freeze();
m_listbox_codes_list->Clear();
for (const ActionReplay::AREntry& entry : cd->code.ops)
{
if ((int)code_index.uiIndex == index)
{
m_ar_ignore_callback = true;
ActionReplay::SetARCode_IsActive(m_checklistbox_cheats_list->IsChecked(index), code_index.index);
}
m_listbox_codes_list->Append(wxString::Format("%08x %08x", entry.cmd_addr, entry.value));
}
m_listbox_codes_list->Thaw();
m_tab_cheats->Layout();
}
void wxCheatsWindow::OnEvent_CheatsList_Update(wxThreadEvent&)
void wxCheatsWindow::OnEvent_CheatsList_Update(wxCommandEvent& ev)
{
if (m_ar_ignore_callback)
ev.Skip();
if (WxStrToStr(ev.GetString()) != m_game_id)
return;
if (m_ignore_ini_callback)
{
m_ar_ignore_callback = false;
m_ignore_ini_callback = false;
return;
}
Load_ARCodes();
}
void wxCheatsWindow::OnActionReplayModified()
{
// NOTE: This is an arbitrary thread context
GetEventHandler()->QueueEvent(new wxThreadEvent(DOLPHIN_EVT_UPDATE_CHEAT_LIST));
UpdateGUI();
}
void wxCheatsWindow::OnEvent_ApplyChanges_Press(wxCommandEvent& ev)
{
// Convert embedded metadata back into ARCode vector and update active states
std::vector<ActionReplay::ARCode> code_vec;
code_vec.reserve(m_checklistbox_cheats_list->GetCount());
for (unsigned int i = 0; i < m_checklistbox_cheats_list->GetCount(); ++i)
{
CodeData* cd = static_cast<CodeData*>(m_checklistbox_cheats_list->GetClientObject(i));
cd->code.active = m_checklistbox_cheats_list->IsChecked(i);
code_vec.push_back(cd->code);
}
// Apply Action Replay code changes
ActionReplay::ApplyCodes(code_vec);
// Apply Gecko Code changes
Gecko::SetActiveCodes(m_geckocode_panel->GetCodes());
// Save gameini, with changed gecko codes
// Save gameini, with changed codes
if (m_gameini_local_path.size())
{
ActionReplay::SaveCodes(&m_gameini_local, code_vec);
Gecko::SaveCodes(m_gameini_local, m_geckocode_panel->GetCodes());
m_gameini_local.Save(m_gameini_local_path);
wxCommandEvent ini_changed(DOLPHIN_EVT_LOCAL_INI_CHANGED);
ini_changed.SetString(StrToWxStr(m_game_id));
ini_changed.SetInt(m_game_revision);
m_ignore_ini_callback = true;
wxTheApp->ProcessEvent(ini_changed);
}
ev.Skip();
@ -291,6 +300,12 @@ void wxCheatsWindow::OnEvent_ButtonUpdateLog_Press(wxCommandEvent& WXUNUSED(even
wxEndBusyCursor();
}
void wxCheatsWindow::OnClearActionReplayLog(wxCommandEvent& event)
{
ActionReplay::ClearSelfLog();
OnEvent_ButtonUpdateLog_Press(event);
}
void wxCheatsWindow::OnEvent_CheckBoxEnableLogging_StateChange(wxCommandEvent& WXUNUSED(event))
{
ActionReplay::EnableSelfLogging(m_checkbox_log_ar->IsChecked());

View File

@ -29,6 +29,8 @@ namespace Gecko
class CodeConfigPanel;
}
wxDECLARE_EVENT(DOLPHIN_EVT_ADD_NEW_ACTION_REPLAY_CODE, wxCommandEvent);
class wxCheatsWindow final : public wxDialog
{
public:
@ -37,11 +39,7 @@ public:
void UpdateGUI();
private:
struct ARCodeIndex
{
u32 uiIndex;
size_t index;
};
struct CodeData;
// --- GUI Controls ---
wxButton* m_button_apply;
@ -63,18 +61,14 @@ private:
wxStaticBox* m_groupbox_info;
wxArrayString m_cheat_string_list;
std::vector<ARCodeIndex> m_index_list;
Gecko::CodeConfigPanel* m_geckocode_panel;
IniFile m_gameini_default;
IniFile m_gameini_local;
std::string m_gameini_local_path;
std::string m_game_id;
u32 m_game_revision;
// ActionReplay::UnregisterCodeChangeCallback handle
void* m_ar_callback_token = nullptr;
bool m_ar_ignore_callback = false;
bool m_ignore_ini_callback = false;
void Init_ChildControls();
@ -82,6 +76,8 @@ private:
void Load_GeckoCodes();
// --- Wx Events Handlers ---
// Cheat Search
void OnNewARCodeCreated(wxCommandEvent& ev);
// Close Button
void OnEvent_ButtonClose_Press(wxCommandEvent& event);
@ -89,15 +85,14 @@ private:
// Cheats List
void OnEvent_CheatsList_ItemSelected(wxCommandEvent& event);
void OnEvent_CheatsList_ItemToggled(wxCommandEvent& event);
void OnEvent_CheatsList_Update(wxThreadEvent& event);
void OnActionReplayModified();
void OnEvent_CheatsList_Update(wxCommandEvent& event);
// Apply Changes Button
void OnEvent_ApplyChanges_Press(wxCommandEvent& event);
// Update Log Button
void OnEvent_ButtonUpdateLog_Press(wxCommandEvent& event);
void OnClearActionReplayLog(wxCommandEvent& event);
// Enable Logging Checkbox
void OnEvent_CheckBoxEnableLogging_StateChange(wxCommandEvent& event);

View File

@ -12,6 +12,7 @@
#include "Core/ConfigManager.h"
#include "DolphinWX/ISOProperties.h"
#include "DolphinWX/WxUtils.h"
#include "DolphinWX/Cheats/CheatsWindow.h"
#include "DolphinWX/Cheats/CreateCodeDialog.h"
CreateCodeDialog::CreateCodeDialog(wxWindow* const parent, const u32 address)
@ -70,27 +71,16 @@ void CreateCodeDialog::PressOK(wxCommandEvent& ev)
return;
}
//wxString full_code = textctrl_code->GetValue();
//full_code += ' ';
//full_code += wxString::Format("0x%08x", code_value);
// create the new code
ActionReplay::ARCode new_cheat;
new_cheat.active = false;
new_cheat.user_defined = true;
new_cheat.name = WxStrToStr(code_name);
new_cheat.ops.emplace_back(ActionReplay::AREntry(m_code_address, code_value));
ActionReplay::AddCode(new_cheat);
// pretty hacky - add the code to the gameini
// FIXME: The save logic should be ActionReplay since it mirrors the parser
{
CISOProperties isoprops(GameListItem(SConfig::GetInstance().m_LastFilename, {}), this);
// add the code to the isoproperties arcode list
isoprops.AddARCode(new_cheat);
// save the gameini
isoprops.SaveGameConfig();
}
wxCommandEvent add_event(DOLPHIN_EVT_ADD_NEW_ACTION_REPLAY_CODE, GetId());
add_event.SetClientData(&new_cheat);
GetParent()->GetEventHandler()->ProcessEvent(add_event);
Close();
}

View File

@ -223,6 +223,7 @@ bool CRenderFrame::ShowFullScreen(bool show, long style)
// help button.
wxDEFINE_EVENT(wxEVT_HOST_COMMAND, wxCommandEvent);
wxDEFINE_EVENT(DOLPHIN_EVT_LOCAL_INI_CHANGED, wxCommandEvent);
BEGIN_EVENT_TABLE(CFrame, CRenderFrame)

View File

@ -331,10 +331,11 @@ enum
// custom message macro
#define EVT_HOST_COMMAND(id, fn) \
DECLARE_EVENT_TABLE_ENTRY(\
wxEVT_HOST_COMMAND, id, wxID_ANY, \
(wxObjectEventFunction)(wxEventFunction) wxStaticCastEvent(wxCommandEventFunction, &fn), \
(wxObject*) nullptr \
),
EVT_COMMAND(id, wxEVT_HOST_COMMAND, fn)
wxDECLARE_EVENT(wxEVT_HOST_COMMAND, wxCommandEvent);
// Sent to wxTheApp
// GetString() == Game's Unique ID
// GetInt() == Game's Revision
wxDECLARE_EVENT(DOLPHIN_EVT_LOCAL_INI_CHANGED, wxCommandEvent);

View File

@ -16,6 +16,7 @@
#include <type_traits>
#include <vector>
#include <mbedtls/md5.h>
#include <wx/app.h>
#include <wx/bitmap.h>
#include <wx/button.h>
#include <wx/checkbox.h>
@ -63,7 +64,7 @@
#include "DiscIO/VolumeCreator.h"
#include "DolphinWX/ARCodeAddEdit.h"
#include "DolphinWX/GameListCtrl.h"
//#include "DolphinWX/Frame.h"
#include "DolphinWX/Globals.h"
#include "DolphinWX/ISOFile.h"
#include "DolphinWX/ISOProperties.h"
#include "DolphinWX/PatchAddEdit.h"
@ -230,6 +231,8 @@ CISOProperties::CISOProperties(const GameListItem& game_list_item, wxWindow* par
m_Treectrl->Expand(RootId);
}
wxTheApp->Bind(DOLPHIN_EVT_LOCAL_INI_CHANGED, &CISOProperties::OnLocalIniModified, this);
}
CISOProperties::~CISOProperties()
@ -1164,6 +1167,9 @@ bool CISOProperties::SaveGameConfig()
if (success && File::GetSize(GameIniFileLocal) == 0)
File::Delete(GameIniFileLocal);
if (success)
GenerateLocalIniModified();
return success;
}
@ -1211,6 +1217,24 @@ void CISOProperties::LaunchExternalEditor(const std::string& filename, bool wait
#endif
}
void CISOProperties::GenerateLocalIniModified()
{
wxCommandEvent event_update(DOLPHIN_EVT_LOCAL_INI_CHANGED);
event_update.SetString(StrToWxStr(game_id));
event_update.SetInt(OpenGameListItem.GetRevision());
wxTheApp->ProcessEvent(event_update);
}
void CISOProperties::OnLocalIniModified(wxCommandEvent& ev)
{
ev.Skip();
if (WxStrToStr(ev.GetString()) != game_id)
return;
GameIniLocal.Load(GameIniFileLocal);
LoadGameConfig();
}
void CISOProperties::OnEditConfig(wxCommandEvent& WXUNUSED (event))
{
SaveGameConfig();
@ -1221,8 +1245,7 @@ void CISOProperties::OnEditConfig(wxCommandEvent& WXUNUSED (event))
blankFile.close();
}
LaunchExternalEditor(GameIniFileLocal, true);
GameIniLocal.Load(GameIniFileLocal);
LoadGameConfig();
GenerateLocalIniModified();
}
void CISOProperties::OnComputeMD5Sum(wxCommandEvent& WXUNUSED (event))
@ -1301,7 +1324,7 @@ void CISOProperties::ListSelectionChanged(wxCommandEvent& event)
break;
case ID_CHEATS_LIST:
if (Cheats->GetSelection() == wxNOT_FOUND ||
DefaultCheats.find(Cheats->GetString(Cheats->GetSelection()).ToStdString()) != DefaultCheats.end())
DefaultCheats.find(Cheats->RemoveMnemonics(Cheats->GetString(Cheats->GetSelection())).ToStdString()) != DefaultCheats.end())
{
EditCheat->Disable();
RemoveCheat->Disable();
@ -1405,44 +1428,24 @@ void CISOProperties::PatchButtonClicked(wxCommandEvent& event)
void CISOProperties::ActionReplayList_Load()
{
Cheats->Clear();
arCodes = ActionReplay::LoadCodes(GameIniDefault, GameIniLocal);
DefaultCheats.clear();
u32 index = 0;
Cheats->Freeze();
Cheats->Clear();
for (const ActionReplay::ARCode& arCode : arCodes)
{
Cheats->Append(StrToWxStr(arCode.name));
Cheats->Check(index, arCode.active);
int idx = Cheats->Append(Cheats->EscapeMnemonics(StrToWxStr(arCode.name)));
Cheats->Check(idx, arCode.active);
if (!arCode.user_defined)
DefaultCheats.insert(arCode.name);
++index;
}
Cheats->Thaw();
}
void CISOProperties::ActionReplayList_Save()
{
std::vector<std::string> lines;
std::vector<std::string> enabledLines;
u32 index = 0;
u32 cheats_chkbox_count = Cheats->GetCount();
for (const ActionReplay::ARCode& code : arCodes)
{
if (code.active)
enabledLines.push_back("$" + code.name);
// Do not save default cheats.
if (DefaultCheats.find(code.name) == DefaultCheats.end())
{
lines.push_back("$" + code.name);
for (const ActionReplay::AREntry& op : code.ops)
{
lines.push_back(WxStrToStr(wxString::Format("%08X %08X", op.cmd_addr, op.value)));
}
}
++index;
}
GameIniLocal.SetLines("ActionReplay_Enabled", enabledLines);
GameIniLocal.SetLines("ActionReplay", lines);
ActionReplay::SaveCodes(&GameIniLocal, arCodes);
}
void CISOProperties::ActionReplayButtonClicked(wxCommandEvent& event)
@ -1484,11 +1487,6 @@ void CISOProperties::ActionReplayButtonClicked(wxCommandEvent& event)
RemoveCheat->Disable();
}
void CISOProperties::AddARCode(const ActionReplay::ARCode& code)
{
arCodes.emplace_back(code);
}
void CISOProperties::OnChangeBannerLang(wxCommandEvent& event)
{
ChangeBannerDetails(OpenGameListItem.GetLanguages()[event.GetSelection()]);

View File

@ -68,15 +68,6 @@ public:
long style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
virtual ~CISOProperties();
bool bRefreshList;
// These are only public because of the ugly hack in CreateCodeDialog.cpp
void ActionReplayList_Load();
bool SaveGameConfig();
// This only exists because of the ugly hack in CreateCodeDialog.cpp
void AddARCode(const ActionReplay::ARCode& code);
private:
DECLARE_EVENT_TABLE();
@ -242,9 +233,15 @@ private:
std::set<std::string> DefaultPatches;
std::set<std::string> DefaultCheats;
bool bRefreshList;
void LoadGameConfig();
bool SaveGameConfig();
void OnLocalIniModified(wxCommandEvent& ev);
void GenerateLocalIniModified();
void PatchList_Load();
void PatchList_Save();
void ActionReplayList_Load();
void ActionReplayList_Save();
void ChangeBannerDetails(DiscIO::IVolume::ELanguage language);