Mesen/Core/CheatManager.cpp

218 lines
5.4 KiB
C++

#include "stdafx.h"
#include "CheatManager.h"
#include "Console.h"
#include "BaseMapper.h"
#include "MessageManager.h"
#include "NotificationManager.h"
CheatManager::CheatManager(shared_ptr<Console> console)
{
_console = console;
for(int i = 0; i <= 0xFFFF; i++) {
_relativeCheatCodes.push_back(nullptr);
}
}
uint32_t CheatManager::DecodeValue(uint32_t code, uint32_t* bitIndexes, uint32_t bitCount)
{
uint32_t result = 0;
for(uint32_t i = 0; i < bitCount; i++) {
result <<= 1;
result |= (code >> bitIndexes[i]) & 0x01;
}
return result;
}
CodeInfo CheatManager::GetGGCodeInfo(string ggCode)
{
string ggLetters = "APZLGITYEOXUKSVN";
uint32_t rawCode = 0;
for(size_t i = 0, len = ggCode.size(); i < len; i++) {
rawCode |= ggLetters.find(ggCode[i]) << (i * 4);
}
CodeInfo code = { };
code.IsRelativeAddress = true;
code.CompareValue = -1;
uint32_t addressBits[15] = { 14, 13, 12, 19, 22, 21, 20, 7, 10, 9, 8, 15, 18, 17, 16 };
uint32_t valueBits[8] = { 3, 6, 5, 4, 23, 2, 1, 0 };
if(ggCode.size() == 8) {
//Bit 5 of the value is stored in a different location for 8-character codes
valueBits[4] = 31;
uint32_t compareValueBits[8] = { 27, 30, 29, 28, 23, 26, 25, 24 };
code.CompareValue = DecodeValue(rawCode, compareValueBits, 8);
}
code.Address = DecodeValue(rawCode, addressBits, 15) + 0x8000;
code.Value = DecodeValue(rawCode, valueBits, 8);
return code;
}
CodeInfo CheatManager::GetPARCodeInfo(uint32_t parCode)
{
uint32_t shiftValues[31] = {
3, 13, 14, 1, 6, 9, 5, 0, 12, 7, 2, 8, 10, 11, 4, //address
19, 21, 23, 22, 20, 17, 16, 18, //compare
29, 31, 24, 26, 25, 30, 27, 28 //value
};
uint32_t key = 0x7E5EE93A;
uint32_t xorValue = 0x5C184B91;
//Throw away bit 0, not used.
parCode >>= 1;
uint32_t result = 0;
for(int32_t i = 30; i >= 0; i--) {
if(((key ^ parCode) >> 30) & 0x01) {
result |= 0x01 << shiftValues[i];
key ^= xorValue;
}
parCode <<= 1;
key <<= 1;
}
CodeInfo code = { };
code.IsRelativeAddress = true;
code.Address = (result & 0x7fff) + 0x8000;
code.Value = (result >> 24) & 0xFF;
code.CompareValue = (result >> 16) & 0xFF;
return code;
}
void CheatManager::AddCode(CodeInfo &code)
{
if(code.IsRelativeAddress) {
if(code.Address > 0xFFFF) {
//Invalid cheat, ignore it
return;
}
if(_relativeCheatCodes[code.Address] == nullptr) {
_relativeCheatCodes[code.Address].reset(new vector<CodeInfo>());
}
_relativeCheatCodes[code.Address]->push_back(code);
} else {
_absoluteCheatCodes.push_back(code);
}
_hasCode = true;
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::CheatAdded);
}
void CheatManager::AddGameGenieCode(string code)
{
CodeInfo info = GetGGCodeInfo(code);
AddCode(info);
}
void CheatManager::AddProActionRockyCode(uint32_t code)
{
CodeInfo info = GetPARCodeInfo(code);
AddCode(info);
}
void CheatManager::AddCustomCode(uint32_t address, uint8_t value, int32_t compareValue, bool isRelativeAddress)
{
CodeInfo code;
code.Address = address;
code.Value = value;
code.CompareValue = compareValue;
code.IsRelativeAddress = isRelativeAddress;
AddCode(code);
}
void CheatManager::ClearCodes()
{
bool cheatRemoved = false;
for(int i = 0; i <= 0xFFFF; i++) {
if(!_relativeCheatCodes[i]) {
cheatRemoved = true;
}
_relativeCheatCodes[i].reset();
}
cheatRemoved |= _absoluteCheatCodes.size() > 0;
_absoluteCheatCodes.clear();
_hasCode = false;
if(cheatRemoved) {
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::CheatRemoved);
}
}
void CheatManager::ApplyCodes(uint16_t addr, uint8_t &value)
{
if(!_hasCode) {
return;
}
if(_relativeCheatCodes[addr] != nullptr) {
for(uint32_t i = 0, len = i < _relativeCheatCodes[addr]->size(); i < len; i++) {
CodeInfo code = _relativeCheatCodes[addr]->at(i);
if(code.CompareValue == -1 || code.CompareValue == value) {
value = code.Value;
return;
}
}
} else if(!_absoluteCheatCodes.empty()) {
int32_t absAddr = _console->GetMapper()->ToAbsoluteAddress(addr);
if(absAddr >= 0) {
for(CodeInfo &code : _absoluteCheatCodes) {
if(code.Address == (uint32_t)absAddr && (code.CompareValue == -1 || code.CompareValue == value)) {
value = code.Value;
return;
}
}
}
}
}
vector<CodeInfo> CheatManager::GetCheats()
{
//Used by NetPlay
vector<CodeInfo> cheats;
for(unique_ptr<vector<CodeInfo>> &codes : _relativeCheatCodes) {
if(codes) {
std::copy(codes.get()->begin(), codes.get()->end(), std::back_inserter(cheats));
}
}
std::copy(_absoluteCheatCodes.begin(), _absoluteCheatCodes.end(), std::back_inserter(cheats));
return cheats;
}
void CheatManager::SetCheats(CheatInfo cheats[], uint32_t length)
{
_console->Pause();
ClearCodes();
for(uint32_t i = 0; i < length; i++) {
CheatInfo &cheat = cheats[i];
switch(cheat.Type) {
case CheatType::Custom: AddCustomCode(cheat.Address, cheat.Value, cheat.UseCompareValue ? cheat.CompareValue : -1, cheat.IsRelativeAddress); break;
case CheatType::GameGenie: AddGameGenieCode(cheat.GameGenieCode); break;
case CheatType::ProActionRocky: AddProActionRockyCode(cheat.ProActionRockyCode); break;
}
}
_console->Resume();
}
void CheatManager::SetCheats(vector<CodeInfo> &cheats)
{
//Used by NetPlay
ClearCodes();
if(cheats.size() > 0) {
MessageManager::DisplayMessage("Cheats", cheats.size() > 1 ? "CheatsApplied" : "CheatApplied", std::to_string(cheats.size()));
for(CodeInfo &cheat : cheats) {
AddCode(cheat);
}
}
}