Now that we’ve had the opportunity to do some basic analysis of the data structures for The Witcher 3, it’s time to uproot its gameplay and get things going in the crazy Omnified department by throwing in the Apocalypse system. This will completely overhaul the damage system and I think end up being a huge improvement to the game, as its biggest defect, for me at least, has always been its difficulty (or lack thereof).

For detailed information on what the Apocalypse system is, you can read the design article on it here, or if you don’t like reading, feel free to watch this nice little video:

Don’t be scared — here’s a summary on the Apocalypse system.

Let’s begin then by doing a bit of reverse engineering to find the points in the code we need to hook into in order to make this magic happen.

On the Matter of the Damage Application Code

As always, implementing the Apocalypse system requires us to make an initiation point to be injected directly into the game’s damage application code. You can learn more about these terms and exactly what kind of code I’m talking about by checking out the relevant section in my article on the Apocalypse system.

To start our search for the damage application code, we need to first find the health update code, which is simple enough: it’s the code responsible for updating our character’s health in memory to a newly calculated value following some kind of event (like getting your ass smacked by a drowner, etc.).

So, to do that, we just need to right click on our player’s health in the address list and choose Find out what accesses this address. Then we get our butt smacked.

Shows the code that updates a players health to a new value.
Any change to our health is committed by this code.

Above we can see what is undoubtedly our health update code. Most distressingly, however, I noticed right away that the health update code was getting pinged maybe 5-10+ times every time we got hit by an enemy. I’ve been doing this long enough to know that we were going to be in for a fun ride.

And oh boy, were we in for one. Spoiler alert: this was undoubtedly one of the most complicated reverse engineering endeavors I’ve had to undertake in order to figure out whether our player was receiving damage or not.

Tracing the Damage Application Code

Taking a look at where the health update code lives, we see the following code:

Shows the code and surrounding code that updates the player's health for all situations.
Here is the surrounding code for the health updates. We can see the code obtaining our vitals struct from our W3AbilityManager.

Scrolling up from here, there is no immediate arithmetic operation that jumped out at me. So, as is the norm for most games, the updated health value was being calculated at a point most likely far removed from where it was getting committed to our player’s health in memory.

If we’re lucky, maybe we’ll see something by going up the stack a bit.

Shows the method one level up on the stack to the health update code.
Here’s the dynamically addressed function call one level up from the health update code. “Thousands” of functions seem to be using this.

We don’t see anything here of note, arithmetic-wise, but we do see a very disturbing instance of a dynamically addressed function call, only one level above our health update code! This means that there is probably going to be a lot of different kinds of code calling into this, making debugging and our reverse engineering efforts quite a pain.

Indeed, if we place a breakpoint here, just one level up from our health update code, we can observe it getting hit at a near constant rate. Clearly it would appear that, much like the code we were dealing with in regards to W3AbilityManger resolution, that we were knee-deep in the middle of some highly generalized code. This is something I always strive for when writing my own code, but it also something quite painful to deal with at the assembly level when trying to look backwards in time.

We were going to need to do a trace somewhere, but we were going to have to try to figure out how to filter all the threads of execution hitting this function up so we can produce meaningful results. Normally, when we do a trace, we go quite a few levels up on the stack, so let’s do that right now.

Shows a parent function call a few levels above the health update code from which we'll be starting a trace.
This is a number of calls up the chain from the health update code. We’re going to try to do a trace here — difficult since so many functions call it.

This code here, which is calling yet another dynamically addressed function, is very far removed from the health update code. I believe this code is situated at the top of a few of the recursive chains of function calls that I observed. This was the ideal place to start our trace, as I was able to figure out a register value that we could discriminate health updates from other kinds of updates with; namely, the r12 register being set to our W3AbilityManager.

We learned from the previous article that this “property unroller” family of functions that we’re looking at stores the data source of values it’s working with in the r12 register. Since our health comes from our ability manager, that is what we want to end up filtering on. So, we just look at what [playerAbilityManager] resolves to in our code, and set up a trace to go off when r12 equals that.

Shows our trace results from the parent function that led to the health update code.
Here’s the beginning of our successful trace as to how damage gets applied to our health value.

I cannot recall if we got a successful trace on our first try…but I think we did? Anyway, what we’re looking at here is the start of it, and as you can see, the first instruction is that very same dynamically addressed function call that was shown in the previous picture.

We had to scroll through an uncountable number of function calls until we finally got to the meat of the matter: the damage application code.

Shows us the meat of the matter: the damage application code.
Here is the damage application code. However, it is used for MUCH more than just calculating new health values.

There it is! In this instruction, the code is subtracting a damage amount stored in [rsp+40] from a creature’s current health amount, stored in xmm0.

Well that wasn’t very difficult — I think it honestly took me only thirty minutes to find this. I was feeling pretty good. It didn’t take long for me to realize however, that this “damage” application code was actually being used to calculate differences for a plethora of kinds of values. Literally, from the player’s health getting damaged, to the position of elements on the screen’s HUD, to probably the space between Geralt’s chin hairs.

That all meant we needed to devise some proper hit detection in order to be able to distinguish damage to the player (as well damage to the enemy) from all these other calculations.

On the Matter of Hit Detection

Much time was spent on figuring out all the different sorts of values being processed by our “damage” application code — all of it was broadcast on my stream (where I had the privilege to scratch my head going “HMMM” in front a bunch of people). So, if you are curious about the exact process in which I figured this all out, you’ll need to consult either a VOD from there or one of my YouTube videos when I get around to uploading them.

Please by advised that, although I tested this code out quite a bit, what we’re trying to achieve is a rather complicated affair. That means things might change in the future. If that happens, remember that I will always upload the latest version of my hacking source to my source repository at https://github.com/omniscientist/BadEcho.

Lets take a look at the code in question.

Shows our damage application code, with execution paused during an actual damage application.
Shows execution paused during an actual damage application.

This is where the magic happens. Even though this game lacks subtractive code specifically dedicated to damage being done to the health of entities, this is still one of the best places to hook the Apocalypse code into, as it ensures we get a say before anything is done with the game’s original damage.

