Sonic and Sega Retro Message Board: Creating a 60fps patch for Jet Set Radio (PC) - Sonic and Sega Retro Message Board

Jump to content

Hey there, Guest!  (Log In · Register) Help
Page 1 of 1
    Locked
    Locked Forum

Creating a 60fps patch for Jet Set Radio (PC) Need help

#1 User is offline evilhamwizard 

Posted 02 July 2015 - 01:09 PM

  • Posts: 1249
  • Joined: 16-June 04
  • Gender:Male
  • Wiki edits:109
Hey all.

I few months a go I wanted to find a way to get the PC port of Jet Set Radio running at 60fps. I found it odd that the game would unlock it's frame rate at times during fade outs, so I wanted to see if there was a way to have the frame rate unlocked all the time.

I recently discovered that the Android port of Jet Set Radio contains three ARM binaries in the lib folder (libjetsetradio.so and libjetsetradio-nn.so are the more interesting of the bunch). The best part is that the Android port, much like the port of Sonic Advance, contains the original symbols for the data/functions in both binaries. Since I assume the code base for the PC/Android ports are both somewhat similar, I figured that I could use the symbols from the Android version to help find the equivalent subroutines in the PC port.

Currently I'm looking at a function called "njRendererSetFpsLock", which looks as follows (addressing might not be exactly the same for you since I had to decrypt my executable):

.text:01291B40 ; =============== S U B R O U T I N E =======================================
.text:01291B40
.text:01291B40 ; Attributes: bp-based frame
.text:01291B40
.text:01291B40 njRendererSetFpsLock proc near          ; CODE XREF: j_njRendererSetFpsLockj
.text:01291B40
.text:01291B40 arg_0           = dword ptr  8
.text:01291B40
.text:01291B40                 push    ebp
.text:01291B41                 mov     ebp, esp
.text:01291B43                 cmp     [ebp+arg_0], 0
.text:01291B47                 mov     ecx, _ZN6CVideo8instanceE_ptr
.text:01291B4D                 mov     eax, [ecx]
.text:01291B4F                 mov     eax, [eax+98h]
.text:01291B55                 setnz   dl
.text:01291B58                 movzx   edx, dl
.text:01291B5B                 push    edx
.text:01291B5C                 call    eax
.text:01291B5E                 pop     ebp
.text:01291B5F                 retn
.text:01291B5F njRendererSetFpsLock endp
.text:01291B5F
.text:01291B5F ; ---------------------------------------------------------------------------


This function will either take a 0 or 1 as a parameter (1 for enable, 0 for disable I'm assuming). In the Android port, this function is called by a couple subroutines - exAdxlogo, deProgress, coProgress, exSequenceFirstCheckEnd, exSequenceDemoEnd, exSequenceDemoWait, exSequenceFirstCheckInit. These calls exist to some extent in the PC port, but I can only somewhat guess as to which function is which (for example, I know exAdxlogo is where the Heritage video is loaded/played by putting a breakpoint where the call is).

The only problem with this is that I can't seem to get anything to happen if I change the parameter around in the IDA Pro debugger when it's pushed onto the stack. So I don't know if this really is the function that modifies the frame limit. There are some other curious functions in the Android port that I might investigate later.

The reason for this post is that I'm wondering if anyone is interested in helping me get the game running at 60fps. If you want, you can download the interesting binaries from the Android port along with the decrypted exe from the PC port (along with Ida Pro 6.7 databases for each). If you can't open the databases, you can just drag the executables in your favorite disassembler instead.

#2 User is offline Morph 

Posted 02 July 2015 - 02:57 PM

  • AKA SonicFreak94.
  • Posts: 696
  • Joined: 01-August 08
  • Gender:Male
  • Location:Utah
  • Project:SA1/2 hax
  • Wiki edits:11
I'm interested in helping out, and I've got the game on Steam. Not sure how useful I'll actually be though. Looks like it might require figuring out that class, and I have basically no experience with that (since SADX and SA2 PC are pretty much entirely C), but I guess I have an excuse to learn now =P

Either way, those .so/IDBs could be pretty useful for other Dreamcast ports, so thanks for sharing.

#3 User is offline evilhamwizard 

Posted 03 July 2015 - 02:21 PM

  • Posts: 1249
  • Joined: 16-June 04
  • Gender:Male
  • Wiki edits:109
Hm, njRendererSetFpsLock doesn't appear to be it. It unlocks the framerate so it runs at 60, but it causes the game to run at double the framerate (so everything is running too fast). You can tell that this isn't the way it's supposed to run because the in game timer runs at twice the speed. 0 disables the lock, 1 enables it. The game will disable the lock at the initial loading screen, the Heritage logo, and the loading screen in game.

The function that actually sets the njRendererSetFpsLock function to 1 for the gameplay is deProgress(). In my .exe, it's this:

.text:003386F0 ; =============== S U B R O U T I N E =======================================
.text:003386F0
.text:003386F0
.text:003386F0 deProgress      proc near               ; DATA XREF: sub_338740+1Fo
.text:003386F0                 push    1
.text:003386F2                 call    j_njRendererSetFpsLock
.text:003386F7                 push    62h
.text:003386F9                 mov     isBusyLoading, 0
.text:00338703                 call    j_jtActionGet
.text:00338708                 add     esp, 8
.text:0033870B                 test    eax, eax
.text:0033870D                 jnz     short locret_338726
.text:0033870F                 push    0FF000000h
.text:00338714                 push    0FF000000h
.text:00338719                 push    0FF000000h      ; struct CFrameWnd *
.text:0033871E                 call    j_?AFXSetTopLevelFrame@@YAXPAVCFrameWnd@@@Z_33 ; AFXSetTopLevelFrame(CFrameWnd *)
.text:00338723                 add     esp, 0Ch
.text:00338726
.text:00338726 locret_338726:                          ; CODE XREF: deProgress+1Dj
.text:00338726                 retn
.text:00338726 deProgress      endp
.text:00338726
.text:00338726 ; ---------------------------------------------------------------------------


Pseudo code:

void deProgress()
{
  j_njRendererSetFpsLock(1);
  isBusyLoading = 0;
  if ( !j_jtActionGet(98) )
    AFXSetTopLevelFrame((struct CFrameWnd *)0xFF000000);
}


The game is very picky some times when you load it up. Sometimes the game runs at a choppy frame rate if your computer's been on for a while, so that explains why I didn't see the effects of changing the parameter from 1 to 0 initially. I restarted my computer and tried again and this time I got results. But again, this isn't exactly the 60fps I had in mind. I think we'll need to disable the lock as well as change one more thing to allow the game to actually render 60 new frames in one second.

#4 User is offline Morph 

Posted 03 July 2015 - 06:15 PM

  • AKA SonicFreak94.
  • Posts: 696
  • Joined: 01-August 08
  • Gender:Male
  • Location:Utah
  • Project:SA1/2 hax
  • Wiki edits:11

View Postevilhamwizard, on 03 July 2015 - 02:21 PM, said:

Hm, njRendererSetFpsLock doesn't appear to be it. It unlocks the framerate so it runs at 60, but it causes the game to run at double the framerate (so everything is running too fast). You can tell that this isn't the way it's supposed to run because the in game timer runs at twice the speed. 0 disables the lock, 1 enables it. The game will disable the lock at the initial loading screen, the Heritage logo, and the loading screen in game.


I sort of expected that to happen. Most games weren't designed with variable framerate in mind on the Dreamcast--it was either 30 or 60 with slow-mo lag (and conversely, too fast above the desired value). That being said, Sonic Adventure, for example, has a speed modifier somewhere deep inside since it runs at 60FPS on menus much like Jet Set Radio, and that translates to the ingame speed somewhere down the line as well. The PC version of 'DX took advantage of this to add framerate presets for 30FPS, Dreamcast style 60/30FPS, and full 60FPS.

So I think njRendererSetFpsLock is going to be a requirement, and that we just have to hope there is actually a speed modifier to change.
This post has been edited by Morph: 03 July 2015 - 06:16 PM

#5 User is offline evilhamwizard 

Posted 03 July 2015 - 09:11 PM

  • Posts: 1249
  • Joined: 16-June 04
  • Gender:Male
  • Wiki edits:109
I had the same thought as well about the lack of variable frame rate. I'm wondering if the amount of frames produced in game is a hard coded value (41F00000 is 30.0 in single precision, but I think the game actually runs at 31.0 so maybe 41F80000). But then again, I don't know if the game could be passing this value directly within a subroutine or if that float is stored somewhere in the .data segment. It couldn't be something weird like 29.97 right?

Now that I think about it, does anyone know if the Android/iOS port runs at a variable frame rate or if it runs at any other framerate other than 30/31 during game play? I know the iOS version isn't supported with iOS 8, but maybe perhaps the Android port will yield a similar result?

#6 User is offline Hendricks 266 

Posted 04 July 2015 - 07:05 AM

  • Posts: 356
  • Joined: 01-June 05
  • Gender:Male
  • Location:United States
  • Wiki edits:58

View Postevilhamwizard, on 03 July 2015 - 09:11 PM, said:

But then again, I don't know if the game could be passing this value directly within a subroutine or if that float is stored somewhere in the .data segment.
According to Agner Fog,

Quote

String constants and floating point constants are stored in static memory. [...] Integer constants are usually included as part of the instruction code.

This post has been edited by Hendricks 266: 04 July 2015 - 07:05 AM

#7 User is offline MainMemory 

Posted 04 July 2015 - 11:35 PM

  • Every day's the same old thing... Same place, different day...
  • Posts: 3703
  • Joined: 14-August 09
  • Gender:Not Telling
  • Project:SonLVL
  • Wiki edits:1,339
In my experience with SADX and SA2, it depends on how the value is used. If the value is assigned to a variable or passed to a function, it can be treated like an integer constant, and stored in the instruction. If the value is used in an FPU calculation, it is stored in .rdata.

#8 User is offline BlazeHedgehog 

Posted 15 July 2015 - 03:53 AM

  • A "Community Enigma"?
  • Posts: 1389
  • Joined: 23-January 05
  • Gender:Male
  • Wiki edits:51
From what I've been able to tell with my eyeballs in Jet Set Radio on the PC, when the framerate unlocks during fades, it looks like the speed of everything doubles, too. It'll be awesome if you can get it actually running at 60fps, but I'm skeptical.

#9 User is offline JaxTH 

Posted 15 July 2015 - 01:37 PM

  • Pudding Deity
  • Posts: 7192
  • Joined: 29-March 10
  • Gender:Male
  • Location:Los Angeles
  • Project:Jack shit.
  • Wiki edits:2

View PostBlazeHedgehog, on 15 July 2015 - 03:53 AM, said:

From what I've been able to tell with my eyeballs in Jet Set Radio on the PC, when the framerate unlocks during fades, it looks like the speed of everything doubles, too. It'll be awesome if you can get it actually running at 60fps, but I'm skeptical.

JSR is one of those games that plays at double it's speed when at 60.

#10 User is offline evilhamwizard 

Posted 15 July 2015 - 02:50 PM

  • Posts: 1249
  • Joined: 16-June 04
  • Gender:Male
  • Wiki edits:109
The game sets njRendererSetFpsLock to 0 for a split moment when the screen begins to fade and then sets it back to 1 again sometime while game is loading the next area.

I recorded a video of what it looks like when njRendererSetFpsLock is set to 0 for the actual gameplay (video might still be rendering):



If you want to achieve this effect yourself, open your copy of jetsetradio.exe in your favorite hex editor and look for the bytes 6A01E86FB9F4FF6A. Change the 6A01 part to 6A00 and save the .exe.

I still haven't figured out how to get the game to properly run as it should with this enabled. :\
This post has been edited by evilhamwizard: 15 July 2015 - 02:51 PM

#11 User is offline Elliot Gindi 

Posted 07 November 2015 - 03:42 AM

  • Posts: 90
  • Joined: 21-May 14
  • Gender:Male
  • Location:New York
  • Project:Brawl Minus
Don't give up! I believe in you!

#12 User is offline CheatFreak 

Posted 06 December 2015 - 01:28 PM

  • Everything or Nothing.
  • Posts: 29
  • Joined: 22-May 13
  • Gender:Male
  • Location:Michigan
  • Project:BetterSADX Patch
I think sega themselves had 60fps working at some point as well, check out this trailer for way back when.
Is is possible there's remnants of the code used to make this trailer still in the game?

http://www.youtube.c...h?v=jT5_Tro6Ev8
This post has been edited by CheatFreak: 06 December 2015 - 01:34 PM

#13 User is offline Morph 

Posted 06 December 2015 - 03:27 PM

  • AKA SonicFreak94.
  • Posts: 696
  • Joined: 01-August 08
  • Gender:Male
  • Location:Utah
  • Project:SA1/2 hax
  • Wiki edits:11
That's interpolated in post. You can sometimes see artifacts of the interpolation process. For example, at 0:53, bits of the fence sort of phase out of existence as the character goes by, and at 0:56, you can see the cross walk to the left freak out due to the HUD on top making it harder to interpolate. For what it's worth, you can download a program called SVP that will do the same sort of thing to any video you want to watch.

#14 User is offline evilhamwizard 

Posted 07 December 2015 - 03:18 AM

  • Posts: 1249
  • Joined: 16-June 04
  • Gender:Male
  • Wiki edits:109
Hehe, I almost completely forgot about this.

I'll come back to this soon. I want to read up on the Ninja/Shinobi Library documentation to see if maybe there's a function that does what I want. I recall Sonic Adventure DC's 60fps hack fools around with two different variables to get it's 60fps working which is what I originally thought was needed for this game to work. Chances are everything Sonic Adventure DC is doing with controlling it's framerate is done with the help of those nj* library functions, so I'll have to see what's going on in the game so I can try and do it for this one. Luckily, Sega ported both Android and PC versions with calls to the Ninja/Shinobi library functions so this should be pretty interesting if I can get my hands on some documentation (I believe there's some in the Dreamcast SDK).

#15 User is offline Elliot Gindi 

Posted 22 December 2015 - 08:29 AM

  • Posts: 90
  • Joined: 21-May 14
  • Gender:Male
  • Location:New York
  • Project:Brawl Minus
Best news I've read in a while, keep up the good work!

Page 1 of 1
    Locked
    Locked Forum

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users