2015-05-24 06:55:12 +02:00
|
|
|
// Copyright 2010 Dolphin Emulator Project
|
2015-05-18 01:08:10 +02:00
|
|
|
// Licensed under GPLv2+
|
2013-04-17 22:43:11 -04:00
|
|
|
// Refer to the license.txt file included.
|
2012-01-02 13:53:39 +11:00
|
|
|
|
2016-09-23 13:20:28 +00:00
|
|
|
#include <algorithm>
|
|
|
|
|
#include <iterator>
|
2015-05-26 22:44:51 -05:00
|
|
|
#include <mutex>
|
2014-02-17 05:18:15 -05:00
|
|
|
#include <vector>
|
2010-07-22 01:48:48 +00:00
|
|
|
|
2014-02-17 05:18:15 -05:00
|
|
|
#include "Common/CommonPaths.h"
|
2015-05-08 17:28:03 -04:00
|
|
|
#include "Common/FileUtil.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
#include "Common/Thread.h"
|
2010-07-22 01:48:48 +00:00
|
|
|
|
2014-02-17 05:18:15 -05:00
|
|
|
#include "Core/ConfigManager.h"
|
|
|
|
|
#include "Core/GeckoCode.h"
|
|
|
|
|
#include "Core/HW/Memmap.h"
|
|
|
|
|
#include "Core/PowerPC/PowerPC.h"
|
2014-02-04 19:44:02 -05:00
|
|
|
|
2010-07-22 01:48:48 +00:00
|
|
|
namespace Gecko
|
|
|
|
|
{
|
2016-09-23 13:20:28 +00:00
|
|
|
static constexpr u32 INSTALLER_BASE_ADDRESS = 0x80001800;
|
|
|
|
|
static constexpr u32 INSTALLER_END_ADDRESS = 0x80003000;
|
|
|
|
|
static constexpr u32 CODE_SIZE = 8;
|
|
|
|
|
static constexpr u32 MAGIC_GAMEID = 0xD01F1BAD;
|
2014-09-04 00:48:52 +10:00
|
|
|
|
2013-05-05 04:50:39 +02:00
|
|
|
// return true if a code exists
|
2015-04-15 00:38:21 -04:00
|
|
|
bool GeckoCode::Exist(u32 address, u32 data) const
|
2013-05-05 04:50:39 +02:00
|
|
|
{
|
2016-09-23 13:20:28 +00:00
|
|
|
return std::find_if(codes.begin(), codes.end(), [&](const Code& code) {
|
|
|
|
|
return code.address == address && code.data == data;
|
|
|
|
|
}) != codes.end();
|
2013-05-05 04:50:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// return true if the code is identical
|
2015-06-04 14:06:22 -04:00
|
|
|
bool GeckoCode::Compare(const GeckoCode& compare) const
|
2013-05-05 04:50:39 +02:00
|
|
|
{
|
2016-09-23 13:20:28 +00:00
|
|
|
return codes.size() == compare.codes.size() &&
|
|
|
|
|
std::equal(codes.begin(), codes.end(), compare.codes.begin(),
|
|
|
|
|
[](const Code& a, const Code& b) {
|
|
|
|
|
return a.address == b.address && a.data == b.data;
|
|
|
|
|
});
|
2013-05-05 04:50:39 +02:00
|
|
|
}
|
|
|
|
|
|
2016-09-23 13:20:28 +00:00
|
|
|
static bool s_code_handler_installed = false;
|
2013-12-16 22:27:13 -05:00
|
|
|
// the currently active codes
|
2016-09-23 13:20:28 +00:00
|
|
|
static std::vector<GeckoCode> s_active_codes;
|
|
|
|
|
static std::mutex s_active_codes_lock;
|
2010-07-22 01:48:48 +00:00
|
|
|
|
|
|
|
|
void SetActiveCodes(const std::vector<GeckoCode>& gcodes)
|
|
|
|
|
{
|
2016-09-23 13:20:28 +00:00
|
|
|
std::lock_guard<std::mutex> lk(s_active_codes_lock);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-09-23 13:20:28 +00:00
|
|
|
s_active_codes.clear();
|
|
|
|
|
s_active_codes.reserve(gcodes.size());
|
|
|
|
|
std::copy_if(gcodes.begin(), gcodes.end(), std::back_inserter(s_active_codes),
|
|
|
|
|
[](const GeckoCode& code) { return code.enabled; });
|
|
|
|
|
s_active_codes.shrink_to_fit();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-09-23 13:20:28 +00:00
|
|
|
s_code_handler_installed = false;
|
2011-12-31 15:18:48 +11:00
|
|
|
}
|
|
|
|
|
|
2016-09-23 13:20:28 +00:00
|
|
|
// Requires s_active_codes_lock
|
|
|
|
|
// NOTE: Refer to "codehandleronly.s" from Gecko OS.
|
|
|
|
|
static bool InstallCodeHandlerLocked()
|
2011-12-31 15:18:48 +11:00
|
|
|
{
|
2016-06-24 10:43:46 +02:00
|
|
|
std::string data;
|
2016-09-23 13:20:28 +00:00
|
|
|
if (!File::ReadFileToString(File::GetSysDirectory() + GECKO_CODE_HANDLER, data))
|
2016-06-24 10:43:46 +02:00
|
|
|
{
|
2016-09-23 13:20:28 +00:00
|
|
|
ERROR_LOG(ACTIONREPLAY, "Could not enable cheats because " GECKO_CODE_HANDLER " was missing.");
|
2016-06-24 10:43:46 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-23 13:20:28 +00:00
|
|
|
if (data.size() > INSTALLER_END_ADDRESS - INSTALLER_BASE_ADDRESS - CODE_SIZE)
|
|
|
|
|
{
|
|
|
|
|
ERROR_LOG(ACTIONREPLAY, GECKO_CODE_HANDLER " is too big. The file may be corrupt.");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-09-23 13:20:28 +00:00
|
|
|
u8 mmio_addr = 0xCC;
|
2016-06-24 10:43:46 +02:00
|
|
|
if (SConfig::GetInstance().bWii)
|
|
|
|
|
{
|
2016-09-23 13:20:28 +00:00
|
|
|
mmio_addr = 0xCD;
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Install code handler
|
2016-09-23 13:20:28 +00:00
|
|
|
for (u32 i = 0; i < data.size(); ++i)
|
|
|
|
|
PowerPC::HostWrite_U8(data[i], INSTALLER_BASE_ADDRESS + i);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-09-23 13:20:28 +00:00
|
|
|
// Patch the code handler to the current system type (Gamecube/Wii)
|
2016-06-24 10:43:46 +02:00
|
|
|
for (unsigned int h = 0; h < data.length(); h += 4)
|
|
|
|
|
{
|
|
|
|
|
// Patch MMIO address
|
2016-09-23 13:20:28 +00:00
|
|
|
if (PowerPC::HostRead_U32(INSTALLER_BASE_ADDRESS + h) == (0x3f000000u | ((mmio_addr ^ 1) << 8)))
|
2016-06-24 10:43:46 +02:00
|
|
|
{
|
|
|
|
|
NOTICE_LOG(ACTIONREPLAY, "Patching MMIO access at %08x", INSTALLER_BASE_ADDRESS + h);
|
2016-09-23 13:20:28 +00:00
|
|
|
PowerPC::HostWrite_U32(0x3f000000u | mmio_addr << 8, INSTALLER_BASE_ADDRESS + h);
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-23 13:20:28 +00:00
|
|
|
const u32 codelist_base_address =
|
|
|
|
|
INSTALLER_BASE_ADDRESS + static_cast<u32>(data.size()) - CODE_SIZE;
|
|
|
|
|
const u32 codelist_end_address = INSTALLER_END_ADDRESS;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
|
|
|
|
// Write a magic value to 'gameid' (codehandleronly does not actually read this).
|
2016-09-23 13:20:28 +00:00
|
|
|
PowerPC::HostWrite_U32(MAGIC_GAMEID, INSTALLER_BASE_ADDRESS);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
|
|
|
|
// Create GCT in memory
|
|
|
|
|
PowerPC::HostWrite_U32(0x00d0c0de, codelist_base_address);
|
|
|
|
|
PowerPC::HostWrite_U32(0x00d0c0de, codelist_base_address + 4);
|
|
|
|
|
|
2016-09-15 02:39:11 +00:00
|
|
|
// Each code is 8 bytes (2 words) wide. There is a starter code and an end code.
|
|
|
|
|
const u32 start_address = codelist_base_address + CODE_SIZE;
|
|
|
|
|
const u32 end_address = codelist_end_address - CODE_SIZE;
|
|
|
|
|
u32 next_address = start_address;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-09-15 02:39:11 +00:00
|
|
|
// NOTE: Only active codes are in the list
|
2016-09-23 13:20:28 +00:00
|
|
|
for (const GeckoCode& active_code : s_active_codes)
|
2016-06-24 10:43:46 +02:00
|
|
|
{
|
2016-09-15 02:39:11 +00:00
|
|
|
// If the code is not going to fit in the space we have left then we have to skip it
|
|
|
|
|
if (next_address + active_code.codes.size() * CODE_SIZE > end_address)
|
2016-06-24 10:43:46 +02:00
|
|
|
{
|
2016-09-15 02:39:11 +00:00
|
|
|
NOTICE_LOG(ACTIONREPLAY, "Too many GeckoCodes! Ran out of storage space in Game RAM. Could "
|
|
|
|
|
"not write: \"%s\". Need %zu bytes, only %u remain.",
|
|
|
|
|
active_code.name.c_str(), active_code.codes.size() * CODE_SIZE,
|
|
|
|
|
end_address - next_address);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const GeckoCode::Code& code : active_code.codes)
|
|
|
|
|
{
|
|
|
|
|
PowerPC::HostWrite_U32(code.address, next_address);
|
|
|
|
|
PowerPC::HostWrite_U32(code.data, next_address + 4);
|
|
|
|
|
next_address += CODE_SIZE;
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-15 02:39:11 +00:00
|
|
|
WARN_LOG(ACTIONREPLAY, "GeckoCodes: Using %u of %u bytes", next_address - start_address,
|
|
|
|
|
end_address - start_address);
|
|
|
|
|
|
2016-09-23 13:20:28 +00:00
|
|
|
// Stop code. Tells the handler that this is the end of the list.
|
2016-09-15 02:39:11 +00:00
|
|
|
PowerPC::HostWrite_U32(0xF0000000, next_address);
|
|
|
|
|
PowerPC::HostWrite_U32(0x00000000, next_address + 4);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
|
|
|
|
// Turn on codes
|
|
|
|
|
PowerPC::HostWrite_U8(1, INSTALLER_BASE_ADDRESS + 7);
|
|
|
|
|
|
|
|
|
|
// Invalidate the icache and any asm codes
|
|
|
|
|
for (unsigned int j = 0; j < (INSTALLER_END_ADDRESS - INSTALLER_BASE_ADDRESS); j += 32)
|
|
|
|
|
{
|
|
|
|
|
PowerPC::ppcState.iCache.Invalidate(INSTALLER_BASE_ADDRESS + j);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
2010-07-22 01:48:48 +00:00
|
|
|
}
|
|
|
|
|
|
2011-12-31 15:18:48 +11:00
|
|
|
void RunCodeHandler()
|
|
|
|
|
{
|
2016-09-23 13:20:28 +00:00
|
|
|
if (!SConfig::GetInstance().bEnableCheats)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
std::lock_guard<std::mutex> codes_lock(s_active_codes_lock);
|
|
|
|
|
if (s_active_codes.empty())
|
2016-09-15 02:38:48 +00:00
|
|
|
return;
|
|
|
|
|
|
2016-09-23 13:20:28 +00:00
|
|
|
if (!s_code_handler_installed || PowerPC::HostRead_U32(INSTALLER_BASE_ADDRESS) - MAGIC_GAMEID > 5)
|
2016-06-24 10:43:46 +02:00
|
|
|
{
|
2016-09-23 13:20:28 +00:00
|
|
|
s_code_handler_installed = InstallCodeHandlerLocked();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-09-15 02:38:48 +00:00
|
|
|
// A warning was already issued for the install failing
|
2016-09-23 13:20:28 +00:00
|
|
|
if (!s_code_handler_installed)
|
2016-06-24 10:43:46 +02:00
|
|
|
return;
|
2016-09-15 02:38:48 +00:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-09-15 02:38:48 +00:00
|
|
|
// If the last block that just executed ended with a BLR instruction then we can intercept it and
|
|
|
|
|
// redirect control into the Gecko Code Handler. The Code Handler will automatically BLR back to
|
|
|
|
|
// the original return address (which will still be in the link register) at the end.
|
|
|
|
|
if (PC == LR)
|
|
|
|
|
{
|
|
|
|
|
PC = NPC = INSTALLER_BASE_ADDRESS + 0xA8;
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
2010-07-22 01:48:48 +00:00
|
|
|
}
|
|
|
|
|
|
2016-06-24 10:43:46 +02:00
|
|
|
} // namespace Gecko
|