forked from dolphin-emu/dolphin
		
	
		
			
				
	
	
		
			192 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			192 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// Copyright 2010 Dolphin Emulator Project
 | 
						|
// Licensed under GPLv2+
 | 
						|
// Refer to the license.txt file included.
 | 
						|
 | 
						|
#include <mutex>
 | 
						|
#include <vector>
 | 
						|
 | 
						|
#include "Common/CommonPaths.h"
 | 
						|
#include "Common/FileUtil.h"
 | 
						|
#include "Common/Thread.h"
 | 
						|
 | 
						|
#include "Core/ConfigManager.h"
 | 
						|
#include "Core/GeckoCode.h"
 | 
						|
#include "Core/HW/Memmap.h"
 | 
						|
#include "Core/PowerPC/PowerPC.h"
 | 
						|
 | 
						|
namespace Gecko
 | 
						|
{
 | 
						|
 | 
						|
static const u32 INSTALLER_BASE_ADDRESS = 0x80001800;
 | 
						|
static const u32 INSTALLER_END_ADDRESS = 0x80003000;
 | 
						|
 | 
						|
// return true if a code exists
 | 
						|
bool GeckoCode::Exist(u32 address, u32 data) const
 | 
						|
{
 | 
						|
	for (const GeckoCode::Code& code : codes)
 | 
						|
	{
 | 
						|
		if (code.address == address && code.data == data)
 | 
						|
			return true;
 | 
						|
	}
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
// return true if the code is identical
 | 
						|
bool GeckoCode::Compare(const GeckoCode& compare) const
 | 
						|
{
 | 
						|
	if (codes.size() != compare.codes.size())
 | 
						|
		return false;
 | 
						|
 | 
						|
	unsigned int exist = 0;
 | 
						|
 | 
						|
	for (const GeckoCode::Code& code : codes)
 | 
						|
	{
 | 
						|
		if (compare.Exist(code.address, code.data))
 | 
						|
			exist++;
 | 
						|
	}
 | 
						|
 | 
						|
	return exist == codes.size();
 | 
						|
}
 | 
						|
 | 
						|
static bool code_handler_installed = false;
 | 
						|
// the currently active codes
 | 
						|
static std::vector<GeckoCode> active_codes;
 | 
						|
static std::mutex active_codes_lock;
 | 
						|
 | 
						|
void SetActiveCodes(const std::vector<GeckoCode>& gcodes)
 | 
						|
{
 | 
						|
	std::lock_guard<std::mutex> lk(active_codes_lock);
 | 
						|
 | 
						|
	active_codes.clear();
 | 
						|
 | 
						|
	// add enabled codes
 | 
						|
	for (const GeckoCode& gecko_code : gcodes)
 | 
						|
	{
 | 
						|
		if (gecko_code.enabled)
 | 
						|
		{
 | 
						|
			// TODO: apply modifiers
 | 
						|
			// TODO: don't need description or creator string, just takin up memory
 | 
						|
			active_codes.push_back(gecko_code);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	code_handler_installed = false;
 | 
						|
}
 | 
						|
 | 
						|
static bool InstallCodeHandler()
 | 
						|
{
 | 
						|
	std::string data;
 | 
						|
	std::string _rCodeHandlerFilename = File::GetSysDirectory() + GECKO_CODE_HANDLER;
 | 
						|
	if (!File::ReadFileToString(_rCodeHandlerFilename, data))
 | 
						|
	{
 | 
						|
		NOTICE_LOG(ACTIONREPLAY, "Could not enable cheats because codehandler.bin was missing.");
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	u8 mmioAddr = 0xCC;
 | 
						|
 | 
						|
	if (SConfig::GetInstance().bWii)
 | 
						|
	{
 | 
						|
		mmioAddr = 0xCD;
 | 
						|
	}
 | 
						|
 | 
						|
	// Install code handler
 | 
						|
	for (size_t i = 0, e = data.length(); i < e; ++i)
 | 
						|
		PowerPC::HostWrite_U8(data[i], (u32)(INSTALLER_BASE_ADDRESS + i));
 | 
						|
 | 
						|
	// Patch the code handler to the system starting up
 | 
						|
	for (unsigned int h = 0; h < data.length(); h += 4)
 | 
						|
	{
 | 
						|
		// Patch MMIO address
 | 
						|
		if (PowerPC::HostRead_U32(INSTALLER_BASE_ADDRESS + h) == (0x3f000000u | ((mmioAddr ^ 1) << 8)))
 | 
						|
		{
 | 
						|
			NOTICE_LOG(ACTIONREPLAY, "Patching MMIO access at %08x", INSTALLER_BASE_ADDRESS + h);
 | 
						|
			PowerPC::HostWrite_U32(0x3f000000u | mmioAddr << 8, INSTALLER_BASE_ADDRESS + h);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	u32 codelist_base_address = INSTALLER_BASE_ADDRESS + (u32)data.length() - 8;
 | 
						|
	u32 codelist_end_address = INSTALLER_END_ADDRESS;
 | 
						|
 | 
						|
	// Write a magic value to 'gameid' (codehandleronly does not actually read this).
 | 
						|
	PowerPC::HostWrite_U32(0xd01f1bad, INSTALLER_BASE_ADDRESS);
 | 
						|
 | 
						|
	// Create GCT in memory
 | 
						|
	PowerPC::HostWrite_U32(0x00d0c0de, codelist_base_address);
 | 
						|
	PowerPC::HostWrite_U32(0x00d0c0de, codelist_base_address + 4);
 | 
						|
 | 
						|
	std::lock_guard<std::mutex> lk(active_codes_lock);
 | 
						|
 | 
						|
	int i = 0;
 | 
						|
 | 
						|
	for (const GeckoCode& active_code : active_codes)
 | 
						|
	{
 | 
						|
		if (active_code.enabled)
 | 
						|
		{
 | 
						|
			for (const GeckoCode::Code& code : active_code.codes)
 | 
						|
			{
 | 
						|
				// Make sure we have enough memory to hold the code list
 | 
						|
				if ((codelist_base_address + 24 + i) < codelist_end_address)
 | 
						|
				{
 | 
						|
					PowerPC::HostWrite_U32(code.address, codelist_base_address + 8 + i);
 | 
						|
					PowerPC::HostWrite_U32(code.data, codelist_base_address + 12 + i);
 | 
						|
					i += 8;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	PowerPC::HostWrite_U32(0xff000000, codelist_base_address + 8 + i);
 | 
						|
	PowerPC::HostWrite_U32(0x00000000, codelist_base_address + 12 + i);
 | 
						|
 | 
						|
	// 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);
 | 
						|
	}
 | 
						|
	for (unsigned int k = codelist_base_address; k < codelist_end_address; k += 32)
 | 
						|
	{
 | 
						|
		PowerPC::ppcState.iCache.Invalidate(k);
 | 
						|
	}
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
void RunCodeHandler()
 | 
						|
{
 | 
						|
	if (SConfig::GetInstance().bEnableCheats && active_codes.size() > 0)
 | 
						|
	{
 | 
						|
		if (!code_handler_installed || PowerPC::HostRead_U32(INSTALLER_BASE_ADDRESS) - 0xd01f1bad > 5)
 | 
						|
			code_handler_installed = InstallCodeHandler();
 | 
						|
 | 
						|
		if (!code_handler_installed)
 | 
						|
		{
 | 
						|
			// A warning was already issued.
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		if (PC == LR)
 | 
						|
		{
 | 
						|
			u32 oldLR = LR;
 | 
						|
			PowerPC::CoreMode oldMode = PowerPC::GetMode();
 | 
						|
 | 
						|
			PC = INSTALLER_BASE_ADDRESS + 0xA8;
 | 
						|
			LR = 0;
 | 
						|
 | 
						|
			// Execute the code handler in interpreter mode to track when it exits
 | 
						|
			PowerPC::SetMode(PowerPC::MODE_INTERPRETER);
 | 
						|
 | 
						|
			while (PC != 0)
 | 
						|
				PowerPC::SingleStep();
 | 
						|
 | 
						|
			PowerPC::SetMode(oldMode);
 | 
						|
			PC = LR = oldLR;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
} // namespace Gecko
 |