Thanks. It would be interesting to have all civilizations open to you, and the computer, that way you never know which civilization you will face off against...
Since I'm working on it, I thought I'd write some kind of "blog" of what's going on, step by step...
1. First step is: hack CIV so that is
shows all Civilizations to select from instead of limiting to the total number of Civs:
What CIV does
When showing the Civ selection dialog, CIV first lists all Civs from 1 (Roman) to levelOfCompetition+1 (included), then lists all Civ from 9 (Russian) to 8 + levelOfCompetition+1 (included).
Examples:
- if levelOfCompetition is 2, meaning that there are 2 computer-controlled opponents,
Civ will lists all civs from 1 to (2+1)=3 included and from 9 to 8+(2+1)=11 included,
that is:
Code:
1 = Roman
2 = Babylonian
3 = German
9 = Russian
10 = Zulu
11 = French
- if levelOfCompetition is 5, meaning that there are 5 computer-controlled opponents,
Civ will lists all civs from 1 to (5+1)=6 included and from 9 to 8+(5+1)=14 included,
that is:
Code:
1 = Roman
2 = Babylonian
3 = German
4 = Egyptians
5 = Americans
6 = Greeks
9 = Russian
10 = Zulu
11 = French
12 = Aztecs
13 = Chinese
14 = English
What we need to patch
We need to tell CIV to show all Civs, regardless of the value of "levelOfCompetition".
The actual code assembly for what I described above is a sequence of 2 loops, each loop looking like this
Code:
mov varLoop, 1 ; loop counter initialized to 1
loop_start:
mov ax, LevelOfCompetition ; ax now equals levelOfCompetition
inc ax ; ax now equals levelOfCompetition+1
inc ax ; ax now equals levelOfCompetition+2
cmp varLoop, ax ; CIV compares the varLoop value and "ax"
jl short <some location> ; if varLoop is strictly below ax (<) it displays the Civ name (else breaks out of the loop)
some_location:
.... code to display Civ name ...
inc varLoop ; varLoop incremented by 1 (varLoop++)
<jump back to loop_start>
Written in C/C++ or Java, this is a typical
for loop:
Code:
for(varLoop = 1; varLoop < levelOfCompetition+2; varLoop++) {
// display name of Civ with ID 'varLoop'
}
So how can we alter it to ignore the level of competition when displaying selectable Civs ?
The obvious way is to change the loop condition to "varLoop < 8"; but doing in this in assembly is actually tricky because assembly instructions have variable byte-length... Let me explain:
- the assembly "cmp varLoop, ax" is a comparison between a value in memory (varLoop is a local variable in the stack) and a register (ax); in x86 assembly this is written (hex) "39 46 F6";
- however, "cmp varLoop, 8" is a comparison between a value in memory and a direct value, which is rather coded as (hex) "83 46 F6 08";
- so the binary code for "cmp varLoop, 8" takes 4 bytes, which is bigger than the binary code for "cmp varLoop, ax" which only takes 3 bytes, so we cannot patch it this way...
The next best option would be to replace the initial "mov ax, levelOfCompetition" by hardcoding the value of ax at this stage, for example "
mov ax, 6"; let's see the binary:
- "mov ax, levelOfCompetition" is a value copy between memory and a register; in binary, it is written as (hex) "A1 14 E2"; without verifying, I am guessing that opcode "A1" is for instruction "mov memory value to AX", and "14 E2" is the address of the memory value to copy to ax... but that's really just a guess
- then, "mov ax, 6" is a hard-coded value assignment to ax register; in binary it is written as (hex) "B8 06 00"; again, I am not verifying in the x86 reference, but I guess that B8 is the opcode for "write hard-coded short to ax"
- bingo! both instructions have the same byte-length, and so we can now (attempt to) apply the patch
The actual patch in CIV.EXE:
As explained above, CIV has 2 similar, consecutive loops to display the available Civs, so we need to patch both loops.
The original code and locations for the 2 occurences "mov ax, levelOfCompetition" differ by version:
Code:
EN 474.01: "[B]A1 14 E2[/B]" located at [B]0x2FB53[/B] and [B]0x2FB8D[/B]
EN 474.05: "[B]A1 F4 D2[/B]" located at [B]0x2FB58[/B] and [B]0x2FB92[/B]
FR 474.05: "[B]A1 34 D8[/B]" located at [B]0x30F58[/B] and [B]0x30F92[/B]
Now the idea is to replace those occurrences with "mov ax, 6", which is spelled "
B8 06 00" in x86 binary.
After applying the patch, it is now possible to select any Civ in the selection screen, even with only 2 opponents, YAY!
But wait a minute there: I selected 4 Civs total (level of competition = 3), then I chose to be the Greeks, and I end up
being Shaka of the Zulus!!!
Indeed, there is still some work to do...
TO BE CONTINUED