From a0c8d2799d04d79565417bae2a62583d85c01345 Mon Sep 17 00:00:00 2001 From: Supremekirb <99102603+Supremekirb@users.noreply.github.com> Date: Tue, 11 Mar 2025 12:38:23 +1030 Subject: [PATCH 1/3] Search for footer and use pointer there --- coilsnake/model/eb/musicpack.py | 37 ++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/coilsnake/model/eb/musicpack.py b/coilsnake/model/eb/musicpack.py index 4a68e43..ceb55da 100644 --- a/coilsnake/model/eb/musicpack.py +++ b/coilsnake/model/eb/musicpack.py @@ -805,7 +805,15 @@ def load_from_files(self, file_loader): raise CoilSnakeInternalError("Dependent song is not in same pack as parent song") class EngineMusicPack(SongMusicPack): + # This is used in modified engine.bins to have a different pointer to the song table. + # asar assembler uses UTF-8 encoding. + # The footer can be located anywhere, but usually goes at the end of the file (as such we search backwards). + # The format is this string followed by a pointer to the song table. + FOOTER_IDENTIFIER_BYTES = bytes("COILSNAKE SONG TABLE POINTER", encoding="UTF-8") + + # Default song table address. Used when the footer is not found. SONG_ADDRESS_TABLE_ADDR = 0x2E4A + ENGINE_FIXED_PARTS = {0x6E00: 'data-6E00.bin', 0x6F80: 'data-6F80.bin', 0x0500: 'engine.bin'} # These values are for the part starting at $0500, containing the main SPC program MAIN_PART_ADDR = 0x0500 @@ -940,9 +948,36 @@ def get_song_address_table_data(self, size: int) -> Block: start_addr = EngineMusicPack.SONG_ADDRESS_TABLE_ADDR - EngineMusicPack.MAIN_PART_ADDR return block[start_addr:start_addr + size] + def get_song_address_table_pointer(self) -> int: + # search for CoilSnake footerw + # (there's probably a better way to do this. I don't know how to best work with Block objects.) + engine_bytes = bytes(self.engine_parts[EngineMusicPack.MAIN_PART_ADDR].to_list()) + footer_match = EngineMusicPack.FOOTER_IDENTIFIER_BYTES + song_table_pointer_pointers: list[int] = [] + try: + song_table_pointer_pointers = [(i+len(footer_match)) for i in range(len(engine_bytes)) if engine_bytes[i:i+len(footer_match)] == footer_match] + except OutOfBoundsError: + pass + if len(song_table_pointer_pointers) > 1: + raise InvalidUserDataError("engine.bin contains more than one footer pointing to the location of the song table. Found at: {}").format( + "$" + hex(i-len(footer_match))[2:].zfill(4) for i in song_table_pointer_pointers + ) + + if len(song_table_pointer_pointers) == 0: + log.info("Using default song table location.") + target_addr = EngineMusicPack.SONG_ADDRESS_TABLE_ADDR + else: + target_addr = engine_bytes[song_table_pointer_pointers[0]:song_table_pointer_pointers[0]+2] + target_addr = int.from_bytes(target_addr, 'little') + log.info("Relocating song table to {} based on footer found in the engine.".format( + hex(target_addr).zfill(4) + )) + + return target_addr + def set_song_address_table_data(self, block: Block) -> None: assert self.parts - self.set_aram_region(EngineMusicPack.SONG_ADDRESS_TABLE_ADDR, block.size, block) + self.set_aram_region(self.get_song_address_table_pointer(), block.size, block) @classmethod def apply_engine_patches(cls, engine_block: Block) -> Block: From c12632b8a0276953442cfcd987f2a47dda4c4a0c Mon Sep 17 00:00:00 2001 From: Supremekirb <99102603+Supremekirb@users.noreply.github.com> Date: Tue, 11 Mar 2025 12:41:09 +1030 Subject: [PATCH 2/3] Small fix to error --- coilsnake/model/eb/musicpack.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/coilsnake/model/eb/musicpack.py b/coilsnake/model/eb/musicpack.py index ceb55da..1dcf36c 100644 --- a/coilsnake/model/eb/musicpack.py +++ b/coilsnake/model/eb/musicpack.py @@ -949,7 +949,7 @@ def get_song_address_table_data(self, size: int) -> Block: return block[start_addr:start_addr + size] def get_song_address_table_pointer(self) -> int: - # search for CoilSnake footerw + # search for CoilSnake footer # (there's probably a better way to do this. I don't know how to best work with Block objects.) engine_bytes = bytes(self.engine_parts[EngineMusicPack.MAIN_PART_ADDR].to_list()) footer_match = EngineMusicPack.FOOTER_IDENTIFIER_BYTES @@ -959,9 +959,9 @@ def get_song_address_table_pointer(self) -> int: except OutOfBoundsError: pass if len(song_table_pointer_pointers) > 1: - raise InvalidUserDataError("engine.bin contains more than one footer pointing to the location of the song table. Found at: {}").format( - "$" + hex(i-len(footer_match))[2:].zfill(4) for i in song_table_pointer_pointers - ) + raise InvalidUserDataError("engine.bin contains more than one footer pointing to the location of the song table. Found at: {}".format( + ["$" + hex(i-len(footer_match))[2:].zfill(4) for i in song_table_pointer_pointers] + )) if len(song_table_pointer_pointers) == 0: log.info("Using default song table location.") From 66fe4e8003fa6998a22220f026e37b2d71c57ae8 Mon Sep 17 00:00:00 2001 From: Supremekirb <99102603+Supremekirb@users.noreply.github.com> Date: Wed, 12 Mar 2025 13:58:51 +1030 Subject: [PATCH 3/3] Use SPC address in error reporting --- coilsnake/model/eb/musicpack.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/coilsnake/model/eb/musicpack.py b/coilsnake/model/eb/musicpack.py index 1dcf36c..b73b134 100644 --- a/coilsnake/model/eb/musicpack.py +++ b/coilsnake/model/eb/musicpack.py @@ -807,7 +807,7 @@ def load_from_files(self, file_loader): class EngineMusicPack(SongMusicPack): # This is used in modified engine.bins to have a different pointer to the song table. # asar assembler uses UTF-8 encoding. - # The footer can be located anywhere, but usually goes at the end of the file (as such we search backwards). + # The footer can be located anywhere, though, but the convention is at the end. # The format is this string followed by a pointer to the song table. FOOTER_IDENTIFIER_BYTES = bytes("COILSNAKE SONG TABLE POINTER", encoding="UTF-8") @@ -959,8 +959,8 @@ def get_song_address_table_pointer(self) -> int: except OutOfBoundsError: pass if len(song_table_pointer_pointers) > 1: - raise InvalidUserDataError("engine.bin contains more than one footer pointing to the location of the song table. Found at: {}".format( - ["$" + hex(i-len(footer_match))[2:].zfill(4) for i in song_table_pointer_pointers] + raise InvalidUserDataError("engine.bin contains more than one footer pointing to the location of the song table. Found footers at: {} (SPC addresses)".format( + ["$" + hex(i-len(footer_match) + 0x500)[2:].zfill(4).upper() for i in song_table_pointer_pointers] )) if len(song_table_pointer_pointers) == 0: