-
-
Save sedmelluq/7dc8cc5d7b78c3d42deca216f35e7c5c to your computer and use it in GitHub Desktop.
Mod limit patcher
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Windows.h> | |
#include <string> | |
#include <filesystem> | |
#include <fstream> | |
#include <cstdint> | |
namespace fs = std::experimental::filesystem; | |
static const uint64_t address_not_found = 0; | |
static const uint64_t address_already_patched = 1; | |
static const uint64_t check_range_start = 0xB94000; | |
static const uint64_t check_range_end = 0xB98000; | |
static const uint64_t patch_offset_in_sequence = 1; | |
static const uint32_t patched_limit = 500; | |
static const std::vector<uint64_t> known_candidates = { 0xB95CF6, 0xB96366, 0xB967A6, 0xB95E56 }; | |
static const std::vector<uint8_t> unpatched_sequence = { 0xBA, 0xC0, 0x00, 0x00, 0x00, 0x48, 0x8D, 0x4B }; | |
static const std::vector<uint8_t> patched_sequence = { 0xBA, 0xF4, 0x01, 0x00, 0x00, 0x48, 0x8D, 0x4B }; | |
uint64_t find_patch_address(const std::vector<uint8_t>& search_buffer) { | |
for (auto candidate : known_candidates) { | |
size_t candidate_range_start = candidate - check_range_start - patch_offset_in_sequence; | |
std::vector<uint8_t> checked_bytes(&search_buffer[candidate_range_start], &search_buffer[candidate_range_start + unpatched_sequence.size()]); | |
if (checked_bytes == unpatched_sequence) { | |
return candidate; | |
} else if (checked_bytes == patched_sequence) { | |
return address_already_patched; | |
} | |
} | |
for (size_t i = 0; i < search_buffer.size() - unpatched_sequence.size(); i++) { | |
std::vector<uint8_t> checked_bytes(&search_buffer[i], &search_buffer[i + unpatched_sequence.size()]); | |
if (checked_bytes == unpatched_sequence) { | |
return i + check_range_start + patch_offset_in_sequence; | |
} | |
} | |
for (size_t i = 0; i < search_buffer.size() - patched_sequence.size(); i++) { | |
std::vector<uint8_t> checked_bytes(&search_buffer[i], &search_buffer[i + patched_sequence.size()]); | |
if (checked_bytes == patched_sequence) { | |
return address_already_patched; | |
} | |
} | |
return address_not_found; | |
} | |
int WINAPI wWinMain(HINSTANCE instance, HINSTANCE previous_instance, PWSTR command_line, int show_command) { | |
auto path = fs::absolute(fs::current_path() / "witcher3.exe"); | |
if (!fs::exists(path)) { | |
MessageBoxW(nullptr, L"Could not find witcher3.exe file, make sure you are running this in the bin/x64 directory.", L"Error", MB_OK); | |
return 0; | |
} | |
size_t read_size = check_range_end - check_range_start; | |
std::vector<uint8_t> search_buffer(read_size); | |
{ | |
std::ifstream file(path.wstring(), std::ios::binary); | |
file.seekg(check_range_start); | |
if (file.eof() || !file.read(reinterpret_cast<char*>(search_buffer.data()), read_size)) { | |
MessageBoxW(nullptr, L"witcher3.exe is unexpectedly small, aborting patching.", L"Error", MB_OK); | |
return 0; | |
} | |
} | |
uint64_t patch_address = find_patch_address(search_buffer); | |
if (patch_address == address_not_found) { | |
MessageBoxW(nullptr, L"Did not find the address to patch, possibly an unexpected version of executable. Contact sedmelluq and give him your exe.", L"Error", MB_OK); | |
return 0; | |
} else if (patch_address == address_already_patched) { | |
MessageBoxW(nullptr, L"It seems the executable is already patched", L"Error", MB_OK); | |
return 0; | |
} | |
auto backup_path = fs::absolute(fs::current_path() / "witcher3.exe.orig"); | |
if (fs::exists(backup_path)) { | |
MessageBoxW(nullptr, L"Backup file witcher3.exe.orig already exists. Rename it or delete it (might be left over from previous patch attempt).", L"Error", MB_OK); | |
return 0; | |
} else if (!fs::copy_file(path, backup_path)) { | |
MessageBoxW(nullptr, L"Failed to create backup file, try running as administrator.", L"Error", MB_OK); | |
return 0; | |
} | |
{ | |
std::fstream file(path.wstring(), std::fstream::binary | std::fstream::in | std::fstream::out); | |
file.seekp(patch_address); | |
if (file.eof()) { | |
MessageBoxW(nullptr, L"Unexpectedly encountered file end when writing patch.", L"Error", MB_OK); | |
} else if (!file.write((char*)& patched_limit, sizeof(patched_limit))) { | |
MessageBoxW(nullptr, L"Failed to write patch", L"Error", MB_OK); | |
} else { | |
MessageBoxW(nullptr, L"Successfully patched.", L"Success", MB_OK); | |
} | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment