don't click here

Sonic CD Quirks/Deconstruction

Discussion in 'Engineering & Reverse Engineering' started by Devon, Jul 11, 2022.

  1. Devon

    Devon

    La mer va embrassé moi et délivré moi lakay. Tech Member
    1,485
    1,808
    93
    your mom
    That's because the remake merely just doubles the ring count for each subsequent rings UFO that is destroyed.

    [​IMG]

    In the original, it uses a separate counter for the chain bonus, which is initialized as 20, and doubles for each subsequent rings UFO, which is then added to the ring counter.

    [​IMG]

    [​IMG]

    Here is a demonstration.

     
    Last edited: Sep 7, 2023
    • Informative Informative x 4
    • Like Like x 1
    • List
  2. Blue Spikeball

    Blue Spikeball

    Member
    2,585
    1,086
    93
    Which version would you say is glitchier, the original or the remaster?
     
  3. Devon

    Devon

    La mer va embrassé moi et délivré moi lakay. Tech Member
    1,485
    1,808
    93
    your mom
    I'm placing my bets on the original, to be honest, with how messy the code can get.

    Bonus quirk: there is a bug where a Tamabboh's missiles will despawn if it flies past a block with solidity.

    [​IMG]

    This is due to an oversight where it checks for floor collision (aka, it checks for collision with a block downwards) even if it's moving upwards. When it moves high enough past a block with solidity for the downwards check to register as touching the floor, it despawns.

    [​IMG]

    The first bit just checks if the missile is on screen, and if not, despawn it. Then comes the floor collision check without an additional check to see if it's moving downwards.

    Here is a visualization of this. Notice how the left missile despawns once the bottom of its collision sensor touches the block with solidity.

    [​IMG]
     
    Last edited: Sep 7, 2023
    • Informative Informative x 6
    • Like Like x 1
    • List
  4. Devon

    Devon

    La mer va embrassé moi et délivré moi lakay. Tech Member
    1,485
    1,808
    93
    your mom
    If you have an astute eye, you can see that the drowning countdown numbers are actually bubbles that come out of Sonic's mouth. (Yes this is in all the Genesis titles, but it's also in CD, so...)

    [​IMG]

    Also, the colors are a bit messed up here in Sonic CD for when the number forms. The reason for this is because they are using the wrong palette line. The number itself uses line 1 for the yellows, but it seems they got a bit lazy and also made the bubble formation use it as well, when they were designed for line 0, using Sonic's blues.

    [​IMG]

    The reason why this is because in prototypes, the fully formed number graphic weren't yellow like in Sonic 1, but rather also appeared like a bubble, so it used line 0. When the numbers went back to being yellow, they went back to line 1, but took the formation with it.

    [​IMG]

    The code changes between v0.51 and the final are highlighted here. When the number gets spawned, its palette line is set from the start, and also changed the initialization code to OR the tile ID instead of directly setting it so that the palette line change can go into effect.

    [​IMG]

    [​IMG]

    Interesting thing to note is that these graphics appear to stem from a later prototype of Sonic 1. The numbers originally being bubbles makes the whole thing with them coming out of Sonic's mouth make more sense.

    [​IMG]

    The 2011 remake mostly fixes this, but the lightest shade of blue was accidentally left as the regular one instead of the underwater one, so it appears more purple instead of baby blue.

    [​IMG]
     
    Last edited: Sep 14, 2023
    • Like Like x 6
    • Informative Informative x 2
    • List
  5. Devon

    Devon

    La mer va embrassé moi et délivré moi lakay. Tech Member
    1,485
    1,808
    93
    your mom
    If your Backup RAM (used for storing save data) data is corrupted, in English, it will display this error message, implying that "Robotonic" messed with it.

    [​IMG]

    In Japanese, it's the same message, but "Robotonic" is referred to as Eggman instead.

    [​IMG]

    The other error messages aren't as amusing. I will note that the game will only be playable in the event that the Backup RAM is full. The other errors just freeze at the message.

    [​IMG] [​IMG] [​IMG]

    (The same messages in Japanese, plus a bonus alternate message in the European version)

    [​IMG] [​IMG] [​IMG]
    [​IMG]
     
    • Informative Informative x 1
    • List
  6. saxman

    saxman

    Oldbie Tech Member
    Interesting. I've never seen a lot of those. The only one I'm familiar with is the one saying "Internal RAM is not initialized". It was dumb too, because you could not play the game unless you initialized the RAM. I didn't know the first thing about RAM. I didn't understand the Sega CD control panel. I thought the game just wasn't working and ended up exchanging my copy for another one. I can't be the only one who ran into that as a kid.
     
  7. BenoitRen

    BenoitRen

    Tech Member
    848
    441
    63
    If pl_suu is the amount of lives, then the Special Stage caps it at 250. No cap seems to exist in the rest of the code.
     
  8. Devon

    Devon

    La mer va embrassé moi et délivré moi lakay. Tech Member
    1,485
    1,808
    93
    your mom
    Indeed that is the case. It's also reflected in the Sega CD version here
    Code (Text):
    1.  
    2. ; -------------------------------------------------------------------------
    3. ; Add points to score
    4. ; -------------------------------------------------------------------------
    5. ; PARAMETERS:
    6. ;    d0.l - Points to add
    7. ; -------------------------------------------------------------------------
    8.  
    9. AddScore:
    10.     move.l   score,d1                ; Add points
    11.     add.l    d1,d0
    12.  
    13. .Check1UP:
    14.     cmp.l    nextLifeScore,d0        ; Have we crossed an extra life score threshold?
    15.     bcs.s    .SetScore               ; If not, branch
    16.     addi.l   #5000,nextLifeScore     ; Set next extra life score threshold
    17.     addq.b   #1,lives                ; Add life
    18.     addq.w   #1,extraPlayerCnt.w     ; Add extra player icon
    19.     cmpi.b   #250,lives              ; <-- HERE ; Do we have more than 249 lives?
    20.     bcs.s    .Check1UP               ; If not, check for more lives to add
    21.     move.b   #249,lives              ; Cap at 249 lives
    22.     bra.s    .Check1UP               ; Check for more lives to add
    23.  
    24. .SetScore:
    25.     move.l   d0,score                ; Set score
    26.     cmpi.l   #1000000,score          ; Is the score too large?
    27.     bcs.s    .End                    ; If not, branch
    28.     move.l   #999999,score           ; Cap the score
    29.  
    30. .End:
    31.     rts

    Unfortunately, 249 is not a good place to cap it, because the lives value is signed in Sonic CD, unlike the other Genesis Sonic games, thanks to the subtraction code checking if the number has become negative. The maximum positive value in this case is 127, and anything above it is an overflow into the negatives.
    Code (Text):
    1.     subq.b   #1,lives
    2.     bpl.s    .CapLives              ; If we still have lives left, branch
    3.     clr.b    lives                  ; Cap lives at 0
    4.  
    5. .CapLives:
     
  9. BenoitRen

    BenoitRen

    Tech Member
    848
    441
    63
    In the PC/Gems version, pl_suu is an unsigned char. However, when it is decreased, it's cast to a signed char to check if it has become negative.

    This would mean that the life counter can exceed 127, but as soon as you lose a life, you're right back at zero.
     
  10. muteKi

    muteKi

    Fuck it Member
    7,909
    160
    43
    One thing that genuinely surprised me about the Sonic CD manual is that at no point is the save system actually described in it, at least for the US. The Japanese manual does have a section about it.
     
  11. BenoitRen

    BenoitRen

    Tech Member
    848
    441
    63
    Now that I've decompiled the PC/PS2 version of the Special Stage, I thought I'd revisit this with the C version. :)

    The timer update function in the port is called time_dec:
    Code (Text):
    1. void time_dec() {
    2.   if (spgmmode & 2)
    3.   {
    4.     time_time_attack();
    5.     return;
    6.   }
    7.   --tcnt20;
    8.   if (tcnt20 == 0)
    9.   {
    10.     tcnt20 = 20;
    11.     dec();
    12.   }
    13.  
    14.  
    15.   if (tdecflg != 0)
    16.   {
    17.     --tdecflg;
    18.     dec();
    19.   }
    20. }
    Just like the Mega CD version, it counts down one second every 20 frames (remember that the Special Stage is 20 FPS). The second counter, which decreases a second as long as it's not zero, is called a flag for some reason. (Side note: the port really likes to use 'flag' variables with more than two values)

    This second flag counter is set by "sibuki" functions. Apparently this means "splash" in Japanese.

    The splash has three states in the port. The first state is function sibuki0:
    Code (Text):
    1. void sibuki0(sprite_status_sp* sibukiwk) {
    2.  
    3.   sibukiwk->pattbl = mpsibuki;
    4.   sibukiwk->sx_posi.w.h = 256;
    5.   sibukiwk->sy_posi.w.h = 344;
    6.   patinit(sibukiwk, 0);
    7.   *(short*)(sibukiwk->actfree) = 14;
    8.   ++sibukiwk->exeno;
    9.   key_set(162);
    10.   if (!(spgmmode & 2)) tdecflg = 10;
    11.   sibuki1(sibukiwk);
    12. }
    The animation of the large splash is set to last 14 frames just like in the original, and tdecflg is set to 10, so the fast countdown is, just like in the original slightly shorter than the animation's duration.

    The second state is what actually does the bookkeeping for the animation, a function called sibuki2:
    Code (Text):
    1. void sibuki1(sprite_status_sp* sibukiwk) {
    2.   if (--*(short*)(sibukiwk->actfree) != 0) return;
    3.   if (actwk[0].scno_ce != 3) { sibukiwk->actflg |= 1; return; }
    4.   patinit(sibukiwk, 1);
    5.   sibukiwk->exeno = 2;
    6. }
    I have no idea what scno_ce is, so I have no idea what it's checking Sonic for. But apparently it delays the transition tot he next state, handled by function sibuki3:
    Code (Text):
    1. void sibuki2(sprite_status_sp* sibukiwk) {
    2.   if (actwk[0].scno_ce != 3) sibukiwk->actflg |= 1;
    3.   else if (sibukiwk->patno == 0) tdecflg = 2;
    4. }
    I think this handles setting the counter to 2 frames on every animation loop, but I don't know enough about the code to dissect this function.

    There's also a function that clear the splash:
    Code (Text):
    1. void sibuki_clr() {
    2.   if (actwk[2].actno != 0) actwk[2].actflg |= 1;
    3. }
    This gets called from functions evt01, evt04 and jumpset. The latter is obvious, so what about the first two? They're part of a series of collision events, and the game decides which to call based on... Sonic's scno_ce value! So, it'd seem that colliding with certain things stops the splashing.
     
    • Informative Informative x 2
    • List
  12. BenoitRen

    BenoitRen

    Tech Member
    848
    441
    63
    We all know how in the C port of Sonic CD each stage version has its own DLL/ELF, right? This inevitably leads to some duplicate code.

    I'm going through all the code for R1, and each stage version seems to have its own version of the code that handles scrolling. It's annoying. Why did they do it like that? Even the code for the two futures of a stage has differences, and that's supposed to be the same stage internally.
     
  13. Devon

    Devon

    La mer va embrassé moi et délivré moi lakay. Tech Member
    1,485
    1,808
    93
    your mom
    That's how it was done in the original Mega CD version, too, I'm afraid. Even more annoying is that R11A's setup for scrolling has slight differences from the rest of the game's...

    Welcome to the horrors of Sonic CD's code structure. The alcohol can be found on the top shelf... you're gonna need it :eng99:
     
  14. Bobblen

    Bobblen

    Member
    452
    232
    43
    There's a reason 99% of the mods you see are for the rsdk remake. I always assumed that decision was done to suit how the hardware accessed data, but have no idea if that's actually true!
     
  15. Chimes

    Chimes

    The One SSG-EG Maniac Member
    959
    661
    93
    (wait wtfs stopping the RAM from loading a selected area of the same variables once and keeping it through all of the game and only loading swappable modules of data when needed)
     
  16. Devon

    Devon

    La mer va embrassé moi et délivré moi lakay. Tech Member
    1,485
    1,808
    93
    your mom
    Nothing at all, actually. When I made my PoC for faster time warps, that's what I did to save loading times. But, that's just what the developers ended up not doing. Possibly because it's simpler to just set up a build system where it just includes the code in each file instead of having to manage symbol linking and stuff.
     
    Last edited: Nov 12, 2023
  17. Chimes

    Chimes

    The One SSG-EG Maniac Member
    959
    661
    93
    You'd think with a game that uses swappable assets as a core game mechanic they would, well, swap the assets and keep the core game, but I guess not! Weird...
     
  18. BenoitRen

    BenoitRen

    Tech Member
    848
    441
    63
    You can't explain what I'm seeing by them using a build system that includes the code in each file, though. When I said each stage version has its own version, I meant that they all have a different version!

    For example, in function scr_set, R11A zeroes the flags in a one-liner:
    Code (Text):
    1. scrh_flag = scrv_flag = scr_die.b.h = scr_timer.b.h = zone_flag.b.h = 0;
    R11B decides to do it in several lines:
    Code (Text):
    1. scrh_flag = 0;
    2. scrv_flag = 0;
    3. scr_die.b.h = 0;
    4. scr_timer.b.h = 0;
    5. zone_flag.b.h = 0;
    R12B uses a combination of those approaches:
    Code (Text):
    1. scrh_flag = scrv_flag = 0;
    2. scr_die = zone_flag = 0;
    3. scr_timer = 0;
    And that's only scratching the surface. The above example accomplishes essentially the same thing, but other functions have notable differences (most often the scroll function). Sometimes they only have one line change, and sometimes they don't exist/are stubbed.
     
  19. Devon

    Devon

    La mer va embrassé moi et délivré moi lakay. Tech Member
    1,485
    1,808
    93
    your mom
    Oh no, I know what you are talking about. Again, the original Mega CD version does that too, but I was responding to them inquiring about using a moduled system for stage data and stuff.
     
  20. Black Squirrel

    Black Squirrel

    let's hurl a bwiki mart Wiki Sysop
    9,210
    3,078
    93
    Northumberland, UK
    the kwiki mart is real d'oh
    With Sonics 1-through-3 (and most Mega Drive games) I get the impression the designers and artist went away, made some assets, and then sent it over to Yuji Naka to merge together. This was before sensible version control - you just passed floppy disks around in the office.

    I can see them running into an issue with this with Sonic CD - there are four timezones to maintain per stage, maybe it's more efficient to hand your designers a stripped-down copy of the code so they can handle their own "section" of the game. Perhaps they were expecting each section to be larger than a floppy disk, and thus a lot more awkward to pass around.

    The net result is you get multiple copies of the same code, but as it's on a CD, there's plenty of space so it doesn't matter.


    Or it could be a loading issue. Perhaps storing the data as they did reduces the need to spin the disc. Or maybe they thought that would be the case, idk.