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

Intro

One of my favorite games this past generation was What Remains of Edith Finch. A underrated beautifully crafted game created by Giant Sparrow. But it doesn’t run so well on the PlayStation 4 so let’s see what’s up with its performace and if I can improve on it.

Inital Performance Impressions

This game targets its proformance profile at 1080p and 30FPS at least for Base Console.

25 - 30FPS during the opening scene.. the good thing here is that this is the only time the game will give the player to explore this large open space. during the exploration of the house it stays at 30fps almost all the time.

Bruteforcing

In my previous article I discovered the inital value for Unreal Engine Screen Percentage and Sync Intervals. Let’s have a look in the config files and see if there’s anything useful there.

Yikes.. nope.

This is extracted using the UT4 QuickBMS script and it didn’t look exactly great. UnrealPak gives up and doesn’t extract correctly either, So let’s try bruteforcing by searching for values in memory instead.

100.0 float for screen percentage, and 2 for sync interval.

Looks like I found it.

That was simple at least for screen percentage but I ran into a deadend with sync interval.. how about we look in the executable? Could be clues that should lead us somewhere.

XREF[1]:     FUN_027149a0:027149f1
                               (*)
042247ac 72 00 2e        unicode    u"r.VSync"
         00 56 00 
         53 00 79 ...

Searching for sync came up with only a couple of releavent results. There’s r.VSync though. let’s checkout that cvar and see what’s up.

027149ee 48 8b 07        MOV        RAX,qword ptr [RDI]
027149f1 48 8d 35        LEA        RSI,[u_r.VSync_042247ac]
         b4 fd b0 01
027149f8 ff 90 80        CALL       qword ptr [RAX + 0x80]
         00 00 00
027149fe 31 c9           XOR        ECX,ECX
02714a00 48 85 c0        TEST       RAX,RAX
02714a03 74 0c           JZ         LAB_02714a11
...
02714a29 48 8b 05        MOV        RAX,qword ptr [DAT_0532b158]
         28 67 c1 02
02714a30 c5 f0 57 c9     VXORPS     XMM1,XMM1,XMM1
02714a34 83 38 00        CMP        dword ptr [RAX],0x0
02714a37 74 18           JZ         LAB_02714a51
02714a39 0f b6 83        MOVZX      EAX,byte ptr [RBX + 0x608]
         08 06 00 00
02714a40 c5 f0 57 c9     VXORPS     XMM1,XMM1,XMM1
02714a44 83 f8 02        CMP        EAX,0x2
02714a47 74 08           JZ         LAB_02714a51
02714a49 c5 fa 10        VMOVSS     XMM1,dword ptr [RBX + 0x60c]
         8b 0c 06 
         00 00

A mov and cmp instructions? Seems to use a pointer that is then allocated in memory but this is not eboot space as we’ll see later so let’s set a breakpoint and see where it takes us.

RAX has our memory location somewhere up high, let's check it out.

A row of values. seems to be 4 byte int. setting one of these values to 0 introduces tearing.

Oof, not looking good.

This isn’t ideal because we are targetting up to 60FPS and not completely unlocked.

02714a34 83 38 00        CMP        dword ptr [RAX],0x0
02714a37 74 18           JZ         LAB_02714a51

There’s a compare instruction here. Let’s set our value back to 1 and set this jump to Jump Not Zero and see what it does.

60FPS target and no screen tear! Perfect.

How about screen percentage? Almost forgot about it heh.

02893389 48 8b 07        MOV        RAX,qword ptr [RDI]
0289338c 48 8d 35        LEA        RSI,[u_r.ScreenPercentage_0423d4d6]
         43 a1 9a 01
02893393 ff 90 80        CALL       qword ptr [RAX + 0x80]
         00 00 00
02893399 31 c9           XOR        ECX,ECX
0289339b 48 85 c0        TEST       RAX,RAX
0289339e 74 0c           JZ         LAB_028933ac
...
028933bf 48 8b 05        MOV        RAX,qword ptr [DAT_053322f8]
         32 ef a9 02
028933c6 c5 f0 57 c9     VXORPS     XMM1,XMM1,XMM1
028933ca c5 fa 10 00     VMOVSS     XMM0,dword ptr [RAX]
028933ce c5 f8 2e c8     VUCOMISS   XMM1,XMM0

Setting a memory breakpoint on that address I found earlier brought us here.

Similar looking code at the bottom but there isn’t enough space to write our code here. Let’s pick a random spot in the executable nearby to write our code.

Plan is to write a custom float value to that pointer which then will be read by VMOVSS instruction and does all the heavy lifting for us.

028933bf e8 5d 07        CALL       SUB_02893b21 ; cave
         00 00
...
02893b21 48 8b 05        MOV        RAX,qword ptr [DAT_053322f8] ; previous instruction
         d0 e7 a9 02
02893b28 c7 00 0a        MOV        dword ptr [RAX],0x4285570a ; write our float value in hex
         57 85 42
02893b2e c3              RET ; return from subroutine

Pretty simple right? This should work in-game, let’s test it out.

60FPS indoors and of course no tearing to be found.

Result

Image quailty comparisons, can you guess which is which?

Right Click and View Image to see detailed image.

Patch

Patch Code

Patches released since Final Fantasy VII: Remake

Awesome Adventures of Captain Spirit

Dark Souls III

Ghost Of Tsushima

Kingdom Hearts: III

Persona 5: Royal

Life is Strange 2

Patreon Supporters

Thanks to the following folks who supported me on my Patreon page.

You guys are awesome!

  • ac2pic