Skip to content

TitleScreenModule makes working with custom spritemaps difficult #323

@PhoenixBound

Description

@PhoenixBound

Problem

CoilSnake currently tries to repoint the moving characters' layouts ("chars layouts" in function names; more generically now "spritemaps") on the title screen while keeping the table of 16-bit pointers to them in the same place. (Note that new_offset is &'d with 0xFFFF, and the CHARS_LAYOUT_TABLE is a hardcoded constant address.)

# Write the offsets to the layouts to the ROM
new_bank = new_offset >> 16
new_data_start = new_offset & 0xFFFF
data_offset = new_data_start
for c, layout in enumerate(self.chars_layouts):
rom[CHARS_LAYOUT_TABLE + c*2:CHARS_LAYOUT_TABLE + c*2 + 2] = [
data_offset & 0xFF, data_offset >> 8
]
data_offset += len(layout)*TitleScreenLayoutEntry.block_size()

However, in order to separate the "chars layout table" from the actual edits, it edits an assembly function at $C0A0FA to change a dynamic value, representing the bank of a 24-bit pointer, into a hardcoded bank value:

# Change the offset for the character layouts
# The way this normally works is that EarthBound stores the address
# of the bank holding the data (0xE1 by default, hence the 0x210000
# offset); the offsets in the table are then prefixed with that
# address. However, reallocating the data may have changed its
# bank, so we need to manually set it to the new bank address.
# In order to change the offset, we are replacing a LDA instruction
# which addresses a direct page (0xA5) with a LDA instruction
# that treats its operand as the constant to load (0xA9)
# See https://wiki.superfamicom.org/snes/show/65816+Reference#instructions.
rom[CHARS_LAYOUT_BANK:CHARS_LAYOUT_BANK + 2] = [0xA9, new_bank]

This function is a "draw callback," as such functions are currently called in movement script documentation. It defines how a given sprite's OBJ data is generated based on all of its current data. A0FA in particular is used to draw a simple sprite that doesn't need to exist in the context of the EarthBound overworld. All it relies on is the sprite's claimed "priority" value and its "absolute X/Y" positions, which are used directly as screen positions. All of the draw callbacks are fairly generic, reusable functions not specific to the title screen.

It was realized as early as #166's filing that this approach was a suspicious way to do things; the Kirby sprite on the debug also uses A0FA as a draw callback, but its spritemap data is in a completely different bank from the title screen letters. This was worked around in #180 by including a method to change the address of the Kirby spritemap in the title screen module. But last year, Catador found that this was causing problems in some experimental movement scripts of his that used custom spritemaps. (A0FA is a natural callback choice for any kind of sprite that doesn't participate in map collision, which is the main place where custom spritemaps are used in the first place.)

Solution

As alluded to at the beginning of the issue, the proper way to fix this is to keep the table of 16-bit pointers together with the actual layouts. The bank of the layouts is just the bank where the table of pointers is (i.e., the address passed to movement script command 1C, m_set_spritemap). Concretely, that means a big overhaul/simplification of write_chars_layouts_and_kirby_data_to_rom:

  • Get rid of all handling of the Kirby sprite in this function, because it has nothing to do with the title screen data.
  • Allocate a block large enough to hold both the TitleScreenLayoutEntrys and the pointer tables; put them in the same block, with the layouts at the beginning and the pointers at the end. (This should make it easy to round-trip compile and decompile a hack, for CoilSnake developers who want a sanity check that compilation works properly, and matches the way the original game orders the title screen spritemap data.)
  • Change the m_set_spritemap command at C4220E (=> 24-bit pointer at C4220F) to point to the new pointer table at the end of the allocated block.
  • Extend the FREE_RANGES area labeled "Animation Data" to include the table of 16-bit pointers.
  • (Optional) Don't limit the number of entries in the char positions YML file to NUM_CHARS. Now that the table of 16-bit pointers is repointed, there's very little reason to restrict the number of letters to only 9. It's easy to imagine hacks wanting to use more than just 9 flying letters in their titles, and in fact the EB:B remake project by Gabbi has a use case for increasing the number to 12.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions