|
|
|
@ -2,9 +2,15 @@ |
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <cstring>
|
|
|
|
#include <map>
|
|
|
|
#include <sstream>
|
|
|
|
#include "common/assert.h"
|
|
|
|
#include <string>
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
#include "common/hex_util.h"
|
|
|
|
#include "common/logging/log.h"
|
|
|
|
#include "common/swap.h"
|
|
|
|
#include "core/file_sys/ips_layer.h"
|
|
|
|
#include "core/file_sys/vfs_vector.h"
|
|
|
|
@ -17,22 +23,48 @@ enum class IPSFileType { |
|
|
|
Error, |
|
|
|
}; |
|
|
|
|
|
|
|
constexpr std::array<std::pair<const char*, const char*>, 11> ESCAPE_CHARACTER_MAP{ |
|
|
|
std::pair{"\\a", "\a"}, {"\\b", "\b"}, {"\\f", "\f"}, {"\\n", "\n"}, |
|
|
|
{"\\r", "\r"}, {"\\t", "\t"}, {"\\v", "\v"}, {"\\\\", "\\"}, |
|
|
|
{"\\\'", "\'"}, {"\\\"", "\""}, {"\\\?", "\?"}, |
|
|
|
}; |
|
|
|
constexpr std::array<std::pair<const char*, const char*>, 11> ESCAPE_CHARACTER_MAP{{ |
|
|
|
{"\\a", "\a"}, |
|
|
|
{"\\b", "\b"}, |
|
|
|
{"\\f", "\f"}, |
|
|
|
{"\\n", "\n"}, |
|
|
|
{"\\r", "\r"}, |
|
|
|
{"\\t", "\t"}, |
|
|
|
{"\\v", "\v"}, |
|
|
|
{"\\\\", "\\"}, |
|
|
|
{"\\\'", "\'"}, |
|
|
|
{"\\\"", "\""}, |
|
|
|
{"\\\?", "\?"}, |
|
|
|
}}; |
|
|
|
|
|
|
|
static IPSFileType IdentifyMagic(const std::vector<u8>& magic) { |
|
|
|
if (magic.size() != 5) |
|
|
|
if (magic.size() != 5) { |
|
|
|
return IPSFileType::Error; |
|
|
|
if (magic == std::vector<u8>{'P', 'A', 'T', 'C', 'H'}) |
|
|
|
} |
|
|
|
|
|
|
|
constexpr std::array<u8, 5> patch_magic{{'P', 'A', 'T', 'C', 'H'}}; |
|
|
|
if (std::equal(magic.begin(), magic.end(), patch_magic.begin())) { |
|
|
|
return IPSFileType::IPS; |
|
|
|
if (magic == std::vector<u8>{'I', 'P', 'S', '3', '2'}) |
|
|
|
} |
|
|
|
|
|
|
|
constexpr std::array<u8, 5> ips32_magic{{'I', 'P', 'S', '3', '2'}}; |
|
|
|
if (std::equal(magic.begin(), magic.end(), ips32_magic.begin())) { |
|
|
|
return IPSFileType::IPS32; |
|
|
|
} |
|
|
|
|
|
|
|
return IPSFileType::Error; |
|
|
|
} |
|
|
|
|
|
|
|
static bool IsEOF(IPSFileType type, const std::vector<u8>& data) { |
|
|
|
constexpr std::array<u8, 3> eof{{'E', 'O', 'F'}}; |
|
|
|
if (type == IPSFileType::IPS && std::equal(data.begin(), data.end(), eof.begin())) { |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
constexpr std::array<u8, 4> eeof{{'E', 'E', 'O', 'F'}}; |
|
|
|
return type == IPSFileType::IPS32 && std::equal(data.begin(), data.end(), eeof.begin()); |
|
|
|
} |
|
|
|
|
|
|
|
VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { |
|
|
|
if (in == nullptr || ips == nullptr) |
|
|
|
return nullptr; |
|
|
|
@ -47,8 +79,7 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { |
|
|
|
u64 offset = 5; // After header
|
|
|
|
while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) { |
|
|
|
offset += temp.size(); |
|
|
|
if (type == IPSFileType::IPS32 && temp == std::vector<u8>{'E', 'E', 'O', 'F'} || |
|
|
|
type == IPSFileType::IPS && temp == std::vector<u8>{'E', 'O', 'F'}) { |
|
|
|
if (IsEOF(type, temp)) { |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
@ -88,11 +119,20 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (temp != std::vector<u8>{'E', 'E', 'O', 'F'} && temp != std::vector<u8>{'E', 'O', 'F'}) |
|
|
|
if (!IsEOF(type, temp)) { |
|
|
|
return nullptr; |
|
|
|
return std::make_shared<VectorVfsFile>(in_data, in->GetName(), in->GetContainingDirectory()); |
|
|
|
} |
|
|
|
|
|
|
|
return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(), |
|
|
|
in->GetContainingDirectory()); |
|
|
|
} |
|
|
|
|
|
|
|
struct IPSwitchCompiler::IPSwitchPatch { |
|
|
|
std::string name; |
|
|
|
bool enabled; |
|
|
|
std::map<u32, std::vector<u8>> records; |
|
|
|
}; |
|
|
|
|
|
|
|
IPSwitchCompiler::IPSwitchCompiler(VirtualFile patch_text_) : patch_text(std::move(patch_text_)) { |
|
|
|
Parse(); |
|
|
|
} |
|
|
|
@ -291,7 +331,8 @@ VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return std::make_shared<VectorVfsFile>(in_data, in->GetName(), in->GetContainingDirectory()); |
|
|
|
return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(), |
|
|
|
in->GetContainingDirectory()); |
|
|
|
} |
|
|
|
|
|
|
|
} // namespace FileSys
|