We could say that the only other better place to impact the damage is in the code that actually calculates the damage itself (where the health has no bearing), as that would ensure all possible dependent systems are corrected to use our adjusted damage value; however, the code responsible for that would be most likely missing some components required by the Apocalypse system (namely, the health the damage is being applied to). This would end up requiring multiple hooks into the code for Apocalypse initiation, etc.

It’s something we can explore in the future.

Types of Values Processed by the Damage Application Code

Let’s talk quickly about all the different kinds of values I observed being operated on by this code. Remember, every single game I’ve put the Apocalypse system into so far had dedicated subtractive code for damage to health. As I said before, I’ve always been surprised to see such specific code for a gameplay matter; it is nice to see some actual generalized code in one of these games.

I observed this code would be executed to process value changes of the type including, but not limited to:

  • damage to the player’s health,
  • damage to an enemy’s health,
  • reduction in the player’s stamina due to using a special ability,
  • regeneration to the player’s health due to normal passive health regen (yes, even though a subss instruction is being used),
  • regeneration to the player’s stamina due to normal passive stamina regen,
  • what appeared to be changes in position for elements on the screen’s HUD, and
  • many other unknown changes to known values as well as changes to unknown values.

This is a lot of things to filter on. And, don’t forget, that we’re of course operating in “machine-land”, given that we’re writing assembly. We need to be real clever in order to cherry pick the kinds of operations we’re interested in. Many times when we’re trying to filter out various operations from code we’re hooking into, we’re able to discern (most likely) statically determined values stored in registers that indicate the state of the operation. Can we do that with The Witcher 3?

To make a long story short, and to answer the question: no. No such thing was possible. Indeed, I must say that the fact I figured out how to successfully filter out all these operations is really a testament to my method and gives me confidence that I’ll be able to do the same in any kind of software. It also just gives me one of those nice warm feelings.

Filtering Out All Operations Unrelated to W3AbilityManager

All of the filtering we’re about to do is done by acting on the values of various bits of data stored on the stack, expect for one particular piece of data: the contents of the r12 register. Remember, in this “property unroller” code, the r12 register points to the data source for the values being calculated. If we can filter out all operations executed by this code that have nothing to do with a W3AbilityManager instance, we remove a lot of the “unknown changes” I briefly alluded to above.

How do we do that? Well, we know our own W3AbilityManager instance, but we need to hook into damage being done to enemies as well (since the Apocalypse system includes the Enemy Apocalypse, which does a lot of cool things for us in regards to damage done to enemies such as statistical tracking, etc.). So, we need to be able to determine the type of the data in r12.

That might sound like a hard thing to do in the scary world of assembly, but fortunately for us, it is rather easy to do in the case of The Witcher 3. Let’s take a look at a snapshot of our own ability manager first:

Our player's W3AbilityManager and its addresses, including its type identity.
Here’s my ability manager at the time of trying to develop a filter for only damage applications.

The very first member of this data structure, like other structures in this game, points to essentially a collection of instances of similar data, and essentially acts as an identifier for the data structure’s type. The address of this type identity, 0x7FF676D60F78, will never change (well, barring a software update), and we’ll be using that as this type’s identity value to match up against. To make things simpler, we’ll just be looking at the lower 2 bytes of the address: 0x0F78.

W3AbilityManager Filter Code

  // Check if operation is being done on an entity's ability manager first.
  // r12 is the source of the values being calculated.
  cmp r12,0
  // If r12 isn't set, this has nothing to do with damage.
  je initiateApocalypseExit
  mov rax,[r12]
  // Lower 2 bytes of W3AbilityManager's type identity is 0x0F78.
  cmp ax,0x0F78
  jne initiateApocalypseExit 

This bit of code will filter out a large, large amount of operations that we are not interested in. But we are by no means out of the woods yet.

Filtering Out All Operations Unrelated to Damage to Player

If you recall from the last article, the ability manager structure (which points to the Vitals structure) is responsible for maintaining not only the character’s health, but also their stamina, as well as other values. Additionally, we have a number of operations involved here that deal with the regeneration of values, something we really don’t care about!

Figuring out how to separate health loss due to enemy damage from all the other stuff took some hardcore reverse engineering and pattern matching. That basically translates to me doing what I needed to in order to get a particular operation to execute in the game, taking a snapshot of the stack at that point in time, and then comparing it to the stack as it was when the player was being damaged. It took awhile, but we ended up with some very reliable filtering code.

Here is the method in which I filter out all operations not related to the player losing health due to an enemy attack:

  1. First, we see if r12 is equal to our own player ability manager. If it isn’t, then nothing is happening to the player. Perhaps it is damage that is being done to an enemy instead; this is covered in its own section below.
  2. [rsp+A0] is zero when a vital stat amount is changing for Geralt. If it isn’t zero, then we bail.
  3. [rsp+50] is zero only when stamina is changing. It is non-zero if health might be changing.
  4. Assuming [rsp+50] is not zero, then a zero value stored at [rsp+80] indicates health is changing.
  5. Finally, we need to see what is being pointed to by [rsp+68]. It must point to something, however it cannot be pointing to a data structure of the type CFunction. If it does point to such a function, that means the operation is most likely the result of a recurring regenerative type function, such as the normal restoration of health over time.
  6. If and only if all of the above conditions pass, we can conclude that we are dealing with an operation that is concerned with the reduction of the player’s health due to an attack from an enemy.

Fun little fact: even though the game states that there is no health regeneration on the highest difficulty (I think it does at least), there actually is a very small amount of regeneration (I’m pretty sure it’s not from some kind of item I have equipped…don’t quote me though). This complicated matters, but it did give us better code in the end, since we need to be able to filter out health regeneration from sources like food or potion, etc.

Anyway, as you have no doubt concluded yourselves, this is all very complicated. Don’t worry, it gets worse. Rest assured however, what we have in the end is very, very reliable hit detection code for damage being done to the player from the enemy.

