don't click here

Basic Questions & Answers thread

Discussion in 'Engineering & Reverse Engineering' started by Tweaker, May 29, 2008.

  1. Halved

    Halved

    What Member
    28
    3
    3
    I have inserted the 128x128 and 16x16 mappings for HPZ. But I don't know if those are the chunks/blocks. How can I compress them if that's the case?
    EDIT: Those were the block mappings, but I have never seen the chunk ones.
     
    Last edited: Feb 12, 2023
  2. Nik Pi

    Nik Pi

    Member
    501
    349
    63
    Kazakhstan
    Sonic 2: Archives
    Chunk is larger, block is less. Check out SW hacking guide, I don't remember correctly what exactly you need to compress. The one that I remember, is that compression is Kosinski
     
  3. Halved

    Halved

    What Member
    28
    3
    3
    WORKED! thanks. Now gotta fix the collision and the chunks. It currently uses the wrong chunks for BG and FG.
     
  4. Nik Pi

    Nik Pi

    Member
    501
    349
    63
    Kazakhstan
    Sonic 2: Archives
    You need to port layout and collisions at this moment. Also, you need to make a pointers for that, to make zone use a concrete files. Also notice, that you need to edit the list of primary and secondary collisions, because if it wasn't made- collisions will be work on level incorrectly
     
  5. Halved

    Halved

    What Member
    28
    3
    3
    I did it! All I had to do was to convert the S1 Level format to S2.
    Now time to bring back the badniks...
     
    Last edited: Feb 12, 2023
  6. MarkeyJester

    MarkeyJester

    Original, No substitute Resident Jester
    2,237
    490
    63
    Japan
    You're on the right tracks, and are understanding correctly as far as I'm concerned~

    The 0x380 mask is necessary for inner funnelling, and outer wrapping at the same time.

    Say you have an object at X position 0, and Y position 0x102. This object is touching the first chunk of the second row. Any Y position from 0x100 to 0x1FF needs to touch this same chunk. After shifting the Y position right by 1, you'll have 0x81. You do NOT want that 1 on the end to interfer with the X position, if you do not mask it out with the 0x380, then it will access the SECOND chunk of the second row, which is not what you want. That's the inner funnelling.

    The outer wrapping is simpler, objects are expected to be within the level's capable range, the height in our case is 0x7FF maximum. Going 0x800 or below, or going above 0 is outside the level. While unlikely, it is possible for an object to end up accidentally out of the level range, in this case the engine will start reading bytes from RAM addresses outside the layout RAM area, and treat those non-layout bytes as if they are layout bytes, and thus you have undefined behaviour. Since the behaviour is undefined it could at best do nothing, at worst cause a crash. You could put in extra instructions to check if the object is off-level, but it's quicker, and simpler to use the same AND mask for the inner funnelling to do the outer wrapping, and simply force the object to stay in the level and let nature takes its course.

    The AND mask of 0x380 is a right shift 1 of 0x700, meaning every 0x100 pixels is a separate chunk, but going 0x800 and beyond will wrap back to 0x000 again.
     
  7. E-122-Psi

    E-122-Psi

    Member
    2,477
    629
    93
    I've been trying to port bosses into different levels, but I can't seem to get one to load, I used EHZ's event routine for reference then applied it to the other level, however while the scroll lock, music and even the boss tiles all load, Eggman himself never shows up.

    Here is the code for reference:

    Code (Text):
    1.  
    2. LevEvents_001:
    3.     moveq    #0,d0
    4.     move.b    (Dynamic_Resize_Routine).w,d0
    5.     move.w    LevEvents_EGZ_Index(pc,d0.w),d0
    6.     jmp    LevEvents_EGZ_Index(pc,d0.w)
    7. ; ===========================================================================
    8. ; off_E66E:
    9. LevEvents_EGZ_Index:
    10.     dc.w LevEvents_EGZ_Routine1 - LevEvents_EGZ_Index    ; 0
    11.     dc.w LevEvents_EGZ_Routine2 - LevEvents_EGZ_Index    ; 2
    12.     dc.w LevEvents_EGZ_Routine3 - LevEvents_EGZ_Index    ; 4
    13.     dc.w LevEvents_EGZ_Routine4 - LevEvents_EGZ_Index    ; 6
    14. ; ===========================================================================
    15. LevEvents_EGZ_Routine1:
    16.     cmpi.w    #$F00,(Camera_X_pos).w
    17.     bcs.s    return_EGZ0
    18.     move.w    (Camera_X_pos).w,(Camera_Min_X_pos).w
    19.     move.w    (Camera_X_pos).w,(Tails_Min_X_pos).w
    20.     move.w    #$220,(Camera_Max_Y_pos).w
    21.     move.w    #$220,(Tails_Max_Y_pos).w
    22.     addq.b    #2,(Dynamic_Resize_Routine).w ; => LevEvents_EGZ2_Routine2
    23.  
    24. return_EGZ0:
    25.     rts
    26. ; ---------------------------------------------------------------------------
    27. loc_EGZ6A2:
    28.     move.w    #$10A0,(Camera_Max_X_pos).w
    29.     move.w    #$10A0,(Tails_Max_X_pos).w
    30.     rts
    31. ; ===========================================================================
    32. LevEvents_EGZ_Routine2:
    33.     cmpi.w    #$1060,(Camera_X_pos).w
    34.     bcs.s    return_EGZ1
    35.     move.w    #$1060,(Camera_Min_X_pos).w
    36.     move.w    #$10A0,(Camera_Max_X_pos).w
    37.     move.w    #$1060,(Tails_Min_X_pos).w
    38.     move.w    #$10A0,(Tails_Max_X_pos).w
    39.     addq.b    #2,(Dynamic_Resize_Routine).w ; => LevEvents_EGZ2_Routine3
    40.     move.w    #$F9,d0
    41.     bsr.w    JmpTo3_PlayMusic
    42.     clr.b    ($FFFFF73A).w
    43.     move.b    #2,(Current_Boss_ID).w
    44.     moveq    #$29,d0
    45.     bsr.w    JmpTo2_LoadPLC
    46.  
    47. return_EGZ1:
    48.     rts
    49. ; ===========================================================================
    50. ; loc_E6EE:
    51. LevEvents_EGZ_Routine3:
    52.     cmpi.w    #$218,(Camera_Y_pos).w
    53.     bcs.s    +
    54.     move.w    #$218,(Camera_Min_Y_pos).w
    55.     move.w    #$218,($FFFFEEFC).w
    56. +
    57.     addq.b    #1,($FFFFF73A).w
    58.     cmpi.b    #$5A,($FFFFF73A).w
    59.     bcs.s    return_EGZ2
    60.     bsr.w    JmpTo_SingleObjLoad
    61.     bne.s    +
    62.  
    63.     move.b    #$56,(a1) ; load obj56 (EHZ boss)
    64.     move.b    #$81,subtype(a1)
    65.     move.w    #$1140,x_pos(a1)
    66.     move.w    #$2C6,y_pos(a1)
    67. +
    68.     addq.b    #2,(Dynamic_Resize_Routine).w ; => LevEvents_EGZ2_Routine4
    69.     move.w    #$93,d0
    70.     bsr.w    JmpTo3_PlayMusic
    71.  
    72. return_EGZ2:
    73.     rts
    74. ; ===========================================================================
    75. ; loc_E738:
    76. LevEvents_EGZ_Routine4:
    77.     tst.b    ($FFFFF7A7).w
    78.     beq.s    return_EGZ3
    79.     move.w    (Camera_X_pos).w,(Camera_Min_X_pos).w
    80.     move.w    (Camera_Max_X_pos).w,(Tails_Max_X_pos).w
    81.     move.w    (Camera_X_pos).w,(Tails_Min_X_pos).w
    82.  
    83. return_EGZ3:
    84.     rts
    And here is a picture for the positioning, I think it's where it's meant to be:
    EGZ_Boss_Area_01.png

    Just to note, the level uses vertical looping, dunno if that might be a hinderance.
     
  8. Halved

    Halved

    What Member
    28
    3
    3
    So is there a way to fix SwScrl_HPZ? By fix I mean it just stops rendering the BG correctly after a point. Also, does the Simon Wai prototype just doesn't use make_art_tiles or am I missing something?
     
  9. Halved

    Halved

    What Member
    28
    3
    3
    I don't know much about this game, so don't take me seriously but maybe the boss is actually spawning elsewhere? I dunno. (EHZ boss code writes $29D0 to its x_pos, maybe change that and it could work.)
     
  10. E-122-Psi

    E-122-Psi

    Member
    2,477
    629
    93
    It's not the scroll code that's the problem. It's that the background layout only goes a short distance, you need to map it longer using SonLVL or another editor.

    It's a shorter distance because it's placed earlier in the new level.
     
  11. Before I can continue, I need to know how the water works in Labyrinth act 3. I said I wasn't going to do any intense coding work for this due to not having the knowledge, but against my better judgement, I think it'll be better in the long run to just remove the water from the rest of the act like it is at the beginning. Plus, it might open the door for a fun level design idea with the vertical wrapping. So, how would I change the water for act 3? Preferably to just disable it outright.
     
  12. Cokie

    Cokie

    C. Okie Member
    76
    22
    8
    Thanks . Wouldnt and the y with 0x700 first and then shift right right 1 make more intuitive sense ( although same effect ) since and.i 380 is involving a weird format of the layout instead of of the level we are dealing with ?

    are inner funneling and outer wrapping computer science ,computer engineering terms or specific to game development in assembly. I ask as i looked it up regarding comp sci , computer engineering and assembly programming and didn't see anything .

    I am guessing this ….

    move.w d3,d1 ; get x-pos. of object
    lsr.w #8,d1
    andi.w #$7F,d1

    gets the columns ( chunk ) of the row that the object is in . And the anding with 7F is to guard if the player goes outside the range of chunk 1-64 width wise by wrapping it if so?

    The extend word of d1 seems to always put 0s in the upper bye of d1 and never sign extend as the chunk number doesn't seem to ever be above 0x7F . Is this instructions purpose to always zero out the upper byte in d1 ( that is to never Sign Extend)… Because if d1 was >7F it would branch over this .

    Mind explaining the rest of the code here please?

    lea (v_lvllayout).w,a1
    move.b (a1,d0.w),d1 ; get 256x256 tile number
    beq.s .blanktile ; branch if 0
    bmi.s .specialtile ; branch if >$7F
    subq.b #1,d1
    ext.w d1
    ror.w #7,d1
    move.w d2,d0
    add.w d0,d0
    andi.w #$1E0,d0
    add.w d0,d1
    move.w d3,d0
    lsr.w #3,d0
    andi.w #$1E,d0
    add.w d0,d1
    .blanktile:
    movea.l d1,a1
    rts

    Realized how shuffled my last post was posted it in a hurry .

    Thank You So Much!
     
  13. Cokie

    Cokie

    C. Okie Member
    76
    22
    8
     
  14. MarkeyJester

    MarkeyJester

    Original, No substitute Resident Jester
    2,237
    490
    63
    Japan
    Speculation, their mentallity was on getting the chunk ID from the layout, so they wrap based on the layout address range, which was probably more intuitive sense as far as they were concerned. It's a matter of perspective, and wouldn't matter either way. You're looking too deep into this really...

    There's no official terms as far as I know, I chose terms which would make sense given the current circumstance of what the AND was used for.

    Correct :)

    Exactly that. There is a "moveq" you've ommitted from the code below which sets the entire of d1 to FFFFFFFF.

    The chunk ID is loaded to the lowest byte (for example chunk ID 01, so d1 = FFFFFF01). The ext.w clears the upper byte (as you gathered rightly, so d1 = FFFF0001).

    The chunk data itself is loaded into 68k RAM $0000, and can be accessed through address $FFFF0000 onwards. The combination of instructions (the moveq, the loading byte and only allowing for positive 00-7F, the ext.w) are setting up d1 to become a chunk address (along with the subq to make 01 access chunk 00, 02 access chunk 01, 03 access chunk 02, etc...). The ror rotates the lower byte around to the upper area making 0000 - 007F become 0000 - FE00. 0001 would become 0200, 0002 would become 0400, 0003 would become 0600. Every $200 bytes in chunk RAM is a chunk.

    Well now that you have the chunk's starting address calculated and stored in d1 right after that ror instruction. You now need to get the exact block Sonic is touching within the chunk. The next set of instructions are doing a similar calculation you just did to get the chunk ID from the layout. This time you're getting the block ID from the chunk, but this time instead of getting every $100 pixel positions = 1 chunk, you're getting every $10 pixels = 1 block.

    Every block ID in the chunk is 2 bytes big. So the X position is wrapped within the chunk range (within $100 pixels) but funnelled every block range ($10 pixels), and shifted where every $10 pixels becomes every 2 bytes, and for Y every $10 pixels becomes every $20 bytes (because there are $10 blocks in a chunk horizontally, 2 bytes each = $20 bytes per row).

    The address once it's setup correctly in d1, is copied to a1 where the block ID can be accessed directly.
     
    Last edited: Feb 17, 2023
  15. I hope I'm not coming across as a nagging ass, but I went over the entire hacking guide and didn't find anything to disable water in Sonic 1, so I've hit a complete roadblock now. If anyone cares to explain this to me, it would be greatly appreciated and allow me to continue production.
     
  16. Cokie

    Cokie

    C. Okie Member
    76
    22
    8
    MarkeyJester, when I am trying to understand things like I was relating to FindNearestTile , is there an exercise / process of questions I can ask myself to efficiently come to a conclusion (that is correct) ?

    Thank you for everything !
     
  17. Cokie

    Cokie

    C. Okie Member
    76
    22
    8
    So let me see if I understand Inter funnelling and wrapping ... Regarding to the position of the mask bit relative to the bit of the target value we are anding .

    Wrapping occurs WHEN: the mask bits that are ANDEd are less significant than the source bits

    And Occur On - Those bits of source that are more significant than the bits set in Mask .

    Example 0x300 & 0x500 = 0x100

    Inner Funneling Occurs
    When the mask bit are more significant than the source bit

    AND Occur On those lesser significant bits of the source

    Example : 0x80 & 0x8E = 0x80

    And the masked bits have to to have no gaps of off bit in them to work, due to the very nature of base 2. Or any base N system .
     
  18. Astro

    Astro

    Member
    This is less technical and more a general question so apologies if it doesn't fit here, but given its infamy and after not really finding too much documented here or elsewhere, is there a reason why Sonic 4 hacking never really took off? I've seen some fan games in the Mania engine that serve as a 'remake' of Sonic 4, but besides some basic art rips/digging around the files was there any real work done for Episode 1/2 to play around with the physics, dial back the boosters/bubbles, swap out the music and that sort of thing? Or is it too hard a format to crack/ the game's reputation meant there wasn't much motivation for people to look into it more?

    By comparison I've seen a ton of hacks/mods for lesser-received Sonic games like 06/Lost World/Forces, which I'd have figured were far harder to work out and in service of games people also weren't too fond of.
     
  19. Chimes

    Chimes

    The One SSG-EG Maniac Member
    870
    603
    93
    I believe it's because Sonic 4 has fallen in the same state that the rest of Sega's weird reboot phase has settled to (i.e. golden axe, altered beast, after burner, pengo), where the eyes that need it the most aren't even aware of it or hadn't thought "hmm, i can mod this". As you've stated, 06 later got mods and a remake, and Forces has had a good number of eyes on it.
    These are because the two games had quite the attention at launch and aftermath, whereas I couldn't find anyone else who bought 4 back whfn it came out. It's only recently someone looked at Sonic 4 and thought "I wonder if there's something in here?": the TCRF page is very recent.
     
    • Agree Agree x 1
    • Informative Informative x 1
    • List
  20. Okay, so I've been helping MDTravisYT restoring Sonic 1 objects to Sonic 2 Nick Arcade (as part of a S2NA > S1 conversion project), however we've hit a road block in LZ with THIS one platform object (also used in MZ) randomly causing chunk corruption. You can view the bug here. Can anyone find out what is causing it? (code here)