// Copyright (C) 2003 Dolphin Project. // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, version 2.0. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License 2.0 for more details. // A copy of the GPL 2.0 should have been included with the program. // If not, see http://www.gnu.org/licenses/ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ // 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 "StringUtil.h" #include "PatchEngine.h" #include "HW/Memmap.h" #include "ActionReplay.h" #include "GeckoCode.h" #include "GeckoCodeConfig.h" #include "FileUtil.h" using namespace Common; namespace PatchEngine { const char *PatchTypeStrings[] = { "byte", "word", "dword", }; std::vector onFrame; std::map speedHacks; std::vector discList; void LoadPatchSection(const char *section, std::vector &patches, IniFile &ini) { std::vector lines; if (!ini.GetLines(section, lines)) return; Patch currentPatch; for (std::vector::const_iterator iter = lines.begin(); iter != lines.end(); ++iter) { std::string line = *iter; if (line.size()) { if (line[0] == '+' || line[0] == '$') { // Take care of the previous code if (currentPatch.name.size()) patches.push_back(currentPatch); currentPatch.entries.clear(); // Set active and name currentPatch.active = (line[0] == '+') ? true : false; if (currentPatch.active) currentPatch.name = line.substr(2, line.size() - 2); else currentPatch.name = line.substr(1, line.size() - 1); continue; } std::string::size_type loc = line.find_first_of('=', 0); if (loc != std::string::npos) line.at(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()) patches.push_back(currentPatch); } static void LoadDiscList(const char *section, std::vector &_discList, IniFile &ini) { std::vector lines; if (!ini.GetLines(section, lines)) return; for (std::vector::const_iterator iter = lines.begin(); iter != lines.end(); ++iter) { std::string line = *iter; if (line.size()) _discList.push_back(line); } } static void LoadSpeedhacks(const char *section, std::map &hacks, IniFile &ini) { std::vector keys; ini.GetKeys(section, keys); for (std::vector::const_iterator iter = keys.begin(); iter != keys.end(); ++iter) { std::string key = *iter; std::string value; ini.Get(section, key.c_str(), &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(const char *gameID) { IniFile ini; std::string filename = std::string(File::GetUserPath(D_GAMECONFIG_IDX)) + gameID + ".ini"; if (ini.Load(filename.c_str())) { LoadPatchSection("OnFrame", onFrame, ini); ActionReplay::LoadCodes(ini, false); // lil silly std::vector gcodes; Gecko::LoadCodes(ini, gcodes); Gecko::SetActiveCodes(gcodes); LoadSpeedhacks("Speedhacks", speedHacks, ini); LoadDiscList("DiscList", discList, ini); } } void ApplyPatches(const std::vector &patches) { for (std::vector::const_iterator iter = patches.begin(); iter != patches.end(); ++iter) { if (iter->active) { for (std::vector::const_iterator iter2 = iter->entries.begin(); iter2 != iter->entries.end(); ++iter2) { u32 addr = iter2->address; u32 value = iter2->value; switch (iter2->type) { case PATCH_8BIT: Memory::Write_U8((u8)value, addr); break; case PATCH_16BIT: Memory::Write_U16((u16)value, addr); break; case PATCH_32BIT: Memory::Write_U32(value, addr); break; default: //unknown patchtype break; } } } } } void ApplyFramePatches() { ApplyPatches(onFrame); } void ApplyARPatches() { ActionReplay::RunAllActive(); // w/e this can be changed later Gecko::RunActiveCodes(); } } // namespace