• Civilization 7 has been announced. For more info please check the forum here .

Shrinking resource bubbles by modifying the EXE

f1rpo

plastics
Joined
May 22, 2014
Messages
1,846
Location
Germany
I've found the four places in the code section of the EXE where the size of the plot indicators ("bubbles") is hardcoded. Those values aren't unreasonable, the real problem is that the EXE increases them based on the screen height. I've located some code in that vein in the debugger, but I'm not at all sure that that's really it. (Also not sure if the width doesn't also have some impact.) But we can just decrease the base values to compensate for the adjustment.

For a static approach, one can use a hex editor [edit #3: see instructions in this post] and simply replace all occurrences of the floating-point numbers 42 and 68 (FP32 encoding) in Civ4BeyondSword.exe with a smaller number. The 68 is for the bubble shown when the selected unit is off-screen. Well, this is not totally simple because one needs to get the encoding right. [Edit #2: Moreover, there are actually four occurrences of 42 in the EXE and seven occurrences of 68. Only the first three of the former and the first one of the latter ought to be replaced as far as I know.] I can imagine a little tool that let's the user choose the size and perhaps also offers replacing GameSpy with Zulan's server – but I don't imagine anyone actually implementing that.

What I've implemented, tentatively, is a mod component that overwrites the offending values at runtime in the main memory and that could be merged into other mods with little effort (beyond recompiling the DLL). Here's the mod component with its source code:
https://github.com/f1rpo/Civ4_SMC_PoC (download)
The name is short for "self-modifying code - proof of concept" – I've written a little bit of a framework.
After unzipping, the mod folder should be renamed to match the name of the INI file inside. Caveat: The DLL has assertions enabled; hopefully none will fail, but, if they do, one may have to Alt+Tab out of fullscreen mode to unfreeze the game. Edit #1: And when using 1024x768 resolution, the mod will have almost no effect.

Works nicely on my end (screenshot; original size is at the bottom), and, for my AdvCiv mod, I've already implemented options on the BUG menu for either canceling out the height adjustment or setting a particular size or adjusting to the field-of-view value. (Edit #7: Now also implemented in a fork of BULL.) I've only been able to test this on Win 8.1 and Win XP and only with my Complete Edition (CE) from DVD. The virtual memory layout of the EXE should not depend on the OS and hardware, but the OS could still matter because the mod needs to temporarily lift write protections on a few pages of the code segment. This behavior might also cause trouble with virus scanners. Letting VirusTotal.com scan my DLL has not resulted in any detections; however, there could possibly also be detections at runtime based on memory accesses. Another concern are localized versions of the game. I don't know if those use the exact same build of the EXE as the CE (not really sure about the Steam "beta" and GoG version either). I suppose the differences in the memory layout can't be that great. My code already performs a sanity test, and, if that fails, will search for a particular sequence of binary instructions to determine an address offset that aligns the relevant part of the present EXE with the CE EXE. Edit #4: This thread makes me hopeful that localized editions don't actually use a different build of the EXE. Edit #5: Also heard back from a Linux user who didn't seem to have trouble. Edit #6: Tried Zulan's alternative EXE; no problem (only makes changes in the data segment, which doesn't affect code addresses).

So – do let me know if you encounter or foresee problems or if you have a better idea how to go about this.

Edit #1: This view shows the DLL changes relevant for merging this mod pretty well.
Edit #8 (July 2023): It turns out that this won't be compatible with the default Steam version (which, however, many DLL mods don't support in the first place):
[O]n the default steam release, using Realism Invictus, a `push 42280000h` shows up at address 0x00465058. (Kind of near where where your patch applies I think?) It looks like I'm able to change that value and toggle balloons and their size updates. [...] I think your patch doesn't work on this version because the surrounding code/needle is different? I didn't look closely at the other version where it works so I'm just speculating. [...]
I don't really remember why I was using this exe version for Realism Invictus but not for other mods, or why there's two versions on Steam anyway. I think the default one had gamespy removed or something.

Edit #7: Removed mention of the large-address awareness (LAA) patch from this post - I've come to realize that the official BtS 3.19 EXE is already LAA, so this should not be a concern.
 
Last edited:
@f1rpo
I like it. :thumbsup:

I always felt that the Icons were a bit too large and obtrusive.
Still I liked to activate them from time to time for better strategic overview.

------------

Questions:

1. Does it affect performance?
2. Is it compatible to Civ4Col EXE?

------------

Edit:

I was considering to merge this into WTP or ask one of the WTP team colleagues if they would like to do it.
But I checked the amount and complexity of changes required and it simply too much for my own skills as a programmer.

Also I am afraid to introduce bugs that we would later not be able to trace back.
I consider changing data in memory during runtime extremely dangerous.

And last but not least I am afraid that it may cause compatibility issues.
(e.g. with other localizations and versions of the EXE that I do not have and thus would never test.)

------------

So please do not misunderstand me:

What you have done here needs extremely high skills as a programmer and the quality of your code is great as well.
But that is also my problem actually. Code on such a high level of skill comes with extreme liabilities.

Most likely my WTP team members like @Nightinggale or @devolution may maintain such code because they are better programmers than myself.
But each modder should only merge / implement code he wants to take the burden of liability to fix or maintain on himself.

------------

Summary:

What you did is great and showed your outstanding skill as a programmer.
But I do not feel comfortable to integrate code like this into WTP myself.
(Because as I said, I could not maintain or adapt it in the future either.)
 
Last edited:
In case that anyone (other than ray :)) ever wants to try porting this to Civ4Col:
Spoiler :
Although CvPlayer::getGlobeLayerColors is not part of the Col DLL, it should still be possible to locate the hardcoded base size values in the debugger by placing an assertion in CvArtFileMgr::getInterfaceArtInfo that fails when INTERFACE_PLOTPICKER_OVERLAY is accessed; then investigate the caller (and the caller's callers) in the EXE in the various relevant contexts: resource display toggled on, unit selected, interface message displayed. However, it'll probably suffice to just search the Col EXE with a hex editor.

I've just done that, and the first four occurrences of 42.0f in the Col EXE are spaced apart similarly enough to the first three in the BtS EXE to suspect that they serve the same purpose: 5F372, 5FDFC, 8D5C0, ADCB7. The first two are quite close together; no such pair exists in BtS, so maybe something was added there in Col. Could change those four on disk through the hex editor for confirmation. Then, to change them at runtime, it's probably sufficient to add 0x400000; this appears to be the "base address" of most executables. I.e. just prepend 0x004 to the addresses found in the hex editor. (Those addresses would already include the "OperandOffsets" in my code.) Could do a manual test upfront through the Memory window of the Visual Studio debugger.

For the off-screen size 68.0f, I suspect that the Col address is (0x004)8FA7D.
And in case that someone wants to experiment with changing the size in their Civ4BeyondSword.exe (or maybe Colonization.exe; see above) on disk, here are some instructions using Frhed, an open-source hex editor:
Spoiler :
Make a backup of the EXE, start Frhed, open the original EXE (can use drag-and-drop for that), open the Replace dialog (Ctrl+H), searching for <fl:42> (that's a lower-case L) and replacing with e.g. <fl:27>:
frhed-replace.jpg
Since we only want to replace the first three occurrences (Colonization: four?), hit
"Find Next" - "Replace" - "Find Next" - "Replace" - "Find Next" - "Replace"
Close the Replace window, move the cursor back to the top of the file (Ctrl+Home), open the Replace window again, change the search pattern to <fl:68> and the replacement to e.g. <fl:38>, hit "Find Next" once then "Replace" once.
Close the Replace window again, save the EXE (Ctrl+S), close Frhed, done.

To figure out what size to use, my best bet is to calculate
(42 * 768) / screen_height
That's ca. 27 when the height is 1200 pixels. I suppose one can also use a fractional value, e.g. <fl:26.88>. It seems that a large field of view will result in bigger plot indicators. If that's a concern (e.g. when using the BUG field-of-view slider), taking the result from above times
square_root(42 / FoV)
should mostly cancel that out. (Another small factor is camera distance; plot indicators, strangely, seem to get a bit bigger when zooming out.) E.g. when using FoV=60, the 27 would become ca. 23.
For the offscreen unit icon size, 1.4 times the base plot indicator size works well for me, e.g. 32 in the example above.

And you may want to decrease the plot indicator size in Globe view a little bit too. That's a humble XML change.
1. Does it affect performance?
Hard to imagine that there's any performance penalty. The brief lifting and reinstating of memory protections might be a somewhat slow operation (no idea really), but, normally, that happens only once upon loading the main map. (If a mod allows players to change the camera's field of view, the EXE will get patched again after such a change.) The EXE should run just as fast or slow as before with those few replaced constants.

As for compatibility, sure, it would seem prudent to get confirmation from users of various operating systems and builds of the EXE before including such a hack in a release. That said, defensive programming also goes some way. My code already checks whether the relevant code exists at the proper address and will take no action otherwise. Failed VirtualProtect calls (to suspend memory protections) should also be recoverable.

This would be a more attractive proposition if some other limitations imposed by the EXE could be worked around using the same technique. The speed of combat animations comes to mind (I'd like them a bit faster, no way to do that through the DLL afaik), a wider help text area (WtP and RI have run into that issue), or bypassing the mod name check when loading savegames. But, arguably, it's too laborious to reverse-engineer the EXE for such minor nuisances, even if the modification at runtime were a nonissue.
 
Last edited:
... a wider help text area (WtP and RI have run into that issue) ...
I was really annoyed by the fact that I could not change the width of help texts.
At that point of time I also did not understand why. So thanks for explaining now. :thumbsup:
 
Edit #6: Tried Zulan's alternative EXE; no problem (only makes changes in the data segment, which doesn't affect code addresses).

Meant to look at this one for a while...
Do you have the exe changed like this (the Zulan one) available ? You mind sharing it ?
Will it work in multiplayer if one player has the 'default' version and the other has the changed one ?

:thumbsup:
 
I didn't keep a copy of that EXE, but I think I downloaded it from that thread, the link under "Option B": Civ4BeyondSword2015.exe
I don't see why there would be a compatibility problem (in Direct IP games), afair they've only replaced the GameSpy URLs; but I haven't tested it.
 
Yes you're right on all counts, it's to redirect the IP to Zulan's server. I think it's pretty much universally used now, except by Steam's players.
I thought you meant you modified that very file with the shrinkied bubble (?) Or is that why you did but threw away the file ?
Just to be clear my question was between A/ 1 or more players with Zulan file 'normal bubble' vs 2/ 1 or more players with Zulan file + new bubble size
 
I've tested that EXE with my DLL, which shrinks the bubbles by modifying the EXE at runtime. I have not modified the Zulan EXE on harddisk. Well, I do have the hex editor installed, so I might as well do it now; attaching the result. (Maybe I'd better delete that attachment in a few days; seeing that the EXE isn't normally included in mods ...) In a quick test, the shrinking seems to have worked; not so sure about the URLs – if I click on "Internet Game", I'm asked to log into or create a GameSpy account. But I do see Zulan's URL in the hex editor, so maybe I just don't know how to operate the Multiplayer menu.

Having edited those size values should not cause any compatibility problem.
 

Attachments

  • Civ4BeyondSword2015.zip
    4.7 MB · Views: 17
Works perfect, amazing.
Thanks for doing that for me !
Yes it's compatible in multiplayer I just tested on lobby + MOD + different combos, I was just worried it detect different .exe and create OOS

I love the size you chose, I were to play in the absolute top resolution maybe I'd do even smaller (I'll do it eventually), but this is better for all the other ones I think
:thumbsup:
 
Hello f1rpo,

I've added an other variant to change the code at runtime in my fork of your repo: https://github.com/civ4-mp/Civ4_SMC_PoC/tree/diplomenu
It's using the minhook-library and changes a few dimensions in drawing calls. A few drawbacks are currently included:

1. It only works in relase-mode. Otherwise the call of the original function after executing my hook, doesn't work. (Maybe this can be resolved.)
2. My hooking functions are mainly assembler code. This reduces the footprint but to avoid to much changes of the stack/register.
If we backup the registers, etc we probably could also use normal C++-Code in the hooking functions.
3. I wasn't able to compile the DLL with a static version of Minhook. Thus you has to copy Minhook.x86.dll into the Civ4:BTS folder.
4. BUG-Options, etc to toggle/control the feature are not included.

 
Now this looks really interesting. Maybe this is the approach to finally enlarge the popup window. It's so annoying that the size of the window is hardcoded in the exe, hence tiny at a modern resolution.
3. I wasn't able to compile the DLL with a static version of Minhook. Thus you has to copy Minhook.x86.dll into the Civ4:BTS folder.
We ran into the same issue with threading building blocks in WTP. The solution ended up being setup.bat, which copies the two dll files into the correct position. We had a bunch of feedback about people failing to copy the dll files, either doing it incorrectly or just giving up. Nobody has complained about double clicking the bat file.

I looked into lazy linking in the DLL and while it's technically supported, Microsoft forgot to include the needed library to do so in the 2003 version. It's only in some expensive (and now gone) enterprise version. They did add it in the free 2005 version, but that's not really helpful for us. If lazy linking is enabled, the DLL would be able to run code prior to opening other DLL files, which in turn would allow opening DLL files inside the mod.
 
We ran into the same issue with threading building blocks in WTP. The solution ended up being setup.bat, which copies the two dll files into the correct position. We had a bunch of feedback about people failing to copy the dll files, either doing it incorrectly or just giving up. Nobody has complained about double clicking the bat file.

I looked into lazy linking in the DLL and while it's technically supported, Microsoft forgot to include the needed library to do so in the 2003 version. It's only in some expensive (and now gone) enterprise version. They did add it in the free 2005 version, but that's not really helpful for us. If lazy linking is enabled, the DLL would be able to run code prior to opening other DLL files, which in turn would allow opening DLL files inside the mod.

Yeah, the loading order of the dependencies made it impossible change the path inside of CvGameCoreDLL.dll.
The batch script is one solution, another solution would be nesting of the Civ4.exe call and adding the path information before loading of CvGameCoreDLL.dll. (This is my approach in BTS_Wrapper.exe. It adds a folder to the path environment variable.)

Maybe we could replace CvGameCoreDLL.dll with a (small) dummy file, waiting on its DllMain call, adding the mod folder and then reloading CvGameCoreDLL library?!
The reloading is already implemented in Civ4 itself: If the dll loading from the mod fails, it loads the default version... Well, we just need provoking a loading in the mod folder again.
 
In Debug-Build I was now successful and can side-load DLLs from the "[Modname]\DLLs" folder :)
Main Idea is that I can add more path for loading DLLs in the first call of CvGameCoreDLL.dll ([Modname]\Assets\CvGameCoreDLL.dll)
and then returning 'false' from DllMain. Note that DllMain is only called if no unknown DLLs are linked.

