Homebrew  Updated

FPSLocker - set custom FPS target in retail games

An overlay that with SaltyNX 0.7.0+ allows to set custom FPS in Nintendo Switch retail games.

Disclaimer: Tool is utilizing detection of graphics API to manipulate FPS. It supports special patches that are helping with going above 30 FPS in games using proprietary FPS locks.

You can see here how it works (I was utilizing Witcher 3 dynamic resolution config mod to make anything above 30 FPS available):


More in README (I recommend to read it + also SaltyNX readme if you don't know how it works)
Repo: https://github.com/masagrator/FPSLocker
Releases: https://github.com/masagrator/FPSLocker/releases
Patches: https://github.com/masagrator/FPSLocker-Warehouse
 
Last edited by masagrator,

cucholix

00000780 00000438
Member
Joined
Jan 17, 2017
Messages
3,246
Trophies
1
Age
44
XP
6,274
Country
Chile
Tested with NEO: TWEWY and Tormented Souls, games I used to modify FPS with cheats and they work perfectly with FPSLocker, no more suffering from updating cheats :lol:
 
  • Love
Reactions: mathew77

ChanseyIsTheBest

Well-Known Member
Member
Joined
Aug 26, 2022
Messages
390
Trophies
0
Location
Australia
XP
1,051
Country
Australia
You're going put 60fps cheats out of a job haha. But seriously this is amazing. Modifying the nvnwindowsetpresentinterval or the other one is such a genius idea.
I was wondering as well if you could hook into unreal 4 settings addition as well because for graphics settings like they show up in a consistent way in memory as in this video of as the many cheats in the database
 

masagrator

The patches guy
OP
Developer
Joined
Oct 14, 2018
Messages
6,277
Trophies
3
XP
12,039
Country
Poland
I was wondering as well if you could hook into unreal 4 settings addition as well because for graphics settings like they show up in a consistent way in memory as in this video of as the many cheats in the database
I would need 100% sure way to do it on first try. Anything below won't cut. I know a way how to reliably detect if it's UE4 game and which version, but nothing more. That's why I want to implement support for patching RAM through cheat style codes for anything that cannot be reliably searched.

If you have 100% sure way to find DR/frametime values for any version of some game, I can implement it directly to plugin.
 
Last edited by masagrator,

Gumistret

Member
Newcomer
Joined
Jun 8, 2022
Messages
23
Trophies
0
Age
28
Location
Indonesia
XP
297
Country
Indonesia
NX-FPS not running at fw 15.0.1 AMS 1.4.1. NX-FPS already installed inside saltynx>plugins. SaltyNX 5.0, Status Ovl 0.8.1, NX-FPS 1.0.

Edited : Nevermind, fix archive bit fixed everything. Something corrupted when i use card reader to my pc.
 
Last edited by Gumistret,

masagrator

The patches guy
OP
Developer
Joined
Oct 14, 2018
Messages
6,277
Trophies
3
XP
12,039
Country
Poland
Designed a binary file format that will be created by using yaml files and loaded by plugin.
My final goal is to implement option in overlay itself to convert yaml to bin so repository would store only yaml files.

I was thinking about implementing yaml parser into plugin, but all existing solutions are bloating plugin too much.
JSON parsers are 3x lighter, but they weren't fitting my needs.

This is for example working yaml file for Xenoblade Chronicles 3 1.3.0 based on this cheat file
YAML:
15FPS:
  -
    type: write
    address: [MAIN, 0x1A08F98]
    value_type: int32
    value: [2, 2]
20FPS:
  -
    type: write
    address: [MAIN, 0x1A08F98]
    value_type: int32
    value: [2, 2]
25FPS:
  -
    type: write
    address: [MAIN, 0x1A08F98]
    value_type: int32
    value: [2, 2]
30FPS:
  -
    type: write
    address: [MAIN, 0x1A08F98]
    value_type: int32
    value: [2, 2]
35FPS:
  -
    type: compare
    compare_address: [MAIN, 0x1A65958]
    compare_type: "!="
    compare_value_type: int8
    compare_value: 1
    address: [MAIN, 0x1A08F98]
    value_type: int32
    value: [1, 1]
  -
    type: compare
    compare_address: [MAIN, 0x1A65958]
    compare_type: "!="
    compare_value_type: int8
    compare_value: 0
    address: [MAIN, 0x1A08F98]
    value_type: int32
    value: [2, 2]
40FPS:
  -
    type: compare
    compare_address: [MAIN, 0x1A65958]
    compare_type: "!="
    compare_value_type: int8
    compare_value: 1
    address: [MAIN, 0x1A08F98]
    value_type: int32
    value: [1, 1]
  -
    type: compare
    compare_address: [MAIN, 0x1A65958]
    compare_type: "!="
    compare_value_type: int8
    compare_value: 0
    address: [MAIN, 0x1A08F98]
    value_type: int32
    value: [2, 2]
45FPS:
  -
    type: compare
    compare_address: [MAIN, 0x1A65958]
    compare_type: "!="
    compare_value_type: int8
    compare_value: 1
    address: [MAIN, 0x1A08F98]
    value_type: int32
    value: [1, 1]
  -
    type: compare
    compare_address: [MAIN, 0x1A65958]
    compare_type: "!="
    compare_value_type: int8
    compare_value: 0
    address: [MAIN, 0x1A08F98]
    value_type: int32
    value: [2, 2]
50FPS:
  -
    type: compare
    compare_address: [MAIN, 0x1A65958]
    compare_type: "!="
    compare_value_type: int8
    compare_value: 1
    address: [MAIN, 0x1A08F98]
    value_type: int32
    value: [1, 1]
  -
    type: compare
    compare_address: [MAIN, 0x1A65958]
    compare_type: "!="
    compare_value_type: int8
    compare_value: 0
    address: [MAIN, 0x1A08F98]
    value_type: int32
    value: [2, 2]
55FPS:
  -
    type: compare
    compare_address: [MAIN, 0x1A65958]
    compare_type: "!="
    compare_value_type: int8
    compare_value: 1
    address: [MAIN, 0x1A08F98]
    value_type: int32
    value: [1, 1]
  -
    type: compare
    compare_address: [MAIN, 0x1A65958]
    compare_type: "!="
    compare_value_type: int8
    compare_value: 0
    address: [MAIN, 0x1A08F98]
    value_type: int32
    value: [2, 2]
60FPS:
  -
    type: compare
    compare_address: [MAIN, 0x1A65958]
    compare_type: "!="
    compare_value_type: int8
    compare_value: 1
    address: [MAIN, 0x1A08F98]
    value_type: int32
    value: [1, 1]
  -
    type: compare
    compare_address: [MAIN, 0x1A65958]
    compare_type: "!="
    compare_value_type: int8
    compare_value: 0
    address: [MAIN, 0x1A08F98]
    value_type: int32
    value: [2, 2]

and this is bin file after conversion
1678537804177.png

Plugin supports for now two things as I don't see for now other valid cases to implement something else.

One is "write fixed value directly to RAM" and second is "if comparison with fixed compare value is true, write fixed value directly to RAM".
It supports writing types in signed and unsigned integers from 8 to 64-bit + float and double. It also supports writing few values one after another (it's used in example)). For now it works with writing to and comparing values from [MAIN + address], but I know we must support heaps and aliases, thus why testing will take a little longer.
 
Last edited by masagrator,

Cooler3D

Well-Known Member
Member
Joined
Sep 4, 2020
Messages
307
Trophies
0
XP
1,014
Country
Russia
One is "write fixed value directly to RAM" and second is "if comparison with fixed value is true, write fixed value directly to RAM".
It supports writing types in signed and unsigned integers from 8 to 64-bit + float and double. It also supports writing few values one after another (it's used in example)). For now it works with writing to and comparing values from [MAIN + address], but I know we must support heaps and aliases, thus why testing will take a little longer.

Brilliant job!
 
  • Like
Reactions: binkinator
1.1.0

masagrator

The patches guy
OP
Developer
Joined
Oct 14, 2018
Messages
6,277
Trophies
3
XP
12,039
Country
Poland
Released 1.1.0 version of FPSLocker.
To work properly it requires updating NX-FPS to 1.1 version and SaltyNX to 0.5.1 version.

Overall plugin now supports loading special LOCK patches to adjust games FPS if plugin is not enough to do the job.
FPSLocker has option to convert yaml configs to LOCK patches when game is running (to retrieve BID of game) that will be applied on next game boot.

if for some reason you would like to convert yaml to LOCK patch on PC, you can use this script:
https://github.com/masagrator/NX-FPS/blob/master/scripts/yamlToBin.py
And for reversing patch to yaml (though it will use different layout, sort keys in alphabetical order and store numbers only in decimal system - because I don't see an option to fix that somehow)
https://github.com/masagrator/NX-FPS/blob/master/scripts/binToYaml.py

Created new repo which whole purpose is to store those yaml files and info about 30 FPS games + how to update each patch yourself if you know how to use required tools:
https://github.com/masagrator/FPSLocker-Warehouse

As I suck at writing documentation I hope you will forgive my Methodology folder that it's not written with perfect English. :D


For starters I have added patches for 3 games that I have currently on my Switch and didn't work as expected without those patches:
- Xenoblade Chronicles 3 1.3.0 (to disable double buffer turn off Sync Wait)
- The Witcher 3 - Complete Edition 3.7 (with dynamic resolution tweaks for each FPS)
- Monster Hunter Rise 14.0.0 (added warning to DETAILS that if game will be running without patch and you will save settings with some custom FPS target, game will crash at boot. If you don't have a patch for your version of game, you can delete settings by opening FPSLocker when no game is running).

I will do more yaml configs from next month since I am not in home for next 2 weeks.
 
Last edited by masagrator,

XRTerra

What if instead of ohio, it was kai cenat land
Member
Joined
Jul 1, 2022
Messages
228
Trophies
0
Location
United States of America
XP
548
Country
United States
I'm really glad one of my ideas came to reality (obviously it's not my idea, I'm sure there are several more people who thought of a general switch fps unlocker). Good job!

I remember asking somewhere if a general fps unlocker could become a reality and the results were a unfortunate no. I bet those people are pretty flabbergasted right now.
 
  • Wow
Reactions: Cooler3D

masagrator

The patches guy
OP
Developer
Joined
Oct 14, 2018
Messages
6,277
Trophies
3
XP
12,039
Country
Poland
the results were a unfortunate no
I was in this camp too. But did it anyway to see the results and it was from the beginning very stable, needed only to find a correction value by trial and error that will cause PFPS value to behave the same across whole FPS range (When setting 45 FPS I have found that status monitor shows 45.2 FPS, but PFPS behavior remains the same as for other FPS, so I guess there is some flaw in calculations at this vicinity. Using Monster Hunter Rise internal FPS lock was a good comparison since setting it to 45 FPS provided exactly the same results). In few hours I have made a completely working project, send to two people for some quick tests, got very positive feedback and released it.
 

Chrisssj2

Well-Known Member
Member
Joined
Feb 12, 2008
Messages
2,704
Trophies
1
XP
4,384
Country
Netherlands
Saltynx seems very inconsistent half the time it says it is not running for some reason. hence revernx or fpslocker wont work. Sometimes a reboot fix its.
 

masagrator

The patches guy
OP
Developer
Joined
Oct 14, 2018
Messages
6,277
Trophies
3
XP
12,039
Country
Poland
Saltynx seems very inconsistent
Well, that's true since the beginning. It was never a clean solution. Issue is nobody made something better to this date. I guess with GDB I can try to figure out where is the issue because causing SaltyNX to not respond is pretty simple.
 
Last edited by masagrator,
  • Like
Reactions: Chrisssj2

Chrisssj2

Well-Known Member
Member
Joined
Feb 12, 2008
Messages
2,704
Trophies
1
XP
4,384
Country
Netherlands
Well, that's true since the beginning. It was never a clean solution. Issue is nobody made something better to this date. I guess with GDB I can try to figure out where is the issue because causing SaltyNX to not respond is pretty simple.
Atleast i know its not something on my end then. cuz with 1 game it seems to trigger and i boot other game and it gone xD
 
1.1.1 / 1.1.2

masagrator

The patches guy
OP
Developer
Joined
Oct 14, 2018
Messages
6,277
Trophies
3
XP
12,039
Country
Poland
NX-FPS was updated to 1.2 to solve issue with some old games being unable to get more than 30 FPS with FPSLocker because they were using nvnQueueAcquireTexture instead of nvnWindowAcquireTexture (for example Outlast), so I recommend to update it.
Post automatically merged:

Updated to version 1.1.1 to implement fix for random crashes in docked mode related to freeing framebuffer.
Post automatically merged:

Well, well. How fast new edge cases I am finding.

Released 1.1.2 version fixing RTLD detection in older games such as L.A. Noire.
It is required to update SaltyNX to 0.5.2+ version and NX-FPS to 1.2.1+ version to completely fix the issue.
 
Last edited by masagrator,

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
    K3Nv2 @ K3Nv2: Crowbar?