Player Hit Detection Code

  // If our ability manager is the data source, then this set to 0 indicates a vital stat amount is
  // changing for Geralt.  
  mov eax,[rsp+A0]
  cmp eax,0
  jne verifyCiriDamaged
  // Check if Geralt's stamina is changing.
  // This is 0 when stamina is changing.
  mov eax,[rsp+50]
  cmp eax,0
  je initiateApocalypseExit
  // If stamina is not changing, check if Geralt's health is changing.
  // This is 0 when health is changing.
  mov rax,[rsp+80]
  cmp eax,0
  jne initiateApocalypseExit  
  // This must point to something, it just can't point to a function.
  mov rbx,[rsp+68]
  push rcx
  lea rcx,[rbx]
  call checkBadPointer
  cmp ecx,0
  pop rcx  
  jne initiateApocalypseExit  
  // If this points to something, it must not be a function. If it is, then the operation is the result
  // of a recurring regenerative type function. If it isn't, then this operation is being caused by
  // damage from an enemy.
  mov rax,[rbx]
  cmp ax,0xFA58
  jne initiatePlayerApocalypse
  jmp initiateApocalypseExit

Please be aware that the offsets used to access the stack do not account for the normal data preservation that takes place in the code that I write. This is done on purpose in order to drive home exactly what we are doing here. The proper offsets are made available in an upcoming section where we’re discussing the initiation point hook as a whole.

So, we’re good, right? No. Not at all. All we have discussed is hit detection code for when the player is being hit by an enemy. Don’t forget: the Apocalypse system has a module for damage done to the player as well as a module for damage done to the enemy by the player. We need to be able to disseminate both cases. Therefore, we need to be able to provide some hit detection logic for when it is enemies receiving the sword stab.

Determining When the Player Is Damaging an Enemy

If you’ll recall, one of the first checks we do during our hit detection code is the determination of whether r12 points to the player’s ability manager or not. This occurs after we’ve determined that r12 actually points to some type of W3AbilityManager. You might think that simply knowing that r12 is both a W3AbilityManager and not our own ability manager is enough to determine that an enemy has been damaged.

You would be correct, however it is not enough to determine that an enemy has been damaged by the player. The Enemy Apocalypse module has an important role to fill, so it is important that only damage coming from the player result in its execution.

Making the determination that the player is the source of the damage is often a difficult thing to do in most games; The Witcher 3 brings all of that difficult to a new level, somewhat.

In order to detect the player as source of damage, we need to look at various places on the stack. We must try not to base our filtering on too many “magical values” that can change at a moments notice. We need to find actual data that is linkable to known instances of data for our player in order to be sure.

Over time I noticed that, while we were able to find such data in the stack that linked back to the player, the exact type of linking data and its location sometimes depended on the type of enemy we were fighting. This is typically a very worrying thing when trying to implement pattern detection, however I’m reasonably confident that the procedure I’ve come up is going to work throughout the playthrough.

If it ends up needing some updates, pay close attention to my source control!

When a non-player entity is being targeted, we determine that the player is responsible using the following procedure:

  1. The address stored in [rsp+68] will point to the player’s ability manager when the player is responsible for damage being done to most enemies. If it doesn’t, we go to the next step.
  2. The address stored in [rsp+410] will point to the player’s ability manager when the player is responsible for damage being done to some enemies. If it doesn’t, we go to the next step.
  3. The address stored in [rsp+8] will point to the player’s ability manager when the player is responsible for damage being done to some enemies. If it doesn’t, we go to the next step.
  4. We take another look at the address stored at [rsp+410] and see if it is pointing to our character’s root structure. For a very select few (but important, i.e. the big monsters in the game) creatures, this will indicate that our player is responsible for the damage being done.
  5. If none of the above conditions pass, then the player is not responsible for the damage being done to an enemy.

After much testing, I’m very satisfied with this hit detection logic.

Enemy Hit by Player Detection Code

  // This will point to our ability manager for MOST enemies when we're responsible for the damage.
  mov rbx,[rsp+68]
  cmp [rax],rbx
  je initiateEnemyApocalypse
  // This will point to our ability manager for SOME enemies when we're responsible for the damage.
  mov rbx,[rsp+410]
  cmp [rax],rbx
  je initiateEnemyApocalypse
  // This will point to our ability manager for SOME enemies when we're responsible for the damage.
  mov rbx,[rsp+8]
  cmp [rax],rbx
  je initiateEnemyApocalypse
  // This will point to our root structure for a select few enemies when we're responsible for the
  // damage.
  mov rax,player
  mov rbx,[rsp+410]
  cmp [rax],rbx
  je initiateEnemyApocalypse
  jmp initiateApocalypseExit

Whew boy. This is some intense hit detection logic! This is all very necessary; it is amazing how many different kinds of values are being calculated by this little bit of code. I am quite happy with myself for figuring out how to separate the wheat from the chaff.

So, we’re good to go right? Nope. If we’re just talking about playing Geralt, then yes, we are good to go. However, as you may know, at certain times during this game you are playing from the perspective of Ciri (Geralt’s ward). And unfortunately, that turns everything a bit on its head as far as hit detection goes.

On the Matter of Ciri’s Hit Detection

Very annoyingly, not long after the first sequence where we play as Ciri started, I noticed that the hit detection for when the player is getting damaged by enemies was no longer sufficient. Simply put, it would incorrectly exclude operations that were genuinely concerned with enemies damaging Ciri’s health.

