How reimplementation works¶
The reimplementation currently works by creating a windows DLL containing reimplemented functions.
Detouring and hooking original functions to check reimplementation quality¶
The project progresses by incrementally including new reimplemented functions. When a new function is reimplemented, its workings can be compared on the machine-code level (reccmp) and/or on the functional level (does the game run the same with and without the reimplementation).
The game’s functions are hooked and detoured into the DLL’s reimplemented functions. An added benefit to this approach is that Debug Symbols improve the developer experience.
Hooking and detouring of course comes with a performance hit but for reimplementation purposes this will likely be negligible. A full reimplementation can reduce the need for hooks.
Interfacing with the game¶
It is essential that data structures and function calls look the same as the original game.
Most the game’s classes are known: it is clear which function belongs to which class.
The current approach consists of a directory and namespace layout that mimicks the roles of the game’s classes.
Note that many classes follow the Singleton pattern. Other classes, such as Scenario Events, do not have any virtual functions. The main exception to this is C++ code such as strings and pipes (<<), but there original C++ definition is well-known and understood.
These facts simplify the approach to interfacing with the game.
The use of macros¶
Macros are used to aid in interfacing with the game. They are needed because C++ doesn’t really like the use of class methods based on function pointers, and we rely on function pointers to interface with the original game.
Some crucial macros are those to invoke an original class method that has not been reimplemented yet.
MACRO_STRUCT_RESOLVERdeclares a global variable as either a pointer to the original game’s memory or as a variable stored in the DLL’s memory (depending on reimplementation status). These objects should be operated on using::ptr(we operate on the pointer agnostic of which memory it is in and reimplemenation status).MACRO_FUNCTION_RESOLVERdeclares a function as reimplemented or not. If reimplemented, all calls to the function in the original game are rerouted to the reimplementation.MACRO_CALL: Used in reimplemented code to call the original or the reimplementation (depending on reimplementation status).MACRO_MEMBER_CALL: Same asMACRO_CALLbut for class methods instead of functions.