don't click here

Sonic CD decompilation

Discussion in 'Engineering & Reverse Engineering' started by BenoitRen, Jul 17, 2023.

  1. CS

    CS

    Member
    29
    4
    3
    Which of the files do I find the code for the Sonic CD spindash? I want to add this spindash to the romhack I'm making of Sonic 1.
     
  2. BenoitRen

    BenoitRen

    Tech Member
    956
    580
    93
    All code related to Sonic can be found in PLAYER.C.
     
  3. BenoitRen

    BenoitRen

    Tech Member
    956
    580
    93
    These glitches have been fixed!

    I've also finally added PlayStation Portable support, but as I can't compile for it I can't test it.
     
  4. BenoitRen

    BenoitRen

    Tech Member
    956
    580
    93
    I thought implementing the Stage Test screen would be easy. Then I looked at the code, and was reminded of the mess that was the title screen. But this time I could base the integration on the earlier code, so I managed to get it done today!
     
  5. BenoitRen

    BenoitRen

    Tech Member
    956
    580
    93
    Implemented the Sound Test screen. Did some refactoring on the way to integrate the loading of extra images cleanly.
     
  6. BenoitRen

    BenoitRen

    Tech Member
    956
    580
    93
    Next, I thought I'd integrate the Best Times screen. Should be easy, right? Just a list. However, I can't implement it in my current Mega Drive-inspired engine.

    The PC port implemented this screen by turning every character into a sprite. Seems reasonable, until you realise that the end result means having over 200 sprites on the screen at the same time. The Mega Drive supports a maximum of 80.

    There would have been no problem if they had used tiles instead. But the list is too long for a plane to contain all of it, and I guess they didn't want to program dynamic tile placement.
     
    • Informative Informative x 2
    • List
  7. Blastfrog

    Blastfrog

    See ya starside. Member
    Perhaps the sprite limit should be that of the PC version’s if it has one?
     
  8. BenoitRen

    BenoitRen

    Tech Member
    956
    580
    93
    After thinking on it, I bolted on a way to pass the sprite list that's built in the Best Times code directly to the sprite code, which will blit all the sprites in it when it's time to draw the screen, with no limits.

    Some testing and corrections later, it works fine.
     
  9. BenoitRen

    BenoitRen

    Tech Member
    956
    580
    93
    I haven't worked on this much last week, but I did decompile the last part of SONICCD.EXE that I had put off: SCORE.C.

    My goal is still to have most things done by 8 April, which is the project's second anniversary, but it doesn't look like I'll make it. The core issue is my lack of confidence to integrate the convoluted code that makes all of these extra screens work. As a result I tend to procrastinate.
     
  10. MarkeyJester

    MarkeyJester

    You smash your heart against the rocks Resident Jester
    2,316
    565
    93
    Japan
    You only fail if you give up, don't worry so much about time limits.

    (Obviously, don't take 20+ years to do it either, I'm looking at you, extended edition...)
     
    • Agree Agree x 5
    • Like Like x 1
    • List
  11. BenoitRen

    BenoitRen

    Tech Member
    956
    580
    93
    The past few days I've been trying to get Sonic CD on my PSP.

    I found that the PSP SDK does exist in the form of a Docker image, but getting it to play nice with Windows was a hassle.
    • The provided command to mount the directory with the source code to the container doesn't work. It has to be an absolute path.
    • This absolute path has to be in UNIX format. So, no C:\Users\John\my_psp_code, but instead /c/Users/John/my_psp_code
    • This same directory has to be somewhere under your home folder, or it will appear empty due to not having permissions to read its contents.
    • You have to run the Docker container with the --privileged=true option. If you don't, you'll get an Operation not permitted error from gmake after executing psp-cmake, and even if you get past that, it'll fail to find SDL2 for some reason.
    I updated the official documentation so others won't bang their head against the same wall.

    But I wasn't out of the woods yet.

    As Sonic CD was ported to Windows, I used backslashes in #include and other file paths as the directory separator. As the container is a GNU/Linux environment, it didn't like that. So I had to change all backslashes to forward slashes.

    Next, due to the source having a mix of lowercase .c and uppercase .C files, the compiler would compile them as C and C++, respectively, and the C code wouldn't be able to find the C++ functions due to name mangling. On Windows, I solve this by passing the -x c option, which tells MinGW to treat everything as C. But just like when I tried to compile using the older minimalist SDK, I got tons of linker errors. The only solution was to rename all uppercase .C files to lowercase .c.

    At last, Sonic CD compiled! I tried to run it on my PSP. It would immediately exit.

    I tried to debug by having the program output to a file, but no matter what I did, the created file would remain empty. One of the SDK maintainers suggested I use SDL_Log, which outputs to a file. So I did, and I finally found the problem: the background music code wasn't able to create a thread. Giving the thread a name solved that.

    The next problem was that it couldn't allocate enough memory to upsample the sound effects. I didn't know the required buffer sizes for them were so big! Thinking on it, I realised that the data after conversion was probably smaller than the buffer, so I could copy the result to a smaller chunk of memory. This indeed saved me lots of memory, but it wasn't enough. The upsampling failed at sample 67 (out of 80) because it needed 22 MB (!) for the buffer.

    Fine, let's try without sound effects. It runs... but in slow motion, and it seems the background music can't be streamed fast enough, as it pauses intermittently. It crashed early in Palmtree Panic.

    Let's try without any sound at all. Result: still in slow motion, and the same early crash.

    At this point, I'm guessing that the conversion from an 8-bit palettised surface to 32-bit texture data is too taxing for the PSP.
     
    • Informative Informative x 2
    • List
  12. Blastfrog

    Blastfrog

    See ya starside. Member
    My suggestion: take a look at Doom source ports on PSP for reference (that retain the software renderer), as Doom uses an 8bpp plane as well.
     
  13. Cooljerk

    Cooljerk

    Professional Electromancer Oldbie
    5,185
    821
    93
    Ahh, swizzling/twiddling. The problem is that stuff like the PSP (and dreamcast) use a non-linear texel encoding when they use a palette. The downside is the way it has to organize texels to facilitate this look up, is that it needs to iterate through pixels on the texture in a z-ordered curve. This draws pixels in little Z (actually N) shaped patterns on the buffer.

    There are some tricks that can speed up twiddle->untwiddled conversion, which is what you'd want to do to convert 8bpp to 32bpp. Here's a page on a whole bunch of tricks to do so, they trade off between speed and memory consumption. The basic gist is to use a LUT for fast calculation: https://dreamcast.wiki/Twiddling

    On modern CPUs, you can use opcodes like PDEP and PEXT to make morton encoding much faster. There is also a trick you can do if your CPU supports multiply-no-carry operations, but I'm pretty sure the PSP does not. So your only real option for speed is a LUT.

    There is ONE trick that can, in constant time, very quickly convert between morton-encoded and non-mortion-encoded textures, but it requires each texel take up 4 times as much vram and thus eats up memory very quickly. A LUT is probably going to be your best, speediest option on the PSP.
     
    Last edited: Apr 8, 2025
  14. BenoitRen

    BenoitRen

    Tech Member
    956
    580
    93
    I opted to try using SDL 1.2 using an 8-bit software surface instead.

    At first I couldn't compile it, because it didn't find module info, which is normally inserted by SDL after replacing your main(). It turned out to be an oversight in SDL_main.h.

    After compilation I was greeted by a black screen. After some thinking I realised this was because I select DirectX as the video driver. Correcting that got a mouse pointer to show up, but that's it.

    There's no such thing as SDL_Log in SDL 1.2, and it hijacks stdio, so outputting to a file is not possible. So I did the next best thing: create files with the log message as the file name! Obviously this has limitations, but I was eventually able to figure out that SDL_LoadBMP inexplicably fails to load some bitmap images for the title screen.

    Instead of investigating it I modified the code to start a new game. I saw a garbled image of Palmtree Panic. Right, that's probably because I assume that the screen surface's pitch equals the surface's width, which worked out until now, but has finally come to bite me in the ass. Modifying the blitting code to account for a different pitch showed me a better image, but things still weren't moving. It seems to be stuck right before fading in.

    At this point I decided to postpone further porting efforts and to continue work on the save data functionality, and the other screens.
     
    • Informative Informative x 1
    • List
  15. Cooljerk

    Cooljerk

    Professional Electromancer Oldbie
    5,185
    821
    93
    When you are talking about using sdl and 8bit surfaces, are you referring to letting sdl handle the palette for you on an actual 8bit surface, or do you mean you are treating a 32bit surface like an 8bit one by plotting pixels in software? I ask because i tried the former several years ago and had to abandon the idea and start from scratch, because i could only hold one palette at a time. So if a tile referred to a different palette, i had to update the palette mid draw, which made my program slow to a crawl as it was changing the palette constantly. Just a word of advice if you're going down the road i tried.

    Re: logging, i used to pipe std out to a file from the command line.
     
  16. BenoitRen

    BenoitRen

    Tech Member
    956
    580
    93
    I let SDL handle the palette on an 8-bit software surface that represents the screen. The palette doesn't need to change mid-draw, so in that aspect I should be safe. The palette does change once between frames.
     
  17. Blastfrog

    Blastfrog

    See ya starside. Member
    In order to minimize input lag and frame drops, would it be better to go the extra mile and convert the assets JIT style to native 32-bit color, and plot pixels directly to the buffer?

    I’ll admit that I don’t know jack about the PSP, just throwing ideas out here. In any case, I’ve been eagerly watching this project as I think it’s worth preserving this port in a more malleable and portable state.
     
  18. BenoitRen

    BenoitRen

    Tech Member
    956
    580
    93
    Converting the assets to 32-bit colour isn't a solution, because the palette can change each frame, which means used colours on those assets can also change.

    I'm honestly not sure what I can do. I read cooljerk's explanation about twiddling, but as I'm using an SDL surface I'm not sure that even applies.

    At any rate, I've implemented the save file. That took me longer than expected.

    I started off with the decompiled code, replacing old 16-bit Windows file API calls with standard C ones. What I didn't realise was that those old APIs always place the file pointer at the start of the file, and the save data functions are written with this in mind. Another gotcha is the following expected behaviour, which the fopen reference doesn't mention, but explains why I couldn't write after a reading operation:
     
  19. ndiddy

    ndiddy

    Member
    54
    68
    18
    Nice work!

    If you're developing on Linux (or Windows with WSL installed), I recommend using the man pages for the C library rather than any online resource. They're really clear and well written. From the fopen man page:
     
  20. BenoitRen

    BenoitRen

    Tech Member
    956
    580
    93
    I've finally completed the savedata management screen!



    Do let me know if something's off. I have little experience with the original incarnation.