Nioh 2

The Omnification of Nioh 2 is going swimmingly, with our initial analysis and implementation of Apocalypse done. We have two game-neutral Omnified systems left to implement: the Predator and Abomnification systems.

You may have noticed that this single article is covering both Predator and Abomnification system implementations. That is because implementing these systems was a rather simple affair, at least in regards to the Abomnification system (for reasons we’ll get into later). Writing about both of them in a single article is me doing my part to conserve some of those precious Interweb resources.

So, let’s start things off with a writeup on the Predator system’s implementation, which, as you may know, intelligently boosts enemy speed so they can zoom towards you at alarming speeds.

Movement Application Code for Samurais

Proper implementation of the Predator system requires us to locate and hook into the movement application code for the game. As the section of the Predator system design article I just linked will explain to you, this particular piece of code is responsible for adding the movement offsets to current working coordinates, yielding new coordinate values that will be committed as the source-of-truth values by the location update code.

Please consult the appropriate sections of the Predator system design article for more information on these terms. Let’s now go over how we went about finding ye olde holy movement application code for Nioh 2, a task which can be quite difficult for some games.

Luckily for us, Nioh 2 was put together by sensible humans. There is a single primary location update code and movement application code for all entities (that I know of, at least) in the game. This makes things so much simpler.

To begin our search, we start at the location update code — easily found by simply logging all instructions responsible for writing new values to our source-of-truth coordinates. This leads us to the following code:

Shows the location update code for Nioh 2.
Here is the primary location update code for Nioh 2. Time to find the relevant movement application code!

As we can see above, the location update code is snuggled away in its own tiny function space. The new coordinate values being committed to memory are read from xmm0. Our job is to find the code responsible for performing the arithmetic that transforms the current coordinate values into the ones we see in xmm0.

To do this, we’re going to go up the call stack a bit, and see if we can’t capture that operation happening in a trace. How high up to go is pure guesswork, as going too high will cause many problems. So, I decided to go two calls up the stack, leading us to the following code:

Shows the code where we started the trace for the movement application code from.
This is the code two levels up on the call stack from the location update code where we’ll be starting our trace.

Let’s see if we can’t capture the movement application code in a trace starting from here. The only thing I needed to do is figure out a way to isolate a call here to a single call to the location update code. With the location update code, I can easily filter on the entity whose location is being updated, as rcx will be pointing to that particular creature’s location structure.

I figured out that we can filter on a particular location structure at the parent function call by looking at the rdi register, which will be the register pointing to the location structure being updated instead of rcx. So, we queue up a trace, with the starting conditions set to rdi being equal to a particular creature’s location structure.

This led us to the following trace, which did capture the location update code:

Shows the results of the trace for the movement application code, positioned at the location update code.
Great! We found a useful (note the change in X from 11957.57 to 11955.21) call to the location update code inside our trace, many instructions away from the start.

Capturing useful information is vital to success, as it becomes quite difficult to find the movement application code if the movement offsets to be applied are actually zero throughout a particular chain of calls (and it will be zero if the creature isn’t moving, a very common thing). Luckily, we captured a meaningful change in position as shown above.

Now that we have that, the only thing we need to do is go backwards, or up, in the trace. We travel backwards in time until we see where the new coordinate values are coming from.

Shows the source of the update location coordinates in the trace.
The new location coordinates are being loaded from a separate data structure in memory according to the trace. This data structure is non-temporary, so we can simply monitor writes to it.

We eventually get to the code that is pictured above. It is not the movement application code, but it is code showing where the updated values are being loaded from in memory. Simply by looking at the address rcx, we can tell that it is pointing to instanced, non-temporary memory most likely attached to a particular creature.

That means/ we should be able to easily get the code writing to this “new coordinate holding place” by simply looking at the code writing to the particular address. Doing that, we get the following results:

Shows the code writing to the source of new location coordinates.
Here’s some new looking code that is responsible for assigning new coordinate values to the place in memory our location update code ultimately gets its values from.

Looks interesting. Taking a looking at the second result here, we don’t have to scroll up too far (actually, only one instruction) for us to collide headfirst into the lovely movement application code for Nioh 2.

Shows the movement application code.
The movement application code, responsible for adding offsets (pointed by rdx to a temporary place in memory) to our current coordinates, giving us a new location.

There we have it. We can hook in here and initiate the Predator system, and hopefully enjoy insane, intelligent enemy speed. I have only myself to blame for all the horrendous deaths that will be visited upon me.

Predator Initiation Hook

Here is the code that makes enemies move like insane devils. Worked pretty much the first time or so! Woot.

// Initiates the Predator system.
// [rdx+0-8]: Movement offsets for x, y, and z-coordinates respectively.
// [rbx]: Target location structure
// UNIQUE AOB: F3 0F 58 02 F3 0F 11 81 80 01 00 00
define(omnifyPredatorHook,"nioh2.exe"+852588)

assert(omnifyPredatorHook,F3 0F 58 02 F3 0F 11 81 80 01 00 00)
alloc(initiatePredator,$1000,omnifyPredatorHook)
alloc(identityValue,8)
alloc(playerSpeedX,8)

registersymbol(playerSpeedX)
registersymbol(identityValue)
registersymbol(omnifyPredatorHook)

initiatePredator:
    pushf
    push rax
    mov rax,playerLocation
    cmp [rax],0
    pop rax
    je initiatePredatorOriginalCode
    // In some instances, rbx does not point to a valid location structure, such as when 
    // a shrine is being used.
    cmp rbx,1
    je initiatePredatorOriginalCode
    // Make sure the player isn't being treated as an enemy NPC!
    push rax
    mov rax,playerLocation
    cmp [rax],rbx
    pop rax
    je applyPlayerSpeed
    // Backing up the registers used to hold Predator system output, as well as an SSE to 
    // hold some of the parameters we'll be passing.
    sub rsp,10
    movdqu [rsp],xmm0
    push rax
    push rbx
    push rcx
    // The first parameter is our player's coordinates.
    mov rax,playerLocation
    mov rcx,[rax]
    push [rcx+F0]
    push [rcx+F8]
    // The next parameter is the target NPC's coordinates.
    push [rbx+F0]
    push [rbx+F8]
    // The third parameter is the NPC's dimensional scales. Jury is still out whether this 
    // game has True Scaling, so we'll just be passing a good ol' identity matrix for now.
    movss xmm0,[identityValue]
    shufps xmm0,xmm0,0
    sub rsp,10
    movdqu [rsp],xmm0
    // The fourth parameter is the NPC's movement offsets. Wow this has been easy!
    push [rdx]
    push [rdx+8]
    call executePredator
    // Now we just take the updated movement offsets and dump them back into [rdx].
    mov [rdx],eax
    mov [rdx+4],ebx
    mov [rdx+8],ecx
    pop rcx
    pop rbx
    pop rax
    movdqu xmm0,[rsp]
    add rsp,10
    jmp initiatePredatorOriginalCode
applyPlayerSpeed:
    sub rsp,10
    movdqu [rsp],xmm0    
    sub rsp,10
    movdqu [rsp],xmm1
    push rax
    sub rsp,10
    // We don't use the Predator system to influence player speed; rather, we just introduce
    // the use of a simple multiplier that is applied to the character's movement offsets.
    // This multiplier will be, except under special circumstances, typically 1x.
    movss xmm0,[playerSpeedX]
    shufps xmm0,xmm0,0
    movups [rsp],xmm0
    mov rax,[identityValue]
    mov [rsp+4],eax
    mov [rsp+C],eax
    movups xmm1,[rsp]
    movups xmm0,[rdx]
    mulps xmm0,xmm1
    movups [rdx],xmm0
    add rsp,10    
    pop rax
    movdqu xmm1,[rsp]
    add rsp,10
    movdqu xmm0,[rsp]
    add rsp,10
initiatePredatorOriginalCode:
    popf
    addss xmm0,[rdx]
    movss [rcx+00000180],xmm0
    jmp initiatePredatorReturn

omnifyPredatorHook:
    jmp initiatePredator
    nop 7
initiatePredatorReturn:


identityValue:
    dd (float)1.0

aggroDistance:
    dd (float)1250.0

threatDistance:
    dd (float)300.0

positiveLimit:
    dd (float)1000.0

negativeLimit:
    dd (float)-1000.0

playerSpeedX:
    dd (float)1.0

And that’s the Predator system implementation for Nioh 2. Check out the stream to see the results.

I Love Easy Abomnifications

The Abomnification system, which is my game-neutral system that has the uncanny ability to apply randomly determined morphing modes that change the shape and size of creatures to new random shapes and sizes at random times, can either be one of the easiest or hardest systems to implement.

