Skip to content

Latest commit

 

History

History
 
 

SWORD OF THE VAGRANT

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

SWORD OF THE VAGRANT

Game info

TitleID: 0100BD000CB2C000
Explanation based on:

  • Internal version: 1.1,
  • Nintendo version ID: v1/v65536
  • BID: 1F1363EC8CC83C73
  • Engine: Unreal Engine 4.26.2

Details

Game can be unlocked to 60 FPS with plugin alone, but because game is using dynamic resolution set to 33.33 ms, performance is subpar. Requires patch to fix that.

How to find offsets

We need to use disassembler in this case. I will provide instructions based on IDA as it will calculate automatically needed offsets for us.

After finishing disassembling main, we need to find this string (it's encoded as UTF-16-LE):

r.DynamicRes.FrameTimeBudget

then we go to its xref.

Below we need to find first STR after second BLR. Code looks like this

.text:0000007102564D90                 ADRP            X1, #aRDynamicresFra@PAGE ; "r.DynamicRes.FrameTimeBudget"
.text:0000007102564D94                 ADD             X1, X1, #aRDynamicresFra@PAGEOFF ; "r.DynamicRes.FrameTimeBudget"
.text:0000007102564D98                 ADRP            X2, #aFrameSTimeBudg@PAGE ; "Frame's time budget in milliseconds."
.text:0000007102564D9C                 ADD             X2, X2, #aFrameSTimeBudg@PAGEOFF ; "Frame's time budget in milliseconds."
.text:0000007102564DA0                 MOV             W3, #0x20
.text:0000007102564DA4                 BLR             X8
.text:0000007102564DA8                 ADRP            X19, #qword_710674CA90@PAGE
.text:0000007102564DAC                 ADD             X19, X19, #qword_710674CA90@PAGEOFF
.text:0000007102564DB0                 STP             X21, X0, [X19]
.text:0000007102564DB4                 LDR             X8, [X0]
.text:0000007102564DB8                 LDR             X8, [X8,#0x68]
.text:0000007102564DBC                 BLR             X8
.text:0000007102564DC0                 STR             X0, [X19,#(qword_710674CAA0 - 0x710674CA90)]

So first final address is stored at 0x674CAA0.

The same way we're searching for

t.MaxFPS

If it has more than 1 xref, we are interested in the one that has description pointer loaded.

We are following similar pattern as for previous command. Whole piece of code looks something like this:

.text:00000071028AE29C                 ADRP            X1, #aTMaxfps@PAGE ; "t.MaxFPS"
.text:00000071028AE2A0                 ADD             X1, X1, #aTMaxfps@PAGEOFF ; "t.MaxFPS"
.text:00000071028AE2A4                 ADRP            X2, #aCapsFpsToTheGi@PAGE ; "Caps FPS to the given value.  Set to <="...
.text:00000071028AE2A8                 ADD             X2, X2, #aCapsFpsToTheGi@PAGEOFF ; "Caps FPS to the given value.  Set to <="...
.text:00000071028AE2AC                 MOV             W3, WZR
.text:00000071028AE2B0                 BLR             X8
.text:00000071028AE2B4                 ADRP            X19, #qword_7106791940@PAGE
.text:00000071028AE2B8                 ADD             X19, X19, #qword_7106791940@PAGEOFF
.text:00000071028AE2BC                 ADRP            X8, #off_71052742F0@PAGE
.text:00000071028AE2C0                 ADD             X8, X8, #off_71052742F0@PAGEOFF
.text:00000071028AE2C4                 STP             X8, X0, [X19]
.text:00000071028AE2C8                 LDR             X8, [X0]
.text:00000071028AE2CC                 LDR             X8, [X8,#0x68]
.text:00000071028AE2D0                 BLR             X8
.text:00000071028AE2D4                 STR             X0, [X19,#(qword_7106791950 - 0x7106791940)]

So our second final address is 0x6791950.

Each of our final address stores pointer that points to two floats. By default t.MaxFPS is always 0. r.DynamicRes.FrameTimeBudget is 33.33.
As we want to use internal FPS lock instead of FPSLocker function whenever possible, our entry for 15 FPS will look like this:

15FPS:
  # r.DynamicRes.FrameTimeBudget = (1000/FPS) cutted to 2 decimals
  -
    type: write
    address: [MAIN, 0x674CAA0, 0]
    value_type: float
    value: [66.66, 66.66]
  # t.MaxFPS
  -
    type: write
    address: [MAIN, 0x6791950, 0]
    value_type: float
    value: [15, 15]
  -
    type: block
    what: timing

But for 30 FPS like this (since plugin's FPS lock is blocked by default at 30 and 60 FPS):

30FPS:
  # r.DynamicRes.FrameTimeBudget (default value)
  -
    type: write
    address: [MAIN, 0x674CAA0, 0]
    value_type: float
    value: [33.33, 33.33]
  # t.MaxFPS (default value)
  -
    type: write
    address: [MAIN, 0x6791950, 0]
    value_type: float
    value: [0, 0]