Some changes and fixes for Sonic 2

Discussion in 'Engineering & Reverse Engineering' started by Esrael, Jun 7, 2012.

  1. Clownacy

    Clownacy

    Tech Members Tech Member
    775
    0
    16
    Okay, there's something seriously wrong with CNZ's background, but I don't know the cause.

    For some bizarre reason, DrawInitialBG seems to skip drawing a row of blocks if the level is Casino Night Zone. Without this check, CNZ act 1 will have a row of blank blocks replacing the lowest part of its background. At the same time, with this check, CNZ act 2 has a row of blank blocks at the top of its background! It's more apparent if you port S3K's layout format, like I did, since the blank blocks will be replaced with garbage.

    Since I have no idea why a hackish check is needed to make act 1 happy, the best I can offer is a workaround to not make act 2 look silly. Just modify the check in DrawInitialBG to check for act 1 only. 'cmpi.w #casino_night_zone_act_1,(Current_ZoneAndAct).w' should do the job.
     
  2. Didn't see this mentioned but on Sonic 2 Final the Waterfall splashes on Aquatic Ruin are garbled while they're fine on Sonic 2 Wai Proto (it was fixed on S2 Mobile!) ;p
     
  3. Clownacy

    Clownacy

    Tech Members Tech Member
    775
    0
    16
    I could have sworn the fix for that was mentioned once in this thread before, but now I can't find it.

    Anyway, the Git disasm contains fixes for a few bugs, complete with explanations. You can find them by searching for '1==0' and '1==1'. This includes a fix for the ARZ waterfall, odd sounding notes in the CNZ part of the credits music (I've yet to see a real fix for that), and even scrambled Sonic art on the Sega screen.
     
  4. flamewing

    flamewing

    Emerald Hunter Tech Member
    1,138
    0
    16
    France
    Sonic Classic Heroes; Sonic 2 Special Stage Editor; Sonic 3&K Heroes (on hold)
    Yeah, the waterfall bug has been found already; SCH has it fixed for years, and I think I mentioned in its thread somewhere (in addition to documenting the fix on Git).
     
  5. Clownacy

    Clownacy

    Tech Members Tech Member
    775
    0
    16
    SndID_SegaSound is actually played twice on the Sega screen. The only reason you don't hear two SEGA chants one after the other is because the stock PlaySound subroutine can only hold one SFX per frame. This will become a problem, however, if PlaySound is expanded to use a larger queue, such as when you port a different sound driver. Just remove one of the references to SndID_SegaSound, and you'll be all set.
     
  6. On the same matter of garbled graphics, CPz doors on Sonic 2 Mobile use different mappings that actually look better. I don't know if they are intended to look that way though.
     
  7. Clownacy

    Clownacy

    Tech Members Tech Member
    775
    0
    16
    Wow, there really is something weird about that graphic (sorry for being a month late, by the way). Look at this:

    [​IMG]

    These are the mappings used by Eggman in DEZ, who hides behind a 'fake' door (it doesn't use the normal door object, and doesn't even actually move, hence the other frames). Because of how its tiles are arranged, it can't display properly. It would need either a more complex set of mappings or an alternate art file.

    Now look at this:

    [​IMG]

    Looks familiar, doesn't it? That first one is what CPZ uses, but, looking at the last two, they look way better. Now try spawning a door in CPZ in debug mode.

    [​IMG]

    Uh... yeah. So it is a bug. A door object's sprite frame is determined by the object's subtype. All of CPZ's doors have a subtype of 0, but this is actually HTZ's subtype, according to what debug mode uses. In fact, the real order of the frames in the above image, from left to right, is HTZ, MTZ, CPZ, ARZ. Still doesn't really explain the DEZ door, beside lazy sprite map work, but, whatever. Who knows, the bug's been there since Simon Wai, so maybe whoever made the DEZ Eggman door used a broken CPZ door as reference. *shrugs*
     
  8. Clownacy

    Clownacy

    Tech Members Tech Member
    775
    0
    16
    Looks like I found a bug in Obj57, the MCZ Boss. When you defeat it, it writes to a random byte of RAM, because of using an uninitialised register. In stock S2, its seems RAM address FFFFED2E get written to, which is part of Ring_Positions. I only noticed the bug because, in my hack, the controls lock and Sonic's DPLCs stop functioning. The code responsible for this is under Obj57_Main_SubA:

    Code (ASM):
    1.     lea (Boss_AnimationArray).w,a1
    2.     move.b  #$D,7(a1)   ; face grin when hit
    3.     _move.b #2,0(a2)
    4.     move.b  #0,1(a1)    ; hover thingies fire off
    The only other time this object uses a2 is during object initialisation. What does it hold? Boss_AnimationArray. This looks to be a mistake made while copy/pasting code from Obj57_InitAnimationData. Just change the a2 to a1, and you should be set.
     
  9. redhotsonic

    redhotsonic

    Also known as RHS Tech Member
    1,584
    0
    0
    United Kingdom
    YouTuber
    How do you mean, the controls lock? AS in when Eggman starts blowing up you can no longer move? Making the level impossible to finish? Because it doesn't happen with me. Or am I misunderstanding your post?


    EDIT: Oh wait, you mean there was a bug with the GIT disassembly that has caused this? Or...?
     
  10. Clownacy

    Clownacy

    Tech Members Tech Member
    775
    0
    16
    I meant that the bug only became noticeable in my hack because of the massive RAM rearrangements. In stock S2, ring RAM gets written to, which is much less noticeable.
     
  11. Clownacy

    Clownacy

    Tech Members Tech Member
    775
    0
    16
    Cool. Debug mode doesn't reset the 'in air' status(a0) bit, meaning, depending on when you entered debug mode, the camera will have a different deadzone. Try moving up or down after entering debug mode while on the ground. Now do it while jumping. Anyway, in the code for exiting debug mode, Sonic's 'in air' bit is set, so maybe it was intended for that bit to be clear while in debug mode, making the camera stricter. To fix this, just go to Debug_Init, and insert 'bclr #1,(MainCharacter+status).w' after the 'move.b #AniIDSonAni_Walk,anim(a0)'.
     
  12. Clownacy

    Clownacy

    Tech Members Tech Member
    775
    0
    16
    The pillar objects in ARZ have a bit of a problem setting their width and height. Obj2B sets its width_pixels to define its collision box, and sets it up so the protruding design at the top isn't collidable, while the centre of the pillar is. However, width_pixels is also used to determine when the object is considered off-screen, and not to be displayed. This has the unfortunate effect of the pillar (the top part anyway) vanishing while it's still on-screen, when you're moving left or right. A solution for this would be to hardcode the object's collision box (width_pixels is just supplied as a parameter to SolidObject, and a lot of objects hardcode it anyway), and redefine width_pixels to account for the protruding part of the pillar.

    Find Obj2B_Init and change width_pixels from '$10' to '$1C'. Then find Obj2B_Main and replace these instructions:

    Code (ASM):
    1.     moveq   #0,d1
    2.     move.b  width_pixels(a0),d1
    3.     addi.w  #$B,d1
    with this:

    Code (ASM):
    1.     moveq   #$10+$B,d1
    But that's not all! Use debug mode and move up to off-screen a fully-extended Obj2B pillar. It'll vanish too early, like before.

    The fix for this one is a lot simpler. Obj2B is special in that it's really big. Most objects are assumed to be no more than 32 pixels tall, but this one goes beyond that. The object makes the mistake of not signalling that it can surpass this height, by not setting bit 4 of render_flags, which enables an accurate check of the object's height using y_radius, rather than a hardcoded value of 32. Since Obj2B uses y_radius for its collision box, we know it's an accurate way of determining the object's height, so all we have to do is enable this 'accurate height check' flag, and the problem will be solved. Simply go to Obj2B_Init and change the ori.b of render_flags from '4' to '$14'.

    And that's Obj2B dealt with... now for Obj82. Unlike before, because you're meant to stand on it, this object's collision is as wide as the top of the pillar, meaning width_pixels is correct, and the object displays correctly. The real problem, however is the same as before: the object is tall, but it doesn't set the 'accurate height check' flag, meaning both the top and the bottom of the pillar vanish early. Like before, change the ori.b of render_flags from '4' to '$14'. But that's not all. There's a slight bit (4 pixels) of the bottom of the pillar that's outside the collision box, meaning it will vanish early. There's no sense in extending the collision box, because that part of the pillar is obviously not meant to be collided with, so we'll have to do some trickery.

    My idea is to extend y_radius to account for those 4 extra pixels, but subtract them when y_radius is passed to SolidObject. We want to be careful, though, to not interfere with the object's other subtype (an unused moving platform. Huh). Before the call to SolidObject, insert these instructions:

    Code (ASM):
    1.     tst.b   mapping_frame(a0)   ; Is this a pillar?
    2.     beq.s   .notPillar
    3.     subq.w  #2,d3           ; If so, shrink the y_radius
    4. .notPillar:
    Then go to Obj82_Properties, and change the '$30' to '$30+2'.

    Problems like these probably exist in a bunch of other objects, but none of them come to mind. The ARZ pillars have always been especially glaring, to me.
     
  13. Clownacy

    Clownacy

    Tech Members Tech Member
    775
    0
    16
    This bug's been noticed a bunch, but I've yet to see a fix for it. If you get a Game Over, and enter the Continue screen in HTZ, Tails' art appears corrupt. This is caused by HTZ's transforming cloud art being loaded over Tails' Continue art.

    Dynamic_HTZ is responsible for queueing the art to be transferred with QueueDMATransfer, which takes effect around the next frame. The problem here is, the art is queued, you die, get a Game Over, advance to the Continue screen, and then finally the art is loaded. What needs to be done is have the DMA queue reset to a blank slate when we enter the Continue screen. How do we do that? Well, Sonic Team already did half the work for us, since almost every other screen does this:

    Code (ASM):
    1. TwoPlayerResults:
    2.     ...
    3.  
    4.     jsrto   (PlaneMapToVRAM_H40).l, PlaneMapToVRAM_H40
    5.  
    6.     ; Clear DMA queue
    7.     clr.w   (VDP_Command_Buffer).w
    8.     move.l  #VDP_Command_Buffer,(VDP_Command_Buffer_Slot).w
    9.  
    10.     clr.b   (Level_started_flag).w
    ContinueScreen lacks this code, oddly, allowing this bug to manifest. Simply add the code, preferable after the clearRAM macro, and the bug should be fixed.

    EDIT: I guess it's about time I ask a question on a bug I don't understand: what the heck is up with HPZ's bridge post art? The top tile is clearly bugged, but that's actually how it is in its art file. Was the file compressed incorrectly, or something? There's no way that ugly thing, that winds up using the cycling waterfall palette, is intentional.
     
  14. redhotsonic

    redhotsonic

    Also known as RHS Tech Member
    1,584
    0
    0
    United Kingdom
    YouTuber

    Thought I replied to this a while ago. Oh well. Anyway, isn't it just that the very first 8x8 is just using the wrong palette line? All of the tiles in that object use palette line 3, when the first part is only meant to use palette line 0. I might be wrong though.
     
  15. Clownacy

    Clownacy

    Tech Members Tech Member
    775
    0
    16
    Ever notice how, during the rumbling as the ARZ boss' pillars rise, the background's scrolling goes screwy? Like, the background shakes, but the scrolling doesn't, so you can see the 'seams' between each scrollable row moving around?

    The cause is just what you think it is: the rumble isn't applied to the scrolling, namely Camera_BG_Y_pos. The fix is just as simple: In SwScrl_ARZ, above the 'tst.b (Screen_Shaking_Flag).w', insert a 'moveq #0,d3'. We're going to be using d3 to hold the rumble's Y-offset. Next, after the 'add.w d0,(Camera_Y_pos_copy).w', insert a 'move.w d0,d3'. We can't just add the rumble directly to Camera_BG_Y_pos, like the other values, because Camera_BG_Y_pos isn't 'reset' on each frame: it's constantly in a 'dirty' state, meaning constantly adding the rumble value to it per frame would actually make it scroll instead of shake. Instead, after 'move.w (Camera_BG_Y_pos).w,d1' add an 'add.w d3,d1', and, with that, the bug should be fixed.
     
  16. Clownacy

    Clownacy

    Tech Members Tech Member
    775
    0
    16
    In HTZ, the stakes that the ziplines are attached to are taller than 32 pixels, yet don't have their 'accurate y-height check' render_flags bit, or y_radius, set, so they disappear while still onscreen. To fix this, go to Obj1C_Radii, and change this

    Code (ASM):
    1.     dc.b   0    ; 4
    2.     dc.b   0    ; 5
    to this

    Code (ASM):
    1.     dc.b $28    ; 4
    2.     dc.b $28    ; 5
    Also, another pair of bugfixes: the bug detailed in the post above also exists in DEZ and MCZ's background-scrolling code. I just never noticed.

    We'll start with the hardest one - DEZ. Unlike ARZ and MCZ, SwScrl_DEZ handles the screen-shaking at the end of its code, instead of the start. We have to change that. Find something that looks like this:

    Code (ASM):
    1.     moveq   #0,d2
    2.     tst.b   (Screen_Shaking_Flag).w
    3.     beq.s   ++  ; rts
    4.     subq.w  #1,(DEZ_Shake_Timer).w
    5.     bpl.s   +
    6.     clr.b   (Screen_Shaking_Flag).w
    7. +
    8.     move.w  (Timer_frames).w,d0
    9.     andi.w  #$3F,d0
    10.     lea_    SwScrl_RippleData,a1
    11.     lea (a1,d0.w),a1
    12.     moveq   #0,d0
    13.     move.b  (a1)+,d0
    14.     add.w   d0,(Vscroll_Factor_FG).w
    15.     add.w   d0,(Vscroll_Factor_BG).w
    16.     add.w   d0,(Camera_Y_pos_copy).w
    17.     move.b  (a1)+,d2
    18.     add.w   d2,(Camera_X_pos_copy).w
    19. +
    and move it all so it's just after the 'move.w (Camera_BG_Y_pos).w,(Vscroll_Factor_BG).w' instruction.

    Now we just do like we did for ARZ's background, and insert a 'moveq #0,d3' after the 'moveq #0,d2', a 'move.w d0,d3' after the 'add.w d0,(Camera_Y_pos_copy).w', and a 'add.w d3,d1' after 'move.w (Camera_BG_Y_pos).w,d1'. Do the same for SwScrl_MCZ.
     
  17. redhotsonic

    redhotsonic

    Also known as RHS Tech Member
    1,584
    0
    0
    United Kingdom
    YouTuber
    Is there a reason why DEZ put it's shaking at the end instead of the beginning?
     
  18. Clownacy

    Clownacy

    Tech Members Tech Member
    775
    0
    16
    Not that I can tell. There's actually a trick to how the shake code works: Before the check for if the screen is set to shake, d2 is cleared. This is strange, because d2 is only used if the screen is shaking, so the clear should only be done if the screen is shaking, too. Furthermore, d2 is used to hold the x-shake value, but y-shake uses d0. x-shake has no reason to not use d0, too, and save a register clear. This turns out to be a leftover from SwScrl_HTZ, which does not suffer from the bugs ARZ, DEZ, and MCZ have. This is because d2 is never overwritten, and is used in the SwScrl's calculations, much like d3 is in my fixes. It's because of this requirement for d2 to hold the 'current x-shake value' that the screen-shaking code usually goes at the beginning. It's possible DEZ didn't receive its shake-feature until late in development, and the programmer just tacked the code onto the end for a quick result.
     
  19. Knucklez

    Knucklez

    Member Member
    Random question... I was wondering whether anyone has thought of or intends to make an official ReadySonic/S3Complete-esque hack of Sonic 2 with as many listed bugs fixed as possible so that we also have a "perfected" version of Sonic 2 available? You know, rather than only just the fixes being posted. Just wondering.
     
  20. MrMaestro

    MrMaestro

    Not your average member of a forum
    20
    0
    0
    Singapore
    Solving a Rubik's cube
    I agree, that would be very nice to have a sequel of ReadySonic, But we do already have "Saxman's Sonic-Boom Engine" to our disassembly collection, maybe something like that in a ReadySonic style fashion with the Elective fixes and the Permanent fixes and what not.