How to extend S2's Level Select menu

Discussion in 'Engineering & Reverse Engineering' started by Clownacy, Jan 21, 2014.

  1. Clownacy

    Clownacy

    Tech Member
    875
    137
    43
    As you may have seen with S3K or ColinC10's Sonic 1 and 2, the S2 Level Select can become really cramped, really fast. Wouldn't it be great if you could somehow extend the menu? To do this, several changes will have to be made to the Level Select's code. This does not require a total rewrite, so your own hacks can coexist with this one. I recommend that you are familiar with the Level Select's many tables, as you will be creating your own. (HINT: look here)

    This guide will focus on the S2 (rev 01) HG disassembly, other disassembles may see support in the future, but not yet.

    The result will be your average S2 Level Select with multiple pages, you toggle between the pages with the 'NEXT' and 'PREV' options. The switch is instantaneous (no fading, no loading screen) and supports numerous pages, the only limit I can think of is RAM, but I suppose dynamic loading can be implemented at a later time. That or you store your mappings uncompressed.


    Let's begin.


    Cartography
    First, open a plane map editor (I will be using PlaneEd), and edit your mappings/misc/Level Select.bin to feature a 'NEXT' option and, optionally, a 'PREV' option. The 'PREV' will simply loop from the first page to the last page. Also, remove the zeroes from the Sound Test selection, as they are redundant and are overwritten almost immediately. Doing so will save you a few bytes. For reference, this is my example:

    [​IMG]

    Take note of the location of your 'NEXT'. Mine is placed after Oil Ocean, and before Metropolis.



    Now, we shall create our second page, so list some new zones! Also, Sound Test must be present on all pages and it must be the last entry, it is currently hardcoded that way, sorry. Use the stock Level Select as a template: The area allocated to the emblem must be left untouched, as should the Sound Test.

    If this new page is your last (there are no pages after it), then you might not want to list a 'NEXT' option unless you want page looping. If it is, for example, the second out of three (there will be a following page), then you must list both 'NEXT' and 'PREV'.

    To give you an idea of what I mean, here is another example (second out of three):

    [​IMG]

    Once done, save as mappings/misc/Level Select2.bin (Next page will be 3, then 4, etc.)

    Repeat the process for each new page you want.


    Setting Up Constants
    Before we touch any ASM, now is a good time to define the 1 byte large "LevSel_Page" RAM address constant. See you then.

    Now, wherever you feel it's appropriate, define the "MaxPageNum" constant. This constant will be used in calculations and checks regarding the maximum number of pages in your Level Select, such as those involved in page looping. Remember however, that zero counts as a number, so if you have three pages, the constant must be defined as 2.

    In the next section, Mappings Decompression, we'll be setting up the decompression of the mappings you made earlier. To make this easier, we'll define some constants, otherwise changing the decompression locations would require changing several lines, doing this requires only the one.

    Again, where appropriate, define the following constants:

    • Page1_RAM
    • Page2_RAM
    • Page3_RAM

    The number of constants need to match the number of pages you plan to have. Adjust accordingly.

    By default, the first page occupies Chunk_Table, while the icons' mappings occupy Chunk_Table+$8C0, we're going to rearrange this a bit to fit with our new pages.

    Go to MenuScreen_LevelSelect and change

    Code (ASM):
    1.     lea (Chunk_Table+$8C0).l,a1
    to

    Code (ASM):
    1.     lea (PageX_RAM+$8C0).l,a1
    With X being the number of your last page. Go to LevelSelect_DrawIcon and do the same thing.

    Now we'll make our constants. Do so in this manner:

    Code (ASM):
    1. Page1_RAM =         Chunk_Table
    2. Page2_RAM =         Page1_RAM+$8C0
    3. Page3_RAM =         Page2_RAM+$8C0

    You see, a decompressed full-screen mappings file always takes up 2240 bytes. This is 8C0 in hex. Doing this makes sure that no space is wasted, allowing more pages without encountering space issues.


    Mappings Decompression
    Inside your s2.asm, go to MapEng_LevSel and, beneath it, add entries for your new mappings.

    Code (ASM):
    1. ; level select page 1 screen mappings (Enigma compressed)
    2. ; byte_9ADE:
    3. MapEng_LevSel:      BINCLUDE "mappings/misc/Level Select.bin"
    4.  
    5. ; level select page 2 screen mappings (Enigma compressed)
    6.  
    7. MapEng_LevSel2:     BINCLUDE "mappings/misc/Level Select2.bin"
    8.  
    9. ; level select page 3 screen mappings (Enigma compressed)
    10.  
    11. MapEng_LevSel3:     BINCLUDE "mappings/misc/Level Select3.bin"


    With that done, go to MenuScreen_LevelSelect and add these two lines below the label:

    Code (ASM):
    1.     clr.b   (LevSel_Page).w
    2.     clr.w   (Level_select_zone).w


    Then replace the two "(Chunk_Table).l"s with "(Page1_RAM).l". After that you should see this (slighted formatted by myself):

    Code (ASM):
    1. MenuScreen_LevelSelect:
    2.     lea (Page1_RAM).l,a1
    3.     lea (MapEng_LevSel).l,a0    ; 2 bytes per 8x8 tile, compressed
    4.     move.w  #make_art_tile(ArtTile_VRAM_Start,0,0),d0
    5.     bsr.w   EniDec
    6.  
    7.     lea (Page1_RAM).l,a1
    8.     move.l  #vdpComm(VRAM_Plane_A_Name_Table,VRAM,WRITE),d0
    9.     moveq   #$27,d1
    10.     moveq   #$1B,d2 ; 40x28 = whole screen
    11.     bsr.w   JmpTo_PlaneMapToVRAM    ; display patterns


    The upper section of code decompresses the Enigma-compressed mappings to a RAM address, the second reads the decompressed data and applies the visuals. Using this we will load our new mappings. Here we have a choice: Do we make the new mappings decompress pre-fade in or post-fade in? Pre-fade will increase the duration of the loading screen (time the screen stays black), but loading it post-fade will run the risk of someone with speedy fingers loading the page before its mappings have fully decompressed.

    Pre-fade
    Copy the upper section of the code found above and paste it after
    Code (ASM):
    1.     lea (MapEng_LevSel).l,a0    ; 2 bytes per 8x8 tile, compressed
    2.     lea (Page1_RAM).l,a1
    3.     move.w  #make_art_tile(ArtTile_VRAM_Start,0,0),d0
    4.     bsr.w   EniDec

    Post-fade
    Copy the upper section of the code found above and paste it before the LevelSelect_Main label
    Code (ASM):
    1. bsr.w   Pal_FadeTo
    2.                 ; <-------- HERE
    3. ;loc_93AC:
    4. LevelSelect_Main:   ; routine running during level select


    Then make some adjustments to make it load your second page by changing the "MapEng_LevSel" to "MapEng_LevSel2", and adding to the "Page1_RAM". Do so again for any other pages you have, using MapEng_LevSelX and using other available RAM spaces. The result should look like this:

    Code (ASM):
    1.     lea (Page2_RAM).l,a1
    2.     lea (MapEng_LevSel2).l,a0   ; 2 bytes per 8x8 tile, compressed
    3.     move.w  #make_art_tile(ArtTile_VRAM_Start,0,0),d0
    4.     bsr.w   EniDec
    5.    
    6.     lea (Page3_RAM).l,a1
    7.     lea (MapEng_LevSel3).l,a0   ; 2 bytes per 8x8 tile, compressed
    8.     move.w  #make_art_tile(ArtTile_VRAM_Start,0,0),d0
    9.     bsr.w   EniDec


    Table Expansion #1 - LevelSelect_Order
    Go to LevelSelect_Order. Remember where you placed your 'NEXT'? That's going to come into play now. Insert "dc.w $3000" into the list to match where the 'NEXT' is on your page 1 mappings. If you're following my example, it should look like this:

    Code (ASM):
    1. LevelSelect_Order:
    2.     dc.w    emerald_hill_zone_act_1
    3.     dc.w    emerald_hill_zone_act_2     ; 1
    4.     dc.w    chemical_plant_zone_act_1   ; 2
    5.     dc.w    chemical_plant_zone_act_2   ; 3
    6.     dc.w    aquatic_ruin_zone_act_1     ; 4
    7.     dc.w    aquatic_ruin_zone_act_2     ; 5
    8.     dc.w    casino_night_zone_act_1     ; 6
    9.     dc.w    casino_night_zone_act_2     ; 7
    10.     dc.w    hill_top_zone_act_1     ; 8
    11.     dc.w    hill_top_zone_act_2     ; 9
    12.     dc.w    mystic_cave_zone_act_1      ; 10
    13.     dc.w    mystic_cave_zone_act_2      ; 11
    14.     dc.w    oil_ocean_zone_act_1        ; 12
    15.     dc.w    oil_ocean_zone_act_2        ; 13
    16.     dc.w    $3000               ; 14 - next page
    17.     dc.w    metropolis_zone_act_1       ; 15
    18.     dc.w    metropolis_zone_act_2       ; 16
    19.     dc.w    metropolis_zone_act_3       ; 17
    20.     dc.w    sky_chase_zone_act_1        ; 18
    21.     dc.w    wing_fortress_zone_act_1    ; 19
    22.     dc.w    death_egg_zone_act_1        ; 20
    23.     dc.w    $4000               ; 21 - special stage
    24.     dc.w    $FFFF               ; 22 - sound test


    We will now create our new pages' tables. In the place of 'PREV' you must place "dc.w $5000". My second page's table looks like this:

    Code (ASM):
    1. LevelSelect_Order2:
    2.     dc.w    emerald_hill_zone_act_1
    3.     dc.w    emerald_hill_zone_act_2     ; 1
    4.     dc.w    death_egg_zone_act_1        ; 2
    5.     dc.w    chemical_plant_zone_act_2   ; 3
    6.     dc.w    aquatic_ruin_zone_act_1     ; 4
    7.     dc.w    aquatic_ruin_zone_act_2     ; 5
    8.     dc.w    casino_night_zone_act_1     ; 6
    9.     dc.w    casino_night_zone_act_2     ; 7
    10.     dc.w    hill_top_zone_act_1     ; 8
    11.     dc.w    hill_top_zone_act_2     ; 9
    12.     dc.w    mystic_cave_zone_act_1      ; 10
    13.     dc.w    mystic_cave_zone_act_2      ; 11
    14.     dc.w    oil_ocean_zone_act_1        ; 12
    15.     dc.w    oil_ocean_zone_act_2        ; 13
    16.     dc.w    $3000               ; 14 - next page
    17.     dc.w    $5000               ; 15 - prev page
    18.     dc.w    metropolis_zone_act_1       ; 16
    19.     dc.w    metropolis_zone_act_2       ; 17
    20.     dc.w    metropolis_zone_act_3       ; 18
    21.     dc.w    sky_chase_zone_act_1        ; 19
    22.     dc.w    wing_fortress_zone_act_1    ; 20
    23.     dc.w    death_egg_zone_act_1        ; 21
    24.     dc.w    $4000               ; 22 - special stage
    25.     dc.w    $FFFF               ; 23 - sound test


    Once all of your tables are complete, we will bundle them all together with an index. Above LevelSelect_Order, paste this:

    Code (ASM):
    1. LevelSelect_OrderIndex: offsetTable
    2.         offsetTableEntry.w LevelSelect_Order    ; 0
    3.         offsetTableEntry.w LevelSelect_Order2   ; 1
    4.         offsetTableEntry.w LevelSelect_Order3   ; 2

    For each page (and, by effect, table) you have, make an entry in the offset table. They must be in order!

    We'll be following this template for several other tables:

    • LevelSelect_SwitchTable
    • LevSel_IconTable
    • LevSel_MarkTable

    Remember to place EVENs at the end of any tables and indexes whose data is byte-sized to avoid misaligned instructions


    Table Expansion #2 - LevelSelect_SwitchTable
    Do as you did with LevelSelect_Order: A table for each page, add an index (same format as the one used above, uses word-sized offsets) Note, if you placed your 'NEXT' before the last entry of the table, then all references to entries after it will be shifted by it. So:

    Code (ASM):
    1. LevelSelect_SwitchTable:
    2.     dc.b $E
    3.     dc.b $F     ; 1
    4.     dc.b $11    ; 2
    5.     dc.b $11    ; 3
    6.     dc.b $12    ; 4
    7.     dc.b $12    ; 5
    8.     dc.b $13    ; 6
    9.     dc.b $13    ; 7
    10.     dc.b $14    ; 8
    11.     dc.b $14    ; 9
    12.     dc.b $15    ; 10
    13.     dc.b $15    ; 11
    14.     dc.b $C     ; 12
    15.     dc.b $D     ; 13
    16.     dc.b 0      ; 14
    17.     dc.b 1      ; 15
    18.     dc.b 1      ; 16
    19.     dc.b 2      ; 17
    20.     dc.b 4      ; 18
    21.     dc.b 6      ; 19
    22.     dc.b 8      ; 20
    23.     dc.b $A     ; 21

    Becomes:

    Code (ASM):
    1. LevelSelect_SwitchTable:
    2.     dc.b $F
    3.     dc.b $10    ; 1
    4.     dc.b $12    ; 2
    5.     dc.b $12    ; 3
    6.     dc.b $13    ; 4
    7.     dc.b $13    ; 5
    8.     dc.b $14    ; 6
    9.     dc.b $14    ; 7
    10.     dc.b $15    ; 8
    11.     dc.b $15    ; 9
    12.     dc.b $16    ; 10
    13.     dc.b $16    ; 11
    14.     dc.b $C     ; 12
    15.     dc.b $D     ; 13
    16.     dc.b $E     ; 14 NEXT
    17.     dc.b 0      ; 15
    18.     dc.b 1      ; 16
    19.     dc.b 1      ; 17
    20.     dc.b 2      ; 18
    21.     dc.b 4      ; 19
    22.     dc.b 6      ; 20
    23.     dc.b 8      ; 21
    24.     dc.b $A     ; 22


    Table Expansion #3 - LevSel_IconTable
    Not much to say here, I just use the Sound Test icon for 'NEXT' and 'PREV'.


    Table Expansion #4 - LevSel_MarkTable
    Do not give this one an index! Again, not much to say except that I hope you know what you're doing.


    Table Expansion - For Reference
    Here are my tables and indexes:

    LevelSelect_Order
    Code (ASM):
    1. LevelSelect_OrderIndex: offsetTable
    2.         offsetTableEntry.w LevelSelect_Order    ; 0
    3.         offsetTableEntry.w LevelSelect_Order2   ; 1
    4.         offsetTableEntry.w LevelSelect_Order3   ; 2
    5.  
    6. ;Misc_9454:
    7. LevelSelect_Order:
    8.     dc.w    emerald_hill_zone_act_1
    9.     dc.w    emerald_hill_zone_act_2     ; 1
    10.     dc.w    chemical_plant_zone_act_1   ; 2
    11.     dc.w    chemical_plant_zone_act_2   ; 3
    12.     dc.w    aquatic_ruin_zone_act_1     ; 4
    13.     dc.w    aquatic_ruin_zone_act_2     ; 5
    14.     dc.w    casino_night_zone_act_1     ; 6
    15.     dc.w    casino_night_zone_act_2     ; 7
    16.     dc.w    hill_top_zone_act_1     ; 8
    17.     dc.w    hill_top_zone_act_2     ; 9
    18.     dc.w    mystic_cave_zone_act_1      ; 10
    19.     dc.w    mystic_cave_zone_act_2      ; 11
    20.     dc.w    oil_ocean_zone_act_1        ; 12
    21.     dc.w    oil_ocean_zone_act_2        ; 13
    22.     dc.w    $3000               ; 14 - next page
    23.     dc.w    metropolis_zone_act_1       ; 15
    24.     dc.w    metropolis_zone_act_2       ; 16
    25.     dc.w    metropolis_zone_act_3       ; 17
    26.     dc.w    sky_chase_zone_act_1        ; 18
    27.     dc.w    wing_fortress_zone_act_1    ; 19
    28.     dc.w    death_egg_zone_act_1        ; 20
    29.     dc.w    $4000               ; 21 - special stage
    30.     dc.w    $FFFF               ; 22 - sound test
    31.  
    32. LevelSelect_Order2:
    33.     dc.w    emerald_hill_zone_act_1
    34.     dc.w    emerald_hill_zone_act_2     ; 1
    35.     dc.w    death_egg_zone_act_1        ; 2
    36.     dc.w    chemical_plant_zone_act_2   ; 3
    37.     dc.w    aquatic_ruin_zone_act_1     ; 4
    38.     dc.w    aquatic_ruin_zone_act_2     ; 5
    39.     dc.w    casino_night_zone_act_1     ; 6
    40.     dc.w    casino_night_zone_act_2     ; 7
    41.     dc.w    hill_top_zone_act_1     ; 8
    42.     dc.w    hill_top_zone_act_2     ; 9
    43.     dc.w    mystic_cave_zone_act_1      ; 10
    44.     dc.w    mystic_cave_zone_act_2      ; 11
    45.     dc.w    oil_ocean_zone_act_1        ; 12
    46.     dc.w    oil_ocean_zone_act_2        ; 13
    47.     dc.w    $3000               ; 14 - next page
    48.     dc.w    $5000               ; 15 - prev page
    49.     dc.w    metropolis_zone_act_1       ; 16
    50.     dc.w    metropolis_zone_act_2       ; 17
    51.     dc.w    metropolis_zone_act_3       ; 18
    52.     dc.w    sky_chase_zone_act_1        ; 19
    53.     dc.w    wing_fortress_zone_act_1    ; 20
    54.     dc.w    death_egg_zone_act_1        ; 21
    55.     dc.w    $4000               ; 22 - special stage
    56.     dc.w    $FFFF               ; 23 - sound test
    57.    
    58. LevelSelect_Order3:
    59.     dc.w    emerald_hill_zone_act_1
    60.     dc.w    emerald_hill_zone_act_2     ; 1
    61.     dc.w    sky_chase_zone_act_1    ; 2
    62.     dc.w    chemical_plant_zone_act_2   ; 3
    63.     dc.w    aquatic_ruin_zone_act_1     ; 4
    64.     dc.w    aquatic_ruin_zone_act_2     ; 5
    65.     dc.w    casino_night_zone_act_1     ; 6
    66.     dc.w    casino_night_zone_act_2     ; 7
    67.     dc.w    hill_top_zone_act_1     ; 8
    68.     dc.w    hill_top_zone_act_2     ; 9
    69.     dc.w    mystic_cave_zone_act_1      ; 10
    70.     dc.w    mystic_cave_zone_act_2      ; 11
    71.     dc.w    oil_ocean_zone_act_1        ; 12
    72.     dc.w    oil_ocean_zone_act_2        ; 13
    73.     dc.w    $5000               ; 14 - prev page
    74.     dc.w    metropolis_zone_act_1       ; 15
    75.     dc.w    metropolis_zone_act_2       ; 16
    76.     dc.w    metropolis_zone_act_3       ; 17
    77.     dc.w    sky_chase_zone_act_1        ; 18
    78.     dc.w    wing_fortress_zone_act_1    ; 19
    79.     dc.w    death_egg_zone_act_1        ; 20
    80.     dc.w    $4000               ; 21 - special stage
    81.     dc.w    $FFFF               ; 22 - sound test

    LevelSelect_SwitchTable
    Code (ASM):
    1. LevelSelect_SwitchTableIndex:   offsetTable
    2.         offsetTableEntry.w LevelSelect_SwitchTable  ; 0
    3.         offsetTableEntry.w LevelSelect_SwitchTable2 ; 1
    4.         offsetTableEntry.w LevelSelect_SwitchTable3 ; 2
    5.  
    6. ;byte_95A2:
    7. LevelSelect_SwitchTable:
    8.     dc.b $F
    9.     dc.b $10    ; 1
    10.     dc.b $12    ; 2
    11.     dc.b $12    ; 3
    12.     dc.b $13    ; 4
    13.     dc.b $13    ; 5
    14.     dc.b $14    ; 6
    15.     dc.b $14    ; 7
    16.     dc.b $15    ; 8
    17.     dc.b $15    ; 9
    18.     dc.b $16    ; 10
    19.     dc.b $16    ; 11
    20.     dc.b $C     ; 12
    21.     dc.b $D     ; 13
    22.     dc.b $E     ; 14
    23.     dc.b 0      ; 15
    24.     dc.b 1      ; 16
    25.     dc.b 1      ; 17
    26.     dc.b 2      ; 18
    27.     dc.b 4      ; 19
    28.     dc.b 6      ; 20
    29.     dc.b 8      ; 21
    30.     dc.b $A     ; 22
    31.  
    32. LevelSelect_SwitchTable2:
    33.     dc.b $10
    34.     dc.b $11    ; 1
    35.     dc.b $13    ; 2
    36.     dc.b $13    ; 3
    37.     dc.b $14    ; 4
    38.     dc.b $14    ; 5
    39.     dc.b $15    ; 6
    40.     dc.b $15    ; 7
    41.     dc.b $16    ; 8
    42.     dc.b $16    ; 9
    43.     dc.b $17    ; 10
    44.     dc.b $17    ; 11
    45.     dc.b $C     ; 12
    46.     dc.b $D     ; 13
    47.     dc.b $E     ; 14
    48.     dc.b $F     ; 15
    49.     dc.b 0      ; 16
    50.     dc.b 1      ; 17
    51.     dc.b 1      ; 18
    52.     dc.b 2      ; 19
    53.     dc.b 4      ; 20
    54.     dc.b 6      ; 21
    55.     dc.b 8      ; 22
    56.     dc.b $A     ; 23
    57.  
    58. LevelSelect_SwitchTable3:
    59.     dc.b $F
    60.     dc.b $10    ; 1
    61.     dc.b $12    ; 2
    62.     dc.b $12    ; 3
    63.     dc.b $13    ; 4
    64.     dc.b $13    ; 5
    65.     dc.b $14    ; 6
    66.     dc.b $14    ; 7
    67.     dc.b $15    ; 8
    68.     dc.b $15    ; 9
    69.     dc.b $16    ; 10
    70.     dc.b $16    ; 11
    71.     dc.b $C     ; 12
    72.     dc.b $D     ; 13
    73.     dc.b $E     ; 14
    74.     dc.b 0      ; 15
    75.     dc.b 1      ; 16
    76.     dc.b 1      ; 17
    77.     dc.b 2      ; 18
    78.     dc.b 4      ; 19
    79.     dc.b 6      ; 20
    80.     dc.b 8      ; 21
    81.     dc.b $A     ; 22
    82.     even

    LevSel_IconTable
    Code (ASM):
    1. LevSel_IconTableIndex:  offsetTable
    2.         offsetTableEntry.w LevSel_IconTable ; 0
    3.         offsetTableEntry.w LevSel_IconTable2    ; 1
    4.         offsetTableEntry.w LevSel_IconTable3    ; 2
    5.  
    6. ;byte_96D8
    7. LevSel_IconTable:
    8.     dc.b   0,0  ;0  EHZ
    9.     dc.b   7,7  ;2  CPZ
    10.     dc.b   8,8  ;4  ARZ
    11.     dc.b   6,6  ;6  CNZ
    12.     dc.b   2,2  ;8  HTZ
    13.     dc.b   5,5  ;$A MCZ
    14.     dc.b   4,4  ;$C OOZ
    15.     dc.b  $E    ;$E Next Page
    16.     dc.b   1,1,1    ;$F MTZ
    17.     dc.b   9    ;$12    SCZ
    18.     dc.b  $A    ;$13    WFZ
    19.     dc.b  $B    ;$14    DEZ
    20.     dc.b  $C    ;$15    Special Stage
    21.     dc.b  $E    ;$16    Sound Test
    22.  
    23. LevSel_IconTable2:
    24.     dc.b   0,0  ;0  EHZ
    25.     dc.b  $B,$B ;2  DEZ
    26.     dc.b   8,8  ;4  ARZ
    27.     dc.b   6,6  ;6  CNZ
    28.     dc.b   2,2  ;8  HTZ
    29.     dc.b   5,5  ;$A MCZ
    30.     dc.b   4,4  ;$C OOZ
    31.     dc.b  $E    ;$E Next Page
    32.     dc.b  $E    ;$F Prev Page
    33.     dc.b   1,1,1    ;$10    MTZ
    34.     dc.b   9    ;$13    SCZ
    35.     dc.b  $A    ;$14    WFZ
    36.     dc.b  $B    ;$15    DEZ
    37.     dc.b  $C    ;$16    Special Stage
    38.     dc.b  $E    ;$17    Sound Test
    39.  
    40. LevSel_IconTable3:
    41.     dc.b   0,0  ;0  EHZ
    42.     dc.b   9,9  ;2  SCZ
    43.     dc.b   8,8  ;4  ARZ
    44.     dc.b   6,6  ;6  CNZ
    45.     dc.b   2,2  ;8  HTZ
    46.     dc.b   5,5  ;$A MCZ
    47.     dc.b   4,4  ;$C OOZ
    48.     dc.b  $E    ;$E Prev Page
    49.     dc.b   1,1,1    ;$F MTZ
    50.     dc.b   9    ;$12    SCZ
    51.     dc.b  $A    ;$13    WFZ
    52.     dc.b  $B    ;$14    DEZ
    53.     dc.b  $C    ;$15    Special Stage
    54.     dc.b  $E    ;$16    Sound Test
    55.     even

    LevSel_MarkTable
    Code (ASM):
    1. ;byte_96EE:
    2. LevSel_MarkTable:   ; 4 bytes per level select entry
    3. ; line primary, 2*column ($E fields), line secondary, 2*column secondary (1 field)
    4.     dc.b   3,  6,  3,$24    ;0
    5.     dc.b   3,  6,  4,$24
    6.     dc.b   6,  6,  6,$24
    7.     dc.b   6,  6,  7,$24
    8.     dc.b   9,  6,  9,$24    ;4
    9.     dc.b   9,  6, $A,$24
    10.     dc.b  $C,  6, $C,$24
    11.     dc.b  $C,  6, $D,$24
    12.     dc.b  $F,  6, $F,$24    ;8
    13.     dc.b  $F,  6,$10,$24
    14.     dc.b $12,  6,$12,$24
    15.     dc.b $12,  6,$13,$24
    16.     dc.b $15,  6,$15,$24    ;$C
    17.     dc.b $15,  6,$16,$24
    18.     dc.b $18,  6,  0,  0
    19. ; --- second column ---
    20.     dc.b   3,$2C,  3,$48
    21.     dc.b   3,$2C,  4,$48    ;$10
    22.     dc.b   3,$2C,  5,$48
    23.     dc.b   6,$2C,  0,  0
    24.     dc.b   9,$2C,  0,  0
    25.     dc.b  $C,$2C,  0,  0    ;$14
    26.     dc.b  $F,$2C,  0,  0
    27.     dc.b $12,$2C,$12,$48
    28.  
    29. LevSel_MarkTable2:  ; 4 bytes per level select entry
    30. ; line primary, 2*column ($E fields), line secondary, 2*column secondary (1 field)
    31.     dc.b   3,  6,  3,$24    ;0
    32.     dc.b   3,  6,  4,$24
    33.     dc.b   6,  6,  6,$24
    34.     dc.b   6,  6,  7,$24
    35.     dc.b   9,  6,  9,$24    ;4
    36.     dc.b   9,  6, $A,$24
    37.     dc.b  $C,  6, $C,$24
    38.     dc.b  $C,  6, $D,$24
    39.     dc.b  $F,  6, $F,$24    ;8
    40.     dc.b  $F,  6,$10,$24
    41.     dc.b $12,  6,$12,$24
    42.     dc.b $12,  6,$13,$24
    43.     dc.b $15,  6,$15,$24    ;$C
    44.     dc.b $15,  6,$16,$24
    45.     dc.b $18,  6,  0,  0
    46.     dc.b $1A,  6,  0,  0
    47. ; --- second column ---
    48.     dc.b   3,$2C,  3,$48    ;$10
    49.     dc.b   3,$2C,  4,$48
    50.     dc.b   3,$2C,  5,$48
    51.     dc.b   6,$2C,  0,  0
    52.     dc.b   9,$2C,  0,  0    ;$14
    53.     dc.b  $C,$2C,  0,  0
    54.     dc.b  $F,$2C,  0,  0
    55.     dc.b $12,$2C,$12,$48
    56.  
    57. LevSel_MarkTable3:  ; 4 bytes per level select entry
    58. ; line primary, 2*column ($E fields), line secondary, 2*column secondary (1 field)
    59.     dc.b   3,  6,  3,$24    ;0
    60.     dc.b   3,  6,  4,$24
    61.     dc.b   6,  6,  6,$24
    62.     dc.b   6,  6,  7,$24
    63.     dc.b   9,  6,  9,$24    ;4
    64.     dc.b   9,  6, $A,$24
    65.     dc.b  $C,  6, $C,$24
    66.     dc.b  $C,  6, $D,$24
    67.     dc.b  $F,  6, $F,$24    ;8
    68.     dc.b  $F,  6,$10,$24
    69.     dc.b $12,  6,$12,$24
    70.     dc.b $12,  6,$13,$24
    71.     dc.b $15,  6,$15,$24    ;$C
    72.     dc.b $15,  6,$16,$24
    73.     dc.b $1A,  6,  0,  0
    74. ; --- second column ---
    75.     dc.b   3,$2C,  3,$48
    76.     dc.b   3,$2C,  4,$48    ;$10
    77.     dc.b   3,$2C,  5,$48
    78.     dc.b   6,$2C,  0,  0
    79.     dc.b   9,$2C,  0,  0
    80.     dc.b  $C,$2C,  0,  0    ;$14
    81.     dc.b  $F,$2C,  0,  0
    82.     dc.b $12,$2C,$12,$48
    83.     even


    Dynamic Data #1 - LevelSelect_Order
    Now we make use of those tables!

    Starting with LevelSelect_Order, go to LevelSelect_PressStart, you should see this:

    Code (ASM):
    1. LevelSelect_PressStart:
    2.     move.w  (Level_select_zone).w,d0
    3.     add.w   d0,d0
    4.     move.w  LevelSelect_Order(pc,d0.w),d0
    5.     bmi.w   LevelSelect_Return  ; sound test
    6.     cmpi.w  #$4000,d0
    7.     bne.s   LevelSelect_StartZone


    Replace

    Code (ASM):
    1.     move.w  (Level_select_zone).w,d0
    2.     add.w   d0,d0
    3.     move.w  LevelSelect_Order(pc,d0.w),d0


    With

    Code (ASM):
    1.     moveq   #0,d0
    2.     move.b  (LevSel_Page).w,d0
    3.     add.b   d0,d0
    4.     move.w  LevelSelect_OrderIndex(pc,d0.w),d0
    5.     lea LevelSelect_OrderIndex(pc,d0.w),a0
    6.     move.w  (Level_select_zone).w,d0
    7.     add.w   d0,d0
    8.     movea.w (a0,d0.w),d0


    LevelSelect_PressStart will now dynamically load LevelSelect_Order/2/3 depending on what page you're on.


    Dynamic Data #2 - LevelSelect_SwitchTable
    Go to LevSelControls_SwitchSide, you should see this:

    Code (ASM):
    1. LevSelControls_SwitchSide:  ; not in soundtest, not up/down pressed
    2.     move.b  (Ctrl_1_Press).w,d1
    3.     andi.b  #button_left_mask|button_right_mask,d1
    4.     beq.s   +               ; no direction key pressed
    5.     move.w  (Level_select_zone).w,d0    ; left or right pressed
    6.     move.b  LevelSelect_SwitchTable(pc,d0.w),d0 ; set selected zone according to table
    7.     move.w  d0,(Level_select_zone).w
    8. +
    9.     rts


    Replace

    Code (ASM):
    1.     move.w  (Level_select_zone).w,d0    ; left or right pressed
    2.     move.b  LevelSelect_SwitchTable(pc,d0.w),d0 ; set selected zone according to table


    With

    Code (ASM):
    1.     move.b  (LevSel_Page).w,d0
    2.     add.b   d0,d0
    3.     move.w  LevelSelect_SwitchTableIndex(pc,d0.w),d0
    4.     lea LevelSelect_SwitchTableIndex(pc,d0.w),a0
    5.     move.w  (Level_select_zone).w,d0    ; left or right pressed
    6.     move.b  (a0,d0.w),d0 ; set selected zone according to table


    LevSelControls_SwitchSide will now dynamically load LevelSelect_SwitchTable/2/3 depending on what page you're on.


    Dynamic Data #3 - LevSel_IconTable
    Go to LevelSelect_DrawIcon, you should see this:

    Code (ASM):
    1. LevelSelect_DrawIcon:
    2.     move.w  (Level_select_zone).w,d0
    3.     lea (LevSel_IconTable).l,a3
    4.     lea (a3,d0.w),a3
    5.     lea (Chunk_Table+$8C0).l,a1
    6.     moveq   #0,d0
    7.     move.b  (a3),d0
    8.     lsl.w   #3,d0
    9.     move.w  d0,d1
    10.     add.w   d0,d0
    11.     add.w   d1,d0
    12. ...


    Replace

    Code (ASM):
    1.     move.w  (Level_select_zone).w,d0
    2.     lea (LevSel_IconTable).l,a3


    With

    Code (ASM):
    1.     moveq   #0,d0
    2.     move.b  (LevSel_Page).w,d0
    3.     add.b   d0,d0
    4.     move.w  LevSel_IconTableIndex(pc,d0.w),d0
    5.     lea LevSel_IconTableIndex(pc,d0.w),a3
    6.     move.w  (Level_select_zone).w,d0


    LevelSelect_DrawIcon will now dynamically load LevSel_IconTable/2/3 depending on what page you're on.


    Dynamic Data #4 - LevSel_MarkTable
    This will be quite a bit different from the other three.

    Go to LevelSelect_MarkFields, you should find this:
    Code (ASM):
    1. LevelSelect_MarkFields:
    2.     lea (Chunk_Table).l,a4
    3.     lea (LevSel_MarkTable).l,a5
    4.     lea (VDP_data_port).l,a6
    5.     moveq   #0,d0
    6.     move.w  (Level_select_zone).w,d0
    7. ...


    Replace

    Code (ASM):
    1.     lea (Chunk_Table).l,a4
    2.     lea (LevSel_MarkTable).l,a5


    With

    Code (ASM):
    1.     moveq   #0,d0
    2.     move.b  (LevSel_Page).w,d0
    3.     add.b   d0,d0
    4.     move.w  LevelSelect_MarkFieldsSubIndex(pc,d0.w),d0
    5.     jsr LevelSelect_MarkFieldsSubIndex(pc,d0.w)


    And above the LevelSelect_MarkFields label, add this:

    Code (ASM):
    1. LevelSelect_MarkFieldsSubIndex: offsetTable
    2.     offsetTableEntry.w LevelSelect_MarkFieldsSub1   ; 0
    3.     offsetTableEntry.w LevelSelect_MarkFieldsSub2   ; 1
    4.     offsetTableEntry.w LevelSelect_MarkFieldsSub3   ; 2
    5.  
    6. LevelSelect_MarkFieldsSub1:
    7.     lea (Page1_RAM).l,a4
    8.     lea (LevSel_MarkTable).l,a5
    9.     rts
    10.  
    11. LevelSelect_MarkFieldsSub2:
    12.     lea (Page2_RAM).l,a4
    13.     lea (LevSel_MarkTable2).l,a5
    14.     rts
    15.  
    16. LevelSelect_MarkFieldsSub3:
    17.     lea (Page3_RAM).l,a4
    18.     lea (LevSel_MarkTable3).l,a5
    19.     rts


    For each mapping, an entry, mapping decompression RAM address, and MarkTable is added. Since I have three pages, three 'subs' entries exist. Adjust accordingly.


    Fixing Sound Test
    Now for some hardcoded values to become a little less hardcoded. The hardcoded Sound Test functions!

    Note where your Sound Test is (Level_select_zone). Count how many entries down LevelSelect_Order/2/3 "dc.w $FFFF ; sound test" is in hex, and there's your value. In vanilla S2, Sound Test is $15, but on your new pages, this is likely to have changed.

    Find these values for each page, and compile a small table, all in bytes, label it "LevSel_LimitTable" and place it above LevSelControls. Don't forget to EVEN it. Here's mine:

    Code (ASM):
    1. LevSel_LimitTable:
    2.     dc.b    $16
    3.     dc.b    $17
    4.     dc.b    $16
    5.     even


    Under LevSelControls, replace

    Code (ASM):
    1.     subq.w  #1,d0   ; decrease by 1
    2.     bcc.s   +   ; >= 0?
    3.     moveq   #$15,d0 ; set to $15


    With

    Code (ASM):
    1.     subq.w  #1,d0   ; decrease by 1
    2.     bcc.s   +   ; >= 0?
    3.     moveq   #0,d0
    4.     move.b  (LevSel_Page).w,d3
    5.     move.b  LevSel_LimitTable(pc,d3.w),d0


    And

    Code (ASM):
    1.     cmpi.w  #$16,d0
    2.     blo.s   +   ; smaller than $16?
    3.     moveq   #0,d0   ; if not, set to 0


    With

    Code (ASM):
    1.     moveq   #0,d2
    2.     move.b  (LevSel_Page).w,d3
    3.     move.b  LevSel_LimitTable(pc,d3.w),d2
    4.  
    5.     cmp.w   d2,d0
    6.     bls.s   +   ; smaller than $18?
    7.     moveq   #0,d0   ; if not, set to 0


    Now go to LevSelControls_CheckLR and replace

    Code (ASM):
    1.     cmpi.w  #$15,(Level_select_zone).w  ; are we in the sound test?


    With

    Code (ASM):
    1.     moveq   #0,d0
    2.     move.b  (LevSel_Page).w,d3
    3.     move.b  LevSel_LimitTable(pc,d3.w),d0
    4.     move.w  (Level_select_zone).w,d1
    5.     cmp.w   d0,d1   ; are we in the sound test?


    Then go to LevelSelect_MarkFields (at the very bottom, right above the LevelSelect_DrawSoundNumber label) and replace

    Code (ASM):
    1.     cmpi.w  #$15,(Level_select_zone).w


    With

    Code (ASM):
    1.     moveq   #0,d0
    2.     move.b  (LevSel_Page).w,d1
    3.     lea (LevSel_LimitTable).l,a0
    4.     move.b  (a0,d1.w),d0
    5.     move.w  (Level_select_zone).w,d1
    6.     cmp.w   d0,d1   ; are we in the sound test?


    Using the New Pages
    Now to make another choice. Do you want the 'NEXT' and 'PREV' to be activated using the start button, like several other elements, or A/B/C, like the Sound Test?


    Using Start
    Now to make 'NEXT' and 'PREV' work. Like the others, they'll be activated using the start button.

    Go to LevelSelect_PressStart and after


    Code (ASM):
    1.     bmi.w   LevelSelect_Return  ; sound test


    Paste this:

    Code (ASM):
    1.     cmpi.w  #$3000,d0       ; was the selection Next Page?
    2.         bne.w   ++
    3.         add.b   #1,(LevSel_Page).w
    4.     cmpi.b  #MaxPageNum+1,(LevSel_Page).w
    5.     bne.s   +
    6.     clr.b   (LevSel_Page).w
    7. +
    8.         bra.w   LevelSelect_LoadPage
    9. +
    10.         cmpi.w  #$5000,d0       ; was the selection Prev Page?
    11.         bne.w   ++
    12.     sub.b   #1,(LevSel_Page).w
    13.     bpl.s   +
    14.     move.b  #MaxPageNum,(LevSel_Page).w
    15. +
    16.         bra.w   LevelSelect_LoadPage
    17. +

    Using A/B/C
    Now to make 'NEXT' and 'PREV' work. In this section, we'll make it so you activate them using the A/B/C buttons, similar to the Sound Test.

    Go to LevelSelect_PressStart and after


    Code (ASM):
    1.     bmi.w   LevelSelect_Return  ; sound test


    Paste this:

    Code (ASM):
    1.     cmpi.w  #$3000,d0   ; was the selection Next Page?
    2.     beq.w   LevelSelect_Main
    3.     cmpi.w  #$5000,d0   ; was the selection Prev Page?
    4.     beq.w   LevelSelect_Main


    Now go to LevelSelect_Main, find this:

    Code (ASM):
    1.     andi.b  #button_start_mask,d0
    2.     bne.s   LevelSelect_PressStart  ; yes
    3.     bra.w   LevelSelect_Main    ; no


    Replace it with this:

    Code (ASM):
    1.     andi.b  #button_start_mask,d0
    2.     bne.s   LevelSelect_PressStart  ; yes
    3.     move.b  (Ctrl_1_Press).w,d0
    4.     or.b    (Ctrl_2_Press).w,d0
    5.     andi.b  #button_A_mask|button_B_mask|button_C_mask,d0
    6.     bne.w   LevelSelect_PressABC    ; yes
    7.     bra.w   LevelSelect_Main    ; no


    Go to LevelSelect_Return, and above the label, paste this:

    Code (ASM):
    1. LevelSelect_PressABC:
    2.     moveq   #0,d0
    3.         move.b  (LevSel_Page).w,d0
    4.         add.b   d0,d0
    5.         lea     LevelSelect_OrderIndex(pc),a0
    6.         move.w  (a0,d0.w),d0
    7.         lea     (a0,d0.w),a0
    8.         move.w  (Level_select_zone).w,d0
    9.         add.w   d0,d0
    10.         movea.w (a0,d0.w),d0
    11.     cmpi.w  #$3000,d0       ; was the selection Next Page?
    12.         bne.w   ++
    13.         add.b   #1,(LevSel_Page).w
    14.     cmpi.b  #MaxPageNum+1,(LevSel_Page).w
    15.     bne.s   +
    16.     clr.b   (LevSel_Page).w
    17. +
    18.         bra.w   LevelSelect_LoadPage
    19. +
    20.         cmpi.w  #$5000,d0       ; was the selection Prev Page?
    21.         bne.w   ++
    22.     sub.b   #1,(LevSel_Page).w
    23.     bpl.s   +
    24.     move.b  #MaxPageNum,(LevSel_Page).w
    25. +
    26.         bra.w   LevelSelect_LoadPage
    27. +
    28.     bra.w   LevelSelect_Main


    Then go to LevelSelect_StartZone, and above the label, paste this:

    Code (ASM):
    1. LevelSelect_LoadPage:
    2.     moveq   #0,d0
    3.     move.b  (LevSel_Page).w,d0
    4.     add.b   d0,d0
    5.     move.w  LevelSelect_LoadPageSubIndex(pc,d0.w),d0
    6.     jsr LevelSelect_LoadPageSubIndex(pc,d0.w)
    7.  
    8.     move.l  #vdpComm(VRAM_Plane_A_Name_Table,VRAM,WRITE),d0
    9.         moveq   #$27,d1
    10.         moveq   #$1B,d2 ; 40x28 = whole screen
    11.         bsr.w   JmpTo_PlaneMapToVRAM    ; display patterns
    12.  
    13.     clr.w   (Level_select_zone).w
    14.  
    15.         moveq   #0,d3
    16.     bsr.w   LevelSelect_DrawSoundNumber
    17.  
    18.         bra.w   LevelSelect_Main
    19.  
    20. LevelSelect_LoadPageSubIndex:   offsetTable
    21.     offsetTableEntry.w LevelSelect_LoadPageSub1 ; 0
    22.     offsetTableEntry.w LevelSelect_LoadPageSub2 ; 1
    23.     offsetTableEntry.w LevelSelect_LoadPageSub3 ; 2
    24.  
    25. LevelSelect_LoadPageSub1:
    26.     lea (Page1_RAM).l,a1
    27.     rts
    28.  
    29. LevelSelect_LoadPageSub2:
    30.     lea (Page2_RAM).l,a1
    31.     rts
    32.  
    33. LevelSelect_LoadPageSub3:
    34.     lea (Page3_RAM).l,a1
    35.     rts


    Each LoadPageSub contains a constant defined back in Setting Up Constants. As with the other indexes, there is one entry per page. Adjust accordingly.


    (Optional) Memorising LevSel_Page
    If you want the page you are on to be memorised, à la Level_Select_Zone, so that when you return to the Level Select after leaving it, you're placed back where you were when you left, you'll have to do the following. This one's a little messy, as if you want that behaviour, you cannot have your page mappings decompress post-fade. Make this so before continuing.


    Go to MenuScreen_LevelSelect and replace this:

    Code (ASM):
    1.         lea     (Page1_RAM).l,a1
    2.         move.l  #vdpComm(VRAM_Plane_A_Name_Table,VRAM,WRITE),d0
    3.         moveq   #$27,d1
    4.         moveq   #$1B,d2 ; 40x28 = whole screen
    5.         bsr.w   JmpTo_PlaneMapToVRAM    ; display patterns


    With this:

    Code (ASM):
    1.         moveq   #0,d0
    2.         move.b  (LevSel_Page).w,d0
    3.         add.b   d0,d0
    4.         move.w  LevelSelect_LoadPageSubIndex(pc,d0.w),d0
    5.         jsr     LevelSelect_LoadPageSubIndex(pc,d0.w)
    6.  
    7.         move.l  #vdpComm(VRAM_Plane_A_Name_Table,VRAM,WRITE),d0
    8.         moveq   #$27,d1
    9.         moveq   #$1B,d2 ; 40x28 = whole screen
    10.         bra.w   JmpTo_PlaneMapToVRAM    ; display patterns


    Also, remove these two lines found at the top of MenuScreen_LevelSelect:

    Code (ASM):
    1.     clr.b   (LevSel_Page).w
    2.     clr.w   (Level_select_zone).w

    (Optional) Using Uncompressed Mappings
    Only follow this if you want uncompressed mappings for whatever reason.


    Go to MenuScreen_LevelSelect and remove this:

    Code (ASM):
    1.     lea (Page1_RAM).l,a1
    2.     lea (MapEng_LevSel).l,a0    ; 2 bytes per 8x8 tile, compressed
    3.     move.w  #make_art_tile(ArtTile_VRAM_Start,0,0),d0
    4.     bsr.w   EniDec


    Do the same for the other pages'.

    Replace the nearby reference to Page1_RAM with MapEng_LevSel, note that you will have deleted that reference if you chose to memorise LevSel_Page.

    Next, head to LevelSelect_LoadPageSub1 and LevelSelect_MarkFieldsSub and begin replacing the references to PageX_RAM with the in-ROM equivalents (MapEng_LevSel, MapEng_LevSel2, etc.)

    Code (ASM):
    1. LevelSelect_LoadPageSub1:
    2.         lea     (MapEng_LevSel).l,a1
    3.         rts
    4.  
    5. LevelSelect_LoadPageSub2:
    6.         lea     (MapEng_LevSel2).l,a1
    7.         rts
    8.  
    9. LevelSelect_LoadPageSub3:
    10.         lea     (MapEng_LevSel3).l,a1
    11.         rts
    Code (ASM):
    1. LevelSelect_MarkFieldsSub1:
    2.         lea     (MapEng_LevSel).l,a4
    3.         lea     (LevSel_MarkTable).l,a5
    4.         rts
    5.  
    6. LevelSelect_MarkFieldsSub2:
    7.         lea     (MapEng_LevSel2).l,a4
    8.         lea     (LevSel_MarkTable2).l,a5
    9.         rts
    10.  
    11. LevelSelect_MarkFieldsSub3:
    12.         lea     (MapEng_LevSel3).l,a4
    13.         lea     (LevSel_MarkTable3).l,a5
    14.         rts


    Doing this removes the decompression to RAM and instead loads directly from ROM. Decompressed plane mappings are quite a bit larger than their Enigma-compressed counterparts. My first page's mappings went to 2240 bytes (the usual size for full-screen mappings) from 340.


    If you encounter any of these errors...

    Code (Text):
    1. > > >s2.asm(11686): error: addressing mode not allowed on 68000
    2. > > >   move.b  LevelSelect_OrderIndex(pc,d0.w),d0
    ...regarding lines such as these:

    Code (ASM):
    1.     move.w  LevelSelect_OrderIndex(pc,d0.w),d0
    2.     lea LevelSelect_OrderIndex(pc,d0.w),a0


    Rearrange them to follow this format:

    Code (ASM):
    1.     lea LevelSelect_OrderIndex(pc),a0   ; if this still doesn't work, use (LevelSelect_OrderIndex).l,a0 instead
    2.     move.w  (a0,d0.w),d0
    3.     lea (a0,d0.w),a0

    After fixing some 'branch out of range' errors, you should be good to go. Save and build.

    I have tested this guide on an untouched HG disasm. A prototype was built into my main hack with no compatibility issues between this hack and my other Level Select hacks. Hack has been tested with up to three pages. Further numbers are untested.

    Update Log (dd/mm/yyyy)
    23/01/2014
    "The only bug I've found so far is that when switching pages, the mappings overwrites the Sound Test selection, and that highlighting it will restore the old value. I've masked this by clearing Sound_test_sound whenever the page is changed."

    The above bug's been fixed, just had to clear d3 and call LevelSelect_DrawSoundNumber upon changing pages.


    Reworded the line "Remember to place EVENs at the end of your tables and indexes to avoid misaligned instructions". It's an old leftover from when all of the hack's indexes were byte-sized. This was changed to how it is in the released version to allow multiple pages without needing to worry about the tables being out of the indexes' range.


    Fixed a silly yet massive typo under Dynamic Data #3.


    Added A/B/C interaction.

    26/01/2014
    Tidied up the guide a little.


    Fixed A/B/C interaction


    Added decompression location constants and matching section.


    Added uncompressed mappings section.


    Page loop added.


    External site-hosted images replaced with local images.

    28/01/2014
    Changed both

    Code (ASM):
    1.         lea     LevelSelect_LoadPageSubIndex(pc,d0.w),a0
    2.         jsr     (a0)    ; dynamic call!


    into

    Code (ASM):
    1.         jsr     LevelSelect_LoadPageSubIndex(pc,d0.w)


    Fixed how LevSel_Page would be remembered, but the initial mapping loaded would not reflect it.


    LevSel_Page memorisation section added.

    05/03/2014
    Fixed incompatible code under Memorising LevSel_Page. The code was adapted for use with byte-sized offsets, but the public version of this uses word-sized, leading to the game hanging upon going to the Level Select.


    Removed redundant branch under Memorising LevSel_Page. Forgot to update this when I did so to the SCHG version.
     
  2. Cinossu

    Cinossu

    Administrator
    2,830
    16
    18
    London, UK
    Sonic the Hedgehog Extended Edition
    An interesting way to extend the menu indeed, and a rather thorough guide on how to do it too. Very nicely done. Please consider adding it to the wiki under the hacking how-tos (SCHG_How-to:Guide), if you haven't already.

    This is most likely due to the mappings themselves, with how you are decompressing to RAM and then drawing to a plane without any modification in-between. In the mappings themselves it probably contains the default 00, meaning the first time drawn will always be this. Add some code to take the sound test value and modify the loaded mappings in RAM before drawing to a plane and it should solve it nicely.
     
  3. Clownacy

    Clownacy

    Tech Member
    875
    137
    43
    Thanks, it turns out your Sound Test fix wasn't necessary: It seems that the zeroes defined in the mappings are redundant, since LevelSelect_DrawSoundNumber is called immediately after the plane map is loaded and replaces them. Applying this to the page loading (sub?)routine fixes the bug, in fact, you can get away with removing the zeroes from the mappings altogether!

    I'm pretty happy that you recommend that the guide be added to the SCHG, but it'll be a while before I feel that it's ready. It's still incomplete, you see; scattered around the guide are a couple of to-do's, such as page-looping and dynamic mapping decompression. I should probably add constants for some of the reoccurring values too.

    EDIT: SCHG version uploaded.
     
  4. RetroKoH

    RetroKoH

    Member
    1,661
    17
    18
    Project Sonic 8x16
    I wonder if I could throw a suggestion into the hat.

    I was messing around with the Sonic 1 Level Select a while back... and did something where you had the zone name itself, followed by an Act # that would be changed ala the Sound Test. While I haven't been able to get the Act numbers to appear properly... the function works without a hitch.

    Image link
    In its current form, it looks like so. It has all planned zones for my hack, as well as Char Select, Score, etc. still could use a tad bit of work but the general aim was met. Also, going past the levels will jump all the way to the Sound Test.

    Any chance you'd be interested in such feature? While it wouldn't be ideal for Sonic 2, or 3... due to the two act system... a modded Sonic 1 Level Select could theoretically make use of it. If so I can send REV C's source to you to pick away at.
     
  5. Caverns 4

    Caverns 4

    Member
    342
    0
    16
    Sanik Quest: Journey To The Right
    KoH, extending the Sonic 1 (Or more like Sonic 2 beta, but they are similar) level select is well within my interest, due to the fact that the level select in one of my hacks really just can't quite fit all the zones I need (And due to the nature of my hack, changing it would be kind of lame). While I would most ideally prefer a scrolling effect, anything to fit all the zones I want would be great. One column just isn't enough to fit even the $10 zones that Sonic 2 has by default.