Quite confusingly, while we were able to make use of the same data points that we use with Geralt, the values of the data points had to be exactly the opposite of what they needed to be when Geralt was getting smacked. Yes, that makes my head hurt as well. Here’s the process for Ciri’s hit detection:

  1. If you can recall, we look at [rsp+A0] to see if the change that is occurring involves one of Geralt’s vital stats. If its value is zero, then that means a vital stat is changing. We previously concluded that a vital stat is not changing if it is not zero. While true, it also signals to us that a vital stat belonging to Ciri might be changing instead. So, we use this particular point in memory to determine whether we’re dealing with Ciri vs Geralt here as far as hit detection goes.
  2. If [rsp+A0] is 0 (and therefore indicative of a change not involving one of Geralt’s vital stats), then we proceed to check if perhaps one of Ciri’s is changing. We look at [rsp+50] next, just like we do for Geralt. Instead of a value of zero being indicative of stamina changing (like it is with Geralt), it’s actually a non-zero value that indicates a (what I assume to be) stamina change as far Ciri is concerned. So, if [rsp+50] is zero, we continue.
  3. We then take a look at [rsp+80], just like we do for Geralt. And, once again, the significance of the value is opposite to its significance when we’re detecting hits on Geralt. So, in the case of Ciri, this value must be not be zero.
  4. Unlike Geralt, we don’t need to check for the presence of a recurring regenerative function. One of the other steps must weed it out.

Yes, very confusing, but the end result is a very reliable hit detection algorithm for both Geralt and Ciri!

Player Hit Detection Code (Ciri)

  // For Ciri, this is not 0 (unlike Geralt) when (assumingly) stamina is changing.
  // If it is 0, then Ciri may be losing health.
  mov eax,[rsp+50]
  cmp eax,0
  jne initiateApocalypseExit
  // For Ciri, if stamina isn't changing, then this is not 0 when losing health.
  // No check for a recurring function is required for Ciri.
  mov rax,[rsp+80]
  cmp eax,0
  je initiateApocalypseExit 

Once again, offsets are not adjusted for all our data preservation that gets pushed to the stack. Proper offsets will be provided to the reader below.

And…yes…that’s it! That’s our hit detection code. I’ve tested it on stream and it is very reliable, and I’m super psyched I was able to do it. Seriously, if we can do it for this function, which is called to process so many different types of calculations, we can do it for anything, in any game.

On the Matter of Ciri’s Coordinates

This may seem a bit off-topic, however anomalies in regards to Ciri’s coordinates were brought to light during our implementation of the Apocalypse system. As you may know, one of the parameters for the Player Apocalypse is the address to the player’s location structure, aligned at the X coordinate. It is required, and if the location coordinates are not known, well we’re just going to get a big ol’ crash, aren’t we?

And that’s what happened with Ciri: some big ol’ crashes. It happened because the hook we created for finding Geralt’s coordinates in the first article did not appear to be executing when we were controlling Ciri.

What a pain! And how strange — in most every game I’ve hacked that featured multiple controllable characters throughout a story, I would observe that hooks into critical data structures for one character would almost always get reused when controlling another character. Clearly Ciri is being treated a bit differently from Geralt — hopefully not too differently.

Luckily for us (since finding the coordinates from scratch is a bit of a pain), Ciri’s immediate coordinates were still being found through the hook into the immediate character physics wrapper:

Shows Ciri's still functioning Immediate coordinate values.
Ciri’s immediate coordinates are still present, so we still have a valid physics wrapper. We’ll use these to find her actual coordinates.

Since these are roughly equivalent to the actual coordinates, it didn’t take long to find her coordinates in memory. I immediately then checked out what code was accessing them.

Shows the code accessing Ciri's actual coordinates
Here are the instructions accessing Ciri’s actual coordinates — only the one we used to find Geralt’s is missing, interestingly enough.

I was disappointed to see that all the instructions I saw accessing Geralt’s coordinates were present here — except for the one we decided to hook into! This disappointment is doubled in magnitude when we consider that the reason we hooked into that particular code was because it was the only one code accessing the coordinates that was constantly executing. All of the other functions, and indeed all of the ones in the above image, only execute when the character moves.

We want to always know what the player’s coordinates are, as soon as possible, before any movement. There are many reasons for this (I’ve provided many examples in the past) — so what do we do? We do what the game does: we find the data relationally, using the character’s root structure as the starting point for our own search.

Technically speaking (and with, I’ll admit, quite a few assumptions being made) if we have access to a character’s root data structure, we should be able to be find any and all data structures pertaining to that character. That requires, however, doing some reverse engineering of the contents of these structures and charting the paths to and from a particular piece of data we’re interested in.

We already found the root structure for the character in the previous article, so we only needed to figure out how to get from there to the character’s location structure. We had no idea how to do this, so I got some inspiration by looking at how the game was charting its own path to the location structure.

After a bit of poking around (of which you can review on your own from recorded footage of my stream), I was overjoyed to be able to find the path one must take in order to find a source of truth location structure from a character’s root structure.

The path to grab ahold of the player’s location structure from the root structure is illustrated in this updated code for our hook that grabs the player’s root structure (the separate player location hook we previously had is no longer needed).

Updated Player Root Structure Hook With Resolved Location

// Gets the player's root structure as well as resolving the source of truth location coordinate
// structure found on it.
define(omniPlayerHook,"witcher3.exe"+AB139)

assert(omniPlayerHook,48 8B 01 48 8D 54 24 60)
alloc(getPlayer,$1000,omniPlayerHook)
alloc(player,8)
alloc(playerLocation,8)

registersymbol(omniPlayerHook)
registersymbol(player)
registersymbol(playerLocation)

getPlayer:
  pushf
  push rax
  push rbx
  push rcx
  mov rax,[rcx]
  cmp ax,0xC418
  jne getPlayerExit
  mov rax,player
  mov [rax],rcx
  // Grab the CMovingPhysicalAgentComponent.
  mov rbx,[rcx+0x218]
  // Grab the CMRPhysicalCharacter.
  mov rcx,[rbx+0x1648]
  // Grab the source CPhysicsCharacterWrapper.
  mov rbx,[rcx+0x10]
  // Grab the location coordinates holder.
  mov rcx,[rbx+0x78]
  mov rax,playerLocation
  add rcx,8
  mov [rax],rcx
getPlayerExit:
  pop rcx
  pop rbx
  pop rax
getPlayerOriginalCode:
  popf
  mov rax,[rcx]
  lea rdx,[rsp+60]
  jmp getPlayerReturn

omniPlayerHook:
  jmp getPlayer
  nop 3
getPlayerReturn:

Being able to retrieve character data directly from a root structure is a superior method in acquiring this kind of data. A big win!

