don't click here

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

Discussion in 'Technical Discussion' started by evilhamwizard, Jul 2, 2015.

  1. evilhamwizard

    evilhamwizard

    Researcher
    1,374
    392
    63
    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):

    Code (Text):
    1. .text:01291B40 ; =============== S U B R O U T I N E =======================================
    2. .text:01291B40
    3. .text:01291B40 ; Attributes: bp-based frame
    4. .text:01291B40
    5. .text:01291B40 njRendererSetFpsLock proc near          ; CODE XREF: j_njRendererSetFpsLockj
    6. .text:01291B40
    7. .text:01291B40 arg_0           = dword ptr  8
    8. .text:01291B40
    9. .text:01291B40                 push    ebp
    10. .text:01291B41                 mov     ebp, esp
    11. .text:01291B43                 cmp     [ebp+arg_0], 0
    12. .text:01291B47                 mov     ecx, _ZN6CVideo8instanceE_ptr
    13. .text:01291B4D                 mov     eax, [ecx]
    14. .text:01291B4F                 mov     eax, [eax+98h]
    15. .text:01291B55                 setnz   dl
    16. .text:01291B58                 movzx   edx, dl
    17. .text:01291B5B                 push    edx
    18. .text:01291B5C                 call    eax
    19. .text:01291B5E                 pop     ebp
    20. .text:01291B5F                 retn
    21. .text:01291B5F njRendererSetFpsLock endp
    22. .text:01291B5F
    23. .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. SF94

    SF94

    Tech Member
    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. evilhamwizard

    evilhamwizard

    Researcher
    1,374
    392
    63
    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:

    Code (ASM):
    1. .text:003386F0 ; =============== S U B R O U T I N E =======================================
    2. .text:003386F0
    3. .text:003386F0
    4. .text:003386F0 deProgress      proc near               ; DATA XREF: sub_338740+1Fo
    5. .text:003386F0                 push    1
    6. .text:003386F2                 call    j_njRendererSetFpsLock
    7. .text:003386F7                 push    62h
    8. .text:003386F9                 mov     isBusyLoading, 0
    9. .text:00338703                 call    j_jtActionGet
    10. .text:00338708                 add     esp, 8
    11. .text:0033870B                 test    eax, eax
    12. .text:0033870D                 jnz     short locret_338726
    13. .text:0033870F                 push    0FF000000h
    14. .text:00338714                 push    0FF000000h
    15. .text:00338719                 push    0FF000000h      ; struct CFrameWnd *
    16. .text:0033871E                 call    [email protected]@[email protected]@@Z_33 ; AFXSetTopLevelFrame(CFrameWnd *)
    17. .text:00338723                 add     esp, 0Ch
    18. .text:00338726
    19. .text:00338726 locret_338726:                          ; CODE XREF: deProgress+1Dj
    20. .text:00338726                 retn
    21. .text:00338726 deProgress      endp
    22. .text:00338726
    23. .text:00338726 ; ---------------------------------------------------------------------------
    Pseudo code:

    Code (Text):
    1. void deProgress()
    2. {
    3.   j_njRendererSetFpsLock(1);
    4.   isBusyLoading = 0;
    5.   if ( !j_jtActionGet(98) )
    6.     AFXSetTopLevelFrame((struct CFrameWnd *)0xFF000000);
    7. }
    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. SF94

    SF94

    Tech Member
    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.
     
  5. evilhamwizard

    evilhamwizard

    Researcher
    1,374
    392
    63
    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. According to Agner Fog,

     
  7. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,651
    218
    43
    SonLVL
    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. BlazeHedgehog

    BlazeHedgehog

    A "Community Enigma"? Oldbie
    1,464
    6
    18
    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. JaxTH

    JaxTH

    Pudding Deity Oldbie
    9,966
    412
    63
    Los Angeles
    Jack shit.
    JSR is one of those games that plays at double it's speed when at 60.
     
  10. evilhamwizard

    evilhamwizard

    Researcher
    1,374
    392
    63
    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):

    [youtube]http://www.youtube.com/watch?v=tkqNcx_ZqN4[/youtube]

    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. :\
     
  11. ELS

    ELS

    Member
    Don't give up! I believe in you!
     
  12. CheatFreak

    CheatFreak

    Catgirl expert. Member
    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.com/watch?v=jT5_Tro6Ev8
     
  13. SF94

    SF94

    Tech Member
    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. evilhamwizard

    evilhamwizard

    Researcher
    1,374
    392
    63
    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. ELS

    ELS

    Member
    Best news I've read in a while, keep up the good work!
     
  16. PkR

    PkR

    Member
    190
    24
    18
    SADX Dreamcast Conversion, The Emeralds' Awakening
    Has anything come out of this? Unlike SA1/SADX, the PC port of Jet Set Radio is surprisingly decent. It has a crap config program and minor transparency issues with trees and police cars, but it's also got proper widescreen support and looks pretty much identical to the original game. I've played it on both DC and PC and couldn't find any major visual or sound differences. The soundtrack is only slightly different, and it's possible to mod the music as well since the file structure is very similar to the original Dreamcast version. A lot of files are identical to the original, and the music is just Dreamcast ADX files converted to OGG.

    The only thing I wish they'd done in this port is proper 60 FPS support. I've been looking for a solution and found this thread. Is anyone still interested in making a 60 FPS patch for Jet Set Radio?
     
  17. Sonikko

    Sonikko

    Bird Member
    57
    4
    8
    Italy
    I just wanted to chime in to express my interest.
    I've somehow never seen this thread during the years of lurking here, but I'd be very interested in a 60fps patch, I feel it's the only thing that is missing from the PC port as of now.
    Did something ever come out of this?
     
  18. Willie

    Willie

    Each day the world turns Laugh 'til it all burns Member
    I hope a 60FPS patch gets made. Sega should have never released the PC version in only 30FPS.
     
  19. JaxTH

    JaxTH

    Pudding Deity Oldbie
    9,966
    412
    63
    Los Angeles
    Jack shit.
    JSR is one of those games that plays by framerate. 60 means the game would run at double speed.
     
  20. SF94

    SF94

    Tech Member
    Assuming the graphics and game logic are separated enough (which I only bring up because of the horrors I've seen in Sonic Adventure), that would be trivial to fix with access to the source code.