Then, Civ4 trying to load the default DLL. I used f1rpo's way described in this thread and force Civ4 to [Modname]\DLLs\CvGameCoreDLL.dll.
Code:
[…]
"Civ4BeyondSword2015.exe": "C:\Windows\SysWOW64\cscapi.dll" geladen, Cannot find or open the PDB file
DLL Load:Mods\SMC_PoC\Assets\CvGameCoreDLL.dll
"Civ4BeyondSword2015.exe": "I:\Olaf\Civ4\Beyond the Sword\Mods\SMC_PoC\Assets\CvGameCoreDLL.dll" geladen, Cannot find or open the PDB file
"Civ4BeyondSword2015.exe": "I:\Olaf\Civ4\Beyond the Sword\Mods\SMC_PoC\Assets\CvGameCoreDLL.dll" entladen.
"Civ4BeyondSword2015.exe": "I:\Olaf\Civ4\Beyond the Sword\Mods\SMC_PoC\Assets\CvGameCoreDLL.dll" geladen, Cannot find or open the PDB file
"Civ4BeyondSword2015.exe": "I:\Olaf\Civ4\Beyond the Sword\Mods\SMC_PoC\Assets\CvGameCoreDLL.dll" entladen.
DLL Load:I:\Olaf\Civ4\Beyond the Sword\Mods\SMC_PoC\DLLs\CvGameCoreDLL.dll
"Civ4BeyondSword2015.exe": "I:\Olaf\Civ4\Beyond the Sword\Mods\SMC_PoC\DLLs\CvGameCoreDLL.dll" geladen, Symbole wurden geladen.
"Civ4BeyondSword2015.exe": "I:\Olaf\Civ4\Beyond the Sword\Mods\SMC_PoC\DLLs\CvGameCoreDLL.dll" entladen.
"Civ4BeyondSword2015.exe": "I:\Olaf\Civ4\Beyond the Sword\Mods\SMC_PoC\DLLs\CvGameCoreDLL.dll" geladen, Symbole wurden geladen.
"Civ4BeyondSword2015.exe": "I:\Olaf\Civ4\Beyond the Sword\Mods\SMC_PoC\DLLs\MinHook.x86.dll" geladen, Die Binärdaten wurden nicht mit Debuginformationen erstellt.
[…]
LoadCivXml (xml\GameInfo/CIV4PlayerOptionInfos.xml)

