Vanilla BotF is limited to displaying 7 special and 7 defense energy structures in the solar system (F2) screen. This patch allows for changing that limit.
Patch Tool
I've written a special patch tool as this complex beast involves offset computations, about 30 trek.exe code patches, relocating/rewriting data in Trek.exe, and rewriting the 5 senergy.wdf files.
You can separately patch Trek.exe and the WDFs, but both must be used or BotF will crash out!
I have left a copy of the original WDF files with the patch. You'll need to copy the modified WDFs to STBOF.RES.
A thing to note is that the patch tool simply lays out the structures in the WDFs in a single row for Special and a single row for Defense. If you add more than 11 items per row, they'll overlap. You can use the UE WDF editor to manually reposition them.
How It Works
Here is an overview of how the patch works. In order to understand how the patch works, we'll need to take a diversion into the Trek.exe User Interface (UI) system. The UI in BotF is comprised of 21 screens (actually the code has an extra screen CivConf, I'll resurrect that in a future mod ). Each screen is comprised of various widgets like buttons, pictures, text labels, etc. Each screen uses a WDF file to control it's appearance. Each widget in a WDF file has ID associated with it, so it can match to a live widget in the program. There is a matching list of such IDs for each screen in Trek.exe. Let's look at the Opening screen.
The Opening Screen
At offset 594394, we see an array like:
Code: Select all
<2, 139, 4>
<2, 140, 8>
<2, 141, 0Ch>
<2, 142, 10h>
<2, 355, 14h>
<2, 143, 18h>
<2, 144, 1Ch>
<2, 356, 20h>
<2, 9600, 24h>
Code: Select all
004D0AF8 mov eax, 28h ; size
004D0AFD call System_Memory_AllocFromUIPool
Code: Select all
00000000 OpeningScreen_t struc ; (sizeof=0x28)
00000000 widget dd ? ; struct offset
00000004 btnContinue dd ? ; struct offset
00000008 btnNewGame dd ? ; struct offset
0000000C btnNewMPGame dd ? ; struct offset
00000010 btnLoad dd ? ; struct offset
00000014 btnSave dd ? ; struct offset
00000018 btnQuit dd ? ; struct offset
0000001C btnFame dd ? ; struct offset
00000020 btnOptions dd ? ; struct offset
00000024 btnRetire dd ? ; struct offset
00000028 OpeningScreen_t ends
So if we wanted to add a new button to the Opening screen, here's what we'd do:
- Expand the screen's data structure to make room for the new button. We'd do this by simply allocating another 4 bytes, 28h+4=2Ch:
Code: Select all
004D0AF8 mov eax, 2Ch ; size 004D0AFD call System_Memory_AllocFromUIPool
- Add an entry to the wdfObject descriptor list with an unused ID, and set it's offset to point to the start of the enlarged area of the screen's structure:
Code: Select all
<2, 145, 28h>
- Add a button (type 2) entry to opening.wdf, with ID 145.
- Add some code to make the button do something. Easy -- here's how the Continue button adds it's callback:
Code: Select all
004D0BE5 mov edx, offset UI_Opening_btnContinue_Click 004D0BEA mov eax, [esi+OpeningScreen_t.btnContinue] ; esi+4 004D0BED call PushBtn_SetClickCallback
The SolarSys:Energy Sub-Screen
The SolarSystem Screen is a bit more complicated than the Opening screen, since it has various subscreens, but we can use the same technique to extend it. In this case, we piece together the screen's data structure as (cut down for brevity):
Code: Select all
00000000 SolarSysScreen_t struc ; (sizeof=0x278)
00000000 widget dd ?
...
00000054 btnSummary dd ?
...
00000190 picEnergySpecial0 dd ?
00000194 picEnergySpecial1 dd ?
00000198 picEnergySpecial2 dd ?
0000019C picEnergySpecial3 dd ?
000001A0 picEnergySpecial4 dd ?
000001A4 picEnergySpecial5 dd ?
000001A8 picEnergySpecial6 dd ?
000001AC txtEnergySpecial0 dd ?
000001B0 txtEnergySpecial1 dd ?
...
000001C4 txtEnergySpecial6 dd ?
000001C8 picEnergyDefense0 dd ?
...
000001E0 picEnergyDefense6 dd ?
000001E4 txtEnergyDefense0 dd ?
...
000001FC txtEnergyDefense6 dd ?
00000200 txtEnergyTitle dd ?
00000204 txtEnergySpecialTitle dd ?
...
00000270 picChat dd ?
00000274 field_274 dd ?
00000278 SolarSysScreen_t ends
Code: Select all
004F115D inc edx
004F115E add eax, 4
004F1161 cmp edx, 7
Code: Select all
004F8AAB mov eax, 856 ; size
004F8AB0 call System_Memory_AllocFromUIPool
You also need to extend the list of Energy subscreen wdfObject descriptors at offset 5960F4:
Code: Select all
005960F4 stru_5960F4
005960F4 wdfObjectDesc_t <14, 2400, 200h>
005960F4 wdfObjectDesc_t <14, 2401, 204h>
005960F4 wdfObjectDesc_t <14, 2402, 208h>
...
005960F4 wdfObjectDesc_t <14, 2446, 1FCh>
Finally, you must add the widgets to the WDF files.
Here's a list of all ASM offsets than need modding:
Code: Select all
// size of SolarSysScreen structure:
0x4F8AAB
// refs to senergy wdfObjects
0x4F9025
0x4F8706
// refs to number of senergy wdfObjects
0x4F9020
0x4F8701
// refs to special energy pictures
0x4F117A
0x4F118F
0x4F1E86
0x4F720D
0x4F721B
// refs to special energy text labels
0x4F1197
0x4F1AF4
0x4F1F11
0x4F1FB1
0x4F2174
// refs to defense energy pictures
0x4F1151
0x4F1166
0x4F1FDE
0x4F724A
0x4F7258
// refs to defense energy text labels
0x4F116E
0x4F1B12
0x4F2069
0x4F2109
0x4F2144
// "cmp X, 7".
// change 7 to NumDefense
0x4F1161
0x4F1E5D
// change 7 to NumSpecial
0x4F118A
0x4F1E7B
//Note: 0x4F1B95 contains "cmp (benefitType), 7"; not relevant here
// change 1C to 4*NumEnergySpecial
0x4F1AE3
// change 1C to 4*NumEnergyDefense
0x4F1B08
Hope someone finds this informative / not too incomprehensible.