I've written a little patch to modify the EXE to remove the hard-coded restriction on selecting non-Christians, Republics and Theocracies. I know you can already play those characters using the console, but I prefer to be able to select them on the normal selection screen. I have written this as a patch to the EXE this time instead of using DLL injection like I used for my duchy limit mod because I play the game on Mac OS X under Crossover and it doesn't seem to support the Windows APIs I used, so I ended up having to patch the EXE anyway. I've only tested it on the GamersGate 1.03b version, it's quite possible it won't work on the Steam version, though I wrote it in a more flexible way than the duchy limit mod so there's a chance it will work. If it doesn't work and you have a copy of the Steam version and you don't mind doing some testing, please PM me.
To create a patched version of the EXE, run it like this from cmd.exe:
It will output a patched version in the working directory, which you can copy/paste/rename to replace the original EXE (back it up first!). If you want to output directly to your game dir in Program Files you will need to run cmd.exe as administrator in Windows Vista and Windows 7 unless you have disabled UAC (I think).
Here's a pre-compiled binary:
View attachment PlayEveryonePatch.exe
Here is the source code (from some reason I can't upload a file with a .cpp extension):
View attachment playeveryone.txt
To create a patched version of the EXE, run it like this from cmd.exe:
Code:
PlayEveryonePatch.exe "C:\Program Files\Paradox Interactive\Crusader Kings II\ck2.exe" ck2_patched.exe
It will output a patched version in the working directory, which you can copy/paste/rename to replace the original EXE (back it up first!). If you want to output directly to your game dir in Program Files you will need to run cmd.exe as administrator in Windows Vista and Windows 7 unless you have disabled UAC (I think).
Here's a pre-compiled binary:
View attachment PlayEveryonePatch.exe
Here is the source code (from some reason I can't upload a file with a .cpp extension):
View attachment playeveryone.txt
Code:
#include <iostream>
#include <fstream>
#include <cstring>
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] << " " << "<input file> <output file>" << 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;
}