Release build didn't work ATM.

Probably a really complex way to solve an non-existing problem:hammer:
 
Note that DllMain is only called if no unknown DLLs are linked.
That's where I stopped. How did you manage to use DLL files from CvGameCoreDLL.dll and still call DllMain?

Actually... I think I an idea on how to do that. You make a tiny DLL, which adds a new location to the search path and then you return false. Next it tries out the new search path where a different CvGameCoreDLL.dll is present and due to the order of the search paths, that one takes priority. This sounds neat, but it brings up the question, if you have already done it, would you mind sharing how you did it to avoid the rest of us from redoing it from scratch? :)
 
Actually... I think I an idea on how to do that. You make a tiny DLL, which adds a new location to the search path and then you return false. Next it tries out the new search path where a different CvGameCoreDLL.dll is present and due to the order of the search paths, that one takes priority. This sounds neat, but it brings up the question, if you have already done it, would you mind sharing how you did it to avoid the rest of us from redoing it from scratch? :)
I will publish it, but currently the code is to messy and it just works for the Debug-DLL and not the release version.
 
I've pushed my code now under the branch 'external_dlls'

The usage of the MinHook-DLL is just an example. The main changes was made in CvGameCoreDLL.cpp.
Currently the Dummy-DLL is just a normal CvGameCoreDLL.dll, but the problematic external references are en-capsuled with 'WITHOUT_EXTERNAL_DLLS', e.g.
Code:
    #ifdef WITHOUT_EXTERNAL_DLLS
    #define    MH_Initialize()
    #define MH_Uninitialize()
    #else
    #include "minhook/MinHook.h"
    #pragma comment(lib, "minhook/MinHook.x86.lib")
    #endif

