Skip to content
Permalink
Browse files

PatchEngine: Allow patching of files on disk

  • Loading branch information...
phire committed Apr 11, 2019
1 parent 372b855 commit f64bc5afae6aeb8c88cf5610c704c83c6b4c460b
@@ -505,10 +505,11 @@ bool CBoot::BootUp(std::unique_ptr<BootParameters> boot)
const SConfig& config;
};

PatchEngine::LoadPatches();

if (!std::visit(BootTitle(), boot->parameters))
return false;

PatchEngine::LoadPatches();
HLE::PatchFixedFunctions();
return true;
}
@@ -31,6 +31,7 @@
#include "Core/HW/Memmap.h"
#include "Core/HW/SystemTimers.h"
#include "Core/IOS/ES/Formats.h"
#include "Core/PatchEngine.h"

#include "DiscIO/Enums.h"
#include "DiscIO/Volume.h"
@@ -88,6 +89,7 @@ static Common::SPSCQueue<ReadResult, false> s_result_queue;
static std::map<u64, ReadResult> s_result_map;

static std::unique_ptr<DiscIO::Volume> s_disc;
static std::unique_ptr<std::map<u64, std::vector<u8>>> s_disc_patches;

void Start()
{
@@ -179,6 +181,9 @@ void SetDisc(std::unique_ptr<DiscIO::Volume> disc)
{
WaitUntilIdle();
s_disc = std::move(disc);

// Apply any file patches
s_disc_patches = PatchEngine::CalculateDiscPatches(*s_disc);
}

bool HasDisc()
@@ -358,6 +363,37 @@ static void FinishRead(u64 id, s64 cycles_late)
buffer);
}

static void PatchReadRequest(std::vector<u8> &buffer, u64 offset) {
if(!s_disc_patches)
return;

u64 end_offset = offset + buffer.size();

// Scan through patches which might overlap our current read request and apply them
for (auto it = s_disc_patches->begin(); it != s_disc_patches->end(); it++) {
if (it->first > end_offset) // Patch starts after the end of the read request
{
return;
}
else if (it->first >= offset) // Patch starts inside the read request
{
u64 start = it->first - offset;
u32 old_data;
u32 new_data;
std::memcpy(&old_data, buffer.data() + start, 4);
std::memmove(buffer.data() + start, it->second.data(), std::min(it->second.size(), buffer.size() - start));
std::memcpy(&new_data, buffer.data() + start, 4);
INFO_LOG(DVDINTERFACE, "patch applied at %08lx - old: %08x new: %08x", offset + start, old_data, new_data);
}
else if (it->first + it->second.size() > offset) // Patch overlaps the start of the read request
{
u64 start = offset - it->first;
std::memmove(buffer.data(), it->second.data() + start, std::min(it->second.size() - start, buffer.size()));
INFO_LOG(DVDINTERFACE, "patch applied at %08lx", offset);
}
}
}

static void DVDThread()
{
Common::SetCurrentThreadName("DVD thread");
@@ -377,6 +413,9 @@ static void DVDThread()
std::vector<u8> buffer(request.length);
if (!s_disc->Read(request.dvd_offset, request.length, buffer.data(), request.partition))
buffer.resize(0);
else
PatchReadRequest(buffer, request.dvd_offset);


request.realtime_done_us = Common::Timer::GetTimeUs();

@@ -28,6 +28,9 @@
#include "Core/PowerPC/MMU.h"
#include "Core/PowerPC/PowerPC.h"

#include "DiscIO/Filesystem.h"
#include "DiscIO/Volume.h"

namespace PatchEngine
{
constexpr std::array<const char*, 3> s_patch_type_strings{{
@@ -37,6 +40,7 @@ constexpr std::array<const char*, 3> s_patch_type_strings{{
}};

static std::vector<Patch> s_on_frame;
static std::vector<Patch> s_file_patches;
static std::map<u32, int> s_speed_hacks;

const char* PatchTypeAsString(PatchType type)
@@ -88,6 +92,14 @@ void LoadPatchSection(const std::string& section, std::vector<Patch>& patches, I
currentPatch.active = enabledNames.find(currentPatch.name) != enabledNames.end();
currentPatch.user_defined = (ini == &localIni);
}
else if (line[0] == '/') {
// Split mult-file patches
if(!currentPatch.path.empty()) {
patches.push_back(currentPatch);
}
currentPatch.entries.clear();
currentPatch.path = line.substr(1, line.size() - 1);
}
else
{
std::string::size_type loc = line.find('=');
@@ -165,6 +177,7 @@ void LoadPatches()
IniFile localIni = SConfig::GetInstance().LoadLocalGameIni();

LoadPatchSection("OnFrame", s_on_frame, globalIni, localIni);
LoadPatchSection("FilePatch", s_file_patches, globalIni, localIni);

// Check if I'm syncing Codes
if (Config::Get(Config::MAIN_CODE_SYNC_OVERRIDE))
@@ -211,6 +224,52 @@ static void ApplyPatches(const std::vector<Patch>& patches)
}
}

std::unique_ptr<std::map<u64, std::vector<u8>>> CalculateDiscPatches(const DiscIO::Volume &disc) {
if (s_file_patches.empty())
return nullptr;

auto partition = disc.GetGamePartition();
const DiscIO::FileSystem *fs = disc.GetFileSystem(partition);

auto patches = std::make_unique<std::map<u64, std::vector<u8>>>();

for (const auto& patch : s_file_patches) {
if (!patch.active)
continue;

const auto& file = fs->FindFileInfo(patch.path);
if (file) {
for (const auto& entry : patch.entries) {
u64 disc_offset = disc.PartitionOffsetToRawOffset(file->GetOffset(), partition) + entry.address;
std::vector<u8> data;
switch (entry.type)
{
case PatchType::Patch32Bit:
data.push_back(static_cast<u8>(entry.value>>24));
data.push_back(static_cast<u8>(entry.value>>16));
case PatchType::Patch16Bit:
data.push_back(static_cast<u8>(entry.value>>8));
case PatchType::Patch8Bit:
data.push_back(static_cast<u8>(entry.value));
break;
default:
// unknown patch type
// In the future we could add support for larger patch entries.
break;
}

//std::reverse(data.begin(), data.end()); // Reverse for endianness
patches->emplace(disc_offset, data);
}

INFO_LOG(ACTIONREPLAY, "Applied patch '%s' to file '%s'", patch.name.c_str(), patch.path.c_str());
}
}

// Return null
return patches->empty() ? nullptr : std::move(patches);
}

// Requires MSR.DR, MSR.IR
// There's no perfect way to do this, it's just a heuristic.
// We require at least 2 stack frames, if the stack is shallower than that then it won't work.
@@ -261,6 +320,7 @@ bool ApplyFramePatches()
void Shutdown()
{
s_on_frame.clear();
s_file_patches.clear();
s_speed_hacks.clear();
ActionReplay::ApplyCodes({});
Gecko::Shutdown();
@@ -4,11 +4,15 @@

#pragma once

#include <map>
#include <memory>
#include <string>
#include <vector>

#include "Common/CommonTypes.h"

#include "DiscIO/Volume.h"

class IniFile;

namespace PatchEngine
@@ -32,6 +36,7 @@ struct PatchEntry
struct Patch
{
std::string name;
std::string path; // Only for file patches
std::vector<PatchEntry> entries;
bool active = false;
bool user_defined = false; // False if this code is shipped with Dolphin.
@@ -40,6 +45,7 @@ struct Patch
const char* PatchTypeAsString(PatchType type);

int GetSpeedhackCycles(const u32 addr);
std::unique_ptr<std::map<u64, std::vector<u8>>> CalculateDiscPatches(const DiscIO::Volume& disc);
void LoadPatchSection(const std::string& section, std::vector<Patch>& patches, IniFile& globalIni,
IniFile& localIni);
void LoadPatches();

0 comments on commit f64bc5a

Please sign in to comment.
You can’t perform that action at this time.