Hello!
It has been a while since my last post, why’s that?
I had worked on a few things.
I have been working on this bug for the past few months on and off and I’m happy to say that it is fixed!
This bug sounds even more strange than my previous bug fix post about the Infected Severed Head Crash Bug for the PS3/PS4 games.
Good thing is, it was officially fixed by the developers in the Re-Remaster on PS5!
Thanks to The Kempy for testing!
Now what exactly is this broken aim bug?
in The Last of Us Part 2, Naughty Dog developers implemented a tiredness feature, where the player charges the bow after a certain amount of time, the aim will become shaky.
But here comes the weird part, they do not reset this factor correctly, leaving the player’s aim shaky even after a few minutes.
The only way to get out of this bugged aiming mode is to Restart to Checkpoint or Quit and reload your save.
Some observations I noticed when I first investigated the bug back in September of 2022 when it was brought to my attention thanks to the speedrunner Anthony Caliber.
Let’s search for some values.
I’ll tackle the charge time first, since it is the only constant variable so far.
20 seconds.. timers is usually a float, for example most game delta time calculations, it is a float. 0.03333
is 33 milliseconds. and 1.0000
is 1000 milliseconds which in turn is 1 second.
After searching for a while and modifiying every 20.0f
floats, memory location 0x110F559E14
is the address the game reads for the charge time.
Let’s try setting this one to 4 seconds.
Check that out, the player can only charge the bow for 4 seconds now.
A breakpoint leads to this instruction.
01555a6d VMOVSS XMM0,dword ptr [RAX + RCX*0x4]
Hmm, two vector floating reads. What if I step there?
Aha! xmm1
seems to contain our current time.
Let’s write some small code to display these.
Yep, this is it. Now we have the max charge time and the current charge time.
Now the multiplier. We know that it increases over time and decreases when we charge the bow.
This took a while to find but luckliy, I found it!
Here I changed the value to 10.0f
and aiming a shotgun does this:
Woah, Ellie needs to take a break from fighting all day huh.
I can’t give any memory location for this one since it is not static (I learned this the hard way). So I set some breakpoints and these two instructions stood out to me.
015550b7 VMOVSS dword ptr [R14 + 0x4a0],XMM0 ; this one writes when aiming
0155577d VMOVSS dword ptr [R14 + 0x4a0],XMM0 ; this one writes when charging bow
015c9011 VMOVSS XMM3,dword ptr [RAX + 0x4a0] ; this one always reads when bow is eqiup
The write instruction is in the same area as the charge timers, if I stub this function 015547a0
, the bow no longer works.
Let’s give it a name.. How about BowController
? that sounds pretty good.
Alright, we have found all three key piece of the puzzle.
But how will we fix it? I have some ideas.
1.0f
.I will go with the latter, because I think the developers wanted a tiredness effect after using the bow for some time but this bug goes unnoticed, let’s fix it for them.
// Pusedo code for the patch, may not be 100% accurate, see sources at
// `https://github.com/GoldHEN/GoldHEN_Patch_Repository/blob/a92199c51bf86ffe10e4413928f6a6872928269f/patches/json/TheLastOfUs2-Orbis.json#L547`
// for more info.
#include "ndlib/process/clock.h" // GetProcessDeltaTime
extern bool is_using_bow;
extern float bow_charge_time;
extern float bow_max_charge_time;
extern float weapon_sway_mul;
float cooldown_timer = 0.0f;
void bow_charge_fix(void)
{
if (weapon_sway_mul == 1.0f) // the default is 1
return;
float max_cooldown_timer = 15.f;
float current_timer = GetProcessDeltaTime();
if (is_using_bow)
{
cooldown_timer = 0.f;
return;
}
if (cooldown_timer > max_cooldown_timer)
{
weapon_sway_mul = 1.f;
cooldown_timer = 0.f;
return;
}
// Count timer up
cooldown_timer = cooldown_timer + current_timer;
return;
}
Let’s test this change in-game.
The timer counts up after using the bow, resets after 15 seconds and most importantly, resets the sway multiplier!
By the way, for those who want to see the stats for themselves can do so. I have included all the patches shown in this post in the patch file.
You will need to enable the following patches:
And in the debug menu, Dev Menu|Weapons|Bow and Arrow|Show Bow Charging
option. The menu can be opened by pressing L3+Touchpad Right
You may have noticed that I did not fix the bug in the recently released The Last of Us Part 1 on PS5, that is because the system have not been fully exploited yet.
Though when the PC version comes out, I will fix it if the developers have not fixed it by then.
If someone can get me in touch with the right people, this fix can be done officially! Both for the PS4 and PS5 games.
My contact info:
illusion0002
I have updated the instructions for use with the new GoldHEN Plugins system.
Below is the source code for the patch.
Thanks to the patrons who supported me on various platforms! You guys are awesome!
Featured on:
Over the past few months I have been working on rewriting the Gravity Rush 2 (Gravity Daze 2) patch from scratch and there has been some small big improvements.
What’s the first one about? Well, if you have read my previous post on the matter, you will have notice that timestep was fixed to 60FPS, meaning if the game were to drop any frames, the game would slow down as well, with this update, this has been resolved.
Notice how Kat is running slower on the middle.
Second update is that the videos no longer slows down and stutters anymore, check out these two videos and you’ll see what I mean.
The first video runs noticeably slower and the sound is out of sync. However, the second video plays just fine.
I have completed the game and added the necessary level checks for the game to be playable with this patch.
Just a reminder that the level checks here is to prevent the game from softlocks and will fall back to the default 30FPS mode. But Episode 23 is a special case. In this episode, you are required to solve puzzles and along the way, there are these sequences.
Which of course, have their scripting tied to the timestep and will never play unless it is 33ms.
To overcome this (or any other softlocks you may find) I have implemented a button combination. these can be pressed at anytime.
Button | Action |
---|---|
L3 + X | Switch between 30 and 60FPS Modes. |
What does it do exactly?
Pressing this combination will cycle through the framerate target between 30 and 60FPS. It can be used during Episode 23 dialogue sequences since the puzzle sections plays out just fine at 60FPS.
Raven’s Story is also playable. Softlock protection are also implemented here and will fallback to 30FPS in sections that are required.
As they say, being a hero doesn’t pay the bills.
Oh, you might want to check your calendar to see what day it is. That’s right, today (June 12th) marks the day Gravity Rush for the PS Vita was released in North America! Animation made by MayruAnimations
Want to see the patch in action? Checkout the video below and you can find the links to support me on GitHub, Patreon and other platforms.
Base PS4/Slim
PS4 Pro
Thanks to the patrons who supported me on various platforms! You guys are awesome!
Motorstorm Titles on PS3 ran at a wobbly 30FPS, 720p with dynamic resolution scaling, that’s right the game has DRS, all the way back in 2006.
Low sample motion blur, although it looks fine at it’s original resolution and framerate but it does not hold up particularly well at high resolution and framerates and at first sight, fixed timestep.
Running the game on the RPCS3 emulator with increased vblank shows disappointing results. sped up gameplay.
The hidden gem here is game speed. When you crash your vehicle, the game slows down to 0.125f. And normal gameplay it’s 1.00f. peeking at this area shows some curious values.
3F 80 00 00 00 00 00 02 00 00 00 00 01
Setting the byte 2 to 1 gives us..
60FPS Gameplay? Alright, that’s cool but it’s still fixed timestep and double speed at 120FPS.
Not shippable if you ask me.
Let’s pay a little more attention to what’s down below, what happens if I set this next int32 to 1…
I apologize if that clip made you all jelly. It did for me seeing it for the first time as well.
The same memory pattern exists in other games all the way to Motorstorm RC.
As for the names, the files were left bare within MotorStorm: Pacific Rift.
-- globalparams.lua: auto-generated by GoStorm2 at 17/07/2007 14:28:25
FrameRateMode("30Hz Lock")
-- Possible values:
-- See game_config.xml for more info.
-- 30Hz Lock -- 30 FPS (No Havok Deltatime) -- Default for all games.
-- 60Hz Lock -- 60 FPS (No Havok Deltatime)
-- Compensation -- Deltatime (Use Havok Deltatime)
Two things stood out to me when I played this with the unlocked framerate, the image can become blurry out of nowhere and the motion blur doesn’t look as good.
Let’s start with the most obvious image quality flaw, dynamic resolution scaling. The game scales the resolution based on frametime targets, if it is below its threshold of 33.3ms, the game drops its resolution to 360p at it’s lowest, to find it? 720p during the clearest moments and Decreased Values
when things get blurry.
I narrowed it down to this subroutine (0x002634c8
) with a compare float statement and all I have to do is to skip this operation, simple.
Peeking at the game executable and searching for Blur, I got a few results. WorldMotionBlurBase
BlurAccel
and BlurDecel
. This smells like some sort of configuration key. Let’s try memory.
Setting these to 0.0f gets rid of the motion blur and it looks good!
Get ready because the things we learned in the past two sections of this post is going to be thrown out the window, Starting with Motorstorm Pacific Rift, they changed the settings to use the XML format and the code being much easier to read.
// Motorstorm RC for demonstration, similar code is used in Pacific Rift and Apocalypse.
// at 0x001a8050 for NPUA80678 01.02
ParseFile("scripts/viewportquality.xml");
// -- snip --
GetSectionKeyName = ParseSectionKey("General");
bool GetBoolValFromFile = ParseValueKey("enableMotionBlur");
// <General enableMotionBlur="true"/>
enableMotionBlur = GetValFromFile;
Take enableMotionBlur
for example, the game code looks for this key in a file named viewportquality.xml
within the scripts
folder and reads this value, and since this may change depending on user tweaks, let’s always load this variable using the load intermediate instruction.
The same can be done for variableScaling
(Dynamic Resolution Scaling)
Comparison Images:
I won’t lie, this is actually me when I figured out that hidden deltatime feature in these games.
I have already covered MLAA in a previous post of mine for Motorstorm Apocalypse so if you are interested, check it out!
That’s going to be it for this post, if you like it be sure to share this with others and let them know that if they have a powerful PC, high framerate Motorstorm is a reality, enjoy your enhanced video games!
Oh and one more thing, I wished we got a remaster for these games on the current platform, would be interesting to see how that plays out. Yes, including Driveclub and my beloved Gravity Rush (Gravity Daze) 2
Ah well, I guess emulation will do just fine on our computers.
For those looking to use the patch on the emulator, you can head over to the patch manager, click on the “Download Latest Patches” and find the patch you wanted to use with your game Title ID and version, click on the checkbox to enable the patch and save changes.
Thanks to the patrons who supported me on various platforms! You guys are awesome!
With the recent PC release of God of War (2018) patches for the series are becoming more relevant than ever. This post will be a little on the longer side because there’s a lot to elaborate on. Let’s get into it!
The PS3 God of War games has two stages of its intro section. One being the Sony logo and the Santa Monica videos.
Let’s start with the simplest. The background with text. The game spawns IntroLoaderThread
PPU thread for this and the solution here is to skip this process.
It took me a while to figure this one out. At first I thought the game referenced the filenames found within the executable, the files being SMLogo
and openingCredits
but removing these references in the executable still plays it, so I gotta come up with a solution.
It turns out the solution was right in front of me. You see, once you start a new game or save it, on subsequent runs, the game enables a boolean on the second intro video, allowing the users to skip the video with the press of the X button. I have a genius idea. What if we always enable this boolean and send the X button input?
The game boots up and there’s no intro, but there’s one problem, since this code will always run when there’s a video playing, meaning it will skip every video that plays, so that’s not very desirable. I passed my working code to ZEROx and we came up with a solution:
int controller_button = cellPadGetData();
int frame_count = 0;
bool UserPressedSkipButton = false;
bool VideoIsFinshed = false;
char[] video_name = m2v_files;
if (video_name == "SMLogo" || video_name == "openingCredits" )
{
skipAlways();
}
else {
skipOnbuttonPress();
}
void skipAlways() {
bool UserPressedSkipButton = true;
bool VideoIsFinshed = true;
return;
}
void skipOnbuttonPress() {
if (controller_button == DEFINE_ACTION_BUTTON /* 64 */ ) {
bool UserPressedSkipButton = true;
bool VideoIsFinshed = true;
}
return;
}
What this code does is it checks if the video filenames match. If they do, we call into the always skip video subroutine. The benefit of this being that it will only skip the intro videos and not the rest of the game, which is handy for those who do not want to watch intros every time they want to play.
In all God of War games, they have deltatime. It calculates the difference between the amount of time it takes to push a frame onto the display and writes that delta between the frames to game logic and other things. But since this is code from the PS2 era, things are weird. They have a specific range the game runs at: 15 to 60FPS. Going below 15FPS will snap to the maximum frametime of 66.67ms, going above 60 will snap to 16.67ms. meaning if you wanted to run the games at 120FPS, you’ll get double speed.
But of course, if there is a will, there is a way, at least for the PS3 titles.
The code is very similar between them so we won’t explain it for both games, but here’s how it goes:
In memory, the game keeps track of a lot of things during gameplay. The area of interest is 0x006406f0
.
This area holds: framerate (int32) and frametimes (float32). Setting a breakpoint on the constantly writing floats gives us a few clues, one is it leads to this rather complicated sub at 0x0026b674
, I have not analyzed this one myself but I can tell that it has very important key information which helped us solve the puzzle.
MinFPS = target_FPS / ( 400 / 100 );
This snippet of code divides the maximum framerate by 4 times a 100. Which adds up to 60/4 = 15. Simple. And changing this to 5000 gives us a minimum framerate of 2. Looking just a little closer, there’s -1.0 on this load float instruction.
That looks interesting, let’s check it out in memory, and..
A constant of 59.94. How interesting.
And the code that gives us this value is even more interesting:
if (frame_height == 576) {
SetMaxFPS(50.00); // PAL
}
else {
SetMaxFPS(59.94); // NTSC
}
It compares if scanline height is 576 for PAL users, and if so sets the max FPS to 50.0, else (usually 480 lines) 59.94.
How about we change this to 120?
Is that God of War 3 at up to 120FPS?
Yes, it is.
But the game is currently broken. There are two problems,
One with the super slow chain swing and two when kratos dives into the water, causing the game to freak out and be stuck in a loop. The solution? Set the reading float to a static 60.
During our testing, we discovered that this patch (accidentally) fixed a very annoying bug: out-of-sync in-game cinematics.
That’s an unexpected bug fix! The best kind.
If there are more bugs that you have found with our patch, please post them in the comments and a save file so we can investigate.
Join in next time where we actually get to see mud tracks and big trucks! Stay tuned!
For those looking to use the patch on the emulator, you can head over to the patch manager, click on the “Download Latest Patches” and find the patch you wanted to use with your game Title ID and version, click on the checkbox to enable the patch and save changes.
Thanks to the patrons who supported me on various platforms! You guys are awesome!
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.
If you have ever played a recent fromsoftware games on console, be it Bloodborne, Dark Souls 3 or the recently released Elden Ring. You may have noticed something off about the presentation. One being that it feels more stuttery than other games with a 30 fps cap. And the other is inconsistent framerate on the enhanced consoles. With the framerate going up and down as it pleases depending on what is happening on screen.
Let’s address the first issue. Improper frame pacing. It is an issue that started to occur on the 8th generation era with Bloodborne.
Now let’s go back to the previous generation, the PS3/Xbox 360 era.
Dark Souls on Xbox 360, no issues here. A consistent 30 fps when it meets the framerate target.
How about Demon Souls?
Yep, no issues here either.
I could only speculate on the decision to implement their own framerate cap for multi plat games is portability. Not having to deal with API calls for each console is a definite win but they did not realize if their timer precision was off, it will cause inconsistency in frame delivery.
But there’s something even more strange. Checkout this video from VG Tech where they showcase Dark Souls 1 in Backcompat Mode on Xbox One and..
Bad framepacing on the newer consoles but not original hardware? Very strange indeed.
So what are the solutions? A very simple fix to this would be to call the relavent API function calls equivalent to the platform.
Here are some suggestions.
Platform | API Calls |
---|---|
PS3 | cellGcmSetFlipMode |
PS4/PS5 | SceVideoOutFlipRate |
Xbox 360 | D3D::PresentInterval |
Xbox One/Series S/X | IDXGISwapChain::Present |
But calling these functions and giving them appropriate device handle is not enough to fix the issue. We must also lift the game’s broken framerate limiter as well.
Either by changing Game.FlipMode
in the code to 60FPS_*
or changing minimum frame update time from 33.33ms to 16.67ms. Let’s see the changes.
Here’s bloodborne with the frame pacing issues resolved.
Volla! With just a few lines of code, we get proper and consistent 30 frames per second! Killing two bug reports with one stone.
The same fix also applies for the uncapped framerate on the enhanced consoles in Dark Souls 3 and later titles. though when implementing this in reality, the framerate cap must always be an option rather than forcing it on the user.
Here’s a little bit of trivia for you, there’s already deltatime calculated for the framerate counter among other things.
But fromsoftware did not use this until the release of Dark Souls 3. Which is also how I ported the framerate patch for Bloodborne by Lance McDonald to the Network Test version.
That is all I have for you today, you can support me using the links below and thanks to the patrons and you for reading!
Catch you next time where we race against opponents in a sea of mud with big trucks?
Need help with installing the patches?
Check out the Installation Guide.
Thanks to the patrons who supported me on various platforms! You guys are awesome!
It is no secret that I have been on the Uncharted Reloaded team for a while, a group of dedicated and talented individuals in an attempt to revive the multiplayer servers that was offline since it’s closure in 2019.
Recently I was tasked with solving a crash in one of the multiplayer public beta upon entering a secret LAN mode.
Yes, this is the same beta that was ran during June 28th to July 14th back in 2011. More details here.
Normally it is not possible to access this mode because on the start screen, you’ll be prompted to sign in to PlayStation Network.
It is possible to have access to the menus that was previous locked behind a PSN sign in prompt. Before you ask, this does not get rid of PSN authentication, it simply unlocks the menu. The code probably goes something like this.
if (sku == NPUA07618) {
LockMenuFeature = true;
} else {
LockMenuFeature = false;
}
The game has a so called blacklist of titleIDs that will restrict access to certain features, such as gore, violence and others, there is a simple trick to get around this but first let me show you something special.
That’s right, Yours truly (finally) got a PlayStation 3!
Special thanks goes to the Patrons, Vampyre, a member of the team and you for supporting me to be able to get a hold of such not-so rare piece of hardware.
It is a Slim model PS3, check it out, quite a beauty isn’t it? This will become useful later on.
Now back to the task at hand.
What is this simple trick you may ask? Well it is quite simple, by changing the SKU ID, which consists of the Game’s Title ID that you can find printed on the spine of the case or disc.
Oh wait, is that too small? Let me zoom in.
For SKU blacklist, in this case it only restricts the use of the main menu.
What will happen if the SKU is changed?
With this one simple trick, we can have access to the whole menu, which includes but not limited to: Online Mode used during the beta, a Local Multiplayer mode and a options menu with a blank controller page. Will get back to that in a moment.
The funny thing about this SKU check code is it will fallback to whatever the default is set to (in this case its UP9000-BCUS98233_00
) if the string does not match. Hot singles in your strcmp
(string compare)? With that knowledge the SKU can be anything but the blacklisted ID of the game.
As for LAN mode, why is it a curiosity? Because upon entering a local username, it crashes with an assertion.
{PPU[0x100200e] Thread (NetInit) [0x008eaebc]} SIG: Thread terminated due to fatal error.
LookupSymbolValue: Symbol '0x6f1c3f87' ("") not found!
(consider the alternate LookupSymbol)
A script file cannot be found, how strange. such is life I suppose.
So I had a brilliant idea, mess around in the game’s memory in the area where the SKU is located.
At first I thought I was out of luck but fear not, with a little bit of trial and error of changing true to false, there is a solution.
You may have already noticed there is some 01
at the top. If you guess correctly that it should be 00
you would be correct!
The crash is solved! But it doesn’t go anywhere, or does it?
Let’s try it on a real PS3.
Oh my. It works? What’s with the poor performance on the menu?
Here, let’s put some numbers on the screen.
It turns out you were never supposed to be able to enter this menu, and Uncharted TV is setup for an online only environment. The rapid flicker and slowdowns are caused by the game trying to do the following:
Spawn video decoding threads -> tries to find video files on a non existent server (they were shutdown a while ago) -> despawn threads -> cycle repeat indefinitely.
http://d2i32rosu1so9x.cloudfront.net/1.mp4
http://d2i32rosu1so9x.cloudfront.net/2.mp4
http://d2i32rosu1so9x.cloudfront.net/u3-reveal.mp4
http://d2i32rosu1so9x.cloudfront.net/u3-e32011-trailer.mp4
http://d2i32rosu1so9x.cloudfront.net/5.mp4
http://d2i32rosu1so9x.cloudfront.net/6.mp4
Some of the URLs the game tries to access for videos.
A solution to this would be to outright get rid of Uncharted TV and stop the game from spawning video threads and causing slow down or if we do eventually get the online functionality working, host our own videos instead.
Alright, where do we begin?
Luckily the video panel goes away when you enter the options menu and comes back when the user returns, with a little bit of back and forth scanning. I narrowed it down to this address. 1224C60
setting this byte to 0 will temporary turn off the video panel but the video threads are still being spawned in. Time for a breakpoint. There are two write addresses, let’s have a look.
00700834
0070158C
Oh wow, check this out, this is exactly what we are after. It even has text reference to the thread name and the relevant calls to spawn a new ppu thread.
if (*(char *)(DAT_01224d18 + 0x2124) == '\0') {
FUN_00700804(param_1);
FUN_00c93f28(param_1,param_2);
iVar12 = FUN_0070015c(param_1,0x1e);
if (iVar12 != 0) {
*(undefined4 *)((int)puVar7 + 0x84) = 0x21;
...
*(undefined *)(param_1 + 0x120) = 1; // enable uctv byte
**(undefined *)(param_1 + 0x1bc) = 0;
sys_ppu_thread_create(param_1 + 0x98,&PTR_FUN_010ae048,param_1,100,0x8000,1,"YouTubeStream");
}
}
return 0;
Since the code turns it on, let’s turn it off.
It’s worse than before and the video panel is not even shown, well something must have went wrong in the code. Should we take another look?
if (((*(int *)(param_1 + 0xb0) != 0) && (*(int *)(param_1 + 0xa4) != 0)) && (iVar12 != 0)) {
uVar15 = 0xffffffff; // -1
FUN_00c97c30(iVar12,0,0xe1000);
...
return -1;
Ah, it could have been that if that byte was 0 it could have return -1 which is ffffffff
in hex and caused it to run into the issue we were having earlier, let’s return 0 and skip the code that tries to spawn the threads and accompanying video panel.
Here comes the moment of truth, will other players be able to connect after the various patches has been applied?
What about the blank controller page I mentioned earlier? After loading into the multiplayer menu, the assets necessary for it is also loaded.
Well that’s all for this post, below is where you can find the patch and support me as a independent patch creator. Have a good rest of your day! I’ll catch you next time.
Uncharted Reloaded and Vampyre for their support.
ZEROx for help with some of the written code.
Thanks to the patrons who supported me on various platforms! You guys are awesome!
With the recent release of the Uncharted Collection on the PS5 I thought it would be fun to try to skip the intro and understand a little more how the bootflow works.
If you remember from a previous post where I discussed on finding task ID, the layout goes something like this.
String (ASCII)
Address pointer (32 or 64 Bit depending on the platform)
Task ID (usually 32 but 64 Bit in The Last of Us: Part 2 with the change to entirely 64 bit hashes).
32 Bit Pointer and Hashes (PS3, Big Endian, Uncharted 3)
64 Bit Pointer and Hashes (PS4, Litle Endian, The Last of Us: Part 2)
HD era naughty dog games have this task called ndi
which stands for Naughty Dog Intro. Simple enough. The ID associated with this task has not changed since Uncharted 1, which is 0xed857351
and menu
with its ID being 0xd006e7b5
.
Now why is this important? Because the game’s code looks up these id on what to load. For instance here is a snippet of code from Uncharted 3.
// taken from uc3 ps3 code
// language-selection is exclusive to uncharted 3
if (skip_ndi) {
task = 0x66b93942; // language-selection
// bootflow goes like this:
// t1, uc4, and uc-tll
// ndi -> menu
// uc3
// language-selection -> ndi (handled by language-selection flow) -> menu
// uc1 and uc2
// autosave-warning -> ndi -> menu
} else {
task = 0xd006e7b5; // menu
}
Code is simple enough, in this case, if this condition is true, it will skip over language_selection
and ndi
.
The very same process can be repeated for the following games.
Uncharted 1: Drake's Fortune (PS4 only)
Uncharted 2: Among Thieves (PS3/PS4)
Uncharted 3: Drake's Deception (PS3/PS4)
The Last of Us (PS3/PS4)
Uncharted 4: A Thief's End
Uncharted: The Lost Legacy
The Last of Us: Part 2 (not yet possible)
As to why Uncharted 1 intro skip is not yet possible on the PS3?
Because the compiled script file on that version is not the easiest thing to read. Well same for Uncharted 2 but in that case searching for the menu task ID and always booting into that does the trick but not for the first game.
As for The Last of Us Part 2, the intro is baked directly into the menu
task. Which also explains why there are no ndi
task to be found in the task list.
I think it is still possible if I look hard enough into the game’s code or it could simply be a case of…
Yeah.. I wouldn’t touch this script file with a ten foot pole.
Let’s see if the PS4 version is any difficult for the PS3 era titles. Thankfully, Bluepoint did all of the hard work for us on the trilogy collection and we can simply enable Boolean to skip over the video that plays during the ndi
task and call it a day.
bp_skipintromovies
this is a command line parameter, it only exists in Uncharted 1 and 2 but it shows that they probably got bored watching the intro so they implemented this cvar. :p
This Boolean is used when the user switches games from the option menu.
However, things are not as cut and dry as I would have thought.
In Uncharted 3, this string cannot be found. Another solution must have existed. And there is a solution to this. A while ago back in 2021..? When I was looking for a way to optimize the developer menu enable code I came across this magnificent piece of code.
if (0x027b9d22 != 0) { // is game launched from app chooser?
0x027baf7e = true;
0x027baf6a = true;
0x027baf67 = true;
0x027baf69 = true;
0x027baf64 = false; // dev menu bool
0x027b9d21 = true;
0x027b9d6c = true; // skip ndi bool
}
The last one happens to be the same one that skips the intro in the public beta version of the original game. I can simply turn this Boolean off and there we go. Uncharted 3 on the PS4 intro is skipped. Nothing to be worried about as there are no multiplayer components to this one.
I got the motivation to do intro skips was from fixing a bug where Uncharted 3 Public Beta on the PS3 would crash upon entering the Lan menu, which will be an upcoming post for those curious about it.
Well that’s all for this post, below are the showcase video, where you can find the patch and support me as a independent patch creator.
Have a good rest of your day! I’ll catch you next time.
ZEROx for porting to the PS3 version.
Freako and HereisMe for publishing their knowledge about reversing Naughty Dog Titles.
This patch is available for PS3 (RPCS3), and PS4.
For those looking to use the patch on the RPCS3 emulator, you can head over to the patch manager, click on the “Download Latest Patches” button and find the patch you wanted to use with your game Title ID and version, click on the checkbox to enable the patch and save changes.
Patch Source Code: Uncharted 2 (RPCS3)
Patch Source Code: Uncharted 3 (RPCS3)
Patch Source Code: The Last of Us (RPCS3)
PlayStation 4 users, can follow this guide on how to install the patch here. (the following tutorial does not cover how to build the update package. only to install the patch into the binary.)
Patch: Uncharted: The Nathan Drake Collection (PS4)
Patch: The Last of Us Remastered (PS4)
Patch: Uncharted: The Lost Legacy (PS4)
Thanks to the patrons who supported me on various platforms! You guys are awesome!
When I got into framerate patching 8 months ago, I looked at the exclusives on the PlayStation website and saw 1 title that seem rather interesting and it does not have a PS5 Patch back then and even now. Not even a framerate unlock on Pro hardware. Yes, I’m talking about Gravity Rush 2 or Gravity Daze 2 if you are in Japan.
What’s so special about this title that took me so long to breakthrough? First is that there are barely any string references, second is they have their own frame limiter for video playback which also clamps down during gameplay.
So where do we start? Fliprate to 0 for 60hz output got us that. 32 to 33 framerate limit. And I went on for months, on and off looking for what this is about. And I even managed to break the framebuffer.
So I gave up. For a while at least. A few months went by, I decided to take a break and then validate the dozen projects I have, I realized that this game still hadn’t gotten a patch from anyone after I put out my findings over on my git page. So I went looking, after a couple of days, I found it.
Looking at the framerate graph, it averages about 33 fps. Why is this important? Because 33fps is 30 milliseconds per frame! You know what that means.
1000 / 33
equals to 30.. ish. But wait, what happens if we plug this number into the executable?
A couple results, seems boring. Or so I thought..
lVar3 = sceKernelGetProcessTime();
uVar4 = lVar3 - (param_1);
if (uVar4 < 30000) {
sceKernelUsleep(30000 - (int)uVar4);
}
scePthreadMutexLock();
sceGnmSubmitDone();
What’s this? It sleeps draw thread for 30ms?
Another case of Japanese developers reinventing the wheel. Its wonderful.
But anyway, removing this code gives us, you guess it, unlocked framerate.
Since I have last published my findings, I have determined that this game is subject to fixed timestep, which means if the game framerate goes above or below the target framerate, it will speed up or slow down. Which in this case is 30 frames a second.
If you have seen my findings a few months back, you have seen that there’s a byte for this particular setup that will purposely set everything to 16.7ms for locked 60 fps gameplay but the thing is, this isn’t perfect.
Some sections will softlock the game because the game tick is running twice as fast; so we must implement a check that will set the game back to 30 fps for those problematic sections, otherwise run freely at up to 60 fps, with slowdown in hardware limited areas because we are still using fixed rate.
Saving between two points, the start of epiosde 1 and the end of the epoiode and comparing the results can easily give us the level name we are looking for.
For level names we can search for this in memory then save the string into somewhere in the executable for easier access.
if (in_R12 == "ep" /* story episodes */ || in_R12 == "ft" /* free roam */ || in_R12 == "sm" /*side missions*/ ) {
level_text = in_R12;
}
return;
The code I’m using here write asset strings into upper portions of memory, so I have to filter out most level prefix names found in the save file. Like ep
for episodes or sm
for side missions.
lVar3 = sceKernelGetProcessTime();
uVar4 = lVar3 - (param_1);
// if (uVar4 < 30000) {
// sceKernelUsleep(30000 - (int)uVar4);
// }
scePthreadMutexLock();
sceGnmSubmitDone();
Few commented lines of code, what does this do?
We skipped the code to stall the rendering thread and well it does nothing as you may expect. But there are caveats to this.
Most notable being that if there is nothing to stall the thread, the video playback thread can’t keep up and lags behind as a result, if anyone wants to take the challenge of improving on my initial semi working code, feel free to do so. Patch file
int game_flag = 2; // default
if (game_flag == 1) { // 60hz
const float frametime = 0.01666667;
const int32 fliprate = 0;
sceVideoOutSetFlipRate(handle, fliprate);
return;
}
if (game_flag == 2) { // dangerous if failed on startup or loading levels
return; // But this shouldn't fail because flag is initialized with mode 2
}
if (game_flag == 3) { // 30hz, only use when softlock mode is true
const float frametime = 0.03333334;
const int32 fliprate = 1;
sceVideoOutSetFlipRate(handle, fliprate);
return;
}
Alright, what does all this do exactly? Let’s go one by one.
The variable game flag is something I created, this is to keep track of what state the game is currently in.
When it is 1
set the constant for the fixed timestep to 16.7ms and set the fliprate to 0 for 60hz output. As a reminder, we have already skipped the code to sleep the render thread so this should work.
When it is 2
it does nothing, the function gets called and it just exits. This is to prevent any render thread hard lock during the game initialization period.
And 3
as you guess, swaps to 30 fps and timestep to 33.3 milliseconds.
string level_name = ep_xxx; // default
if ( level_name == ep00_c /* regression, todo: is this caused by our code? */
|| level_name == ep01_com
|| level_name == ep01_d
|| level_name == ep02_a ) {
game_flag = 3; // 30hz
} else {
game_flag = 1; // 60hz
return;
}
This one checks during level changes, if any of the levels matched, set the game back to 30 fps, then 60 once we leave that particular area or level of the game. The example being the end of the intro, first episode and the tutorial of episode 2. Note that the intro having a softlock is a regression potentially in my code? I’ll look into it at a later date but it should not be a big deal.
A minor correction to the video, the resolution showed in the video was 1920x1080 (1080p) not 1280x720 (720p). A blur has been added to avoid confusion.
This patch is best experinced on PS4 Pro with 1080p output and supersampling disabled.
For PS4 Patches, an installation guide on how to install the patches can be found here. (the following tutorial does not cover how to build the update package. It only shows how to install the patch into the binary.
Thanks to the patrons who supported me on various platforms! You guys are awesome!
A few weeks back I was browsing through AnthonyCaliber videos. Showcasing various glitches in The Last of Us Remastered. Most of the glitches in the video are out of bounds and does not prevent progressing through the game but there is one glitch that caught my eye.
Once I saw this, a great idea popped into my head. Either to give the player infinite health during this section or making the nailbomb ineffective. I went with the latter option as the former caused issues. As you might’ve seen from the video, if the two hunters die then the camera will freeze but the player can still move so that is a no go. My plan is to research into the weapons, which in this case the nailbomb and how the game knows what task or level it’s currently on.
Let’s start by seeing how the game keeps track of what level or task we are on. Thanks to the work of Freako and HereisMe. Some of this has already been worked out and is published. In the Naughty Dog Modding server, Freako has kindly provided all the info that he knows about the game in an archive which will become very useful as this post goes on. So let’s have a look at it.
A few folders, the ones we’re interested in are tasks
and weapon-loadout
.
Inside the task folder is a text file and some example files. This text file list where things are, what’s been discovered, etc.
The image is barebones but enough for us to understand. From the text file on how to find a given task is as follows: get string address, find string offset as address. The identifier is found right next to the string. Obviously this is for the ps3 version soo, give me a moment while I extract the PS4 version. Alright here’s the list of task we’ll need to keep track and to later patch.
uni-4-lab-rebar-ambush
uni-4-lab-rebar-aiming
uni-4-lab-help-off-rebar
uni-4-lab-injured-escape-start
uni-4-lab-injured-escape-vault
Since we’re on x86_amd64 (PlayStation4), the CPU architecture is little funny. You see there’s this thing called a little or big endian and it gets confusing if you’re new, like me. Say you have a value, 09 87 65 43 21 00 00 00
this is how it would be represented on big endian, which is what the PlayStation 3 uses. But on x86, it’s little endian which means, these bytes are swapped. Now it is 43 65 87 09 00 00 00 21
. Wrap your head around that when you have to keep track of things, worse of all, hexadecimal data displayed via printf is byte swapped! So what’s the point?
Anyways, here’s the task ids
uni-4-lab-rebar-ambush // F0 8E BC 40
uni-4-lab-rebar-aiming // CF F0 66 A4
uni-4-lab-help-off-rebar // 6A 60 B6 9A
uni-4-lab-injured-escape-start // 5C 5D 81 11
uni-4-lab-injured-escape-vault // C8 5F D3 4A
Alright, now for the weapons, fortunately, there’s only one we need to know what the ID is and that is the nailbomb, already figured out by the legend that is HereisMe.
0x18A76844 == WeaponIdNailBomb()
Reverse that and that’s what our id is on ps4.
Now to the reproduction steps. Following what Anthony did, got us the same result, the hunter dies and the camera is stuck. Or the nailbomb explodes and the player is put into an indefinite loop. A softlock.
InCase anyone is wondering, yes I do know that you can restart encounters to “resolve” these issues but it should not even happen in the first place.
I made the decision to get rid of the explosion but left the nailbomb intact. The player can shoot and it will do absolutely nothing.
Searching for the string of nailbomb, nada. what about bomb?
Welp, that was a disappointment alright.
Hey, what about that weapon ID earlier? This yielded some results. I will now introduce you, to the method of lazy reversing. Look for a branch and skip it or put a return from subroutine at the beginning of the function.
Alright, let’s have a look at some of these references.
Most of these don’t do anything interesting and there’s only one reference that did do something.
This reference disables the damage output.
Okay, now what? if we use this code for our fix, it could work but the explosions particles would still go off. Looks a little distracting, don’t you think?
Oh look, there’s an error message that I missed. Explosion Missing Parameters
these unfortunately doesn’t print anything on to the screen but it gives us a clue. the word explosion can be used to find other references.
if (local_2b0 == 0) {
FUN_01c84620(6);
FUN_01c84a80("Explosion Missing Parameters");
}
// ...
if ((bool)uVar31) {
uVar30 = uVar2 < 0x18a76844;
The refernce that disables the particles is around SpawnExplosionEffects - no explosion settings
string.
Let’s give it a try.
Hmm, it still blows up. even if there is no particles. that’s distracting if you ask me.
Wait a minute, there’s two SpawnExplosion
strings.
SpawnExplosion - can't find explosion settings
sounds interesting.
Hmm, let’s have a look at the call references to this function. Alright time to bisect what does what. Some of these did what we saw before and some even crashed the game outright, so that’s not good. How do I test this you may ask?
Removing the call. using the no operation instruction.
There is one call that stood out. 0x016a299a
if ((param_1 + 0x40) == 0) {
// ...
FUN_01a7f5c0(param_1);
}
return;
Close to a return.. hmm, what could that mean?
There’s a branch up here, let’s skip it.
Not only does this not blow-up, it does absolutely nothing when an npc goes near it!
Time for the patch.
Keep in mind that this is my interpretation of the code I wrote in assembly but the pseudocode should be readable enough to your average amateur programmers.
if (task == 0x40bc8ef0 || 0xa466f0cf || 0x9ab6606a || 0x11815d5c || 0x4ad35fc8 )
{
// uni-4-lab-rebar-ambush 40bc8ef0
// uni-4-lab-rebar-aiming a466f0cf
// uni-4-lab-help-off-rebar 9ab6066a
// uni-4-lab-injured-escape-start 11815d5c
// uni-4-lab-injured-vault 4ad35fc8
//
return; // disable nailbomb explosion output
}
// enable nailbomb explosion, normal path
SpawnExplosion();
SpawnExplosionEffects();
Looks good! time to try it out.
Oh no, what happened?
As it turns out, I made a minor typo. I was comparing against 0x9ab6606a and not the supposed 0x9ab6066a. A simple fix solved this.
If you enjoyed reading and would like to support, you can do so by supporting me my Patreon page or follow my YouTube channel and Blog for more content like this.
As always, I hope you learned something from these posts, catch you next time!
Enjoy your one-less bug video game!
Special thanks to:
ZEROx for porting to the PS3 version.
Freako and HereisMe for publishing their knowledge about reversing Naughty Dog Titles.
This patch is available for PS3 (RPCS3), and PS4.
For those looking to use the patch on the RPCS3 emulator, you can head over to the patch manager, click on the “Download Latest Patches” button and find the patch you wanted to use with your game Title ID and version, click on the checkbox to enable the patch and save changes.
PlayStation 4 users, can follow this guide on how to install the patch here. (the following tutorial does not cover how to build the update package. only to install the patch into the binary.)
Thanks to the patrons who supported me on various platforms! You guys are awesome!
Time for some more framerate PlayStation 3 patches, or to be more specific, RPCS3 patches. You know, an emulator. With the knowledge gained with the previous post, I decided to tackle a series of titles, that is the Ratchet and Clank HD era games. Ranging from Tools of Destruction all the way to the Nexus.
When I first boot up one of their games with the frame limit set to off, I saw this.
What about the newer games?
Alright, let’s fire up the debugger and see what we can find.
These look like a group of frametime values and after a little bit of trial and error it turns out that E67B04
is the minimum frametime. Take note of the hex value 3C 5A 74 0E
or 0.01333
in float. let’s try changing this to 0.005.
Is it really increased though? Introducing the tried and true method of… Looking at the ground.
Doing a search for this value in the executable shows that this is a base value for the min frametime value.
But wait, 13ms? wasn’t the cap we see in the game is 12ms?
Juhn the developer behind the KAMI, Kot(Cat) and Mouse Injector did some investigation and found out that this number is multiplied by 0.9
A little bit of code explanation,
lfs
which loads float from offset to f13
that has the 0.9 value, then loads into f1
with 0.013333 and reads the value.
Then it gets multiplied by the fmuls
instruction and thus get us the 12ms we saw earlier.
That would be it for the rest of the games, right?
Starting with A Crack in Time, a later entry in the series, things changed. changing the value we previously discoverd works but has a side effect.
(If the video does not play in your web browser, right click and save the video to play it locally.)
In this case if it exceeds the minimum frametime set by the game it will speed up, it does not slow down below the target frametime but it speeds up, if above.
16.67ms is here to save our day, searching for 3c 88 88 89
and changing some values around resolved the issue.
Thought the headache was over? Far from it. Repeating the same method works in memory but there is no such value as 3C 5A 74 0E
in the executable.
However, setting a breakpoint on that value got us here.
0072c1e4 lfd f1,0x8(param_1)=>DOUBLE_0072c140 = 0.013333333 // 3f 8b 4e 81 c0 00 00 00
0072c1e8 fsub f2,f3,f1
0072c1ec frsp f2,f2
0072c1f0 fsel f1,f2,f3,f1
0072c1f4 frsp 1,f1
0072c1f8 stfs f1,0x4(r31) // <-- breakpoint
Wait a minute, a double? They set the value initally in double, which is twice the bits as float for more accurate decimals and then converts it to float again.
The end results is the same as the rest of the games, unlocked framerate with no speedups.
If you enjoyed reading and would like to support, you can do so by supporting me my Patreon page or follow my YouTube channel and Blog for more content like this.
As always, I hope you learned something from these posts, catch you next time!
Juhn for explanation on framerate limits.
Note that the video below is recorded in slow motion to show the patch at its best.
For those looking to use the patch on the emulator, you can head over to the patch manager, click on the “Download Latest Patches” and find the patch you wanted to use with your game Title ID and version, click on the checkbox to enable the patch and save changes.
Thanks to the patrons who supported me on various platforms! You guys are awesome!