illusion's Blog
Contact Patches

Porting and Improving Cheat Codes in The Last of Us

Please note that you are required to have a exploited PlayStation 4 console on firmware 9.00 or lower to run the patches mentioned in this article.

Recently, I was browsing through the ArtemisPS3 repository of cheats for PS3 and I found myself wondering… “why aren’t these on the PS4?” Some of them look pretty useful, so let’s see if we can port a few over, and perhaps improve them as well!

Looking for clues

I picked out a few cheats that seem reasonably affect Gameplay:

Flashlight Never Dies
bungholio ported by Randy97Killa
0 006ACBC8 3C0043B4
0 006ACBCC 901F05E4

Infinite Everything (Ammo, Items, Skills, Parts)
Medo ported by Randy97Killa
0 000336C0 60000000


Let’s see what these cheats do…

Flashlight never dies: self explanatory, makes flashlight never runs out of battery.

Infinite Parts: Using Parts, items in-game will result in high value.

Let’s start with flashlight. We can see in the disassembled code that it loads a high float value (360.00f) into register 0 and stores it. But can we do better?

Original Code
006acbc8 ed ad 00 28     fsubs      f13,f13,f0
006acbcc d1 bf 05 f4     stfs       f13,0x5f4(r31)

Patched code
006acbc8 3c 00 43 b4     lis       r0, 0x43b4
006acbcc 90 1f 05 e4     stw       r0, 0x5e4 (r31)

Yes! We can nop out fsub, which subtracts the float value by 0.1, we can achieve same result.

Let’s have a look in memory to see what clues we can find to port this to the PS4 version.

Remember ProcessWeaponFlashlight and a few float values, as these will be helpful later on.

Now on to parts.

We can do a quick search through the games memory for the number of parts in Joel’s inventory.

There are 2 results - One address for the HUD, and another for the “real” value. Attempting to modify the HUD value will result in it being overwritten by the real value, so we can ignore the HUD value and focus only on the real value.

Unfortunately, peeking into memory here won’t do much, since the real value is dynamic and it’s address is always shifting when the game is loaded.

Improving cheats

For flashlight code, we already nop fsub instead of loading 360.00f into it, so there’s not much we can do to improve this cheat, so let’s focus on the items cheat.

For items, since we gain on item use, let’s make it opposite, gain high consistent amount on pickup.

Now let’s compare the two codes, on use and on pickup.

// On Use
000336c0 7c 0b 4a 2e     lhzx       r0,r11,r9
000336c4 54 08 04 3e     rlwinm     r8,r0,0x0,0x10,0x1f
000336c8 7c 05 00 50     subf       r0,r5,r0
000336cc 7f 88 28 00     cmpw       cr7,r8,r5
000336d0 40 9c 00 08     bge        cr7,LAB_000336d8
000336d4 38 00 00 00     li         r0,0x0
000336d8 2f 87 00 00     cmpwi      cr7,r7,0x0
000336dc 7c 0b 4b 2e     sthx       r0,r11,r9

// On Pickup
0003495c 7f 7f d2 2e     lhzx       r27,r31,r26
00034960 7f bb 52 14     add        r29,r27,param_8
00034964 4b ff f3 91     bl         FUN_00033cf4
00034968 7f 9d 18 00     cmpw       cr7,r29,param_1
0003496c 40 9d 00 08     ble        cr7,LAB_00034974
00034970 7c 7d 1b 78     or         r29,param_1,param_1
00034974 57 a0 04 3e     rlwinm     r0,r29,0x0,0x10,0x1f
00034978 7f bf d3 2e     sthx       r29,r31,r26

We see here that the old cheat code nops out lhzx.

Infinite Everything (Ammo, Items, Skills, Parts)
Medo ported by Randy97Killa
000336C0 60000000
60000000 is nop.

But we can do better than just a nop.

# add
  - [ be32, 0x003495c, 0x48e58c35 ] # bl 00e8d590
  - [ be32, 0x0e8d590, 0x3fa0014f ] # r29 14f0000
  - [ be32, 0x0e8d594, 0x881d3412 ] # lbz r0, 0x3412, (r29)
  - [ be32, 0x0e8d598, 0x2f800000 ] # cmp r0, 0x0
  - [ be32, 0x0e8d59c, 0x409e0008 ] # bne 0xe8d5a4
  - [ be32, 0x0e8d5a0, 0x7f7fd22e ] # og lhzx
  - [ be32, 0x0e8d5a4, 0x4e800020 ] # ret
  - [ be32, 0x0034968, 0x2f9d0200 ] # r29 to 512
  - [ be32, 0x0034970, 0x3ba00200 ] # li r29 512

# sub
  - [ be32, 0x00336c0, 0x7e2802a6 ] # mflr r17,LR
  - [ be32, 0x00336c4, 0x48e59f01 ] # call 3
  - [ be32, 0x00336c8, 0x7e2803a6 ] # mtlr LR,r17
  - [ be32, 0x00336cc, 0x3a200000 ] # r17 = 0
  - [ be32, 0x0e8d5c4, 0x7c0b4a2e ] # og lhzx
  - [ be32, 0x0e8d5c8, 0x5408043e ] # og rlwinm
  - [ be32, 0x0e8d5cc, 0x3e00014f ] # r16 = 14f0000
  - [ be32, 0x0e8d5d0, 0x89f03412 ] # lbz r15,0x3412,r16
  - [ be32, 0x0e8d5d4, 0x2f8f0000 ] # r15 vs 0
  - [ be32, 0x0e8d5d8, 0x409e0008 ] # bne
  - [ be32, 0x0e8d5dc, 0x7c050050 ] # og sub
  - [ be32, 0x0e8d5e0, 0x7f882800 ] # og cmpw
  - [ be32, 0x0e8d5e4, 0x39e00000 ] # r15 = 0
  - [ be32, 0x0e8d5e8, 0x3a000000 ] # r16 = 0
  - [ be32, 0x0e8d5ec, 0x4e800020 ] # ret

Maybe we went a little overboard but here’s what it boils down to.

In the first block of code, if the byte at memory address 14f3412 is 1, the program will skip lhzx and load a value of 512 into register 29. Be it parts, pills, ammo, you name it.

In the second block, if the byte is 1, item usage will not decrease.

Making Our Own Cheat

For those who have already played the game. You may have noticed that we haven’t covered tools yet.

What’s the point of having thousands of scrap metal if you can’t use it…

Let’s try making our own cheat.

Searching for Tools level, we get 2 results. We can ignore 1 in executable space. As it’s only for HUD value.

Tools value has been changed to 127. Let’s set a breakpoint on pickup.

00068742 ff 84 bb        INC        dword ptr [RBX + RDI*0x4 + 0x16448]
         48 64 01 00

Guessing from intruction name, could this be incrementing value?

We can instead, move 5 into this specific address on pickup instead of incrementing by 1.

  # PS3 1.11
      - [ be32, 0x00082960, 0x38000005 ] # tools level load
      - [ be32, 0x00082788, 0x60000000 ]
00068742 67 67 e8        CALL       SUB_00068462 //cave
         19 fd ff ff

00068462 c7 84 bb        MOV        dword ptr [RBX + RDI*0x4 + 0x16448],0x5 // move 5 into addr
         48 64 01 
         00 05 00 
0006846d c3              RET //returm


Ports, Ports, Ports, come get ‘em

On PS4 we can do a search for the text we noted down earlier: “ProcessWeaponFlashlight” - These floats will become useful later on when we peek into memory.

4 results; let’s check for similarities. Found it. Highlighted are the same floats and values. Sweet!

Let’s set a breakpoint and see where it takes us.

00697b83 c5 f2 5c        VSUBSS     XMM2,XMM1,dword ptr [RAX + 0x18] // subtract by 0.1
         50 18
00697b88 c4 c1 7a        VMOVSS     dword ptr [R13 + 0x7d4],XMM2
         11 95 d4 
         07 00 00

Let’s try changing VMOVSS XMM2 to XMM1.

Success! Achieved the same result and no use of nop.

00697b88 c4 81 7a        VMOVSS     dword ptr [R13 + 0x7d4],XMM1
         11 8d d4 
         07 00 00

How about parts? Let’s try searching for parts…

Same story here but it’s just a little different.

On Pickup, the address will shift, but on use or spending it does not.

000894dc 66 42 89        MOV        word ptr [RDI + R8*0x2 + 0xf4],AX
         84 47 f4 
         00 00 00

Let’s try searching for f4 00 00 00.

00089022 66 45 89        MOV        word ptr [R15 + RBX*0x2 + 0xf4],R14W
         b4 5f f4 
         00 00 00

How about setting a breakpoint?

RAX, RCX and R14 stores the newest value ready to be loaded into R13.

Let’s try loading our specified value, would it work?

MOV word ptr [R15 + RBX*0x2 + 0xf4],0x96F

But, we can do better than just loading into a register. How about make it optional?

Options are nice

I had a thought - what if a user wanted to turn off the cheat without exiting the game and patching the executable manually? that’d be much more convenient…

With My knowledge with the inner workings of the menu code, I came up with a solution: utilizing the Developer Menu.

Since the menu is baked into the executable, I can modify, add, or remove contents within the limits of the executable space.

For the PS3 I came up with this:

  - [ be64, 0x0e8d5b0, 0x496E66696E697465 ] # str Infinite Everything (Ammo, Items, Skills, Parts)
  - [ be64, 0x0e8d5b8, 0x2045766572797468 ]
  - [ be64, 0x0e8d5c0, 0x696E672028416D6D ]
  - [ be64, 0x0e8d5c8, 0x6F2C204974656D73 ]
  - [ be64, 0x0e8d5d0, 0x2C20536B696C6C73 ]
  - [ be64, 0x0e8d5d8, 0x2C20506172747329 ]
  - [ be32, 0x01286780, 0x00e8d5b0 ] # point addr to e8e490
  - [ be32, 0x01286784, 0x014f3412 ] # point addr to 014f3412

First few lines writes the text.

Last two lines points previous addresses to our new location, this replaces debug difficulty in Gameplay... Menu

Now on to the PS4.

// menu code
0006f23b bf a0 00        MOV        EDI,0xa0 // type entry
         00 00
0006f240 e8 db 1d        CALL       FUN_00c81020 // construct entry
         c1 00
0006f245 48 89 c3        MOV        RBX,RAX
0006f248 48 8d 35        LEA        RSI,[0x10954af] // point to some src code path str
         60 62 02 01
0006f24f 48 8d 15        LEA        RDX,[0x1524d28] // some mem address
         d2 5a 4b 01
0006f256 48 89 df        MOV        RDI,RBX 
0006f259 e8 e2 a5        CALL       FUN_00a29840
         9b 00
0006f25e 4c 89 f7        MOV        RDI,R14
0006f261 48 8b f3        MOV        RSI,RBX
0006f264 e8 67 ea        CALL       FUN_00a2dcd0 // end of entry code
         9b 00

// call
00089022 e8 59 8a        CALL       FUN_00c21a80
         b9 00

// compare ADD code
00c21a80 80 3d a1        CMP        byte ptr [DAT_01524d28],0x0 // compare byte
         32 90 00 00
00c21a87 74 0d           JZ         LAB_00c21a96 // if 0, run normal code
00c21a89 66 41 c7        MOV        word ptr [R15 + RBX*0x2 + 0xf4],0x96f // load 96f into addr
         84 5f f4 
         00 00 00 
00c21a94 eb 09           JMP        LAB_00c21a9f // go to return
00c21a96 66 45 89        MOV        word ptr [R15 + RBX*0x2 + 0xf4],R14W // load from r14w into addr as noraml
         b4 5f f4 
         00 00 00
00c21a9f c3              RET // rerturn

// compare SUB code
000894d0 e8 1b 86        CALL       SUB_00c21af0
         b9 00

00c21af0 44 89 ce        MOV        ESI,R9D // stolen instruction
00c21af3 80 3d 2e        CMP        byte ptr [DAT_01524d28],0x0 // compare byte
         32 90 00 00
00c21afa 75 02           JNZ        LAB_00c21afe // if not 0 return
00c21afc 29 d6           SUB        ESI,EDX // if 0 sub as normal
00c21afe c3              RET // return

That might be a little too much to take in, so here’s a quick run down.

The Menu code section replaces Memory... in the Quick Menu and makes it a toggle.

The Compare ADD code is the same story as the PS3 - if a byte is enabled, load 0x96f into it’s register; if the byte isn’t enabled, run the original code.

The Compare SUB allows for infinite items - when byte is enabled, parts, item, etc will not decrease.



Patch Codes


ZEROx for help with compare and porting code to the PS3 version.

hejran7 for help with finding address in the PS4 version.