In the Makefile you can control which version of the DLL should be build, see commit message.
 
Great to see someone more proficient than me tackle this subject. (Or two people if Nightinggale gets going too.) :thumbsup:
Things I never knew:
MS.com said:
Detours is a software package for re-routing Win32 APIs [...] and [is] used by nearly every product team at Microsoft.
So my worries about trouble with virus scanners are probably unfounded – code modification at runtime is commonplace. And MinHook is a free alternative to Detours. Judging from the sample code here and Ram's code it seems pretty easy to use. Not sure if the plot indicator changes could be easily implemented through hooks; I would've tried it that way had I known. Well, a Civ player trying to reinvent the wheel isn't go to shock anyone ... Maybe the diplo screen changes could've been implemented fairly easily without MinHooks too, but seeing that the open issues with that approach have been resolved, I guess there's little point in pondering that.

I do wonder, @Ramkhamhaeng, how you went about the reverse-engineering, i.e. how (in broad strokes) you identified the various functions listed in the DiploHooks constructor.
Spoiler :
Code:
DiploHooks::DiploHooks():
    Replace_DipLeftTop((VoidFunc)0x0057F4E0, &Cv_DipLeftTop),
    Replace_DipLeftBottom((VoidFunc)0x0057F59B, &Cv_DipLeftBottom),
    Replace_DipRightTop((VoidFunc)0x0057F656, &Cv_DipRightTop),
    Replace_DipRightBottom((VoidFunc)0x0057F713, &Cv_DipRightBottom),
    Replace_DipMidHeadline_MP((VoidFunc)0x0058CE53, &Cv_DipMidHeadline_MP),
    Replace_DipMidHeadline_SP((VoidFunc)0x0055496C, &Cv_DipMidHeadline_SP),
    Replace_DipMidCenter_MP((VoidFunc)0x0058D71F, &Cv_DipMidCenter_MP),
    Replace_DipMidCenter_SP((VoidFunc)0x005521F2, &Cv_DipMidCenter_SP),
    Replace_DipLeaderhead_RestoreCenter((VoidFunc)0x00552215, &Cv_DipLeaderhead_RestoreCenter),
    Replace_DipMidBottom_MP((VoidFunc)0x0058D966, &Cv_DipMidBottom_MP),
    Replace_DipMidBottom_SP((VoidFunc)0x005541FC, &Cv_DipMidBottom_SP),
    Replace_addLeaderheadGFC((VoidFunc)0x0054F180, &Cv_addLeaderheadGFC),

    Replace_setText((VoidFunc)0x00570E70, &Cv_setText),
    Replace_setLabel((VoidFunc)0x00570F90, &Cv_setLabel) {
    };
