TQCam is a camera mod for Titan Quest. It allows you to control the camera direction by holding down the middle mouse button and moving the mouse. It also provides adjustment for view distance, field of view as well as how far you can zoom in and out.
My main motivation for writing this mod was that the existing mod only used keyboard controls and I wanted to rotate the camera with the mouse.
This is the second camera mod I've done. The first was for Torchlight. In both cases there was a preexisting camera mod that people were using. And in both cases I made no effort to get my mod out there for people to use.
For Titan Quest there was TitanCamMod by jdoe407 and for Torchlight there was TorchlightCam by Fincodr. Unlike Fincodr, jdoe407 was kind enough to release the source code with the mod.
The Torchlight mod ran entirely in it's own process and used ReadProcessMemory and WriteProcessMemory to manipulate the running game. While the Titan Quest mod I looked at had a DLL that it injected into the game's process with CreateRemoteThread and LoadLibrary. Not having the source code I found out what TorchlightCam was doing by running it in Wine with WINEDEBUG=+relay.
Looking up code injection I found this article: Three Ways to Inject Your Code into Another Process. The three ways listed in that article were, and I quote:
As you can see TitanCamMod uses the 2nd method while TorchlightCam doesn't fit any of them. (You could possibly consider it a variation on the 3rd.)
What really grabbed my attention was the first method and the use of windows hooks. By adding a mouse event hook I could use the hook for more than just injecting code. I could use it for the mouse input I needed for the mod to work.
The first problem I had was because initially I was compiling everything as ELF using Winelib. The first was that Wine's LoadLibrary does not append .so to the library name when looking for local files. The second was a bug in Wine where it would crash using SetWindowsHook to hook a function on an ELF DLL into a remote thread. This is because it uses an offset from the dll base address only ELF shared objects are loaded 4k aligned and Wine makes the base address 64k aligned. Switching to mingw and compiling everything Windows native fixed those problems
The next improvement I made was to get rid of the use of any fixed addresses. TorchlightCam gets around the problem of fixed address that can change from one build to another by scanning a range of memory for the code it wants to change. The way the Titan Quest is built makes it easy to avoid fixed address. As well the main EXE it is split into two DLLs; Game.dll and Engine.dll. These export a wealth of symbolic information allowing me to obtain every address I need with GetProcAddress.
The next problem was trying to call a member function when all I had was a regular pointer. In C++ pointers to member functions are not regular pointers. C++ is messed up like that. I was unable to get gcc to cast to or otherwise construct a pointer to member function from a regular pointer. I was unable to call the function using a normal pointer to function because it used the thiscall calling convention, which passes the object pointer in eax and the parameters on the stack and there is no matching calling convention for regular functions. In the end I used inline assembly to set eax and the call the function using the stdcall calling convention.