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): .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.
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.
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): .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: Code (Text): 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.
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.
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?
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.
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.
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. :\
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
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.
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).
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?
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?
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.