#include #include #include struct Patch { char *orig; char *patch; int size; }; char * memmem(char *src, int src_size, char *pattern, int pattern_size); int main(int argc, char **argv) { const static Patch PATCHES[] = { //Patches for map tooltip //Is unplayable religion { { "\x80\x78\x71\x00" //CMP [EAX+71], 0 "\x0F\x85\xD7\x00\x00\x00" //JNE +0xD7 }, { "\x80\x78\x71\x00" //CMP [EAX+71], 0 "\xE9\xD8\x00\x00\x00" //JMP +0xD8 "\x90" //NOP }, 10 }, //Is a republic { { "\x83\xFE\x01" //CMP ESI, 1 "\x0F\x85\x36\x01\x00\x00" //JNE +0x136 }, { "\x83\xFE\x01" //CMP ESI, 1 "\xE9\x37\x01\x00\x00" //JMP +0x137 "\x90" //NOP }, 9 }, //Is a theocracy { { "\x83\xFE\x02" //CMP ESI, 2 "\x74\x33" //JE +0x33 }, { "\x83\xFE\x02" //CMP ESI, 2 "\x90" //NOP "\x90" //NOP }, 5 }, //Playable test? - jumps to RETN { { "\x80\xB8\xF8\x02\x00\x00\x00" //CMP [EAX+2F8], 0 "\x0F\x84\x23\xFD\xFF\xFF" //JE -0x2DD }, { "\x80\xB8\xF8\x02\x00\x00\x00" //CMP [EAX+2F8], 0 "\xE9\x24\xFD\xFF\xFF" //JMP -0x2DC "\x90" //NOP }, 13 }, //Patches for "Play" button //Is a theocracy { { "\x38\x98\xF8\x02\x00\x00" //CMP [EAX+2F8], BL "\x0F\x85\x15\x01\x00\x00" //JNE +0x115 }, { "\x38\x98\xF8\x02\x00\x00" //CMP [EAX+2F8], BL "\x90" //NOP "\x90" //NOP "\x90" //NOP "\x90" //NOP "\x90" //NOP "\x90" //NOP }, 12 }, //Is unplayable religion //Is a republic { { "\x0F\x84\xAD\x00\x00\x00" //JE +0xAD "\x39\x5D\xE8" //CMP [EBP-18], BL "\x0F\x85\xA4\x00\x00\x00" //JNE +0xA4 }, { "\x90" //NOP "\x90" //NOP "\x90" //NOP "\x90" //NOP "\x90" //NOP "\x90" //NOP "\x39\x5D\xE8" //CMP [EBP-18], BL "\x90" //NOP "\x90" //NOP "\x90" //NOP "\x90" //NOP "\x90" //NOP "\x90" //NOP }, 15 }, //Unplayable tooltip on button hover { { "\x3B\xF3" //CMP ESI, EBX "\x0F\x84\x87\x07\x00\x00" //JE +0x787 }, { "\x3B\xF3" //CMP ESI, EBX "\xE9\x88\x07\x00\x00" //JMP +0x788 "\x90" //NOP }, 8 }, }; const static int NUM_PATCHES = 7; if (argc != 3) { std::cerr << "Invalid arguments" << std::endl; std::cerr << "Usage: " << argv[0] << " " << " " << std::endl; return 1; } std::fstream in_file; in_file.open(argv[1], std::ios::in | std::ios::binary | std::ios::ate); if (!in_file.is_open()) { std::cerr << "Unable to open " << argv[1] << " for reading" << std::endl; return 1; } int file_size = in_file.tellg(); char *buf = new char[file_size]; in_file.seekg(0, std::ios::beg); in_file.read(buf, file_size); in_file.close(); char *patch_point; for (int i = 0; i < NUM_PATCHES; i++) { const Patch *p = &PATCHES[i]; patch_point = memmem(buf, file_size, p->orig, p->size); if (patch_point != NULL) { std::cout << "Patching " << p->size << " bytes at offset " << (int) patch_point - (int) buf << std::endl; std::memcpy(patch_point, p->patch, p->size); } else { std::cerr << "Failed to find patch location " << i << ", dying" << std::endl; return 1; } } std::fstream out_file; out_file.open(argv[2], std::ios::out | std::ios::binary | std::ios::trunc); if (!out_file.is_open()) { std::cerr << "Unable to open " << argv[2] << " for writing" << std::endl; return 1; } out_file.write(buf, file_size); out_file.close(); delete[] buf; return 0; } char * memmem(char *src, int src_size, char *pattern, int pattern_size) { char *pos = src; do { pos = (char *) std::memchr(pos, pattern[0], src + src_size - pos); if (pos != NULL) { if (pos + pattern_size < src + src_size) { if (std::memcmp(pos, pattern, pattern_size) == 0) { break; } else { pos++; } } else { pos = NULL; } } } while (pos != NULL); return pos; }