To understand why it can sometimes be the hardest of my systems to implement, just check out some of my Abomnification implementation articles where I had to write my own custom scaling rendering code. It is definitely a few levels above in terms of reverse engineering difficulty in comparison to what most people are typically looking to manipulate in a game.

But, if the game provides its own easy scaling parameters — that is, data points that directly control the dimensional scale of a character, it is truly one of the easiest and quickest systems to implement. Sadly, I feel like most games I’ve Omnified have lacked easy scaling parameters — thankfully, Nioh 2 is not one of those.

Praise the Nioh 2.

I found the scaling parameters very easily by simply manipulating the character’s height in the character creator and searching for changes while doing so. You can find the dimensional scales starting at the 0x140 offset in the creature’s location structure. There are three floats there, one for each dimensional scale (height, width, and depth).

With that figured out, all we need to do is implement the Abomnification initiation hook in a place where all creatures are being polled. We want to do it at a place that is being executed less frequently than your super-fast rendering code, more along the lines of a map-wide creature location coordinate poll.

This was easy enough to find by simply looking at code that was accessing player and NPC coordinates. Then, we just throw in a simple hook, and we have absolute magic in our game. I mean, it really does look like LSD-fueled magic.

Abomnification Initiation Hook

// Initiates the Abomnification system.
// This polls both the player's and NPC coordinates.
// [rdi+140]: Height
// [rdi+144]: Depth
// [rdi+148]: Width
// UNIQUE AOB: 4C 8D BF F0 00 00 00 41
define(omnifyAbomnificationHook,"nioh2.exe"+84AA21)

assert(omnifyAbomnificationHook,4C 8D BF F0 00 00 00)
alloc(initiateAbomnification,$1000,omnifyAbomnificationHook)
alloc(abomnifyPlayer,8)

registersymbol(abomnifyPlayer)
registersymbol(omnifyAbomnificationHook)

initiateAbomnification:
    pushf
    push rax
    mov rax,playerLocation
    cmp [rax],rdi
    pop rax
    jne skipAbomnifyPlayerCheck
    cmp [abomnifyPlayer],1
    jne initiateAbomnificationOriginalCode
skipAbomnifyPlayerCheck:
    // Back up the registers used as outputs of the Abomnification system.
    push rax
    push rbx
    push rcx
    // Push the address to the creature's location structure as its identifying
    // address to the stack.
    push rdi
    call executeAbomnification
    // Load the Abomnified scales into the creature's location structure.
    mov [rdi+148],eax
    mov [rdi+140],ebx
    mov [rdi+144],ecx
    pop rcx
    pop rbx
    pop rax
initiateAbomnificationOriginalCode:
    popf
    lea r15,[rdi+000000F0]
    jmp initiateAbomnificationReturn

omnifyAbomnificationHook:
    jmp initiateAbomnification
    nop 2
initiateAbomnificationReturn:


abominifyMorphStepsResultUpper:
    dd #550

abominifyHeightResultUpper:
    dd #200

abominifyDepthResultUpper:
    dd #180

abominifyWidthResultUpper:
    dd #240

unnaturalBigX:
    dd (float)1.5

abomnifyPlayer:
    dd 0

That’s literally all there is to implementing some absolute crazy magic into Nioh 2. Abomnification sounds ridiculous at first (and it is), but it is a very creative, unique, and effective way to boost difficulty, as it completely messes with your perceptions. Reading enemies becomes incredibly more difficult, and you are definitely more on your toes.

It’s the best.

Nioh 2 Is Omnified

In record time, Nioh 2 is fully Omnified. It has been my fastest full Omnification of a game to date. What a thrill, as it gives me time to work on other things. To give you an idea of how fast I Omnified this game, know that I’ve had around a good 8 or so streams of a fully Omnified Nioh 2 at the time of this article’s writing.

Literally took just a few evenings worth of work.

Please remember that code posted in articles can very often end up outdated and no longer reflective of the current product. You can always find the latest version of the source code by checking out the Bad Echo technologies repository, with the Nioh 2 target files residing here.

If you can, please try to support what I do by following my stream, and letting me know if what I do is something entertaining and worthwhile to you. I really appreciate knowing about any and all who get anything out of this strange little thing that I do, a thing I put so much time and heart into.

Thanks for reading!