Adding Apocalypse Support for Coordinate Doubles

In primarily the previous article as well this article, I’ve spoken about how this game uses double as the value type for its source of truth coordinates. Every other game I’ve hacked so far has used float, save for Subnautica, which used doubles, but only for while the player was on land (very odd, but whatever).

If one consults the Apocalypse system design article, one will become very familiar with one of the standout features of the system: the teleportitis effect. This effect randomly teleports the character somewhere following some damage being received. The way we effectuate changes to the player’s location is by directly writing to the place in memory in which the coordinates live.

Well, up until now, we’ve been able to do this the same way for every game, since every game has used floating points to hold its coordinates. Writing directly to such a structure truly seemed to be quite “game-neutral”, given that every game I’ve encountered has had the exact same memory footprint as far as the three well known axes (X, Y, and Z) were concerned.

Well, this game uses doubles. That means the memory footprint has changed. That also means, then, that we have to do something I don’t have to do much, if ever, when implementing an Omnified system into a game: we need to add some additional functionality to the Omnified system in order for it to support said game.

In particular, we need to be able to support exporting teleportitis affected coordinate values to coordinate location structures that use doubles for each axis value. Doing so required the addition of a new external parameter: coordinatesAreDoubles, as well as modification of the teleportitis effect code found under the commitTeleportitis label in the Omnified framework assembly code file.

Here now is the updated teleportitis effect code with double coordinate support added into it:

Updated Teleportitis Effect Code in Omnified Framework

commitTeleportitis:
  // Some games will disable modifications being made to the player's coordinates
  // during certain animations such as weapon attacks or getting knocked back.
  // This code needs to be hooked into and temporarily disabled in order for 
  // teleportitis to work. This can be done by checking the "teleported" symbol 
  // and preventing those coordinates from being reset. That code will then 
  // need to set this symbol to 0. In most games I've hacked, this is not 
  // required. In fact, only one: Dark Souls I.
  mov [teleported],1
  // Load the player coordinates address parameter.
  mov rbx,[rsp+48]
  // Load the parameters for generating the random displacement value to be 
  // applied to the X coordinate.
  push [teleportitisResultLower]
  push [teleportitisResultUpper]
  mov rax,playerApocalypseRandomState
  push rax
  call generateRandomNumber
  // The random number is an integer and will need to be converted to a float.
  mov [teleportitisResult],eax
  cvtsi2ss xmm1,[teleportitisResult]
  // The random number is divided by the following divisor to bring it into the
  // expected range (0-10) along with some decimal precision (3 decimal places).
  divss xmm1,[teleportitisDivisor]
  // We cannot generate random negative integers, we instead shift what we have 
  // here by a negative amount. The range 0 to 10 becomes -5 to 5.
  subss xmm1,[teleportitisShifter]
  // We finally take what we have and then multiply it by the displacement 
  // multiplier to get the final displacement value to apply to the player's 
  // X coordinate.
  mulss xmm1,[teleportitisDisplacementX]
  // We then take the player's current X coordinate from memory and add the
  // displacement value to it.
  cmp [coordinatesAreDoubles],1
  je loadXAsDouble
  movss xmm2,[rbx]
  jmp addChangeToX
loadXAsDouble:
  cvtsd2ss xmm2,[rbx]
addChangeToX:
  addss xmm2,xmm1
  // The updated X coordinate is committed back into the memory, which will 
  // move the player.
  cmp [coordinatesAreDoubles],1
  je commitXAsDouble
  movss [rbx],xmm2
  jmp teleportY
commitXAsDouble:
  cvtss2sd xmm1,xmm2
  movsd [rbx],xmm1
teleportY:
  // Load the parameters for generating the random displacement value to be 
  // applied to the Y coordinate. 
  push [teleportitisResultLower]
  push [teleportitisResultUpper]
  mov rax,playerApocalypseRandomState
  push rax
  call generateRandomNumber
  mov [teleportitisResult],eax
  cvtsi2ss xmm1,[teleportitisResult]
  divss xmm1,[teleportitisDivisor]
  // If the Y-axis is not the vertical axis, then we we don't need to check 
  // whether vertical displacement is enabled.
  cmp [yIsVertical],1
  jne skipYSkipCheck
  // If negative vertical displacement is not enabled we do not want to shift it,
  // this causes the random value to remain in the range of 0 to 10.
  cmp [negativeVerticalDisplacementEnabled],1
  jne skipNegativeVerticalYDisplacement
skipYSkipCheck:
  subss xmm1,[teleportitisShifter]
skipNegativeVerticalYDisplacement:
  mulss xmm1,[teleportitisDisplacementX]
  // The vertical displacement value is logged and displayed to viewers as 
  // changes to are often the most consequential. We make sure the Y-axis is 
  // the vertical one before logging it.
  cmp [yIsVertical],1
  jne skipLastYVerticalDisplacement
  movss [lastVerticalDisplacement],xmm1
skipLastYVerticalDisplacement:
  // We then take the player's current Y coordinate from memory and add the
  // displacement value to it.
  cmp [coordinatesAreDoubles],1
  je loadYAsDouble
  movss xmm2,[rbx+4]
  jmp addChangeToY
loadYAsDouble:
  cvtsd2ss xmm2,[rbx+8]
addChangeToY:
  addss xmm2,xmm1
  // The updated Y coordinate is commited back into the memory, which will 
  // move the player.
  cmp [coordinatesAreDoubles],1
  je commitYAsDouble
  movss [rbx+4],xmm2
  jmp teleportZ
commitYAsDouble:
  cvtss2sd xmm1,xmm2  
  movsd [rbx+8],xmm1
teleportZ:
  // Load the parameters for generating the random displacement value to be 
  // applied to the Z coordinate. 
  push [teleportitisResultLower]
  push [teleportitisResultUpper]
  mov rax,playerApocalypseRandomState
  push rax
  call generateRandomNumber
  mov [teleportitisResult],eax
  cvtsi2ss xmm1,[teleportitisResult]
  divss xmm1,[teleportitisDivisor]
  // Like the Y-axis, the Z-axis can sometimes be the vertical axis. So checks 
  // similar to the ones made in the Y coordinate displacement code are made.
  cmp [yIsVertical],0
  jne skipZSkipCheck
  cmp [negativeVerticalDisplacementEnabled],1
  jne skipNegativeVerticalZDisplacement
skipZSkipCheck:
  subss xmm1,[teleportitisShifter]
skipNegativeVerticalZDisplacement:
  mulss xmm1,[teleportitisDisplacementX]
  cmp [yIsVertical],0
  jne skipLastZVerticalDisplacement
  movss [lastVerticalDisplacement],xmm1
skipLastZVerticalDisplacement:
  // We then take the player's current Z coordinate from memory and add the
  // displacement value to it.
  cmp [coordinatesAreDoubles],1
  je loadZAsDouble
  movss xmm2,[rbx+8]  
  jmp addChangeToZ
loadZAsDouble:
  cvtsd2ss xmm2,[rbx+10]
addChangeToZ:
  addss xmm2,xmm1
  // The updated Z coordinate is commited back into the memory, which will 
  // move the player.
  cmp [coordinatesAreDoubles],1
  je commitZAsDouble
  movss [rbx+8],xmm2
  jmp updateEnemyDamageStats
commitZAsDouble:
  cvtss2sd xmm1,xmm2
  movsd [rbx+10],xmm1  
  jmp updateEnemyDamageStats

Just want to state for the record that the changes worked first try! I don’t care how mundane the matter is: that isn’t easy when we’re writing raw assembly!! Yes I know I’m bragging, forgive me.

Writing the Apocalypse Initiation Hook

Time to make this game freaking great by adding some Apocalypse magic to it. We’re going to hook directly into the code we identified earlier as our damage application code. Here’s a look at the initial template for our hook:

Apocalypse Initiation Hook – Template

// Initiates the Apocalypse system.
// [rsp+40] | {rsp+62}: Damage amount.
// xmm0: Player's current health.
// r12: Data source for values being calculated.
define(omnifyApocalypseHook,"witcher3.exe"+16529F4)

assert(omnifyApocalypseHook,F3 0F 5C 44 24 40)
alloc(initiateApocalypse,$1000,omnifyApocalypseHook)

registersymbol(omnifyApocalypseHook)

initiateApocalypse:
  
initiateApocalypseOriginalCode:
  subss xmm0,[rsp+40]
  jmp initiateApocalypseReturn
omnifyApocalypseHook:
  jmp initiateApocalypse
  nop 
initiateApocalypseReturn:

What we’re going to have to do is implement that hit detection code talked about at length in the previous sections and then execute the primary functions of the Apocalypse system, which will be either executePlayerApocalypse or executeEnemyApocalypse depending on who or what is getting smacked.

Because of the hit detection required, we need to do a little bit more work here than normal prior to calling the Apocalypse system. Because I discussed in detail what is involved for proper hit detection, I won’t rehash that here. Before stepping into the code however, we need to account for all the data that we need to push to the stack in order to preserve it.

Let’s go over what we’re going to need to push to the stack to ensure data integrity:

  1. As always, we want to back up all conditional flags. That’s going to eat up 2 bytes.
  2. Next, we’ll need a least one SSE register to hold floating point values to do whatever we need to do with them. We’ll be backing up xmm1, and that’ll eat up 16 bytes.
  3. Finally, we’ll want to back up rax and rbx since the Apocalypse system uses those registers as return values. That’s two registers at 8 bytes each for a total of 16 bytes.

Adding all of that up, we’re given a total of 34 bytes, or 0x22 bytes when we’re speaking hex talk (which is, you know, what we speak around here)!

As always, for more information in regards to the requirements involved when writing Apocalypse initiation points, please consult the relevant sections in the Apocalypse overview article on the Player Apocalypse API and the Enemy Apocalypse API. Hope that answers everything!

To start everything off, let’s back up all the data we need to and implement our girthy hit detection system.

Apocalypse Initiation Hook – First Steps

initiateApocalypse:
  pushf
  // Ensure that player ability manager pointer has been initialized.
  push rax
  mov rax,playerAbilityManager
  cmp [rax],0
  pop rax
  je initiateApocalypseOriginalCode
  sub rsp,10
  movdqu [rsp],xmm1
  push rax
  push rbx
  // Check if operation is being done on an entity's ability manager first.
  // r12 is the source of the values being calculated.
  cmp r12,0
  // If r12 isn't set, this has nothing to do with damage.
  je initiateApocalypseExit
  mov rax,[r12]
  // Lower 2 bytes of W3AbilityManager's type identity is 0x0F78.
  cmp ax,0x0F78
  jne initiateApocalypseExit  
  // Check if player is damaging an enemy. 
  // If we're damaging an enemy, r12 will not point to our own ability manager.
  mov rax,playerAbilityManager
  cmp [rax],r12
  je verifyPlayerDamaged
  // This will point to our ability manager for MOST enemies when we're responsible for the damage.
  mov rbx,[rsp+8A]
  cmp [rax],rbx
  je initiateEnemyApocalypse
  // This will point to our ability manager for SOME enemies when we're responsible for the damage.
  mov rbx,[rsp+432]
  cmp [rax],rbx
  je initiateEnemyApocalypse
  // This will point to our ability manager for SOME enemies when we're responsible for the damage.
  mov rbx,[rsp+2A]
  cmp [rax],rbx
  je initiateEnemyApocalypse  
  // This will point to our root structure for a select few enemies when we're responsible for the
  // damage. 
  mov rax,player
  mov rbx,[rsp+432]
  cmp [rax],rbx
  je initiateEnemyApocalypse
  jmp initiateApocalypseExit  
verifyPlayerDamaged:
  // If our ability manager is the data source, then this set to 0 indicates a vital stat amount is
  // changing for Geralt.  
  mov eax,[rsp+C2]
  cmp eax,0
  jne verifyCiriDamaged
  // Check if Geralt's stamina is changing.
  // This is 0 when stamina is changing.
  mov eax,[rsp+72]
  cmp eax,0
  je initiateApocalypseExit
  // If stamina is not changing, check if Geralt's health is changing.
  // This is 0 when health is changing.
  mov rax,[rsp+A2]
  cmp eax,0
  jne initiateApocalypseExit  
  // This must point to something, it just can't point to a function.
  mov rbx,[rsp+8A]
  push rcx
  lea rcx,[rbx]
  call checkBadPointer
  cmp ecx,0
  pop rcx  
  jne initiateApocalypseExit  
  // If this points to something, it must not be a function. If it is, then the operation is the
  // result of a recurring regenerative type function. If it isn't, then this operation is being
  // caused by damage from an enemy.
  mov rax,[rbx]
  cmp ax,0xFA58
  jne initiatePlayerApocalypse
  jmp initiateApocalypseExit  
verifyCiriDamaged:
  // For Ciri, this is not 0 (unlike Geralt) when (assumingly) stamina is changing.
  // If it is 0, then Ciri may be losing health.
  mov eax,[rsp+72]
  cmp eax,0
  jne initiateApocalypseExit
  // For Ciri, if stamina isn't changing, then this is not 0 when losing health.
  // No check for a recurring function is required for Ciri.
  mov rax,[rsp+A2]
  cmp eax,0
  je initiateApocalypseExit  

Oh yeah. That hit detection code is no joke. But it works! It’s fabulous! At least until I run into something that throws it all to crap. But I’m sure that won’t happen (knock on wood furiously).

In the above code, execution is directed accordingly based on whether the player is being damaged, whether an enemy is being damaged by a player, and whether something else is happening. If the player is being damaged, the Player Apocalypse needs to be called. Let’s implement that next.

Apocalypse Initiation Hook – Player Apocalypse

initiatePlayerApocalypse:
  // Push the damage amount parameter.
  movss xmm1,[rsp+62]
  sub rsp,8
  movd [rsp],xmm1
  // Push the player's current working health value.
  sub rsp,8
  movd [rsp],xmm0
  // Push the player's maximum health value.
  mov rax,playerVitals
  mov rbx,[rax]
  movss xmm1,[rbx+4]
  sub rsp,8
  movd [rsp],xmm1
  // Align the player's location struct to the X coordinate and push it.
  mov rax,playerLocation
  mov rbx,[rax]
  lea rax,[rbx+1B8]
  push rax
  call executePlayerApocalypse
  jmp initiateApocalypseUpdateDamage

Ah nice and easy. Once this code is done executing, we’ll have an updated damage amount in rax and a potentially updated working health value in rbx. These return values will then get processed.

But before we get to that, let’s check out what our call to the Enemy Apocalypse looks like.

Apocalypse Initiation Hook – Enemy Apocalypse

initiateEnemyApocalypse:  
  // Push the damage amount parameter.
  movss xmm1,[rsp+62]
  sub rsp,8
  movd [rsp],xmm1
  // Push the target's current working health value.
  sub rsp,8
  movd [rsp],xmm0
  call executeEnemyApocalypse

As always, the Enemy Apocalypse initiation code is a bit simpler than what we have to do for the player, given the fewer number of parameters.

Like the Player Apocalypse code, we end up having return values saved in the rax and rbx registers. We need to process these return values and update the places in memory that the game code is going to be looking at in order to do its damage application. After that, we’ll want to wrap everything up and restore the stack and any registers we used to be what they were before we hand control back to the game.

Let’s write that bit of code up now.

Apocalypse Initiation Hook – Return Value Processing and Cleanup

initiateApocalypseUpdateDamage:
  // Commit updated damage and working health.
  mov [rsp+62],eax
  movd xmm0,ebx
initiateApocalypseExit:
  pop rbx
  pop rax
  movdqu xmm1,[rsp]
  add rsp,10

That’s our Apocalypse system implementation for The Witcher 3 folks. Before we can call it a day, however, there are a few Apocalypse system external parameters we need to set.

Apocalypse Initiation Hook – External Parameters

coordinatesAreDoubles:
  dd 1

negativeVerticalDisplacementEnabled:
  dd 0

yIsVertical:
  dd 0

In The Witcher 3, the Y axis is not the vertical axis; rather, the Z axis is. We’re also disabling negative vertical displacement because teleporting Geralt underneath the Earth just causes him to fall until he’s forced to fast travel; no death ever occurs. And, if there’s no death involved: I’m not interested! Finally, the coordinates used in this game are double value types, not floats.

Wrapping Up

Well that was a lot of interesting work that we attacked very patiently and methodically on stream. In the end, we have the awesome Apocalypse system hooked into The Witcher 3. I present to you now the complete code required to add Apocalypse system support to the game:

Apocalypse Initiation Hook – Complete

// Initiates the Apocalypse system.
// [rsp+40] | {rsp+62}: Damage amount.
// xmm0: Player's current health.
// r12: Data source for values being calculated.
// Conditions
// ----------
// If player is being targeted:
//
// [rsp+A0] | {rsp+C2}: Value is 0 if a vital stat amount is changing for Geralt.
// A value of 0 means either a non-vital stat is changing or that Ciri is losing a vital stat amount. Confusing, I know.
// [rsp+50] | {rsp+72}: Value is 0 when Geralt's stamina is changing. Value is 0 when Ciri might be losing health.
// [rsp+80] | {rsp+A2}: Value is 0 when Geralt's health is changing (assuming [rsp+50] is not 0).
// Value is not 0 when Ciri is losing health (assuming [rsp+50] is 0). Yes. Confusing lol.
// [rsp+68] | {rsp+8A}: If Geralt's health is changing, we must ensure that this points to some kind of data, but 
// that it does not point to data of type CFunction. If it does, then the operation is most likely a regenerative 
// one. This does not apply to Ciri for some reason.
//
// If non-player is being targeted:
//
// [rsp+410] | {rsp+432}: Set to player's ability manager when player is damaging SOME enemies.
// For A VERY FEW number of enemies, this will be set to the player's root structure.
// [rsp+8] | {rsp+2A}: Set to player's ability manager when player is damaging SOME enemies.
// [rsp+68] | {rsp+8A}: Set to player's ability manager when player is damaging MOST enemies.
define(omnifyApocalypseHook,"witcher3.exe"+16529F4)

assert(omnifyApocalypseHook,F3 0F 5C 44 24 40)
alloc(initiateApocalypse,$1000,omnifyApocalypseHook)

registersymbol(omnifyApocalypseHook)

initiateApocalypse:
  pushf
  // Ensure that player ability manager pointer has been initialized.
  push rax
  mov rax,playerAbilityManager
  cmp [rax],0
  pop rax
  je initiateApocalypseOriginalCode
  sub rsp,10
  movdqu [rsp],xmm1
  push rax
  push rbx
  // Check if operation is being done on an entity's ability manager first.
  // r12 is the source of the values being calculated.
  cmp r12,0
  // If r12 isn't set, this has nothing to do with damage.
  je initiateApocalypseExit
  mov rax,[r12]
  // Lower 2 bytes of W3AbilityManager's type identity is 0x0F78.
  cmp ax,0x0F78
  jne initiateApocalypseExit  
  // Check if player is damaging an enemy. 
  // If we're damaging an enemy, r12 will not point to our own ability manager.
  mov rax,playerAbilityManager
  cmp [rax],r12
  je verifyPlayerDamaged
  // This will point to our ability manager for MOST enemies when we're responsible for the damage.
  mov rbx,[rsp+8A]
  cmp [rax],rbx
  je initiateEnemyApocalypse
  // This will point to our ability manager for SOME enemies when we're responsible for the damage.
  mov rbx,[rsp+432]
  cmp [rax],rbx
  je initiateEnemyApocalypse
  // This will point to our ability manager for SOME enemies when we're responsible for the damage.
  mov rbx,[rsp+2A]
  cmp [rax],rbx
  je initiateEnemyApocalypse  
  // This will point to our root structure for a select few enemies when we're responsible for the damage.
  mov rax,player
  mov rbx,[rsp+432]
  cmp [rax],rbx
  je initiateEnemyApocalypse
  jmp initiateApocalypseExit  
verifyPlayerDamaged:
  // If our ability manager is the data source, then this set to 0 indicates a vital stat amount is changing
  // for Geralt.  
  mov eax,[rsp+C2]
  cmp eax,0
  jne verifyCiriDamaged
  // Check if Geralt's stamina is changing.
  // This is 0 when stamina is changing.
  mov eax,[rsp+72]
  cmp eax,0
  je initiateApocalypseExit
  // If stamina is not changing, check if Geralt's health is changing.
  // This is 0 when health is changing.
  mov rax,[rsp+A2]
  cmp eax,0
  jne initiateApocalypseExit  
  // This must point to something, it just can't point to a function.
  mov rbx,[rsp+8A]
  push rcx
  lea rcx,[rbx]
  call checkBadPointer
  cmp ecx,0
  pop rcx  
  jne initiateApocalypseExit  
  // If this points to something, it must not be a function. If it is, then the operation is the result
  // of a recurring regenerative type function. If it isn't, then this operation is being caused by damage
  // from an enemy.
  mov rax,[rbx]
  cmp ax,0xFA58
  jne initiatePlayerApocalypse
  jmp initiateApocalypseExit  
verifyCiriDamaged:
  // For Ciri, this is not 0 (unlike Geralt) when (assumingly) stamina is changing.
  // If it is 0, then Ciri may be losing health.
  mov eax,[rsp+72]
  cmp eax,0
  jne initiateApocalypseExit
  // For Ciri, if stamina isn't changing, then this is not 0 when losing health.
  // No check for a recurring function is required for Ciri.
  mov rax,[rsp+A2]
  cmp eax,0
  je initiateApocalypseExit  
initiatePlayerApocalypse:
  // Push the damage amount parameter.
  movss xmm1,[rsp+62]
  sub rsp,8
  movd [rsp],xmm1
  // Push the player's current working health value.
  sub rsp,8
  movd [rsp],xmm0
  // Push the player's maximum health value.
  mov rax,playerVitals
  mov rbx,[rax]
  movss xmm1,[rbx+4]
  sub rsp,8
  movd [rsp],xmm1
  // Align the player's location struct to the X coordinate and push it.
  mov rax,playerLocation
  mov rbx,[rax]
  lea rax,[rbx+1B8]
  push rax
  call executePlayerApocalypse
  jmp initiateApocalypseUpdateDamage
initiateEnemyApocalypse:  
  // Push the damage amount parameter.
  movss xmm1,[rsp+62]
  sub rsp,8
  movd [rsp],xmm1
  // Push the target's current working health value.
  sub rsp,8
  movd [rsp],xmm0
  call executeEnemyApocalypse
initiateApocalypseUpdateDamage:
  // Commit updated damage and working health.
  mov [rsp+62],eax
  movd xmm0,ebx
initiateApocalypseExit:
  pop rbx
  pop rax
  movdqu xmm1,[rsp]
  add rsp,10
initiateApocalypseOriginalCode:
  popf
  subss xmm0,[rsp+40]
  jmp initiateApocalypseReturn
omnifyApocalypseHook:
  jmp initiateApocalypse
  nop 
initiateApocalypseReturn:

coordinatesAreDoubles:
  dd 1

negativeVerticalDisplacementEnabled:
  dd 0

yIsVertical:
  dd 0

And does it work? Oh you bet it works. And let me say: The Witcher 3 with the Apocalypse system is freaking fantastic. It’s so fun, and I’m getting my butt whipped like you wouldn’t believe.

I can’t wait to play more of this game with Apocalypse system support, which you can catch live on my stream. Please check it out, it’s going to be an amazing time! And of course, we’ll be adding more of the Omnified systems to the game as the days progress.

Follow my stream and say hello the next time you see me streaming — join my Discord to get real time notifications as to when that happens. Otherwise, check out the schedule.

For my next article, we’ll be implementing the Predator system. See you then.