My approach has been to guess some line in the DLL that gets executed as close as possible to the relevant computations in the EXE. For the plot indicators, that has been CvArtFileMgr::getInterfaceArtInfo. Then I've set a breakpoint there (conditional on INTERFACE_PLOTPICKER_OVERLAY being accessed) and stepped into the disassembly. I copied a lot of that assembler code into a text editor to annotate it, successively stepped into function calls, made notes about the call graph, quite tiresome. Same deal for allowing the Taurus mod to import savegames, i.e. to bypass mod name checks in the EXE.

I'm just wondering if there's an easier way (that doesn't involve static decompilation of large parts of the EXE, which I understand is not allowed by the forum rules), some technique that I'm missing. For example, to make the help text area on the main interface wider – which might, in the end, just require a single value to be overwritten –, I wouldn't even have a clue what entry point to use. Maybe this is actually a pretty difficult case because it's unclear why and at which point the EXE (apparently) ignores the fWidth and iMinWidth arguments provided to CyGInterfaceScreen.setHelpTextArea in CvMainInterface.py.
 
Great to see someone more proficient than me tackle this subject. (Or two people if Nightinggale gets going too.) :thumbsup:
I was busy so @devolution beat me to it.
how you went about the reverse-engineering, i.e. how (in broad strokes) you identified the various functions listed in the DiploHooks constructor.
That's what I'm wondering too. I suspect I might be doing this the hard way.
 
Great to see someone more proficient than me tackle this subject. (Or two people if Nightinggale gets going too.) :thumbsup:
Things I never knew:
So my worries about trouble with virus scanners are probably unfounded – code modification at runtime is commonplace. And MinHook is a free alternative to Detours. Judging from the sample code here and Ram's code it seems pretty easy to use.
I've found your way by modifying it by hand also very interesting! Your code showed me a missing puzzle tile: You has to gave the memory to alter write access first.
And using MinHook is always a simple target vector for some (stupid) anti virus protection tools which just scans the name of the library to load. (I'm sure that just annoying for us, but not a hurdle for really bad guys…). Fortunately in the last years only one player of our MP Pitboss games noted that some anti virus tool complained.

I'm just wondering if there's an easier way (that doesn't involve static decompilation of large parts of the EXE, which I understand is not allowed by the forum rules), some technique that I'm missing. For example, to make the help text area on the main interface wider – which might, in the end, just require a single value to be overwritten –, I wouldn't even have a clue what entry point to use. Maybe this is actually a pretty difficult case because it's unclear why and at which point the EXE (apparently) ignores the fWidth and iMinWidth arguments provided to CyGInterfaceScreen.setHelpTextArea in CvMainInterface.py.

If there is an easy way, I'm do not knowing it. I've also looked many hours over Civ4's exe assembly code. I will describe my approaches to find a suitable spot for hooking/changing values.

1. Searching hard coded values (working in the quarry by hand ;))
Tools like Cheat Engine and Ida Free (?) allowing to search for numbers/strings at runtime. You can collect all positions of your value/range and re-run the search later to shrink the number of possible positions. E.g. if you know the value is an offset and depends on the resolution you can search for the value for two resolutions.
Example:
I've made screenshots of the diplo menu and measured the sizes of the boxes. Somehow I've found a point where the values was used as arguments and then you can look around (set breakpoint and watch which functions will be called). Then I realized that the data is bundled as struct with values (x,y,width,height). So after found one position it is easy to find the other, nearby positions.
By using breakpoints in Cheat Engine you also can change values at runtime and you've got feedback if you found the correct position.

Note that I'm not sure if my hooks really reflects the call of the 'draw something' function. It is just a function call after setting the correct values on the stack and before the values will be used.

2. Using the PythonApi as entry point:
If you know the EXE function, you're interested in, is also be called by a python wrapper you can dig in there. Set a breakpoint in the Cy*.cpp-function and follow the trace until you reaching the EXE.
Example: addLeaderheadGFC (Incomplete list for other drawing functions: https://github.com/civ4-mp/pbmod/bl.../sources/CivSaveOverHttp/DrawingFunctions.cpp )
Calling this Python-Function with different X,Y values show your how this values flows towards some functions of the EXE. This was a hard task for me because it turns
out that the diplo menu drawing calls (without using python) directly write into 'ECX' instead pushing the function argument on the stack.
Code:
00553EBA mov     ecx, 161h
So, my hook as to alter the register.

3. Using the EBP register: (This wasn't used in the diplo menu hooks, but in my pbmod.)
You knowing the EBP register from the debug builds. It marks the parent strack pointer and is used build the frame stacks if you hit a breakpoint.
I'm using this to distinct if a DLL-function was called by the exe or not.

Example: In Multiplayer/Pitboss games, the players got a list of civs if they're joining the server. The Civ4 developer made a mistake and they sending
this list in one big network packet to the client. For MP games with many players this UDP network packet will be too big to reach some players.

I've edited 'CvInitCore::getLeaderName' and take a look at the caller of this function:
Code:
        void ** volatile puEBP = NULL;
        __asm { mov puEBP, ebp };
        void * pvReturn1 = puEBP[1]; // this is the caller of my function and can be compared with a pointer from the exe.
For the critical caller I shrink the Name to a short string. (Screenshot)

Big problem: This didn't works for the normal release build because of '/Oy' the EBP register is not set after getLeayerName was called.
As solution I compiled the DLL with '/Oy-'.
Drawback is the overhead on each function call, but today I realized that I could put the affected functions in one file and could compile the rest with '/Oy' :)
(Edit: It's not so easy to mixing different compiling options: Compiler compains with warning C4653: inconsistent with precompiled header; current command-line option ignored )

===================================
Things I could never found/fixing:
• Width of info overlays (like f1rpo).
• Size of GameFontIcons.
Here two screenshot from my rescaler mod where I've stopped coding this PoC mod: (Well, I don't have a 4k monitor anyway. ;))
screenshot3820.jpg screenshot3822.jpg
 
Last edited:
(Edit: It's not so easy to mixing different compiling options: Compiler compains with warning C4653: inconsistent with precompiled header; current command-line option ignored )
You can always remove the precompiled header as a requirement as well.
Code:
PRECOMPILE_CFLAGS1 = /Yu"
PRECOMPILE_CFLAGS2 = CvGameCoreDLL.h" /Fp"$(Target_PCH)"

// for compiling the cpp file
$(PRECOMPILE_CFLAGS1)$(PRECOMPILE_CFLAGS2)
Just remove this for the file, which you compile with different flags.
Size of GameFontIcons.
Isn't that just resizing the boxes in the tga file? I never tried it, but vanilla has BULLET_CHAR smaller than the other ones and indeed it has a different size ingame.
 
Top Bottom