Merge pull request #3776 from EmptyChaos/ar-isoprop-corrupt
ActionReplay: Fix ISOProperties corrupting active code set
This commit is contained in:
commit
c514a48f4d
|
@ -19,7 +19,13 @@
|
||||||
// Zero Codes: any code with no address. These codes are used to do special operations like memory copy, etc
|
// Zero Codes: any code with no address. These codes are used to do special operations like memory copy, etc
|
||||||
// -------------------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <atomic>
|
||||||
|
#include <iterator>
|
||||||
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
@ -72,14 +78,14 @@ enum
|
||||||
SUB_MASTER_CODE = 0x03,
|
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)
|
// pointer to the code currently being run, (used by log messages that include the code name)
|
||||||
static ARCode const* current_code = nullptr;
|
static const ARCode* s_current_code = nullptr;
|
||||||
|
static bool s_disable_logging = false;
|
||||||
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;
|
|
||||||
|
|
||||||
struct ARAddr
|
struct ARAddr
|
||||||
{
|
{
|
||||||
|
@ -102,33 +108,64 @@ struct ARAddr
|
||||||
|
|
||||||
// ----------------------
|
// ----------------------
|
||||||
// AR Remote Functions
|
// AR Remote Functions
|
||||||
void LoadCodes(const IniFile& globalIni, const IniFile& localIni, bool forceLoad)
|
void ApplyCodes(const std::vector<ARCode>& codes)
|
||||||
{
|
{
|
||||||
// Parses the Action Replay section of a game ini file.
|
if (!SConfig::GetInstance().bEnableCheats)
|
||||||
if (!SConfig::GetInstance().bEnableCheats &&
|
|
||||||
!forceLoad)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
arCodes.clear();
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> enabledLines;
|
void AddCode(ARCode code)
|
||||||
std::set<std::string> enabledNames;
|
{
|
||||||
localIni.GetLines("ActionReplay_Enabled", &enabledLines);
|
if (!SConfig::GetInstance().bEnableCheats)
|
||||||
for (const std::string& line : enabledLines)
|
return;
|
||||||
|
|
||||||
|
if (code.active)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(s_lock);
|
||||||
|
s_disable_logging = false;
|
||||||
|
s_active_codes.emplace_back(std::move(code));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadAndApplyCodes(const IniFile& global_ini, const IniFile& local_ini)
|
||||||
|
{
|
||||||
|
ApplyCodes(LoadCodes(global_ini, local_ini));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parses the Action Replay section of a game ini file.
|
||||||
|
std::vector<ARCode> LoadCodes(const IniFile& global_ini, const IniFile& local_ini)
|
||||||
|
{
|
||||||
|
std::vector<ARCode> codes;
|
||||||
|
|
||||||
|
std::unordered_set<std::string> enabled_names;
|
||||||
|
{
|
||||||
|
std::vector<std::string> enabled_lines;
|
||||||
|
local_ini.GetLines("ActionReplay_Enabled", &enabled_lines);
|
||||||
|
for (const std::string& line : enabled_lines)
|
||||||
{
|
{
|
||||||
if (line.size() != 0 && line[0] == '$')
|
if (line.size() != 0 && line[0] == '$')
|
||||||
{
|
{
|
||||||
std::string name = line.substr(1, line.size() - 1);
|
std::string name = line.substr(1, line.size() - 1);
|
||||||
enabledNames.insert(name);
|
enabled_names.insert(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const IniFile* inis[2] = {&globalIni, &localIni};
|
const IniFile* inis[2] = {&global_ini, &local_ini};
|
||||||
for (const IniFile* ini : inis)
|
for (const IniFile* ini : inis)
|
||||||
{
|
{
|
||||||
std::vector<std::string> lines;
|
std::vector<std::string> lines;
|
||||||
std::vector<std::string> encryptedLines;
|
std::vector<std::string> encrypted_lines;
|
||||||
ARCode currentCode;
|
ARCode current_code;
|
||||||
|
|
||||||
ini->GetLines("ActionReplay", &lines);
|
ini->GetLines("ActionReplay", &lines);
|
||||||
|
|
||||||
|
@ -144,22 +181,22 @@ void LoadCodes(const IniFile& globalIni, const IniFile& localIni, bool forceLoad
|
||||||
// Check if the line is a name of the code
|
// Check if the line is a name of the code
|
||||||
if (line[0] == '$')
|
if (line[0] == '$')
|
||||||
{
|
{
|
||||||
if (currentCode.ops.size())
|
if (current_code.ops.size())
|
||||||
{
|
{
|
||||||
arCodes.push_back(currentCode);
|
codes.push_back(current_code);
|
||||||
currentCode.ops.clear();
|
current_code.ops.clear();
|
||||||
}
|
}
|
||||||
if (encryptedLines.size())
|
if (encrypted_lines.size())
|
||||||
{
|
{
|
||||||
DecryptARCode(encryptedLines, currentCode.ops);
|
DecryptARCode(encrypted_lines, current_code.ops);
|
||||||
arCodes.push_back(currentCode);
|
codes.push_back(current_code);
|
||||||
currentCode.ops.clear();
|
current_code.ops.clear();
|
||||||
encryptedLines.clear();
|
encrypted_lines.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
currentCode.name = line.substr(1, line.size() - 1);
|
current_code.name = line.substr(1, line.size() - 1);
|
||||||
currentCode.active = enabledNames.find(currentCode.name) != enabledNames.end();
|
current_code.active = enabled_names.find(current_code.name) != enabled_names.end();
|
||||||
currentCode.user_defined = (ini == &localIni);
|
current_code.user_defined = (ini == &local_ini);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -174,7 +211,7 @@ void LoadCodes(const IniFile& globalIni, const IniFile& localIni, bool forceLoad
|
||||||
|
|
||||||
if (success_addr && success_val)
|
if (success_addr && success_val)
|
||||||
{
|
{
|
||||||
currentCode.ops.push_back(op);
|
current_code.ops.push_back(op);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -195,110 +232,91 @@ void LoadCodes(const IniFile& globalIni, const IniFile& localIni, bool forceLoad
|
||||||
// Encrypted AR code
|
// Encrypted AR code
|
||||||
// Decryption is done in "blocks", so we must push blocks into a vector,
|
// 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.
|
// 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.
|
// Handle the last code correctly.
|
||||||
if (currentCode.ops.size())
|
if (current_code.ops.size())
|
||||||
{
|
{
|
||||||
arCodes.push_back(currentCode);
|
codes.push_back(current_code);
|
||||||
}
|
}
|
||||||
if (encryptedLines.size())
|
if (encrypted_lines.size())
|
||||||
{
|
{
|
||||||
DecryptARCode(encryptedLines, currentCode.ops);
|
DecryptARCode(encrypted_lines, current_code.ops);
|
||||||
arCodes.push_back(currentCode);
|
codes.push_back(current_code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateActiveList();
|
return codes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadCodes(std::vector<ARCode> &_arCodes, IniFile &globalIni, IniFile& localIni)
|
void SaveCodes(IniFile* local_ini, const std::vector<ARCode>& codes)
|
||||||
{
|
{
|
||||||
LoadCodes(globalIni, localIni, true);
|
std::vector<std::string> lines;
|
||||||
_arCodes = arCodes;
|
std::vector<std::string> enabled_lines;
|
||||||
|
for (const ActionReplay::ARCode& code : codes)
|
||||||
|
{
|
||||||
|
if (code.active)
|
||||||
|
enabled_lines.emplace_back("$" + code.name);
|
||||||
|
|
||||||
|
if (code.user_defined)
|
||||||
|
{
|
||||||
|
lines.emplace_back("$" + code.name);
|
||||||
|
for (const ActionReplay::AREntry& op : code.ops)
|
||||||
|
{
|
||||||
|
lines.emplace_back(StringFromFormat("%08X %08X", op.cmd_addr, op.value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
local_ini->SetLines("ActionReplay_Enabled", enabled_lines);
|
||||||
|
local_ini->SetLines("ActionReplay", lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void LogInfo(const char* format, ...)
|
static void LogInfo(const char* format, ...)
|
||||||
{
|
{
|
||||||
if (!b_RanOnce)
|
if (s_disable_logging)
|
||||||
{
|
return;
|
||||||
if (LogManager::GetMaxLevel() >= LogTypes::LINFO || logSelf)
|
bool use_internal_log = s_use_internal_log.load(std::memory_order_relaxed);
|
||||||
{
|
if (LogManager::GetMaxLevel() < LogTypes::LINFO && !use_internal_log)
|
||||||
|
return;
|
||||||
|
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, format);
|
va_start(args, format);
|
||||||
std::string text = StringFromFormatV(format, args);
|
std::string text = StringFromFormatV(format, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
INFO_LOG(ACTIONREPLAY, "%s", text.c_str());
|
INFO_LOG(ACTIONREPLAY, "%s", text.c_str());
|
||||||
|
|
||||||
if (logSelf)
|
if (use_internal_log)
|
||||||
{
|
{
|
||||||
text += '\n';
|
text += '\n';
|
||||||
arLog.push_back(text);
|
s_internal_log.emplace_back(std::move(text));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GetCodeListSize()
|
|
||||||
{
|
|
||||||
return arCodes.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
ARCode GetARCode(size_t index)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
arCodes[index].active = active;
|
|
||||||
UpdateActiveList();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateActiveList()
|
|
||||||
{
|
|
||||||
bool old_value = SConfig::GetInstance().bEnableCheats;
|
|
||||||
SConfig::GetInstance().bEnableCheats = false;
|
|
||||||
b_RanOnce = false;
|
|
||||||
activeCodes.clear();
|
|
||||||
for (auto& arCode : arCodes)
|
|
||||||
{
|
|
||||||
if (arCode.active)
|
|
||||||
activeCodes.push_back(arCode);
|
|
||||||
}
|
|
||||||
SConfig::GetInstance().bEnableCheats = old_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EnableSelfLogging(bool enable)
|
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()
|
bool IsSelfLogging()
|
||||||
{
|
{
|
||||||
return logSelf;
|
return s_use_internal_log.load(std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------
|
// ----------------------
|
||||||
|
@ -353,7 +371,7 @@ static bool Subtype_RamWriteAndFill(const ARAddr& addr, const u32 data)
|
||||||
LogInfo("Bad Size");
|
LogInfo("Bad Size");
|
||||||
PanicAlertT("Action Replay Error: Invalid size "
|
PanicAlertT("Action Replay Error: Invalid size "
|
||||||
"(%08x : address = %08x) in Ram Write And Fill (%s)",
|
"(%08x : address = %08x) in Ram Write And Fill (%s)",
|
||||||
addr.size, addr.gcaddr, current_code->name.c_str());
|
addr.size, addr.gcaddr, s_current_code->name.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,7 +431,7 @@ static bool Subtype_WriteToPointer(const ARAddr& addr, const u32 data)
|
||||||
LogInfo("Bad Size");
|
LogInfo("Bad Size");
|
||||||
PanicAlertT("Action Replay Error: Invalid size "
|
PanicAlertT("Action Replay Error: Invalid size "
|
||||||
"(%08x : address = %08x) in Write To Pointer (%s)",
|
"(%08x : address = %08x) in Write To Pointer (%s)",
|
||||||
addr.size, addr.gcaddr, current_code->name.c_str());
|
addr.size, addr.gcaddr, s_current_code->name.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -459,8 +477,10 @@ static bool Subtype_AddCode(const ARAddr& addr, const u32 data)
|
||||||
LogInfo("--------");
|
LogInfo("--------");
|
||||||
|
|
||||||
const u32 read = PowerPC::HostRead_U32(new_addr);
|
const u32 read = PowerPC::HostRead_U32(new_addr);
|
||||||
const float fread = *((float*)&read) + (float)data; // data contains an integer value
|
const float read_float = reinterpret_cast<const float&>(read);
|
||||||
const u32 newval = *((u32*)&fread);
|
// 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);
|
PowerPC::HostWrite_U32(newval, new_addr);
|
||||||
LogInfo("Old Value %08x", read);
|
LogInfo("Old Value %08x", read);
|
||||||
LogInfo("Increment %08x", data);
|
LogInfo("Increment %08x", data);
|
||||||
|
@ -473,7 +493,7 @@ static bool Subtype_AddCode(const ARAddr& addr, const u32 data)
|
||||||
LogInfo("Bad Size");
|
LogInfo("Bad Size");
|
||||||
PanicAlertT("Action Replay Error: Invalid size "
|
PanicAlertT("Action Replay Error: Invalid size "
|
||||||
"(%08x : address = %08x) in Add Code (%s)",
|
"(%08x : address = %08x) in Add Code (%s)",
|
||||||
addr.size, addr.gcaddr, current_code->name.c_str());
|
addr.size, addr.gcaddr, s_current_code->name.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -487,18 +507,20 @@ static bool Subtype_MasterCodeAndWriteToCCXXXXXX(const ARAddr& addr, const u32 d
|
||||||
// u8 mcode_count = (data & 0xFF00) >> 8;
|
// u8 mcode_count = (data & 0xFF00) >> 8;
|
||||||
// u8 mcode_number = data & 0xFF;
|
// u8 mcode_number = data & 0xFF;
|
||||||
PanicAlertT("Action Replay Error: Master Code and Write To CCXXXXXX not implemented (%s)\n"
|
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;
|
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 u32 new_addr = ARAddr(val_last).GCAddress();
|
||||||
const u8 size = ((ARAddr*)&val_last)->size;
|
const u8 size = ARAddr(val_last).size;
|
||||||
|
|
||||||
const s16 addr_incr = (s16)(data & 0xFFFF);
|
const s16 addr_incr = static_cast<s16>(data & 0xFFFF);
|
||||||
const s8 val_incr = (s8)(data >> 24);
|
const s8 val_incr = static_cast<s8>(data >> 24);
|
||||||
const u8 write_num = (data & 0xFF0000) >> 16;
|
const u8 write_num = static_cast<u8>((data & 0xFF0000) >> 16);
|
||||||
|
|
||||||
u32 val = addr;
|
u32 val = addr;
|
||||||
u32 curr_addr = new_addr;
|
u32 curr_addr = new_addr;
|
||||||
|
@ -559,7 +581,8 @@ static bool ZeroCode_FillAndSlide(const u32 val_last, const ARAddr& addr, const
|
||||||
|
|
||||||
default:
|
default:
|
||||||
LogInfo("Bad Size");
|
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 false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -606,7 +629,8 @@ static bool ZeroCode_MemoryCopy(const u32 val_last, const ARAddr& addr, const u3
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogInfo("Bad Value");
|
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 false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -642,9 +666,9 @@ static bool NormalCode(const ARAddr& addr, const u32 data)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
LogInfo("Bad Subtype");
|
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;
|
return false;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -656,43 +680,36 @@ static bool CompareValues(const u32 val1, const u32 val2, const int type)
|
||||||
{
|
{
|
||||||
case CONDTIONAL_EQUAL:
|
case CONDTIONAL_EQUAL:
|
||||||
LogInfo("Type 1: If Equal");
|
LogInfo("Type 1: If Equal");
|
||||||
return (val1 == val2);
|
return val1 == val2;
|
||||||
break;
|
|
||||||
|
|
||||||
case CONDTIONAL_NOT_EQUAL:
|
case CONDTIONAL_NOT_EQUAL:
|
||||||
LogInfo("Type 2: If Not Equal");
|
LogInfo("Type 2: If Not Equal");
|
||||||
return (val1 != val2);
|
return val1 != val2;
|
||||||
break;
|
|
||||||
|
|
||||||
case CONDTIONAL_LESS_THAN_SIGNED:
|
case CONDTIONAL_LESS_THAN_SIGNED:
|
||||||
LogInfo("Type 3: If Less Than (Signed)");
|
LogInfo("Type 3: If Less Than (Signed)");
|
||||||
return ((int)val1 < (int)val2);
|
return static_cast<s32>(val1) < static_cast<s32>(val2);
|
||||||
break;
|
|
||||||
|
|
||||||
case CONDTIONAL_GREATER_THAN_SIGNED:
|
case CONDTIONAL_GREATER_THAN_SIGNED:
|
||||||
LogInfo("Type 4: If Greater Than (Signed)");
|
LogInfo("Type 4: If Greater Than (Signed)");
|
||||||
return ((int)val1 >(int)val2);
|
return static_cast<s32>(val1) > static_cast<s32>(val2);
|
||||||
break;
|
|
||||||
|
|
||||||
case CONDTIONAL_LESS_THAN_UNSIGNED:
|
case CONDTIONAL_LESS_THAN_UNSIGNED:
|
||||||
LogInfo("Type 5: If Less Than (Unsigned)");
|
LogInfo("Type 5: If Less Than (Unsigned)");
|
||||||
return (val1 < val2);
|
return val1 < val2;
|
||||||
break;
|
|
||||||
|
|
||||||
case CONDTIONAL_GREATER_THAN_UNSIGNED:
|
case CONDTIONAL_GREATER_THAN_UNSIGNED:
|
||||||
LogInfo("Type 6: If Greater Than (Unsigned)");
|
LogInfo("Type 6: If Greater Than (Unsigned)");
|
||||||
return (val1 > val2);
|
return val1 > val2;
|
||||||
break;
|
|
||||||
|
|
||||||
case CONDTIONAL_AND:
|
case CONDTIONAL_AND:
|
||||||
LogInfo("Type 7: If And");
|
LogInfo("Type 7: If And");
|
||||||
return !!(val1 & val2); // bitwise AND
|
return !!(val1 & val2); // bitwise AND
|
||||||
break;
|
|
||||||
|
|
||||||
default: LogInfo("Unknown Compare type");
|
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;
|
return false;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -708,11 +725,11 @@ static bool ConditionalCode(const ARAddr& addr, const u32 data, int* const pSkip
|
||||||
switch (addr.size)
|
switch (addr.size)
|
||||||
{
|
{
|
||||||
case DATATYPE_8BIT:
|
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;
|
break;
|
||||||
|
|
||||||
case DATATYPE_16BIT:
|
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;
|
break;
|
||||||
|
|
||||||
case DATATYPE_32BIT_FLOAT:
|
case DATATYPE_32BIT_FLOAT:
|
||||||
|
@ -722,9 +739,9 @@ static bool ConditionalCode(const ARAddr& addr, const u32 data, int* const pSkip
|
||||||
|
|
||||||
default:
|
default:
|
||||||
LogInfo("Bad Size");
|
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;
|
return false;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the comparison failed we need to skip some lines
|
// if the comparison failed we need to skip some lines
|
||||||
|
@ -741,58 +758,41 @@ static bool ConditionalCode(const ARAddr& addr, const u32 data, int* const pSkip
|
||||||
// Skip lines until a "00000000 40000000" line is reached
|
// Skip lines until a "00000000 40000000" line is reached
|
||||||
case CONDTIONAL_ALL_LINES:
|
case CONDTIONAL_ALL_LINES:
|
||||||
case CONDTIONAL_ALL_LINES_UNTIL:
|
case CONDTIONAL_ALL_LINES_UNTIL:
|
||||||
*pSkipCount = -(int) addr.subtype;
|
*pSkipCount = -static_cast<int>(addr.subtype);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
LogInfo("Bad Subtype");
|
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;
|
return false;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: Lock needed to give mutual exclusion to s_current_code and LogInfo
|
||||||
void RunAllActive()
|
static bool RunCodeLocked(const ARCode& arcode)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
// The mechanism is different than what the real AR uses, so there may be compatibility problems.
|
// The mechanism is different than what the real AR uses, so there may be compatibility problems.
|
||||||
|
|
||||||
bool doFillNSlide = false;
|
bool do_fill_and_slide = false;
|
||||||
bool doMemoryCopy = false;
|
bool do_memory_copy = false;
|
||||||
|
|
||||||
// used for conditional codes
|
// used for conditional codes
|
||||||
int skip_count = 0;
|
int skip_count = 0;
|
||||||
|
|
||||||
u32 val_last = 0;
|
u32 val_last = 0;
|
||||||
|
|
||||||
current_code = &arcode;
|
s_current_code = &arcode;
|
||||||
|
|
||||||
LogInfo("Code Name: %s", arcode.name.c_str());
|
LogInfo("Code Name: %s", arcode.name.c_str());
|
||||||
LogInfo("Number of codes: %zu", arcode.ops.size());
|
LogInfo("Number of codes: %zu", arcode.ops.size());
|
||||||
|
|
||||||
for (const AREntry& entry : arcode.ops)
|
for (const AREntry& entry : arcode.ops)
|
||||||
{
|
{
|
||||||
const ARAddr& addr = *(ARAddr*)&entry.cmd_addr;
|
const ARAddr addr(entry.cmd_addr);
|
||||||
const u32 data = entry.value;
|
const u32 data = entry.value;
|
||||||
|
|
||||||
// after a conditional code, skip lines if needed
|
// after a conditional code, skip lines if needed
|
||||||
|
@ -824,9 +824,9 @@ bool RunCode(const ARCode &arcode)
|
||||||
//LogInfo("Command: %08x", cmd);
|
//LogInfo("Command: %08x", cmd);
|
||||||
|
|
||||||
// Do Fill & Slide
|
// Do Fill & Slide
|
||||||
if (doFillNSlide)
|
if (do_fill_and_slide)
|
||||||
{
|
{
|
||||||
doFillNSlide = false;
|
do_fill_and_slide = false;
|
||||||
LogInfo("Doing Fill And Slide");
|
LogInfo("Doing Fill And Slide");
|
||||||
if (false == ZeroCode_FillAndSlide(val_last, addr, data))
|
if (false == ZeroCode_FillAndSlide(val_last, addr, data))
|
||||||
return false;
|
return false;
|
||||||
|
@ -834,9 +834,9 @@ bool RunCode(const ARCode &arcode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Memory Copy
|
// Memory Copy
|
||||||
if (doMemoryCopy)
|
if (do_memory_copy)
|
||||||
{
|
{
|
||||||
doMemoryCopy = false;
|
do_memory_copy = false;
|
||||||
LogInfo("Doing Memory Copy");
|
LogInfo("Doing Memory Copy");
|
||||||
if (false == ZeroCode_MemoryCopy(val_last, addr, data))
|
if (false == ZeroCode_MemoryCopy(val_last, addr, data))
|
||||||
return false;
|
return false;
|
||||||
|
@ -859,7 +859,7 @@ bool RunCode(const ARCode &arcode)
|
||||||
// Zero codes
|
// Zero codes
|
||||||
if (0x0 == addr) // Check if the code is a zero code
|
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);
|
LogInfo("Doing Zero Code %08x", zcode);
|
||||||
|
|
||||||
|
@ -868,7 +868,6 @@ bool RunCode(const ARCode &arcode)
|
||||||
case ZCODE_END: // END OF CODES
|
case ZCODE_END: // END OF CODES
|
||||||
LogInfo("ZCode: End Of Codes");
|
LogInfo("ZCode: End Of Codes");
|
||||||
return true;
|
return true;
|
||||||
break;
|
|
||||||
|
|
||||||
// TODO: the "00000000 40000000"(end if) codes fall into this case, I don't think that is correct
|
// 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
|
case ZCODE_NORM: // Normal execution of codes
|
||||||
|
@ -881,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)");
|
LogInfo("ZCode: Executes all codes in the same row, Set register 1BB4 to 1 (zcode not supported)");
|
||||||
PanicAlertT("Zero 3 code not supported");
|
PanicAlertT("Zero 3 code not supported");
|
||||||
return false;
|
return false;
|
||||||
break;
|
|
||||||
|
|
||||||
case ZCODE_04: // Fill & Slide or Memory Copy
|
case ZCODE_04: // Fill & Slide or Memory Copy
|
||||||
if (0x3 == ((data >> 25) & 0x03))
|
if (0x3 == ((data >> 25) & 0x03))
|
||||||
{
|
{
|
||||||
LogInfo("ZCode: Memory Copy");
|
LogInfo("ZCode: Memory Copy");
|
||||||
doMemoryCopy = true;
|
do_memory_copy = true;
|
||||||
val_last = data;
|
val_last = data;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogInfo("ZCode: Fill And Slide");
|
LogInfo("ZCode: Fill And Slide");
|
||||||
doFillNSlide = true;
|
do_fill_and_slide = true;
|
||||||
val_last = data;
|
val_last = data;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -902,7 +900,6 @@ bool RunCode(const ARCode &arcode)
|
||||||
LogInfo("ZCode: Unknown");
|
LogInfo("ZCode: Unknown");
|
||||||
PanicAlertT("Zero code unknown to Dolphin: %08x", zcode);
|
PanicAlertT("Zero code unknown to Dolphin: %08x", zcode);
|
||||||
return false;
|
return false;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// done handling zero codes
|
// done handling zero codes
|
||||||
|
@ -928,9 +925,25 @@ bool RunCode(const ARCode &arcode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
b_RanOnce = true;
|
|
||||||
|
|
||||||
return 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
|
} // namespace ActionReplay
|
||||||
|
|
|
@ -30,14 +30,16 @@ struct ARCode
|
||||||
};
|
};
|
||||||
|
|
||||||
void RunAllActive();
|
void RunAllActive();
|
||||||
bool RunCode(const ARCode &arcode);
|
|
||||||
void LoadCodes(const IniFile &globalini, const IniFile &localIni, bool forceLoad);
|
void ApplyCodes(const std::vector<ARCode>& codes);
|
||||||
void LoadCodes(std::vector<ARCode> &_arCodes, IniFile &globalini, IniFile &localIni);
|
void AddCode(ARCode new_code);
|
||||||
size_t GetCodeListSize();
|
void LoadAndApplyCodes(const IniFile& global_ini, const IniFile& local_ini);
|
||||||
ARCode GetARCode(size_t index);
|
|
||||||
void SetARCode_IsActive(bool active, size_t index);
|
std::vector<ARCode> LoadCodes(const IniFile& global_ini, const IniFile& local_ini);
|
||||||
void UpdateActiveList();
|
void SaveCodes(IniFile* local_ini, const std::vector<ARCode>& codes);
|
||||||
|
|
||||||
void EnableSelfLogging(bool enable);
|
void EnableSelfLogging(bool enable);
|
||||||
const std::vector<std::string> &GetSelfLog();
|
std::vector<std::string> GetSelfLog();
|
||||||
|
void ClearSelfLog();
|
||||||
bool IsSelfLogging();
|
bool IsSelfLogging();
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -166,7 +166,7 @@ void LoadPatches()
|
||||||
IniFile localIni = SConfig::GetInstance().LoadLocalGameIni();
|
IniFile localIni = SConfig::GetInstance().LoadLocalGameIni();
|
||||||
|
|
||||||
LoadPatchSection("OnFrame", onFrame, globalIni, localIni);
|
LoadPatchSection("OnFrame", onFrame, globalIni, localIni);
|
||||||
ActionReplay::LoadCodes(globalIni, localIni, false);
|
ActionReplay::LoadAndApplyCodes(globalIni, localIni);
|
||||||
|
|
||||||
// lil silly
|
// lil silly
|
||||||
std::vector<Gecko::GeckoCode> gcodes;
|
std::vector<Gecko::GeckoCode> gcodes;
|
||||||
|
|
|
@ -160,6 +160,7 @@ void CARCodeAddEdit::SaveCheatData(wxCommandEvent& WXUNUSED(event))
|
||||||
newCheat.name = WxStrToStr(EditCheatName->GetValue());
|
newCheat.name = WxStrToStr(EditCheatName->GetValue());
|
||||||
newCheat.ops = decryptedLines;
|
newCheat.ops = decryptedLines;
|
||||||
newCheat.active = true;
|
newCheat.active = true;
|
||||||
|
newCheat.user_defined = true;
|
||||||
|
|
||||||
arCodes->push_back(newCheat);
|
arCodes->push_back(newCheat);
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,7 +174,6 @@ void CheatSearchTab::OnCreateARCodeClicked(wxCommandEvent&)
|
||||||
const u32 address = m_search_results[idx].address | ((m_search_type_size & ~1) << 24);
|
const u32 address = m_search_results[idx].address | ((m_search_type_size & ~1) << 24);
|
||||||
|
|
||||||
CreateCodeDialog arcode_dlg(this, address);
|
CreateCodeDialog arcode_dlg(this, address);
|
||||||
arcode_dlg.SetExtraStyle(arcode_dlg.GetExtraStyle() & ~wxWS_EX_BLOCK_EVENTS);
|
|
||||||
arcode_dlg.ShowModal();
|
arcode_dlg.ShowModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <wx/app.h>
|
||||||
#include <wx/button.h>
|
#include <wx/button.h>
|
||||||
#include <wx/checkbox.h>
|
#include <wx/checkbox.h>
|
||||||
#include <wx/checklst.h>
|
#include <wx/checklst.h>
|
||||||
|
@ -31,6 +33,7 @@
|
||||||
#include "Core/GeckoCode.h"
|
#include "Core/GeckoCode.h"
|
||||||
#include "Core/GeckoCodeConfig.h"
|
#include "Core/GeckoCodeConfig.h"
|
||||||
#include "DolphinWX/Frame.h"
|
#include "DolphinWX/Frame.h"
|
||||||
|
#include "DolphinWX/Globals.h"
|
||||||
#include "DolphinWX/Main.h"
|
#include "DolphinWX/Main.h"
|
||||||
#include "DolphinWX/WxUtils.h"
|
#include "DolphinWX/WxUtils.h"
|
||||||
#include "DolphinWX/Cheats/CheatSearchTab.h"
|
#include "DolphinWX/Cheats/CheatSearchTab.h"
|
||||||
|
@ -38,6 +41,13 @@
|
||||||
#include "DolphinWX/Cheats/CreateCodeDialog.h"
|
#include "DolphinWX/Cheats/CreateCodeDialog.h"
|
||||||
#include "DolphinWX/Cheats/GeckoCodeDiag.h"
|
#include "DolphinWX/Cheats/GeckoCodeDiag.h"
|
||||||
|
|
||||||
|
wxDEFINE_EVENT(DOLPHIN_EVT_ADD_NEW_ACTION_REPLAY_CODE, wxCommandEvent);
|
||||||
|
|
||||||
|
struct wxCheatsWindow::CodeData : public wxClientData
|
||||||
|
{
|
||||||
|
ActionReplay::ARCode code;
|
||||||
|
};
|
||||||
|
|
||||||
wxCheatsWindow::wxCheatsWindow(wxWindow* const parent)
|
wxCheatsWindow::wxCheatsWindow(wxWindow* const parent)
|
||||||
: wxDialog(parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX | wxMINIMIZE_BOX | wxDIALOG_NO_PARENT)
|
: wxDialog(parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX | wxMINIMIZE_BOX | wxDIALOG_NO_PARENT)
|
||||||
{
|
{
|
||||||
|
@ -46,6 +56,7 @@ wxCheatsWindow::wxCheatsWindow(wxWindow* const parent)
|
||||||
|
|
||||||
// load codes
|
// load codes
|
||||||
UpdateGUI();
|
UpdateGUI();
|
||||||
|
wxTheApp->Bind(DOLPHIN_EVT_LOCAL_INI_CHANGED, &wxCheatsWindow::OnEvent_CheatsList_Update, this);
|
||||||
|
|
||||||
SetSize(wxSize(-1, 600));
|
SetSize(wxSize(-1, 600));
|
||||||
Center();
|
Center();
|
||||||
|
@ -66,18 +77,17 @@ void wxCheatsWindow::Init_ChildControls()
|
||||||
// Cheats List Tab
|
// Cheats List Tab
|
||||||
m_tab_cheats = new wxPanel(m_notebook_main, wxID_ANY);
|
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_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_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_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);
|
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);
|
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_label_num_codes, 0, wxALL, 5);
|
||||||
sGroupBoxInfo->Add(m_listbox_codes_list, 1, wxALL, 5);
|
sGroupBoxInfo->Add(m_listbox_codes_list, 1, wxALL, 5);
|
||||||
|
|
||||||
|
@ -95,6 +105,8 @@ void wxCheatsWindow::Init_ChildControls()
|
||||||
|
|
||||||
wxButton* const button_updatelog = new wxButton(m_tab_log, wxID_ANY, _("Update"));
|
wxButton* const button_updatelog = new wxButton(m_tab_log, wxID_ANY, _("Update"));
|
||||||
button_updatelog->Bind(wxEVT_BUTTON, &wxCheatsWindow::OnEvent_ButtonUpdateLog_Press, this);
|
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 = new wxCheckBox(m_tab_log, wxID_ANY, _("Enable AR Logging"));
|
||||||
m_checkbox_log_ar->Bind(wxEVT_CHECKBOX, &wxCheatsWindow::OnEvent_CheckBoxEnableLogging_StateChange, this);
|
m_checkbox_log_ar->Bind(wxEVT_CHECKBOX, &wxCheatsWindow::OnEvent_CheckBoxEnableLogging_StateChange, this);
|
||||||
|
@ -105,6 +117,7 @@ void wxCheatsWindow::Init_ChildControls()
|
||||||
wxBoxSizer *HStrip1 = new wxBoxSizer(wxHORIZONTAL);
|
wxBoxSizer *HStrip1 = new wxBoxSizer(wxHORIZONTAL);
|
||||||
HStrip1->Add(m_checkbox_log_ar, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
|
HStrip1->Add(m_checkbox_log_ar, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
|
||||||
HStrip1->Add(button_updatelog, 0, wxALL, 5);
|
HStrip1->Add(button_updatelog, 0, wxALL, 5);
|
||||||
|
HStrip1->Add(button_clearlog, 0, wxALL, 5);
|
||||||
|
|
||||||
wxBoxSizer *sTabLog = new wxBoxSizer(wxVERTICAL);
|
wxBoxSizer *sTabLog = new wxBoxSizer(wxVERTICAL);
|
||||||
sTabLog->Add(HStrip1, 0, wxALL, 5);
|
sTabLog->Add(HStrip1, 0, wxALL, 5);
|
||||||
|
@ -119,19 +132,15 @@ void wxCheatsWindow::Init_ChildControls()
|
||||||
m_notebook_main->AddPage(tab_cheat_search, _("Cheat Search"));
|
m_notebook_main->AddPage(tab_cheat_search, _("Cheat Search"));
|
||||||
m_notebook_main->AddPage(m_tab_log, _("Logging"));
|
m_notebook_main->AddPage(m_tab_log, _("Logging"));
|
||||||
|
|
||||||
// Button Strip
|
Bind(wxEVT_BUTTON, &wxCheatsWindow::OnEvent_ApplyChanges_Press, this, wxID_APPLY);
|
||||||
m_button_apply = new wxButton(this, wxID_APPLY, _("Apply"));
|
Bind(wxEVT_BUTTON, &wxCheatsWindow::OnEvent_ButtonClose_Press, this, wxID_CANCEL);
|
||||||
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_CLOSE_WINDOW, &wxCheatsWindow::OnEvent_Close, this);
|
Bind(wxEVT_CLOSE_WINDOW, &wxCheatsWindow::OnEvent_Close, this);
|
||||||
Bind(UPDATE_CHEAT_LIST_EVENT, &wxCheatsWindow::OnEvent_CheatsList_Update, this);
|
Bind(DOLPHIN_EVT_ADD_NEW_ACTION_REPLAY_CODE, &wxCheatsWindow::OnNewARCodeCreated, this);
|
||||||
|
|
||||||
wxStdDialogButtonSizer* const sButtons = new wxStdDialogButtonSizer();
|
wxStdDialogButtonSizer* const sButtons = CreateStdDialogButtonSizer(wxAPPLY | wxCANCEL);
|
||||||
sButtons->AddButton(m_button_apply);
|
m_button_apply = sButtons->GetApplyButton();
|
||||||
sButtons->AddButton(button_cancel);
|
SetEscapeId(wxID_CANCEL);
|
||||||
sButtons->Realize();
|
SetAffirmativeId(wxID_CANCEL);
|
||||||
|
|
||||||
wxBoxSizer* const sMain = new wxBoxSizer(wxVERTICAL);
|
wxBoxSizer* const sMain = new wxBoxSizer(wxVERTICAL);
|
||||||
sMain->Add(m_notebook_main, 1, wxEXPAND | wxALL, 5);
|
sMain->Add(m_notebook_main, 1, wxEXPAND | wxALL, 5);
|
||||||
|
@ -156,7 +165,9 @@ void wxCheatsWindow::UpdateGUI()
|
||||||
const SConfig& parameters = SConfig::GetInstance();
|
const SConfig& parameters = SConfig::GetInstance();
|
||||||
m_gameini_default = parameters.LoadDefaultGameIni();
|
m_gameini_default = parameters.LoadDefaultGameIni();
|
||||||
m_gameini_local = parameters.LoadLocalGameIni();
|
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_ARCodes();
|
||||||
Load_GeckoCodes();
|
Load_GeckoCodes();
|
||||||
|
|
||||||
|
@ -167,32 +178,28 @@ void wxCheatsWindow::UpdateGUI()
|
||||||
|
|
||||||
// write the ISO name in the title
|
// write the ISO name in the title
|
||||||
if (Core::IsRunning())
|
if (Core::IsRunning())
|
||||||
SetTitle(title + ": " + parameters.GetUniqueID() + " - " + parameters.m_strName);
|
SetTitle(title + StrToWxStr(": " + m_game_id + " - " + parameters.m_strName));
|
||||||
else
|
else
|
||||||
SetTitle(title);
|
SetTitle(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxCheatsWindow::Load_ARCodes()
|
void wxCheatsWindow::Load_ARCodes()
|
||||||
{
|
{
|
||||||
using namespace ActionReplay;
|
|
||||||
|
|
||||||
m_checklistbox_cheats_list->Clear();
|
m_checklistbox_cheats_list->Clear();
|
||||||
|
|
||||||
if (!Core::IsRunning())
|
if (!Core::IsRunning())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_index_list.clear();
|
m_checklistbox_cheats_list->Freeze();
|
||||||
size_t size = GetCodeListSize();
|
for (auto& code : ActionReplay::LoadCodes(m_gameini_default, m_gameini_local))
|
||||||
for (size_t i = 0; i < size; i++)
|
|
||||||
{
|
{
|
||||||
ARCode code = GetARCode(i);
|
CodeData* cd = new CodeData();
|
||||||
ARCodeIndex ind;
|
cd->code = std::move(code);
|
||||||
u32 index = m_checklistbox_cheats_list->Append(StrToWxStr(code.name));
|
int index = m_checklistbox_cheats_list->Append(wxCheckListBox::EscapeMnemonics(StrToWxStr(cd->code.name)),
|
||||||
m_checklistbox_cheats_list->Check(index, code.active);
|
cd);
|
||||||
ind.index = i;
|
m_checklistbox_cheats_list->Check(index, cd->code.active);
|
||||||
ind.uiIndex = index;
|
|
||||||
m_index_list.push_back(ind);
|
|
||||||
}
|
}
|
||||||
|
m_checklistbox_cheats_list->Thaw();
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxCheatsWindow::Load_GeckoCodes()
|
void wxCheatsWindow::Load_GeckoCodes()
|
||||||
|
@ -200,64 +207,81 @@ void wxCheatsWindow::Load_GeckoCodes()
|
||||||
m_geckocode_panel->LoadCodes(m_gameini_default, m_gameini_local, SConfig::GetInstance().GetUniqueID(), true);
|
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();
|
CodeData* cd = new CodeData();
|
||||||
for (size_t i = 0; i < m_index_list.size(); i++)
|
cd->code = *code;
|
||||||
{
|
int idx = m_checklistbox_cheats_list->Append(wxCheckListBox::EscapeMnemonics(StrToWxStr(code->name)),
|
||||||
if ((int)m_index_list[i].uiIndex == index)
|
cd);
|
||||||
{
|
m_checklistbox_cheats_list->Check(idx, code->active);
|
||||||
ARCode code = GetARCode(i);
|
}
|
||||||
m_label_code_name->SetLabel(_("Name: ") + StrToWxStr(code.name));
|
|
||||||
|
|
||||||
std::string numcodes = StringFromFormat("Number of Codes: %zu", code.ops.size());
|
void wxCheatsWindow::OnEvent_CheatsList_ItemSelected(wxCommandEvent& event)
|
||||||
m_label_num_codes->SetLabel(StrToWxStr(numcodes));
|
{
|
||||||
|
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();
|
m_listbox_codes_list->Clear();
|
||||||
|
for (const ActionReplay::AREntry& entry : cd->code.ops)
|
||||||
for (const AREntry& entry : code.ops)
|
|
||||||
{
|
{
|
||||||
std::string ops = StringFromFormat("%08x %08x", entry.cmd_addr, entry.value);
|
m_listbox_codes_list->Append(wxString::Format("%08x %08x", entry.cmd_addr, entry.value));
|
||||||
m_listbox_codes_list->Append(StrToWxStr(ops));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
m_listbox_codes_list->Thaw();
|
||||||
|
|
||||||
|
m_tab_cheats->Layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxCheatsWindow::OnEvent_CheatsList_ItemToggled(wxCommandEvent& WXUNUSED(event))
|
void wxCheatsWindow::OnEvent_CheatsList_Update(wxCommandEvent& ev)
|
||||||
{
|
{
|
||||||
int index = m_checklistbox_cheats_list->GetSelection();
|
ev.Skip();
|
||||||
for (const ARCodeIndex& code_index : m_index_list)
|
if (WxStrToStr(ev.GetString()) != m_game_id)
|
||||||
|
return;
|
||||||
|
if (m_ignore_ini_callback)
|
||||||
{
|
{
|
||||||
if ((int)code_index.uiIndex == index)
|
m_ignore_ini_callback = false;
|
||||||
{
|
return;
|
||||||
ActionReplay::SetARCode_IsActive(m_checklistbox_cheats_list->IsChecked(index), code_index.index);
|
|
||||||
}
|
}
|
||||||
}
|
UpdateGUI();
|
||||||
}
|
|
||||||
|
|
||||||
void wxCheatsWindow::OnEvent_CheatsList_Update(wxCommandEvent& event)
|
|
||||||
{
|
|
||||||
Load_ARCodes();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxCheatsWindow::OnEvent_ApplyChanges_Press(wxCommandEvent& ev)
|
void wxCheatsWindow::OnEvent_ApplyChanges_Press(wxCommandEvent& ev)
|
||||||
{
|
{
|
||||||
// Apply AR Code changes
|
// Convert embedded metadata back into ARCode vector and update active states
|
||||||
for (const ARCodeIndex& code_index : m_index_list)
|
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)
|
||||||
{
|
{
|
||||||
ActionReplay::SetARCode_IsActive(m_checklistbox_cheats_list->IsChecked(code_index.uiIndex), code_index.index);
|
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
|
// Apply Gecko Code changes
|
||||||
Gecko::SetActiveCodes(m_geckocode_panel->GetCodes());
|
Gecko::SetActiveCodes(m_geckocode_panel->GetCodes());
|
||||||
|
|
||||||
// Save gameini, with changed gecko codes
|
// Save gameini, with changed codes
|
||||||
if (m_gameini_local_path.size())
|
if (m_gameini_local_path.size())
|
||||||
{
|
{
|
||||||
|
ActionReplay::SaveCodes(&m_gameini_local, code_vec);
|
||||||
Gecko::SaveCodes(m_gameini_local, m_geckocode_panel->GetCodes());
|
Gecko::SaveCodes(m_gameini_local, m_geckocode_panel->GetCodes());
|
||||||
m_gameini_local.Save(m_gameini_local_path);
|
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();
|
ev.Skip();
|
||||||
|
@ -265,11 +289,21 @@ void wxCheatsWindow::OnEvent_ApplyChanges_Press(wxCommandEvent& ev)
|
||||||
|
|
||||||
void wxCheatsWindow::OnEvent_ButtonUpdateLog_Press(wxCommandEvent& WXUNUSED(event))
|
void wxCheatsWindow::OnEvent_ButtonUpdateLog_Press(wxCommandEvent& WXUNUSED(event))
|
||||||
{
|
{
|
||||||
|
wxBeginBusyCursor();
|
||||||
|
m_textctrl_log->Freeze();
|
||||||
m_textctrl_log->Clear();
|
m_textctrl_log->Clear();
|
||||||
for (const std::string& text : ActionReplay::GetSelfLog())
|
for (const std::string& text : ActionReplay::GetSelfLog())
|
||||||
{
|
{
|
||||||
m_textctrl_log->AppendText(StrToWxStr(text));
|
m_textctrl_log->AppendText(StrToWxStr(text));
|
||||||
}
|
}
|
||||||
|
m_textctrl_log->Thaw();
|
||||||
|
wxEndBusyCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxCheatsWindow::OnClearActionReplayLog(wxCommandEvent& event)
|
||||||
|
{
|
||||||
|
ActionReplay::ClearSelfLog();
|
||||||
|
OnEvent_ButtonUpdateLog_Press(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxCheatsWindow::OnEvent_CheckBoxEnableLogging_StateChange(wxCommandEvent& WXUNUSED(event))
|
void wxCheatsWindow::OnEvent_CheckBoxEnableLogging_StateChange(wxCommandEvent& WXUNUSED(event))
|
||||||
|
|
|
@ -29,6 +29,8 @@ namespace Gecko
|
||||||
class CodeConfigPanel;
|
class CodeConfigPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxDECLARE_EVENT(DOLPHIN_EVT_ADD_NEW_ACTION_REPLAY_CODE, wxCommandEvent);
|
||||||
|
|
||||||
class wxCheatsWindow final : public wxDialog
|
class wxCheatsWindow final : public wxDialog
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -37,11 +39,7 @@ public:
|
||||||
void UpdateGUI();
|
void UpdateGUI();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ARCodeIndex
|
struct CodeData;
|
||||||
{
|
|
||||||
u32 uiIndex;
|
|
||||||
size_t index;
|
|
||||||
};
|
|
||||||
|
|
||||||
// --- GUI Controls ---
|
// --- GUI Controls ---
|
||||||
wxButton* m_button_apply;
|
wxButton* m_button_apply;
|
||||||
|
@ -63,14 +61,14 @@ private:
|
||||||
|
|
||||||
wxStaticBox* m_groupbox_info;
|
wxStaticBox* m_groupbox_info;
|
||||||
|
|
||||||
wxArrayString m_cheat_string_list;
|
|
||||||
|
|
||||||
std::vector<ARCodeIndex> m_index_list;
|
|
||||||
|
|
||||||
Gecko::CodeConfigPanel* m_geckocode_panel;
|
Gecko::CodeConfigPanel* m_geckocode_panel;
|
||||||
IniFile m_gameini_default;
|
IniFile m_gameini_default;
|
||||||
IniFile m_gameini_local;
|
IniFile m_gameini_local;
|
||||||
std::string m_gameini_local_path;
|
std::string m_gameini_local_path;
|
||||||
|
std::string m_game_id;
|
||||||
|
u32 m_game_revision;
|
||||||
|
|
||||||
|
bool m_ignore_ini_callback = false;
|
||||||
|
|
||||||
void Init_ChildControls();
|
void Init_ChildControls();
|
||||||
|
|
||||||
|
@ -78,6 +76,8 @@ private:
|
||||||
void Load_GeckoCodes();
|
void Load_GeckoCodes();
|
||||||
|
|
||||||
// --- Wx Events Handlers ---
|
// --- Wx Events Handlers ---
|
||||||
|
// Cheat Search
|
||||||
|
void OnNewARCodeCreated(wxCommandEvent& ev);
|
||||||
|
|
||||||
// Close Button
|
// Close Button
|
||||||
void OnEvent_ButtonClose_Press(wxCommandEvent& event);
|
void OnEvent_ButtonClose_Press(wxCommandEvent& event);
|
||||||
|
@ -85,7 +85,6 @@ private:
|
||||||
|
|
||||||
// Cheats List
|
// Cheats List
|
||||||
void OnEvent_CheatsList_ItemSelected(wxCommandEvent& event);
|
void OnEvent_CheatsList_ItemSelected(wxCommandEvent& event);
|
||||||
void OnEvent_CheatsList_ItemToggled(wxCommandEvent& event);
|
|
||||||
void OnEvent_CheatsList_Update(wxCommandEvent& event);
|
void OnEvent_CheatsList_Update(wxCommandEvent& event);
|
||||||
|
|
||||||
// Apply Changes Button
|
// Apply Changes Button
|
||||||
|
@ -93,6 +92,7 @@ private:
|
||||||
|
|
||||||
// Update Log Button
|
// Update Log Button
|
||||||
void OnEvent_ButtonUpdateLog_Press(wxCommandEvent& event);
|
void OnEvent_ButtonUpdateLog_Press(wxCommandEvent& event);
|
||||||
|
void OnClearActionReplayLog(wxCommandEvent& event);
|
||||||
|
|
||||||
// Enable Logging Checkbox
|
// Enable Logging Checkbox
|
||||||
void OnEvent_CheckBoxEnableLogging_StateChange(wxCommandEvent& event);
|
void OnEvent_CheckBoxEnableLogging_StateChange(wxCommandEvent& event);
|
||||||
|
|
|
@ -12,11 +12,9 @@
|
||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
#include "DolphinWX/ISOProperties.h"
|
#include "DolphinWX/ISOProperties.h"
|
||||||
#include "DolphinWX/WxUtils.h"
|
#include "DolphinWX/WxUtils.h"
|
||||||
|
#include "DolphinWX/Cheats/CheatsWindow.h"
|
||||||
#include "DolphinWX/Cheats/CreateCodeDialog.h"
|
#include "DolphinWX/Cheats/CreateCodeDialog.h"
|
||||||
|
|
||||||
// Fired when an ActionReplay code is created.
|
|
||||||
wxDEFINE_EVENT(UPDATE_CHEAT_LIST_EVENT, wxCommandEvent);
|
|
||||||
|
|
||||||
CreateCodeDialog::CreateCodeDialog(wxWindow* const parent, const u32 address)
|
CreateCodeDialog::CreateCodeDialog(wxWindow* const parent, const u32 address)
|
||||||
: wxDialog(parent, wxID_ANY, _("Create AR Code"))
|
: wxDialog(parent, wxID_ANY, _("Create AR Code"))
|
||||||
, m_code_address(address)
|
, m_code_address(address)
|
||||||
|
@ -73,30 +71,16 @@ void CreateCodeDialog::PressOK(wxCommandEvent& ev)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//wxString full_code = textctrl_code->GetValue();
|
|
||||||
//full_code += ' ';
|
|
||||||
//full_code += wxString::Format("0x%08x", code_value);
|
|
||||||
|
|
||||||
// create the new code
|
// create the new code
|
||||||
ActionReplay::ARCode new_cheat;
|
ActionReplay::ARCode new_cheat;
|
||||||
new_cheat.active = false;
|
new_cheat.active = false;
|
||||||
|
new_cheat.user_defined = true;
|
||||||
new_cheat.name = WxStrToStr(code_name);
|
new_cheat.name = WxStrToStr(code_name);
|
||||||
const ActionReplay::AREntry new_entry(m_code_address, code_value);
|
new_cheat.ops.emplace_back(ActionReplay::AREntry(m_code_address, code_value));
|
||||||
new_cheat.ops.push_back(new_entry);
|
|
||||||
|
|
||||||
// pretty hacky - add the code to the gameini
|
wxCommandEvent add_event(DOLPHIN_EVT_ADD_NEW_ACTION_REPLAY_CODE, GetId());
|
||||||
{
|
add_event.SetClientData(&new_cheat);
|
||||||
CISOProperties isoprops(GameListItem(SConfig::GetInstance().m_LastFilename, std::unordered_map<std::string, std::string>()), this);
|
GetParent()->GetEventHandler()->ProcessEvent(add_event);
|
||||||
// add the code to the isoproperties arcode list
|
|
||||||
isoprops.AddARCode(new_cheat);
|
|
||||||
// save the gameini
|
|
||||||
isoprops.SaveGameConfig();
|
|
||||||
isoprops.ActionReplayList_Load(); // loads the new arcodes
|
|
||||||
//ActionReplay::UpdateActiveList();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Propagate back to the parent frame to update the cheat list.
|
|
||||||
GetEventHandler()->AddPendingEvent(wxCommandEvent(UPDATE_CHEAT_LIST_EVENT));
|
|
||||||
|
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,6 @@
|
||||||
class wxCheckBox;
|
class wxCheckBox;
|
||||||
class wxTextCtrl;
|
class wxTextCtrl;
|
||||||
|
|
||||||
wxDECLARE_EVENT(UPDATE_CHEAT_LIST_EVENT, wxCommandEvent);
|
|
||||||
|
|
||||||
class CreateCodeDialog final : public wxDialog
|
class CreateCodeDialog final : public wxDialog
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -223,6 +223,7 @@ bool CRenderFrame::ShowFullScreen(bool show, long style)
|
||||||
// help button.
|
// help button.
|
||||||
|
|
||||||
wxDEFINE_EVENT(wxEVT_HOST_COMMAND, wxCommandEvent);
|
wxDEFINE_EVENT(wxEVT_HOST_COMMAND, wxCommandEvent);
|
||||||
|
wxDEFINE_EVENT(DOLPHIN_EVT_LOCAL_INI_CHANGED, wxCommandEvent);
|
||||||
|
|
||||||
BEGIN_EVENT_TABLE(CFrame, CRenderFrame)
|
BEGIN_EVENT_TABLE(CFrame, CRenderFrame)
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <wx/app.h>
|
||||||
#include <wx/bitmap.h>
|
#include <wx/bitmap.h>
|
||||||
#include <wx/buffer.h>
|
#include <wx/buffer.h>
|
||||||
#include <wx/colour.h>
|
#include <wx/colour.h>
|
||||||
|
@ -167,51 +168,50 @@ CGameListCtrl::CGameListCtrl(wxWindow* parent, const wxWindowID id, const
|
||||||
Bind(wxEVT_MENU, &CGameListCtrl::OnMultiDecompressISO, this, IDM_MULTI_DECOMPRESS_ISO);
|
Bind(wxEVT_MENU, &CGameListCtrl::OnMultiDecompressISO, this, IDM_MULTI_DECOMPRESS_ISO);
|
||||||
Bind(wxEVT_MENU, &CGameListCtrl::OnDeleteISO, this, IDM_DELETE_ISO);
|
Bind(wxEVT_MENU, &CGameListCtrl::OnDeleteISO, this, IDM_DELETE_ISO);
|
||||||
Bind(wxEVT_MENU, &CGameListCtrl::OnChangeDisc, this, IDM_LIST_CHANGE_DISC);
|
Bind(wxEVT_MENU, &CGameListCtrl::OnChangeDisc, this, IDM_LIST_CHANGE_DISC);
|
||||||
|
|
||||||
|
wxTheApp->Bind(DOLPHIN_EVT_LOCAL_INI_CHANGED, &CGameListCtrl::OnLocalIniModified, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
CGameListCtrl::~CGameListCtrl()
|
CGameListCtrl::~CGameListCtrl()
|
||||||
{
|
{
|
||||||
if (m_imageListSmall)
|
|
||||||
delete m_imageListSmall;
|
|
||||||
|
|
||||||
ClearIsoFiles();
|
ClearIsoFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameListCtrl::InitBitmaps()
|
void CGameListCtrl::InitBitmaps()
|
||||||
{
|
{
|
||||||
wxSize size(96, 32);
|
wxSize size(96, 32);
|
||||||
m_imageListSmall = new wxImageList(96, 32);
|
wxImageList* img_list = new wxImageList(96, 32);
|
||||||
SetImageList(m_imageListSmall, wxIMAGE_LIST_SMALL);
|
AssignImageList(img_list, wxIMAGE_LIST_SMALL);
|
||||||
|
|
||||||
m_FlagImageIndex.resize(DiscIO::IVolume::NUMBER_OF_COUNTRIES);
|
m_FlagImageIndex.resize(DiscIO::IVolume::NUMBER_OF_COUNTRIES);
|
||||||
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_JAPAN] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_Japan", size));
|
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_JAPAN] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_Japan", size));
|
||||||
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_EUROPE] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_Europe", size));
|
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_EUROPE] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_Europe", size));
|
||||||
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_USA] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_USA", size));
|
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_USA] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_USA", size));
|
||||||
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_AUSTRALIA] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_Australia", size));
|
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_AUSTRALIA] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_Australia", size));
|
||||||
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_FRANCE] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_France", size));
|
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_FRANCE] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_France", size));
|
||||||
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_GERMANY] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_Germany", size));
|
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_GERMANY] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_Germany", size));
|
||||||
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_ITALY] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_Italy", size));
|
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_ITALY] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_Italy", size));
|
||||||
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_KOREA] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_Korea", size));
|
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_KOREA] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_Korea", size));
|
||||||
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_NETHERLANDS] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_Netherlands", size));
|
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_NETHERLANDS] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_Netherlands", size));
|
||||||
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_RUSSIA] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_Russia", size));
|
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_RUSSIA] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_Russia", size));
|
||||||
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_SPAIN] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_Spain", size));
|
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_SPAIN] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_Spain", size));
|
||||||
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_TAIWAN] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_Taiwan", size));
|
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_TAIWAN] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_Taiwan", size));
|
||||||
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_WORLD] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_International", size));
|
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_WORLD] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_International", size));
|
||||||
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_UNKNOWN] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Flag_Unknown", size));
|
m_FlagImageIndex[DiscIO::IVolume::COUNTRY_UNKNOWN] = img_list->Add(WxUtils::LoadResourceBitmap("Flag_Unknown", size));
|
||||||
|
|
||||||
m_PlatformImageIndex.resize(4);
|
m_PlatformImageIndex.resize(4);
|
||||||
m_PlatformImageIndex[DiscIO::IVolume::GAMECUBE_DISC] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Platform_Gamecube", size));
|
m_PlatformImageIndex[DiscIO::IVolume::GAMECUBE_DISC] = img_list->Add(WxUtils::LoadResourceBitmap("Platform_Gamecube", size));
|
||||||
m_PlatformImageIndex[DiscIO::IVolume::WII_DISC] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Platform_Wii", size));
|
m_PlatformImageIndex[DiscIO::IVolume::WII_DISC] = img_list->Add(WxUtils::LoadResourceBitmap("Platform_Wii", size));
|
||||||
m_PlatformImageIndex[DiscIO::IVolume::WII_WAD] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Platform_Wad", size));
|
m_PlatformImageIndex[DiscIO::IVolume::WII_WAD] = img_list->Add(WxUtils::LoadResourceBitmap("Platform_Wad", size));
|
||||||
m_PlatformImageIndex[DiscIO::IVolume::ELF_DOL] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("Platform_File", size));
|
m_PlatformImageIndex[DiscIO::IVolume::ELF_DOL] = img_list->Add(WxUtils::LoadResourceBitmap("Platform_File", size));
|
||||||
|
|
||||||
m_EmuStateImageIndex.resize(6);
|
m_EmuStateImageIndex.resize(6);
|
||||||
m_EmuStateImageIndex[0] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("rating0", size));
|
m_EmuStateImageIndex[0] = img_list->Add(WxUtils::LoadResourceBitmap("rating0", size));
|
||||||
m_EmuStateImageIndex[1] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("rating1", size));
|
m_EmuStateImageIndex[1] = img_list->Add(WxUtils::LoadResourceBitmap("rating1", size));
|
||||||
m_EmuStateImageIndex[2] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("rating2", size));
|
m_EmuStateImageIndex[2] = img_list->Add(WxUtils::LoadResourceBitmap("rating2", size));
|
||||||
m_EmuStateImageIndex[3] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("rating3", size));
|
m_EmuStateImageIndex[3] = img_list->Add(WxUtils::LoadResourceBitmap("rating3", size));
|
||||||
m_EmuStateImageIndex[4] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("rating4", size));
|
m_EmuStateImageIndex[4] = img_list->Add(WxUtils::LoadResourceBitmap("rating4", size));
|
||||||
m_EmuStateImageIndex[5] = m_imageListSmall->Add(WxUtils::LoadResourceBitmap("rating5", size));
|
m_EmuStateImageIndex[5] = img_list->Add(WxUtils::LoadResourceBitmap("rating5", size));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameListCtrl::BrowseForDirectory()
|
void CGameListCtrl::BrowseForDirectory()
|
||||||
|
@ -247,16 +247,9 @@ void CGameListCtrl::Update()
|
||||||
if (Core::GetState() != Core::CORE_UNINITIALIZED)
|
if (Core::GetState() != Core::CORE_UNINITIALIZED)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (m_imageListSmall)
|
|
||||||
{
|
|
||||||
delete m_imageListSmall;
|
|
||||||
m_imageListSmall = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Hide();
|
|
||||||
|
|
||||||
ScanForISOs();
|
ScanForISOs();
|
||||||
|
|
||||||
|
Freeze();
|
||||||
ClearAll();
|
ClearAll();
|
||||||
|
|
||||||
if (m_ISOFiles.size() != 0)
|
if (m_ISOFiles.size() != 0)
|
||||||
|
@ -320,6 +313,12 @@ void CGameListCtrl::Update()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Remove existing image list and replace it with the smallest possible one.
|
||||||
|
// The list needs an image list because it reserves screen pixels for the
|
||||||
|
// image even if we aren't going to use one. It uses the dimensions of the
|
||||||
|
// last non-null list so assigning nullptr doesn't work.
|
||||||
|
AssignImageList(new wxImageList(1, 1), wxIMAGE_LIST_SMALL);
|
||||||
|
|
||||||
wxString errorString;
|
wxString errorString;
|
||||||
// We just check for one hide setting to be enabled, as we may only
|
// We just check for one hide setting to be enabled, as we may only
|
||||||
// have GC games for example, and hide them, so we should show the
|
// have GC games for example, and hide them, so we should show the
|
||||||
|
@ -339,7 +338,9 @@ void CGameListCtrl::Update()
|
||||||
}
|
}
|
||||||
if (GetSelectedISO() == nullptr)
|
if (GetSelectedISO() == nullptr)
|
||||||
main_frame->UpdateGUI();
|
main_frame->UpdateGUI();
|
||||||
Show();
|
// Thaw before calling AutomaticColumnWidth so that GetClientSize will
|
||||||
|
// correctly account for the width of scrollbars if they appear.
|
||||||
|
Thaw();
|
||||||
|
|
||||||
AutomaticColumnWidth();
|
AutomaticColumnWidth();
|
||||||
ScrollLines(scrollPos);
|
ScrollLines(scrollPos);
|
||||||
|
@ -367,7 +368,7 @@ static wxString NiceSizeFormat(u64 size)
|
||||||
// Update the column content of the item at _Index
|
// Update the column content of the item at _Index
|
||||||
void CGameListCtrl::UpdateItemAtColumn(long _Index, int column)
|
void CGameListCtrl::UpdateItemAtColumn(long _Index, int column)
|
||||||
{
|
{
|
||||||
GameListItem& rISOFile = *m_ISOFiles[_Index];
|
GameListItem& rISOFile = *m_ISOFiles[GetItemData(_Index)];
|
||||||
|
|
||||||
switch(column)
|
switch(column)
|
||||||
{
|
{
|
||||||
|
@ -382,7 +383,7 @@ void CGameListCtrl::UpdateItemAtColumn(long _Index, int column)
|
||||||
int ImageIndex = -1;
|
int ImageIndex = -1;
|
||||||
|
|
||||||
if (rISOFile.GetBitmap().IsOk())
|
if (rISOFile.GetBitmap().IsOk())
|
||||||
ImageIndex = m_imageListSmall->Add(rISOFile.GetBitmap());
|
ImageIndex = GetImageList(wxIMAGE_LIST_SMALL)->Add(rISOFile.GetBitmap());
|
||||||
|
|
||||||
SetItemColumnImage(_Index, COLUMN_BANNER, ImageIndex);
|
SetItemColumnImage(_Index, COLUMN_BANNER, ImageIndex);
|
||||||
break;
|
break;
|
||||||
|
@ -426,28 +427,32 @@ void CGameListCtrl::UpdateItemAtColumn(long _Index, int column)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameListCtrl::InsertItemInReportView(long _Index)
|
void CGameListCtrl::InsertItemInReportView(long index)
|
||||||
{
|
{
|
||||||
// When using wxListCtrl, there is no hope of per-column text colors.
|
// When using wxListCtrl, there is no hope of per-column text colors.
|
||||||
// But for reference, here are the old colors that were used: (BGR)
|
// But for reference, here are the old colors that were used: (BGR)
|
||||||
// title: 0xFF0000
|
// title: 0xFF0000
|
||||||
// company: 0x007030
|
// company: 0x007030
|
||||||
|
|
||||||
// Insert a first row with nothing in it, that will be used as the Index
|
// Insert a first column with nothing in it, that will be used as the Index
|
||||||
long ItemIndex = InsertItem(_Index, wxEmptyString);
|
long item_index;
|
||||||
|
{
|
||||||
|
wxListItem li;
|
||||||
|
li.SetId(index);
|
||||||
|
li.SetData(index);
|
||||||
|
li.SetMask(wxLIST_MASK_DATA);
|
||||||
|
item_index = InsertItem(li);
|
||||||
|
}
|
||||||
|
|
||||||
// Iterate over all columns and fill them with content if they are visible
|
// Iterate over all columns and fill them with content if they are visible
|
||||||
for (int i = 1; i < NUMBER_OF_COLUMN; i++)
|
for (int i = 1; i < NUMBER_OF_COLUMN; i++)
|
||||||
{
|
{
|
||||||
if (GetColumnWidth(i) != 0)
|
if (GetColumnWidth(i) != 0)
|
||||||
UpdateItemAtColumn(_Index, i);
|
UpdateItemAtColumn(item_index, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Background color
|
// Background color
|
||||||
SetBackgroundColor();
|
SetBackgroundColor();
|
||||||
|
|
||||||
// Item data
|
|
||||||
SetItemData(_Index, ItemIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static wxColour blend50(const wxColour& c1, const wxColour& c2)
|
static wxColour blend50(const wxColour& c1, const wxColour& c2)
|
||||||
|
@ -653,6 +658,41 @@ void CGameListCtrl::ScanForISOs()
|
||||||
std::sort(m_ISOFiles.begin(), m_ISOFiles.end());
|
std::sort(m_ISOFiles.begin(), m_ISOFiles.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CGameListCtrl::OnLocalIniModified(wxCommandEvent& ev)
|
||||||
|
{
|
||||||
|
ev.Skip();
|
||||||
|
std::string game_id = WxStrToStr(ev.GetString());
|
||||||
|
// NOTE: The same game may occur multiple times if there are multiple
|
||||||
|
// physical copies in the search paths.
|
||||||
|
for (std::size_t i = 0; i < m_ISOFiles.size(); ++i)
|
||||||
|
{
|
||||||
|
if (m_ISOFiles[i]->GetUniqueID() != game_id)
|
||||||
|
continue;
|
||||||
|
m_ISOFiles[i]->ReloadINI();
|
||||||
|
|
||||||
|
// The indexes in m_ISOFiles and the list do not line up.
|
||||||
|
// We need to find the corresponding item in the list (if it exists)
|
||||||
|
long item_id = 0;
|
||||||
|
for (; item_id < GetItemCount(); ++item_id)
|
||||||
|
{
|
||||||
|
if (i == static_cast<std::size_t>(GetItemData(item_id)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// If the item is not currently being displayed then we're done.
|
||||||
|
if (item_id == GetItemCount())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Update all the columns
|
||||||
|
for (int j = 1; j < NUMBER_OF_COLUMN; ++j)
|
||||||
|
{
|
||||||
|
// NOTE: Banner is not modified by the INI and updating it will
|
||||||
|
// duplicate it in memory which is not wanted.
|
||||||
|
if (j != COLUMN_BANNER && GetColumnWidth(j) != 0)
|
||||||
|
UpdateItemAtColumn(item_id, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CGameListCtrl::OnColBeginDrag(wxListEvent& event)
|
void CGameListCtrl::OnColBeginDrag(wxListEvent& event)
|
||||||
{
|
{
|
||||||
const int column_id = event.GetColumn();
|
const int column_id = event.GetColumn();
|
||||||
|
@ -1310,19 +1350,19 @@ void CGameListCtrl::OnChangeDisc(wxCommandEvent& WXUNUSED(event))
|
||||||
|
|
||||||
void CGameListCtrl::OnSize(wxSizeEvent& event)
|
void CGameListCtrl::OnSize(wxSizeEvent& event)
|
||||||
{
|
{
|
||||||
|
event.Skip();
|
||||||
if (lastpos == event.GetSize())
|
if (lastpos == event.GetSize())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
lastpos = event.GetSize();
|
lastpos = event.GetSize();
|
||||||
AutomaticColumnWidth();
|
AutomaticColumnWidth();
|
||||||
|
|
||||||
event.Skip();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameListCtrl::AutomaticColumnWidth()
|
void CGameListCtrl::AutomaticColumnWidth()
|
||||||
{
|
{
|
||||||
wxRect rc(GetClientRect());
|
wxRect rc(GetClientRect());
|
||||||
|
|
||||||
|
Freeze();
|
||||||
if (GetColumnCount() == 1)
|
if (GetColumnCount() == 1)
|
||||||
{
|
{
|
||||||
SetColumnWidth(0, rc.GetWidth());
|
SetColumnWidth(0, rc.GetWidth());
|
||||||
|
@ -1359,6 +1399,7 @@ void CGameListCtrl::AutomaticColumnWidth()
|
||||||
SetColumnWidth(COLUMN_TITLE, resizable);
|
SetColumnWidth(COLUMN_TITLE, resizable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Thaw();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameListCtrl::UnselectAll()
|
void CGameListCtrl::UnselectAll()
|
||||||
|
|
|
@ -102,6 +102,7 @@ private:
|
||||||
void OnMultiCompressISO(wxCommandEvent& event);
|
void OnMultiCompressISO(wxCommandEvent& event);
|
||||||
void OnMultiDecompressISO(wxCommandEvent& event);
|
void OnMultiDecompressISO(wxCommandEvent& event);
|
||||||
void OnChangeDisc(wxCommandEvent& event);
|
void OnChangeDisc(wxCommandEvent& event);
|
||||||
|
void OnLocalIniModified(wxCommandEvent& event);
|
||||||
|
|
||||||
void CompressSelection(bool _compress);
|
void CompressSelection(bool _compress);
|
||||||
void AutomaticColumnWidth();
|
void AutomaticColumnWidth();
|
||||||
|
|
|
@ -331,10 +331,11 @@ enum
|
||||||
|
|
||||||
// custom message macro
|
// custom message macro
|
||||||
#define EVT_HOST_COMMAND(id, fn) \
|
#define EVT_HOST_COMMAND(id, fn) \
|
||||||
DECLARE_EVENT_TABLE_ENTRY(\
|
EVT_COMMAND(id, wxEVT_HOST_COMMAND, fn)
|
||||||
wxEVT_HOST_COMMAND, id, wxID_ANY, \
|
|
||||||
(wxObjectEventFunction)(wxEventFunction) wxStaticCastEvent(wxCommandEventFunction, &fn), \
|
|
||||||
(wxObject*) nullptr \
|
|
||||||
),
|
|
||||||
|
|
||||||
wxDECLARE_EVENT(wxEVT_HOST_COMMAND, wxCommandEvent);
|
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);
|
||||||
|
|
|
@ -123,13 +123,6 @@ GameListItem::GameListItem(const std::string& _rFileName, const std::unordered_m
|
||||||
m_company = DiscIO::GetCompanyFromID(m_UniqueID.substr(4, 2));
|
m_company = DiscIO::GetCompanyFromID(m_UniqueID.substr(4, 2));
|
||||||
|
|
||||||
if (IsValid())
|
if (IsValid())
|
||||||
{
|
|
||||||
IniFile ini = SConfig::LoadGameIni(m_UniqueID, m_Revision);
|
|
||||||
ini.GetIfExists("EmuState", "EmulationStateId", &m_emu_state);
|
|
||||||
ini.GetIfExists("EmuState", "EmulationIssues", &m_issues);
|
|
||||||
m_has_custom_name = ini.GetIfExists("EmuState", "Title", &m_custom_name);
|
|
||||||
|
|
||||||
if (!m_has_custom_name)
|
|
||||||
{
|
{
|
||||||
std::string game_id = m_UniqueID;
|
std::string game_id = m_UniqueID;
|
||||||
|
|
||||||
|
@ -137,14 +130,13 @@ GameListItem::GameListItem(const std::string& _rFileName, const std::unordered_m
|
||||||
if (m_Platform == DiscIO::IVolume::WII_WAD && game_id.size() > 4)
|
if (m_Platform == DiscIO::IVolume::WII_WAD && game_id.size() > 4)
|
||||||
game_id.erase(4);
|
game_id.erase(4);
|
||||||
|
|
||||||
auto end = custom_titles.end();
|
|
||||||
auto it = custom_titles.find(game_id);
|
auto it = custom_titles.find(game_id);
|
||||||
if (it != end)
|
if (it != custom_titles.end())
|
||||||
{
|
{
|
||||||
m_custom_name = it->second;
|
m_custom_name_titles_txt = it->second;
|
||||||
m_has_custom_name = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReloadINI();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsValid() && IsElfOrDol())
|
if (!IsValid() && IsElfOrDol())
|
||||||
|
@ -183,6 +175,24 @@ GameListItem::~GameListItem()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameListItem::ReloadINI()
|
||||||
|
{
|
||||||
|
if (!IsValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
IniFile ini = SConfig::LoadGameIni(m_UniqueID, m_Revision);
|
||||||
|
ini.GetIfExists("EmuState", "EmulationStateId", &m_emu_state, 0);
|
||||||
|
ini.GetIfExists("EmuState", "EmulationIssues", &m_issues, std::string());
|
||||||
|
|
||||||
|
m_custom_name.clear();
|
||||||
|
m_has_custom_name = ini.GetIfExists("EmuState", "Title", &m_custom_name);
|
||||||
|
if (!m_has_custom_name && !m_custom_name_titles_txt.empty())
|
||||||
|
{
|
||||||
|
m_custom_name = m_custom_name_titles_txt;
|
||||||
|
m_has_custom_name = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool GameListItem::LoadFromCache()
|
bool GameListItem::LoadFromCache()
|
||||||
{
|
{
|
||||||
return CChunkFileReader::Load<GameListItem>(CreateCacheFilename(), CACHE_REVISION, *this);
|
return CChunkFileReader::Load<GameListItem>(CreateCacheFilename(), CACHE_REVISION, *this);
|
||||||
|
|
|
@ -25,6 +25,9 @@ public:
|
||||||
GameListItem(const std::string& _rFileName, const std::unordered_map<std::string, std::string>& custom_titles);
|
GameListItem(const std::string& _rFileName, const std::unordered_map<std::string, std::string>& custom_titles);
|
||||||
~GameListItem();
|
~GameListItem();
|
||||||
|
|
||||||
|
// Reload settings after INI changes
|
||||||
|
void ReloadINI();
|
||||||
|
|
||||||
bool IsValid() const {return m_Valid;}
|
bool IsValid() const {return m_Valid;}
|
||||||
const std::string& GetFileName() const {return m_FileName;}
|
const std::string& GetFileName() const {return m_FileName;}
|
||||||
std::string GetName(DiscIO::IVolume::ELanguage language) const;
|
std::string GetName(DiscIO::IVolume::ELanguage language) const;
|
||||||
|
@ -86,7 +89,8 @@ private:
|
||||||
int m_ImageWidth, m_ImageHeight;
|
int m_ImageWidth, m_ImageHeight;
|
||||||
u8 m_disc_number;
|
u8 m_disc_number;
|
||||||
|
|
||||||
std::string m_custom_name;
|
std::string m_custom_name_titles_txt; // Custom title from titles.txt
|
||||||
|
std::string m_custom_name; // Custom title from INI or titles.txt
|
||||||
bool m_has_custom_name;
|
bool m_has_custom_name;
|
||||||
|
|
||||||
bool LoadFromCache();
|
bool LoadFromCache();
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <mbedtls/md5.h>
|
#include <mbedtls/md5.h>
|
||||||
|
#include <wx/app.h>
|
||||||
#include <wx/bitmap.h>
|
#include <wx/bitmap.h>
|
||||||
#include <wx/button.h>
|
#include <wx/button.h>
|
||||||
#include <wx/checkbox.h>
|
#include <wx/checkbox.h>
|
||||||
|
@ -62,8 +63,7 @@
|
||||||
#include "DiscIO/Volume.h"
|
#include "DiscIO/Volume.h"
|
||||||
#include "DiscIO/VolumeCreator.h"
|
#include "DiscIO/VolumeCreator.h"
|
||||||
#include "DolphinWX/ARCodeAddEdit.h"
|
#include "DolphinWX/ARCodeAddEdit.h"
|
||||||
#include "DolphinWX/GameListCtrl.h"
|
#include "DolphinWX/Globals.h"
|
||||||
//#include "DolphinWX/Frame.h"
|
|
||||||
#include "DolphinWX/ISOFile.h"
|
#include "DolphinWX/ISOFile.h"
|
||||||
#include "DolphinWX/ISOProperties.h"
|
#include "DolphinWX/ISOProperties.h"
|
||||||
#include "DolphinWX/PatchAddEdit.h"
|
#include "DolphinWX/PatchAddEdit.h"
|
||||||
|
@ -76,8 +76,7 @@ BEGIN_EVENT_TABLE(CISOProperties, wxDialog)
|
||||||
EVT_BUTTON(ID_EDITCONFIG, CISOProperties::OnEditConfig)
|
EVT_BUTTON(ID_EDITCONFIG, CISOProperties::OnEditConfig)
|
||||||
EVT_BUTTON(ID_MD5SUMCOMPUTE, CISOProperties::OnComputeMD5Sum)
|
EVT_BUTTON(ID_MD5SUMCOMPUTE, CISOProperties::OnComputeMD5Sum)
|
||||||
EVT_BUTTON(ID_SHOWDEFAULTCONFIG, CISOProperties::OnShowDefaultConfig)
|
EVT_BUTTON(ID_SHOWDEFAULTCONFIG, CISOProperties::OnShowDefaultConfig)
|
||||||
EVT_CHOICE(ID_EMUSTATE, CISOProperties::SetRefresh)
|
EVT_CHOICE(ID_EMUSTATE, CISOProperties::OnEmustateChanged)
|
||||||
EVT_CHOICE(ID_EMU_ISSUES, CISOProperties::SetRefresh)
|
|
||||||
EVT_LISTBOX(ID_PATCHES_LIST, CISOProperties::ListSelectionChanged)
|
EVT_LISTBOX(ID_PATCHES_LIST, CISOProperties::ListSelectionChanged)
|
||||||
EVT_BUTTON(ID_EDITPATCH, CISOProperties::PatchButtonClicked)
|
EVT_BUTTON(ID_EDITPATCH, CISOProperties::PatchButtonClicked)
|
||||||
EVT_BUTTON(ID_ADDPATCH, CISOProperties::PatchButtonClicked)
|
EVT_BUTTON(ID_ADDPATCH, CISOProperties::PatchButtonClicked)
|
||||||
|
@ -95,6 +94,7 @@ BEGIN_EVENT_TABLE(CISOProperties, wxDialog)
|
||||||
EVT_MENU(IDM_EXTRACTDOL, CISOProperties::OnExtractDataFromHeader)
|
EVT_MENU(IDM_EXTRACTDOL, CISOProperties::OnExtractDataFromHeader)
|
||||||
EVT_MENU(IDM_CHECKINTEGRITY, CISOProperties::CheckPartitionIntegrity)
|
EVT_MENU(IDM_CHECKINTEGRITY, CISOProperties::CheckPartitionIntegrity)
|
||||||
EVT_CHOICE(ID_LANG, CISOProperties::OnChangeBannerLang)
|
EVT_CHOICE(ID_LANG, CISOProperties::OnChangeBannerLang)
|
||||||
|
EVT_CHECKLISTBOX(ID_CHEATS_LIST, CISOProperties::OnActionReplayCodeChecked)
|
||||||
END_EVENT_TABLE()
|
END_EVENT_TABLE()
|
||||||
|
|
||||||
CISOProperties::CISOProperties(const GameListItem& game_list_item, wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& position, const wxSize& size, long style)
|
CISOProperties::CISOProperties(const GameListItem& game_list_item, wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& position, const wxSize& size, long style)
|
||||||
|
@ -112,8 +112,6 @@ CISOProperties::CISOProperties(const GameListItem& game_list_item, wxWindow* par
|
||||||
GameIniLocal = SConfig::LoadLocalGameIni(game_id, m_open_iso->GetRevision());
|
GameIniLocal = SConfig::LoadLocalGameIni(game_id, m_open_iso->GetRevision());
|
||||||
|
|
||||||
// Setup GUI
|
// Setup GUI
|
||||||
bRefreshList = false;
|
|
||||||
|
|
||||||
CreateGUIControls();
|
CreateGUIControls();
|
||||||
|
|
||||||
LoadGameConfig();
|
LoadGameConfig();
|
||||||
|
@ -229,6 +227,8 @@ CISOProperties::CISOProperties(const GameListItem& game_list_item, wxWindow* par
|
||||||
|
|
||||||
m_Treectrl->Expand(RootId);
|
m_Treectrl->Expand(RootId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxTheApp->Bind(DOLPHIN_EVT_LOCAL_INI_CHANGED, &CISOProperties::OnLocalIniModified, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
CISOProperties::~CISOProperties()
|
CISOProperties::~CISOProperties()
|
||||||
|
@ -643,8 +643,6 @@ void CISOProperties::OnClose(wxCloseEvent& WXUNUSED (event))
|
||||||
{
|
{
|
||||||
if (!SaveGameConfig())
|
if (!SaveGameConfig())
|
||||||
WxUtils::ShowErrorDialog(wxString::Format(_("Could not save %s."), GameIniFileLocal.c_str()));
|
WxUtils::ShowErrorDialog(wxString::Format(_("Could not save %s."), GameIniFileLocal.c_str()));
|
||||||
if (bRefreshList)
|
|
||||||
((CGameListCtrl*)GetParent())->Update();
|
|
||||||
Destroy();
|
Destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -987,11 +985,8 @@ void CISOProperties::CheckPartitionIntegrity(wxCommandEvent& event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CISOProperties::SetRefresh(wxCommandEvent& event)
|
void CISOProperties::OnEmustateChanged(wxCommandEvent& event)
|
||||||
{
|
{
|
||||||
bRefreshList = true;
|
|
||||||
|
|
||||||
if (event.GetId() == ID_EMUSTATE)
|
|
||||||
EmuIssues->Enable(event.GetSelection() != 0);
|
EmuIssues->Enable(event.GetSelection() != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1163,6 +1158,9 @@ bool CISOProperties::SaveGameConfig()
|
||||||
if (success && File::GetSize(GameIniFileLocal) == 0)
|
if (success && File::GetSize(GameIniFileLocal) == 0)
|
||||||
File::Delete(GameIniFileLocal);
|
File::Delete(GameIniFileLocal);
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
GenerateLocalIniModified();
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1204,12 +1202,27 @@ void CISOProperties::LaunchExternalEditor(const std::string& filename, bool wait
|
||||||
WxUtils::ShowErrorDialog(_("wxExecute returned -1 on application run!"));
|
WxUtils::ShowErrorDialog(_("wxExecute returned -1 on application run!"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wait_until_closed)
|
|
||||||
bRefreshList = true; // Just in case
|
|
||||||
#endif
|
#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))
|
void CISOProperties::OnEditConfig(wxCommandEvent& WXUNUSED (event))
|
||||||
{
|
{
|
||||||
SaveGameConfig();
|
SaveGameConfig();
|
||||||
|
@ -1220,8 +1233,7 @@ void CISOProperties::OnEditConfig(wxCommandEvent& WXUNUSED (event))
|
||||||
blankFile.close();
|
blankFile.close();
|
||||||
}
|
}
|
||||||
LaunchExternalEditor(GameIniFileLocal, true);
|
LaunchExternalEditor(GameIniFileLocal, true);
|
||||||
GameIniLocal.Load(GameIniFileLocal);
|
GenerateLocalIniModified();
|
||||||
LoadGameConfig();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CISOProperties::OnComputeMD5Sum(wxCommandEvent& WXUNUSED (event))
|
void CISOProperties::OnComputeMD5Sum(wxCommandEvent& WXUNUSED (event))
|
||||||
|
@ -1300,7 +1312,7 @@ void CISOProperties::ListSelectionChanged(wxCommandEvent& event)
|
||||||
break;
|
break;
|
||||||
case ID_CHEATS_LIST:
|
case ID_CHEATS_LIST:
|
||||||
if (Cheats->GetSelection() == wxNOT_FOUND ||
|
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();
|
EditCheat->Disable();
|
||||||
RemoveCheat->Disable();
|
RemoveCheat->Disable();
|
||||||
|
@ -1314,6 +1326,11 @@ void CISOProperties::ListSelectionChanged(wxCommandEvent& event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CISOProperties::OnActionReplayCodeChecked(wxCommandEvent& event)
|
||||||
|
{
|
||||||
|
arCodes[event.GetSelection()].active = Cheats->IsChecked(event.GetSelection());
|
||||||
|
}
|
||||||
|
|
||||||
void CISOProperties::PatchList_Load()
|
void CISOProperties::PatchList_Load()
|
||||||
{
|
{
|
||||||
onFrame.clear();
|
onFrame.clear();
|
||||||
|
@ -1399,46 +1416,24 @@ void CISOProperties::PatchButtonClicked(wxCommandEvent& event)
|
||||||
|
|
||||||
void CISOProperties::ActionReplayList_Load()
|
void CISOProperties::ActionReplayList_Load()
|
||||||
{
|
{
|
||||||
arCodes.clear();
|
arCodes = ActionReplay::LoadCodes(GameIniDefault, GameIniLocal);
|
||||||
Cheats->Clear();
|
DefaultCheats.clear();
|
||||||
ActionReplay::LoadCodes(arCodes, GameIniDefault, GameIniLocal);
|
|
||||||
|
|
||||||
u32 index = 0;
|
Cheats->Freeze();
|
||||||
|
Cheats->Clear();
|
||||||
for (const ActionReplay::ARCode& arCode : arCodes)
|
for (const ActionReplay::ARCode& arCode : arCodes)
|
||||||
{
|
{
|
||||||
Cheats->Append(StrToWxStr(arCode.name));
|
int idx = Cheats->Append(Cheats->EscapeMnemonics(StrToWxStr(arCode.name)));
|
||||||
Cheats->Check(index, arCode.active);
|
Cheats->Check(idx, arCode.active);
|
||||||
if (!arCode.user_defined)
|
if (!arCode.user_defined)
|
||||||
DefaultCheats.insert(arCode.name);
|
DefaultCheats.insert(arCode.name);
|
||||||
++index;
|
|
||||||
}
|
}
|
||||||
|
Cheats->Thaw();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CISOProperties::ActionReplayList_Save()
|
void CISOProperties::ActionReplayList_Save()
|
||||||
{
|
{
|
||||||
std::vector<std::string> lines;
|
ActionReplay::SaveCodes(&GameIniLocal, arCodes);
|
||||||
std::vector<std::string> enabledLines;
|
|
||||||
u32 index = 0;
|
|
||||||
u32 cheats_chkbox_count = Cheats->GetCount();
|
|
||||||
for (const ActionReplay::ARCode& code : arCodes)
|
|
||||||
{
|
|
||||||
// Check the index against the count because of the hacky way codes are added from the "Cheat Search" dialog
|
|
||||||
if ((index < cheats_chkbox_count) && Cheats->IsChecked(index))
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CISOProperties::ActionReplayButtonClicked(wxCommandEvent& event)
|
void CISOProperties::ActionReplayButtonClicked(wxCommandEvent& event)
|
||||||
|
@ -1480,11 +1475,6 @@ void CISOProperties::ActionReplayButtonClicked(wxCommandEvent& event)
|
||||||
RemoveCheat->Disable();
|
RemoveCheat->Disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CISOProperties::AddARCode(const ActionReplay::ARCode& code)
|
|
||||||
{
|
|
||||||
arCodes.emplace_back(code);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CISOProperties::OnChangeBannerLang(wxCommandEvent& event)
|
void CISOProperties::OnChangeBannerLang(wxCommandEvent& event)
|
||||||
{
|
{
|
||||||
ChangeBannerDetails(OpenGameListItem.GetLanguages()[event.GetSelection()]);
|
ChangeBannerDetails(OpenGameListItem.GetLanguages()[event.GetSelection()]);
|
||||||
|
|
|
@ -68,15 +68,6 @@ public:
|
||||||
long style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
|
long style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
|
||||||
virtual ~CISOProperties();
|
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:
|
private:
|
||||||
DECLARE_EVENT_TABLE();
|
DECLARE_EVENT_TABLE();
|
||||||
|
|
||||||
|
@ -208,6 +199,7 @@ private:
|
||||||
void OnComputeMD5Sum(wxCommandEvent& event);
|
void OnComputeMD5Sum(wxCommandEvent& event);
|
||||||
void OnShowDefaultConfig(wxCommandEvent& event);
|
void OnShowDefaultConfig(wxCommandEvent& event);
|
||||||
void ListSelectionChanged(wxCommandEvent& event);
|
void ListSelectionChanged(wxCommandEvent& event);
|
||||||
|
void OnActionReplayCodeChecked(wxCommandEvent& event);
|
||||||
void PatchButtonClicked(wxCommandEvent& event);
|
void PatchButtonClicked(wxCommandEvent& event);
|
||||||
void ActionReplayButtonClicked(wxCommandEvent& event);
|
void ActionReplayButtonClicked(wxCommandEvent& event);
|
||||||
void RightClickOnBanner(wxMouseEvent& event);
|
void RightClickOnBanner(wxMouseEvent& event);
|
||||||
|
@ -217,7 +209,7 @@ private:
|
||||||
void OnExtractDir(wxCommandEvent& event);
|
void OnExtractDir(wxCommandEvent& event);
|
||||||
void OnExtractDataFromHeader(wxCommandEvent& event);
|
void OnExtractDataFromHeader(wxCommandEvent& event);
|
||||||
void CheckPartitionIntegrity(wxCommandEvent& event);
|
void CheckPartitionIntegrity(wxCommandEvent& event);
|
||||||
void SetRefresh(wxCommandEvent& event);
|
void OnEmustateChanged(wxCommandEvent& event);
|
||||||
void OnChangeBannerLang(wxCommandEvent& event);
|
void OnChangeBannerLang(wxCommandEvent& event);
|
||||||
|
|
||||||
const GameListItem OpenGameListItem;
|
const GameListItem OpenGameListItem;
|
||||||
|
@ -242,8 +234,12 @@ private:
|
||||||
std::set<std::string> DefaultCheats;
|
std::set<std::string> DefaultCheats;
|
||||||
|
|
||||||
void LoadGameConfig();
|
void LoadGameConfig();
|
||||||
|
bool SaveGameConfig();
|
||||||
|
void OnLocalIniModified(wxCommandEvent& ev);
|
||||||
|
void GenerateLocalIniModified();
|
||||||
void PatchList_Load();
|
void PatchList_Load();
|
||||||
void PatchList_Save();
|
void PatchList_Save();
|
||||||
|
void ActionReplayList_Load();
|
||||||
void ActionReplayList_Save();
|
void ActionReplayList_Save();
|
||||||
void ChangeBannerDetails(DiscIO::IVolume::ELanguage language);
|
void ChangeBannerDetails(DiscIO::IVolume::ELanguage language);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue