// Copyright 2008 Dolphin Emulator Project // Licensed under GPLv2+ // Refer to the license.txt file included. // PatchEngine // Supports simple memory patches, and has a partial Action Replay implementation // in ActionReplay.cpp/h. // TODO: Still even needed? Zelda WW now works with improved DSP code. // Zelda item hang fixes: // [Tue Aug 21 2007] [18:30:40] 0x802904b4 in US released // [Tue Aug 21 2007] [18:30:53] 0x80294d54 in EUR Demo version // [Tue Aug 21 2007] [18:31:10] we just patch a blr on it (0x4E800020) // [OnLoad] // 0x80020394=dword,0x4e800020 #include #include #include #include #include #include "Common/CommonPaths.h" #include "Common/FileUtil.h" #include "Common/IniFile.h" #include "Common/StringUtil.h" #include "Core/ActionReplay.h" #include "Core/ConfigManager.h" #include "Core/GeckoCode.h" #include "Core/GeckoCodeConfig.h" #include "Core/PatchEngine.h" #include "Core/PowerPC/PowerPC.h" using namespace Common; namespace PatchEngine { const char* PatchTypeStrings[] = { "byte", "word", "dword", }; static std::vector onFrame; static std::map speedHacks; void LoadPatchSection(const std::string& section, std::vector& patches, IniFile& globalIni, IniFile& localIni) { // Load the name of all enabled patches std::string enabledSectionName = section + "_Enabled"; std::vector enabledLines; std::set enabledNames; localIni.GetLines(enabledSectionName, &enabledLines); for (const std::string& line : enabledLines) { if (line.size() != 0 && line[0] == '$') { std::string name = line.substr(1, line.size() - 1); enabledNames.insert(name); } } const IniFile* inis[2] = {&globalIni, &localIni}; for (const IniFile* ini : inis) { std::vector lines; Patch currentPatch; ini->GetLines(section, &lines); for (std::string& line : lines) { if (line.size() == 0) continue; if (line[0] == '$') { // Take care of the previous code if (currentPatch.name.size()) { patches.push_back(currentPatch); } currentPatch.entries.clear(); // Set active and name currentPatch.name = line.substr(1, line.size() - 1); currentPatch.active = enabledNames.find(currentPatch.name) != enabledNames.end(); currentPatch.user_defined = (ini == &localIni); } else { std::string::size_type loc = line.find('='); if (loc != std::string::npos) { line[loc] = ':'; } std::vector items; SplitString(line, ':', items); if (items.size() >= 3) { PatchEntry pE; bool success = true; success &= TryParse(items[0], &pE.address); success &= TryParse(items[2], &pE.value); pE.type = PatchType(std::find(PatchTypeStrings, PatchTypeStrings + 3, items[1]) - PatchTypeStrings); success &= (pE.type != (PatchType)3); if (success) { currentPatch.entries.push_back(pE); } } } } if (currentPatch.name.size() && currentPatch.entries.size()) { patches.push_back(currentPatch); } } } static void LoadSpeedhacks(const std::string& section, IniFile& ini) { std::vector keys; ini.GetKeys(section, &keys); for (const std::string& key : keys) { std::string value; ini.GetOrCreateSection(section)->Get(key, &value, "BOGUS"); if (value != "BOGUS") { u32 address; u32 cycles; bool success = true; success &= TryParse(key, &address); success &= TryParse(value, &cycles); if (success) { speedHacks[address] = (int)cycles; } } } } int GetSpeedhackCycles(const u32 addr) { std::map::const_iterator iter = speedHacks.find(addr); if (iter == speedHacks.end()) return 0; else return iter->second; } void LoadPatches() { IniFile merged = SConfig::GetInstance().LoadGameIni(); IniFile globalIni = SConfig::GetInstance().LoadDefaultGameIni(); IniFile localIni = SConfig::GetInstance().LoadLocalGameIni(); LoadPatchSection("OnFrame", onFrame, globalIni, localIni); ActionReplay::LoadAndApplyCodes(globalIni, localIni); // lil silly std::vector gcodes; Gecko::LoadCodes(globalIni, localIni, gcodes); Gecko::SetActiveCodes(gcodes); LoadSpeedhacks("Speedhacks", merged); } static void ApplyPatches(const std::vector& patches) { for (const Patch& patch : patches) { if (patch.active) { for (const PatchEntry& entry : patch.entries) { u32 addr = entry.address; u32 value = entry.value; switch (entry.type) { case PATCH_8BIT: PowerPC::HostWrite_U8((u8)value, addr); break; case PATCH_16BIT: PowerPC::HostWrite_U16((u16)value, addr); break; case PATCH_32BIT: PowerPC::HostWrite_U32(value, addr); break; default: // unknown patchtype break; } } } } } void ApplyFramePatches() { // TODO: Messing with MSR this way is really, really, evil; we should // probably be using some sort of Gecko OS-style hooking mechanism // so the emulated CPU is in a predictable state when we process cheats. u32 oldMSR = MSR; UReg_MSR newMSR = oldMSR; newMSR.IR = 1; newMSR.DR = 1; MSR = newMSR.Hex; ApplyPatches(onFrame); // Run the Gecko code handler Gecko::RunCodeHandler(); ActionReplay::RunAllActive(); MSR = oldMSR; } void Shutdown() { onFrame.clear(); speedHacks.clear(); ActionReplay::ApplyCodes({}); Gecko::SetActiveCodes({}); } } // namespace