Game Name: Transformers: War for Cybertron
Game Vendor: Steam [delisted/unavailable]
Game Variant: MULTi6-PROPHET [ovagames.com]
Game Process: TWFC.exe
(I've designed the above widget myself based on Steam's model; e.g.: [Link])
Hello folks.
Been busy studying and learning more about Unreal Engine 3 and its internals. While at it, a patron of mine asked whether or not I'd be interested in looking into this game. Since I was busy with Styx: Master of Shadows (table to be posted later), yet another UE3 title, I decided to give this one too a go.
I've learned quite a lot, this game being the FIRST one I've seen that uses UE3 without UnrealScript-ing All of the UE3 UFunctions are streamlined in the executable, exactly as it happens in UE4 today (e.g.: UCheatManager::God -- the function run when you type 'god' in the console -- in UE3 is an UnrealScript executed bytecode stream; not in UE4 and definitely not in this game). So all of it is static cpp code, no bytecode VM running of any compiled UnrealScript.
- - - - - - -
FNames/UObjects Dumper
Took me a bit to understand the UObject model to be able to properly build a dumper, so without any further ado, here it is:
Extract xinput1_3.dll to your Binaries folder (e.g.: F:\Games\Transformers - War for Cybertron\Binaries). Once in-game, press >> Numpad / << key and two text files will be created in same folder: NamesDump.txt and ObjectsDump.txt. They look like this:
And they contain this kind of information:
I've explained in quite a few topics now, both UE3 and UE4 oriented, how one can make use of these dumps.
A few details about this UE3 version's internals:
- UObjects -> the Index is located at offset 0x4 and it's read like this:
Code: Select all
char *UObject::GetName() { int idx = this->Name.Index >> 0xE; <<-- here if( idx > 0 || idx < FName::Names()->Count ) { return FName::Names()->Data[idx]->ToAnsiString(); } return "INVALID NAME INDEX"; }
- xProperty UObjects (x = Bool, Int, Float, Object, etc.) -> the position offset of the property in an UObject is stored at offset 0x30 in the xProperty memory block; and it's read like this:
Similarly, the toggle flag (for BoolProperty, for example) is stored at offset 0x40:Code: Select all
116C7F5F | 8B41 30 | MOV EAX,DWORD PTR DS:[ECX+30] << 116C7F62 | C1E8 14 | SHR EAX,14 << function getOffset( p ) return readInteger( p + 0x30 ) >> 0x14 end
Code: Select all
116C1062 | 8B57 40 | MOV EDX,DWORD PTR DS:[EDI+40] << 116C1065 | 8511 | TEST DWORD PTR DS:[ECX],EDX function getFlags( p ) return readInteger( p + 0x40 ) end
- UFunctions -> the exec function is stored at 0x68 offset in the UFunction memory block
- FNames -> are stored as UNICODE
Special K
Looks like the concept of "borderless window" didn't exist at the time this game was released. Or if it did, either devs didn't implement it or UE3 didn't support it natively. You can play the game in a window if you press Alt+Enter keys, but the game doesn't remember this state and it will always start in exclusive full-screen, after which you will have to press the keys. Every. Damn. Time.
Enter Special K -> [Link].
At the time of writing this post the latest version is 22.9.26: [Link].
With the help of this tool you can send the game in borderless windowed mode via hooking. I found it very useful with old DX9 games with zero windowed-mode support.
Note that I have manually added the game to the list and used Set Custom Artwork to set the background (downloaded from [Link]). Just in case it's not clear what DELISTED means: the game is not available on Steam anymore, ergo any information about it stored in some database won't show up in listings.
And this is how it behaves in-game:
- - - - - - -
Cheat Table
With the help of the dumper above, I was able to map quite a bit of game objects in this UE3 version (v3428, by the way):
The several scripts in the table:
- God Mode
Will toggle a 0/1 flag on disable/enable which in turn flips the bGodMode flag in PlayerController UObject ([ Debug ] > GEngine > [0350] GamePlayers > [0000] LocalPlayer > [002C] PlayerController > [01D8] bGodMode). Effect: god mode for your player. Note that some scripted events might still kill you. If you leave the script enabled, you don't need to disable/enable it when transitioning to other maps or reloading. The hook in FViewport::Draw I use -- activated by [ Initialize ] script -- makes sure to re-enable the flag on transitions. Why? The Engine re-initializes UObjects so the default state of bGodMode becomes off.
- Unlimited Ammo
Will toggle a 0/1 flag on disable/enable which in turn flips the HasUnlimitedAmmo flag in PlayerController UObject ([ Debug ] > GEngine > [0350] GamePlayers > [0000] LocalPlayer > [002C] PlayerController > [0300] AcknowledgedPawn > [04B8] HasUnlimitedAmmo). Effect: unlimited ammo for your player's weapons. If you leave the script enabled, you don't need to disable/enable it when transitioning to other maps or reloading. The hook in FViewport::Draw I use -- activated by [ Initialize ] script -- makes sure to re-enable the flag on transitions. Why? The Engine re-initializes UObjects so the default state of HasUnlimitedAmmo becomes off.
- Set Weapon Properties
Will tamper with several default parameters of the Weapon templates:
- disable camera recoil [FovAdjustment, PitchAdjustmentDeg, RecoilTime, RecoilDelay]
- kill camera shake [Intensity, Roll, RollFalloffDuration, ShakeFalloffDuration]
- eliminate spread [Modifier[], ModifierChangePerShot, Cooldown]
- adjust shots to fire [NumShotsToFire]
- turn the weapons into automatic and enable unlimited ammo (you will see the ∞ symbol in-game for ammo) [bHasUnlimitedReserveAmmo(1)|bAutoReload(2)|bAutoFire(4)]
- adjust fire rate [FireIntervalLerper{StartValue, EndValue, RampTime, CurTime}]
I've also set a hotkey for it -- Numpad 7 -- which you can use to run the script. This can also be changed by right-clicking the script > Set/Change hotkeys > click the line with my key > Edit Hotkey > set yours > Apply > OK.
Note that by design the script won't set an [X] in the box next to its name in CE GUI. It's designed to do its stuff and error, so it doesn't become active. Why? Cuz then you would have to disable/re-enable it every time you want to use it, which makes no sense.
You will have to run the script every time you pick-up a NEW weapon during a level you're playing, every time you swap weapons with others lying on the ground or if you exit game/re-enter it. In general, if you don't see the crosshair reticle collapsed to maximum or your weapon isn't firing automatically (as you keep Mouse 1 down), then you will need to re-press Numpad 7 to re-run the script.
Goodies
1) staticFindObjectEx
The cheat table comes with a Lua function which makes use of internal UE3 functions I've found and labeled. Such as this staticFindObjectEx:
Code: Select all
function staticFindObjectEx( sa, sb, sc )
local StaticFindObject = getAddressSafe( "_StaticFindObject" )
if StaticFindObject == 0x0 then return 0 end
local Class, InOuter, UObject = 0, 0, 0
if sb == nil and sc == nil then
UObject = executeCodeEx( 1, nil, StaticFindObject, 0, -1, {type=4;value=sa}, 1 )
elseif sc == nil then
Class = executeCodeEx( 1, nil, StaticFindObject, 0, -1, {type=4;value=sa}, 1 )
UObject = executeCodeEx( 1, nil, StaticFindObject, Class, -1, {type=4;value=sb}, 0 )
else
Class = executeCodeEx( 1, nil, StaticFindObject, 0, -1, {type=4;value=sa}, 1 )
InOuter = executeCodeEx( 1, nil, StaticFindObject, 0, -1, {type=4;value=sb}, 1 )
UObject = executeCodeEx( 1, nil, StaticFindObject, Class, InOuter, {type=4;value=sc}, 0 )
end
return UObject
end
Code: Select all
local t = staticFindObjectEx( "ObjectProperty", "Engine.Controller", "Pawn" )
if t < 1 then stopExec( "'Pawn' ObjectProperty not found." ) end
printf( "%X", t )
Code: Select all
local t = staticFindObjectEx( "Engine.GameEngine", "Transient.GameEngine" )
if t < 1 then stopExec( "'Pawn' ObjectProperty not found." ) end
printf( "%X", t ) --> error
local t = staticFindObjectEx( "Engine.GameEngine", "Transient.GameEngine_1154" )
if t < 1 then stopExec( "'Pawn' ObjectProperty not found." ) end
printf( "%X", t ) --> valid
These are used to obtain the UProperty offset of a Bool, Int, Float, etc. Property. The rest are to get the LocalPlayer, PlayerController and PlayerPawn memory addresses. You can see how they're used in Set Weapon Properties script.
3) mapped debug symbols
With the help of some leaked UE3 source code and binaries with debug symbols, I was able to map quite a lot of functions in this game. Wasn't easy, as the compilers are different between the builds (TWFC uses VC8/9 while my binaries were all VS2013+). To be able to use these you will need x64dbg: [Link].
Linked the current .exe/.dll so they stay here for posterity.
TWFC binaries ('Large Address Aware' flag applied): [Link]
(password: sunbeam)
TWFC x32dbg database: [Link]
(no password)
How to use, simple:
- get [Link]
- extract/move the .dd32 file to '\x64dbg\x32\db' folder
- run the game
- attach to TWFC.exe
- done
Note that I can't hold your hand while you learn how to use x32dbg, so "my game crashes", "I can't attach", "I don't understand how to do it" etc. are not my concern. The intention here is to provide a helping hand to people wanting to reverse more of this game, not to teach you how to use a debugger. If you have zero knowledge in this regard, maybe you want to ignore this for now; till you get interested, learn a bit, then return here.
The database contains mappings up to what I've - once more, manually - mapped so far. It's not complete by far, you will find CALLs (functions) that have no names/labels still, but a lot of them should make sense once you start debugging.
Enjoy and best regards,
Sun
How to use this cheat table?
- Install Cheat Engine
- Double-click the .CT file in order to open it.
- Click the PC icon in Cheat Engine in order to select the game process.
- Keep the list.
- Activate the trainer options by checking boxes or setting values from 0 to 1