From 96b4ec67f1e874e2de2e5c1c2a8f0a84429666db Mon Sep 17 00:00:00 2001 From: Levi Willrich Date: Tue, 25 Jul 2023 05:55:08 -0500 Subject: [PATCH] SBN encoding (#1085) * add SBN decoding * sbn.yaml instead of files.json/init.json * add id numbers to file name - filenames internal to SBNs are duplicates * Fixed bug where we mistakenly interpreted a bank file ID of -1 as the last file in the list rather than no file * Encoding functional * cleanup * Updated .gitignore to catch sbn files * fixed some bugs and made suggested changes * Fixes some code review issues * fixes, pal sbn --------- Co-authored-by: Alex Bates Co-authored-by: Ethan Roseman --- .gitignore | 1 + tools/build/audio/sbn.py | 25 ++ tools/build/configure.py | 9 + tools/splat_ext/pm_sbn.py | 880 ++++++++++++++++++++++++++++++++++++++ ver/pal/splat.yaml | 158 +------ ver/us/splat.yaml | 150 +------ 6 files changed, 918 insertions(+), 305 deletions(-) create mode 100644 tools/build/audio/sbn.py create mode 100644 tools/splat_ext/pm_sbn.py diff --git a/.gitignore b/.gitignore index 71107f1c99..35a6fd2457 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,7 @@ build/ /assets/jp /assets/pal /assets/ique +*.sbn # Star Rod /sprite/SpriteTable.xml diff --git a/tools/build/audio/sbn.py b/tools/build/audio/sbn.py new file mode 100644 index 0000000000..628cfe7017 --- /dev/null +++ b/tools/build/audio/sbn.py @@ -0,0 +1,25 @@ +#! /usr/bin/python3 + +from sys import argv, path +from pathlib import Path + +# allow importing splat_ext +path.append(str(Path(__file__).parent.parent.parent)) + +from splat_ext.pm_sbn import SBN + +if __name__ == "__main__": + out = argv[1] + inputs = [Path(p) for p in argv[2:]] + + sbn = SBN() + + for input in inputs: + # if input is a sbn.yaml, read its directory + if input.suffix == ".yaml": + sbn.read(input.parent) + + data = sbn.encode() + + with open(out, "wb") as f: + f.write(data) diff --git a/tools/build/configure.py b/tools/build/configure.py index be2901709e..6802e1ee51 100755 --- a/tools/build/configure.py +++ b/tools/build/configure.py @@ -313,6 +313,8 @@ def write_ninja_rules( "effect_data", command=f"$python {BUILD_TOOLS}/effects.py $in_yaml $out_dir" ) + ninja.rule("pm_sbn", command=f"$python {BUILD_TOOLS}/audio/sbn.py $out $in") + with Path("tools/permuter_settings.toml").open("w") as f: f.write( f"compiler_command = \"{cc} {CPPFLAGS.replace('$version', 'pal')} {cflags} -DPERMUTER -fforce-addr\"\n" @@ -378,6 +380,7 @@ class Configure: "pm_effect_shims", "pm_sprite_shading_profiles", "pm_imgfx_data", + "pm_sbn", ] ) if code: @@ -1110,6 +1113,12 @@ class Configure: }, ) build(entry.object_path, [entry.object_path.with_suffix("")], "bin") + elif seg.type == "pm_sbn": + sbn_path = entry.object_path.with_suffix("") + build(sbn_path, entry.src_paths, "pm_sbn") # could have non-yaml inputs be implicit + build(entry.object_path, [sbn_path], "bin") + elif seg.type == "linker" or seg.type == "linker_offset": + pass elif seg.type == "pm_imgfx_data": c_file_path = ( Path(f"assets/{self.version}") / "imgfx" / (seg.name + ".c") diff --git a/tools/splat_ext/pm_sbn.py b/tools/splat_ext/pm_sbn.py new file mode 100644 index 0000000000..b1a68599bb --- /dev/null +++ b/tools/splat_ext/pm_sbn.py @@ -0,0 +1,880 @@ +from pathlib import Path +from dataclasses import astuple, dataclass +import os +import yaml +import struct +from typing import Any, Dict, List +from collections import OrderedDict +from functools import total_ordering + + +# splat imports; will fail if script run directly +try: + from segtypes.n64.segment import N64Segment + from segtypes.linker_entry import LinkerEntry + from util import options + + splat_loaded = True +except ImportError: + splat_loaded = False + + +def decode_null_terminated_ascii(data): + length = 0 + for byte in data: + if byte == 0: + break + length += 1 + + return data[:length].decode("ascii") + + +def align(misaligned: int): + return (misaligned + 0xF) & (~0xF) + + +class SBN: + files: List["SBNFile"] + unknown_data: bytes + init: "INIT" + + def __init__(self): + self.files = [] + self.init = INIT() + + def decode(self, data: bytes): + # Decode header + header = SBNHeader(*struct.unpack_from(SBNHeader.fstring, data)) + + data = data[: header.size] + + self.unknown_data = data[0x7A0:0x7C0] + + print(f"Total size: {header.size}") + + # Decode file entries + entry_addr = header.tableOffset + seen_entry_offsets = set() + for i in range(header.numEntries): + entry = SBNFileEntry( + *struct.unpack_from(SBNFileEntry.fstring, data, entry_addr) + ) + entry_addr += SBNFileEntry.length + + # Check for duplicate entry offsets + if entry.offset in seen_entry_offsets: + raise Exception("Duplicate SBN file entry: {:08}", entry.offset) + seen_entry_offsets.add(entry.offset) + + # Decode file at entry + sbn_file = SBNFile() + sbn_file.decode(data[entry.offset :], i) + sbn_file.fakesize = entry.size + self.files.append(sbn_file) + + # Decode INIT + self.init.decode(data[header.INIToffset :]) + + return len(data) + + def encode(self) -> bytes: + num_bytes = 0 + num_bytes += 0x40 # header + + entries_offset = num_bytes + num_bytes += 8 * len(self.files) # file entry table. + num_bytes = align(num_bytes) + + unknown_data_offset = num_bytes + num_bytes += 0x20 # unknown data + + files_offset = num_bytes + for file in self.files: + num_bytes += file.size # file data itself + num_bytes = align(num_bytes) + + init_header_offset = num_bytes + num_bytes += 0x20 # init table header + + bank_entry_offset = num_bytes + num_bytes += 4 * len(self.init.bk_entries) # bank entries + num_bytes += 4 # bank sentinel + bank_entry_end = num_bytes + num_bytes = align(num_bytes) + + song_entry_offset = num_bytes + num_bytes += 8 * len(self.init.song_entries) # song entries + num_bytes += 8 # song sentinel + song_entry_end = num_bytes + num_bytes = align(num_bytes) + + mseq_offset = num_bytes + num_bytes += 2 * len(self.init.mseq_entries) # mseq entries + num_bytes += 2 # mseq sentinel + mseq_end = num_bytes + + init_file_end = num_bytes + num_bytes = align(num_bytes) + + data = bytearray(num_bytes) + + # header + + header = SBNHeader( + size=num_bytes, + tableOffset=entries_offset, + numEntries=len(self.files), + INIToffset=init_header_offset, + ) + + struct.pack_into(SBNHeader.fstring, data, 0, *header) + + # unknown data + for offset, byte in enumerate(self.unknown_data): + data[unknown_data_offset + offset] = byte + + # files + current_file_offset = files_offset + for index, file in enumerate(self.files): + extension = os.path.splitext(file.file_name())[1] + + FORMATS = { + ".bgm": 0x10, + ".sef": 0x20, + ".bk": 0x30, + ".prg": 0x40, + ".mseq": 0x40, + ".per": 0x40, + } + + if extension in FORMATS: + format = FORMATS[extension] + else: + raise ValueError("Unsupported file extension") + + entry = SBNFileEntry( + offset=current_file_offset, fmt=format, size=file.fakesize + ) + + struct.pack_into( + SBNFileEntry.fstring, + data, + entries_offset + index * SBNFileEntry.length, + *entry, + ) + + for offset, byte in enumerate(file.data): + data[current_file_offset + offset] = byte + + current_file_offset += file.size + current_file_offset = align(current_file_offset) + + initHeader = INITHeader( + size=init_file_end - init_header_offset, + tblOffset=song_entry_offset - init_header_offset, + tblSize=song_entry_end - song_entry_offset, + entriesOffset=bank_entry_offset - init_header_offset, + entriesSize=bank_entry_end - bank_entry_offset, + shortsOffset=mseq_offset - init_header_offset, + shortsSize=mseq_end - mseq_offset, + ) + + struct.pack_into(INITHeader.fstring, data, init_header_offset, *initHeader) + + current_bank_offset = bank_entry_offset + for bank in self.init.bk_entries: + struct.pack_into(BufferEntry.fstring, data, current_bank_offset, *bank) + current_bank_offset += BufferEntry.length + + sentinel = BufferEntry(0xFFFF, 0x00, 0x00) + struct.pack_into(BufferEntry.fstring, data, current_bank_offset, *sentinel) + + current_song_offset = song_entry_offset + for song in self.init.song_entries: + struct.pack_into(InitSongEntry.fstring, data, current_song_offset, *song) + current_song_offset += InitSongEntry.length + + sentinel = InitSongEntry(0xFFFF, 0x00, 0x00, 0x00) + struct.pack_into(InitSongEntry.fstring, data, current_song_offset, *sentinel) + + current_mseq_offset = mseq_offset + for mseq in self.init.mseq_entries: + struct.pack_into(">H", data, current_mseq_offset, mseq) + current_mseq_offset += 2 + struct.pack_into(">H", data, current_mseq_offset, 0xFFFF) + + return bytes(data) + + # Write the files in the SBN to a directory. + def write(self, path: Path): + if not path.exists(): + path.mkdir() + + with open(path / "unknown.bin", "wb") as f: + f.write(self.unknown_data) + + for sbn_file in self.files: + try: + filename = sbn_file.file_name() + sbn_file.write(path / filename) + except Exception as e: + raise Exception(f"Failed to write {sbn_file}: {e}") + + with open(path / "sbn.yaml", "w") as f: + # Filename->ID map + f.write( + "# Mapping of filenames to entry IDs. Use 'id: auto' to automatically assign a unique ID.\n" + ) + f.write( + """ +# 'fakesize is an interesting case. In the final ROM, the size of a file is stored in the file header and the entry table. +# For bank files, however, the entry table has seemingly random data stored for file size. This data appears to be unused. +# It would take a lot of time for no clear benefit to figure out how this data is generated, so instead it's extracted. +# Modders: the fakesize field needs to be set for any file larger than 65536 bytes, however what it's set to doesn't seem to matter\n""" + ) + + f.write("files:\n") + for id, sbn_file in enumerate(self.files): + filename = sbn_file.file_name() + f.write(f" - id: 0x{id:02X}\n") + f.write(f" file: {filename}\n") + + if sbn_file.fakesize != sbn_file.size: + f.write(f" fakesize: {sbn_file.fakesize}\n") + + f.write("\n") + + # INIT songs + f.write("# Songs. Use 'id: auto' to automatically assign a unique ID.\n") + f.write("# Symbols are used to generate the SongIDs C enum.\n") + f.write("songs:\n") + for id, entry in enumerate(self.init.song_entries): + bgm_file_name = self.files[entry.bgmFileIndex].file_name() + bk_file_names = [] + for index in ( + entry.bkFile0Index, + entry.bkFile1Index, + entry.bkFile2Index, + ): + if index == 0: + bk_file_names.append("null") + else: + bk_file_names.append(self.files[index].file_name()) + f.write(f" - id: 0x{id:02x}\n") + f.write(f" symbol: {get_song_symbol_name(id)}\n") + f.write(f" file: {bgm_file_name}\n") + f.write(f" bk_files: [{', '.join(bk_file_names)}]\n") + f.write("\n") + + # INIT mseqs + f.write( + "# AuGlobals::mseqFileList. Not sure why there's non-MSEQ files here!\n" + ) + f.write("mseqs:\n") + for id, entry in enumerate(self.init.mseq_entries): + f.write(f" - id: 0x{id:02x}\n") + f.write(f" file: {self.files[entry].file_name()}\n") + + # INIT banks + f.write("# BK file list.\n") + f.write("banks:\n") + for id, entry in enumerate(self.init.bk_entries): + f.write(f" - id: 0x{id:02x}\n") + if entry.fileIndex != -1: + f.write(f" file: {self.files[entry.fileIndex].file_name()}\n") + f.write(f" bank_index: 0x{entry.bankIndex:02X}\n") + f.write(f" bank_group: 0x{entry.bankGroup:02X}\n") + f.write("\n") + + def read(self, dir: Path) -> List[Path]: + config_file_path = dir / "sbn.yaml" + with config_file_path.open("r") as f: + config = yaml.safe_load(f) + + with open(dir / "unknown.bin", "rb") as f: + self.unknown_data = f.read() + + files = config.get("files", {}) + songs = config.get("songs", []) + mseqs = config.get("mseqs", []) + banks = config.get("banks", []) + + sort_by_id_or_auto(files) + sort_by_id_or_auto(songs) + sort_by_id_or_auto(mseqs) + sort_by_id_or_auto(banks) + + # Read files + for file_dict in files: + id = file_dict.get("id", "auto") + if id == "auto": + id = len(self.files) + assert type(id) == int + + filename = file_dict.get("file") + assert type(filename) == str + + fakesize = file_dict.get("fakesize") + assert type(fakesize) == int or fakesize is None + + sbn_file = SBNFile() + sbn_file.read(dir / filename) + + if fakesize is not None: + sbn_file.fakesize = fakesize + else: + sbn_file.fakesize = sbn_file.size + + # Replace self.files[id] + if id < len(self.files): + print("Overwriting file ID {:02X}", id) + self.files[id] = sbn_file + elif id == len(self.files): + self.files.append(sbn_file) + else: + raise Exception(f"Invalid file ID: 0x{id:02X} - cannot have gaps") + + # Read INIT songs + for song in songs: + id = song.get("id", "auto") + if id == "auto": + id = len(self.init.song_entries) + assert type(id) == int + + # Lookup file ID + file = song.get("file") + assert type(file) == str + file_id = self.lookup_file_id(file) + + # Lookup BK file IDs + bk_files = song.get("bk_files", []) + assert type(bk_files) == list + bk_file_ids = [] + for bk_file in bk_files: + if bk_file is None: + bk_file_ids.append(0) + else: + assert type(bk_file) == str + bk_file_ids.append(self.lookup_file_id(bk_file)) + + init_song_entry = InitSongEntry( + file_id, bk_file_ids[0], bk_file_ids[1], bk_file_ids[2] + ) + + # Replace self.init.song_entries[id] + if id < len(self.init.song_entries): + print(f"Overwriting song ID {id:02X}") + self.init.song_entries[id] = init_song_entry + elif id == len(self.init.song_entries): + self.init.song_entries.append(init_song_entry) + else: + raise Exception(f"Invalid song ID: 0x{id:02X} - cannot have gaps") + + # Read INIT mseqs + for mseq in mseqs: + id = mseq.get("id", "auto") + if id == "auto": + id = len(self.init.mseq_entries) + assert type(id) == int + + # Lookup file ID + file = mseq.get("file") + assert type(file) == str + file_id = self.lookup_file_id(file) + + # Replace self.init.mseq_entries[id] + if id < len(self.init.mseq_entries): + print(f"Overwriting MSEQ ID {id:02X}") + self.init.mseq_entries[id] = file_id + elif id == len(self.init.mseq_entries): + self.init.mseq_entries.append(file_id) + else: + raise Exception(f"Invalid MSEQ ID: 0x{id:02X} - cannot have gaps") + + # Read INIT banks + for bank in banks: + id = bank.get("id", "auto") + if id == "auto": + id = len(self.init.bk_entries) + assert type(id) == int + + file = bank.get("file") + assert type(file) == str + file_id = self.lookup_file_id(file) + + bank_index = bank.get("bank_index") + assert type(bank_index) == int + + bank_group = bank.get("bank_group") + assert type(bank_group) == int + + entry = BufferEntry(file_id, bank_index, bank_group) + + # Replace self.init.bk_entries[id] + if id < len(self.init.bk_entries): + print(f"Overwriting bank ID {id:02X}") + self.init.bk_entries[id] = entry + elif id == len(self.init.bk_entries): + self.init.bk_entries.append(entry) + else: + raise Exception(f"Invalid bank ID: {id:02X} - cannot have gaps") + + return [config_file_path] + + def lookup_file_id(self, filename: str) -> int: + for id, sbn_file in enumerate(self.files): + if sbn_file.file_name() == filename: + return id + raise Exception(f"File not found: {filename} - is it in the file_id_map?") + + def __str__(self) -> str: + s = "SBN(\n" + s += " files=[\n" + for sbn_file in self.files: + s += f" {sbn_file},\n" + s += " ],\n" + s += f" init={self.init}\n" + s += ")" + return s + + def __hash__(self) -> int: + h = 0 + for sbn_file in self.files: + h ^= hash(sbn_file) + h ^= hash(self.init) + return h + + +@dataclass +class SBNHeader: + signature: bytes = b"SBN " + size: int = 0 + unk_08: int = 0 + unk_0C: int = 0 + tableOffset: int = 0 + numEntries: int = 0 + unk_18: int = 0x07C0 # TODO figure out what these are, don't hardcode + unk_1C: int = 0x07A0 # TODO figure out what these are, don't hardcode + unk_20: int = 0 + INIToffset: int = 0 + unk_28: int = 0 + unk_2C: int = 0 + unk_30: int = 0 + unk_34: int = 0 + unk_38: int = 0 + unk_3C: int = 0 + + fstring = ">4siii iiii iiii iiii" + length = 0x40 + + def __iter__(self): + return iter(astuple(self)) + + def __post_init__(self): + assert self.signature == b"SBN " + + +@dataclass +class SBNFileEntry: + offset: int + fmt: int + unk__05: int = 0 + size: int = 0 + + fstring = ">iBBh" + length = 0x08 + + def __iter__(self): + return iter(astuple(self)) + + +# Could extend SBNFile as BGM etc in future if we want higher-level formats +class SBNFile: + signature: bytes + size: int + + name: bytes + data: bytes + + host_system_name: str + # the "size" field of the SBN File entry usually, but not always, matches the actual file size. + # since this field is unused (I think), it doesn't actually matter other than for reproducing the original cart + # I couldn't figure out the pattern behind these fake sizes (if there even is one), so I'm just hardcoding them. + fakesize: int + # The file ID of the SBN file - needed to guarantee unique filenames. the filename in the header can't be used because it's not unique + ident: int + + def decode(self, data: bytes, ident: int) -> int: + self.signature, self.size, self.name = struct.unpack_from(">4si4s", data) + self.data = data[: self.size] + self.ident = ident + + self.fakesize = self.size + + return self.size + + def write(self, path: Path): + with open(path, "wb") as f: + f.write(self.data) + + def read(self, path: Path): + with open(path, "rb") as f: + data = f.read() + + filename = os.path.basename(path) + + ident = int(filename.split("_")[0], 16) + + size = self.decode(data, ident) + + assert size == len(data), "File size mismatch" + + def file_name(self) -> str: + prefix = f"{self.ident:02X}_" + stem = decode_null_terminated_ascii(self.name).rstrip(" ") + extension = decode_null_terminated_ascii(self.signature).rstrip(" ").lower() + return prefix + stem + "." + extension + + def __str__(self): + return "SBNFile(" + self.file_name() + ")" + + def __hash__(self): + return hash((self.signature, self.size, self.name, self.data)) + + +class INIT: + song_entries: List["InitSongEntry"] + mseq_entries: List[int] + bk_entries: List["BufferEntry"] + + def __init__(self): + self.song_entries = [] + self.mseq_entries = [] + self.bk_entries = [] + + def decode(self, data: bytes) -> int: + # see au_load_INIT + + header = INITHeader(*struct.unpack_from(INITHeader.fstring, data)) + + data = data[: header.size] + # Decode song list + song_addr = header.tblOffset + song_number = 0 + while True: + song = InitSongEntry( + *struct.unpack_from(InitSongEntry.fstring, data, song_addr) + ) + + if song.bgmFileIndex == 0xFFFF: + break + song_addr_old = song_addr + song_addr += song.length + self.song_entries.append(song) + + song_number += 1 + + # Decode MSEQ list + mseq_addr = header.shortsOffset + while True: + (mseq,) = struct.unpack(">h", data[mseq_addr : mseq_addr + 2]) + mseq_addr += 2 + if mseq < 0: + break + self.mseq_entries.append(mseq) + + # Decode BK file list + entries_addr = header.entriesOffset + # header.entriesSize represents the size of the entries table in bytes. + # the sentinel value at the end of the list is included in this size byte, so subtract 1 to not load it + entries_len = header.entriesSize // 4 - 1 + + for i in range(entries_len): + entry = BufferEntry( + *struct.unpack_from(BufferEntry.fstring, data, entries_addr) + ) + entries_addr += BufferEntry.length + + self.bk_entries.append(entry) + + return len(data) + + def __str__(self) -> str: + s = "INIT(\n" + s += " song_entries = [\n" + for song in self.song_entries: + s += f" {song},\n" + s += " ],\n" + s += " mseq_entries = [\n" + for mseq in self.mseq_entries: + s += f" {mseq},\n" + s += " ],\n" + s += " bk_entries = [\n" + for bk in self.bk_entries: + s += f" {bk},\n" + s += " ]\n" + s += ")" + return s + + def __hash__(self): + h = 0 + for song in self.song_entries: + h ^= hash(song) + for mseq in self.mseq_entries: + h ^= hash(mseq) + for bk in self.bk_entries: + h ^= hash(bk) + return h + + +@dataclass +class INITHeader: + signature: bytes = b"INIT" + size: int = 0 + entriesOffset: int = 0 + entriesSize: int = 0 + tblOffset: int = 0 + tblSize: int = 0 + shortsOffset: int = 0 + shortsSize: int = 0 + unk_14: int = 0 + unk_18: int = 0 + unk_1c: int = 0 + + fstring = ">4si hhhh hhi ii " + length = 0x20 + + def __iter__(self): + return iter(astuple(self)) + + def __post__init__(self) -> int: + assert self.signature == b"INIT" + + +# see au_load_BK_headers +@dataclass(frozen=True) +class BufferEntry: + fileIndex: int + bankIndex: int + bankGroup: int + + fstring = ">HBB" + length = 0x04 + + def __iter__(self): + return iter(astuple(self)) + + +class EndOfDataException(Exception): + pass + + +@dataclass(frozen=True) +class InitSongEntry: + bgmFileIndex: int + bkFile0Index: int + bkFile1Index: int + bkFile2Index: int + + fstring = ">Hhhh" + length = 0x08 + + def __iter__(self): + return iter(astuple(self)) + + +if splat_loaded: + + class N64SegPm_sbn(N64Segment): + def split(self, rom_bytes): + dir = options.opts.asset_path / self.dir / self.name + data = rom_bytes[self.rom_start : self.rom_end] + + if dir.exists(): + raise Exception(f"SBN directory {dir} already exists") + + sbn = SBN() + byte_count = sbn.decode(data) + sbn.write(dir) + + assert self.rom_start is not None + if self.rom_end: + assert byte_count == self.rom_end - self.rom_start + else: + self.rom_end = self.rom_start + byte_count + + def get_linker_entries(self): + dir = options.opts.asset_path / self.dir / self.name + out = options.opts.asset_path / self.dir / (self.name + ".sbn") + + sbn = SBN() + config_files = sbn.read( + dir + ) # TODO: LayeredFS/AssetsFS read, supporting merges + inputs = config_files + [dir / f.file_name() for f in sbn.files] + return [ + LinkerEntry( + self, + inputs, + out, + ".data", + ), + ] + + +def get_song_symbol_name(song_id: int) -> str: + song_names = { + 0x00000000: "SONG_TOAD_TOWN", + 0x00000002: "SONG_NORMAL_BATTLE", + 0x00000003: "SONG_SPECIAL_BATTLE", + 0x00000004: "SONG_JR_TROOPA_BATTLE", + 0x00000005: "SONG_FINAL_BOWSER_BATTLE", + 0x00000007: "SONG_GOOMBA_KING_BATTLE", + 0x00000008: "SONG_KOOPA_BROS_BATTLE", + 0x00000009: "SONG_FAKE_BOWSER_BATTLE", + 0x0000000A: "SONG_TUTANKOOPA_BATTLE", + 0x0000000B: "SONG_TUBBA_BLUBBA_BATTLE", + 0x0000000C: "SONG_GENERAL_GUY_BATTLE", + 0x0000000D: "SONG_LAVA_PIRANHA_BATTLE", + 0x0000000E: "SONG_HUFF_N_PUFF_BATTLE", + 0x0000000F: "SONG_CRYSTAL_KING_BATTLE", + 0x00000010: "SONG_GOOMBA_VILLAGE", + 0x00000011: "SONG_PLEASANT_PATH", + 0x00000012: "SONG_FUZZY_ATTACK", + 0x00000013: "SONG_KOOPA_VILLAGE", + 0x00000014: "SONG_KOOPA_FORTRESS", + 0x00000015: "SONG_DRY_DRY_OUTPOST", + 0x00000016: "SONG_MT_RUGGED", + 0x00000017: "SONG_DRY_DRY_DESERT", + 0x00000018: "SONG_DRY_DRY_RUINS", + 0x00000019: "SONG_RUINS_BASEMENT", + 0x0000001A: "SONG_FOREVER_FOREST", + 0x0000001B: "SONG_BOOS_MANSION", + 0x0000001C: "SONG_CHEERFUL_BOOS_MANSION", + 0x0000001D: "SONG_GUSTY_GULCH", + 0x0000001E: "SONG_TUBBAS_MANOR", + 0x0000001F: "SONG_TUBBA_ESCAPE", + 0x00000020: "SONG_SHY_GUY_TOYBOX", + 0x00000021: "SONG_TOYBOX_TRAIN", + 0x00000022: "SONG_CREEPY_TOYBOX", + 0x00000024: "SONG_JADE_JUNGLE", + 0x00000025: "SONG_DEEP_JUNGLE", + 0x00000026: "SONG_YOSHIS_VILLAGE", + 0x00000027: "SONG_YOSHIS_PANIC", + 0x00000028: "SONG_RAPHAEL_RAVEN", + 0x00000029: "SONG_MT_LAVALAVA", + 0x0000002A: "SONG_VOLCANO_ESCAPE", + 0x0000002B: "SONG_STAR_WAY_OPENS", + 0x0000002C: "SONG_MASTER_BATTLE", + 0x0000002D: "SONG_RADIO_ISLAND_SOUNDS", + 0x0000002E: "SONG_RADIO_HOT_HITS", + 0x0000002F: "SONG_RADIO_GOLDEN_OLDIES", + 0x00000030: "SONG_FLOWER_FIELDS_CLOUDY", + 0x00000031: "SONG_FLOWER_FIELDS_SUNNY", + 0x00000032: "SONG_CLOUDY_CLIMB", + 0x00000033: "SONG_PUFF_PUFF_MACHINE", + 0x00000034: "SONG_SUN_TOWER_CLOUDY", + 0x00000035: "SONG_SUN_TOWER_SUNNY", + 0x00000037: "SONG_CRYSTAL_PALACE", + 0x00000038: "SONG_SHIVER_CITY", + 0x00000039: "SONG_PENGUIN_MYSTERY", + 0x0000003A: "SONG_SHIVER_SNOWFIELD", + 0x0000003B: "SONG_SHIVER_MOUNTAIN", + 0x0000003C: "SONG_STARBORN_VALLEY", + 0x0000003D: "SONG_MERLAR_THEME", + 0x0000003E: "SONG_MAIL_CALL", + 0x0000003F: "SONG_PEACHS_CASTLE_PARTY", + 0x00000040: "SONG_CHAPTER_END", + 0x00000041: "SONG_CHAPTER_START", + 0x00000042: "SONG_ITEM_UPGRADE", + 0x00000044: "SONG_PHONOGRAPH_MUSIC", + 0x00000045: "SONG_TUTANKOOPA_THEME", + 0x00000046: "SONG_KAMMY_KOOPA_THEME", + 0x00000047: "SONG_JR_TROOPA_THEME", + 0x00000048: "SONG_BULLET_BILL_ASSAULT", + 0x00000049: "SONG_MONTY_MOLE_ASSAULT", + 0x0000004A: "SONG_SHY_GUY_INVASION", + 0x0000004B: "SONG_TOAD_TOWN_TUNNELS", + 0x0000004C: "SONG_WHALE_THEME", + 0x0000004D: "SONG_FOREVER_FOREST_WARNING", + 0x0000004E: "SONG_YOSHI_KIDS_FOUND", + 0x0000004F: "SONG_UNUSED_FANFARE", + 0x00000050: "SONG_GOOMBA_KING_THEME", + 0x00000051: "SONG_KOOPA_BROS_INTERLUDE", + 0x00000052: "SONG_KOOPA_BROS_THEME", + 0x00000053: "SONG_TUTANKOOPA_WARNING", + 0x00000054: "SONG_TUTANKOOPA_REVEALED", + 0x00000055: "SONG_TUBBA_BLUBBA_THEME", + 0x00000056: "SONG_GENERAL_GUY_THEME", + 0x00000057: "SONG_LAVA_PIRANHA_THEME", + 0x00000058: "SONG_HUFF_N_PUFF_THEME", + 0x00000059: "SONG_CRYSTAL_KING_THEME", + 0x0000005A: "SONG_BLOOPER_THEME", + 0x0000005B: "SONG_MINIBOSS_BATTLE", + 0x0000005C: "SONG_MONSTAR_THEME", + 0x0000005D: "SONG_CLUB64", + 0x0000005E: "SONG_UNUSED_OPENING", + 0x0000005F: "SONG_BOWSERS_CASTLE_FALLS", + 0x00000060: "SONG_STAR_HAVEN", + 0x00000061: "SONG_SHOOTING_STAR_SUMMIT", + 0x00000062: "SONG_STARSHIP_THEME", + 0x00000063: "SONG_STAR_SANCTUARY", + 0x00000064: "SONG_BOWSERS_CASTLE", + 0x00000065: "SONG_BOWSERS_CASTLE_CAVES", + 0x00000066: "SONG_BOWSER_THEME", + 0x00000067: "SONG_BOWSER_BATTLE", + 0x00000068: "SONG_PEACH_WISHES", + 0x00000069: "SONG_FILE_SELECT", + 0x0000006A: "SONG_MAIN_THEME", + 0x0000006B: "SONG_BOWSER_ATTACKS", + 0x0000006C: "SONG_MARIO_FALLS", + 0x0000006D: "SONG_PEACH_APPEARS", + 0x0000006E: "SONG_THE_END", + 0x0000006F: "SONG_RECOVERED_STAR_ROD", + 0x00000070: "SONG_TWINK_THEME", + 0x00000071: "SONG_STIRRING_CAKE", + 0x00000072: "SONG_GOURMET_GUY_FREAKOUT", + 0x00000073: "SONG_PRISONER_PEACH_THEME", + 0x00000074: "SONG_PEACH_MISSION", + 0x00000075: "SONG_PEACH_SNEAKING", + 0x00000076: "SONG_PEACH_CAUGHT", + 0x00000077: "SONG_PEACH_QUIZ_INTRO", + 0x00000078: "SONG_STAR_SPIRIT_THEME", + 0x00000079: "SONG_PENGUIN_WHODUNIT", + 0x0000007A: "SONG_PENGUIN_WAKES_UP", + 0x0000007B: "SONG_MAGIC_BEANSTALK", + 0x0000007C: "SONG_MERLEE_SPELL", + 0x0000007D: "SONG_LAKILESTER_THEME", + 0x0000007E: "SONG_GOOMBA_BROS_RETREAT", + 0x0000007F: "SONG_SUNSHINE_RETURNS", + 0x00000080: "SONG_RIDING_THE_RAILS", + 0x00000081: "SONG_RIDING_THE_WHALE", + 0x00000082: "SONG_NEW_PARTNER", + 0x00000083: "SONG_DRY_DRY_RUINS_APPEAR", + 0x00000084: "SONG_CANDY_CANES", + 0x00000085: "SONG_PLAYROOM", + 0x00000086: "SONG_MOUSTAFA_THEME", + 0x00000087: "SONG_GAME_OVER", + 0x00000088: "SONG_TAKING_REST", + 0x00000089: "SONG_FLOWER_NPC_THEME", + 0x0000008A: "SONG_FLOWER_GATE_APPEARS", + 0x0000008B: "SONG_BATTLE_END", + 0x0000008C: "SONG_POP_DIVA_SONG", + 0x0000008D: "SONG_BOO_MINIGAME", + 0x0000008E: "SONG_LEVEL_UP", + 0x00000090: "SONG_PARADE_DAY", + 0x00000091: "SONG_PARADE_NIGHT", + 0x00000094: "SONG_MARIO_BROS_HOUSE", + 0x00000095: "SONG_INTRO_STORY", + 0x00000096: "SONG_NEW_PARTNER_JP", + } + return song_names.get(song_id, f"null") + + +# Sorts a list of dicts by their "id" key. If "id" is "auto" or missing, it is sorted to the end. +def sort_by_id_or_auto(list): + def get_id(item): + id = item.get("id") + return 0xFFFF if id == "auto" else id + + list.sort(key=get_id) diff --git a/ver/pal/splat.yaml b/ver/pal/splat.yaml index 22ef5689e9..a37d5f5aeb 100644 --- a/ver/pal/splat.yaml +++ b/ver/pal/splat.yaml @@ -11525,163 +11525,9 @@ segments: - [0xE8D120, bin] -############# -### Audio ### -############# - - [0x13A0000, bin, SBN] - - [0x13A07C0, bin, bgm/Battle_Fanfare_02] # BGM start - - [0x13A2160, bin, bgm/Hey_You_03] - - [0x13A3740, bin, bgm/The_Goomba_King_s_Decree_07] - - [0x13A43F0, bin, bgm/Attack_of_the_Koopa_Bros_08] - - [0x13A73C0, bin, bgm/Trojan_Bowser_09] - - [0x13A8D40, bin, bgm/Chomp_Attack_0A] - - [0x13A9600, bin, bgm/Ghost_Gulping_0B] - - [0x13AA550, bin, bgm/Keeping_Pace_0C] - - [0x13ABAE0, bin, bgm/Go_Mario_Go_0D] - - [0x13ADEC0, bin, bgm/Huffin_and_Puffin_0E] - - [0x13AFD20, bin, bgm/Freeze_0F] - - [0x13B10D0, bin, bgm/Winning_a_Battle_8B] - - [0x13B16C0, bin, bgm/Winning_a_Battle_and_Level_Up_8E] - - [0x13B2320, bin, bgm/Jr_Troopa_Battle_04] - - [0x13B3C20, bin, bgm/Final_Bowser_Battle_interlude_05] - - [0x13B5F40, bin, bgm/Master_Battle_2C] - - [0x13B6F80, bin, bgm/Game_Over_87] - - [0x13B71D0, bin, bgm/Resting_at_the_Toad_House_88] - - [0x13B7370, bin, bgm/Running_around_the_Heart_Pillar_in_Ch1_84] - - [0x13B7570, bin, bgm/Tutankoopa_s_Warning_45] - - [0x13B8940, bin, bgm/Kammy_Koopa_s_Theme_46] - - [0x13B93D0, bin, bgm/Jr_Troopa_s_Theme_47] - - [0x13B9BC0, bin, bgm/Goomba_King_s_Theme_50] - - [0x13BA6F0, bin, bgm/Koopa_Bros_Defeated_51] - - [0x13BABD0, bin, bgm/Koopa_Bros_Theme_52] - - [0x13BC810, bin, bgm/Tutankoopa_s_Warning_2_53] - - [0x13BDBF0, bin, bgm/Tutankoopa_s_Theme_54] - - [0x13BF2E0, bin, bgm/Tubba_Blubba_s_Theme_55] - - [0x13C0FF0, bin, bgm/General_Guy_s_Theme_56] - - [0x13C1780, bin, bgm/Lava_Piranha_s_Theme_57] - - [0x13C2A00, bin, bgm/Huff_N_Puff_s_Theme_58] - - [0x13C3A00, bin, bgm/Crystal_King_s_Theme_59] - - [0x13C4810, bin, bgm/Blooper_s_Theme_5A] - - [0x13C5240, bin, bgm/Midboss_Theme_5B] - - [0x13C6260, bin, bgm/Monstar_s_Theme_5C] - - [0x13C7840, bin, bgm/Moustafa_s_Theme_86] - - [0x13C7E20, bin, bgm/Fuzzy_Searching_Minigame_85] - - [0x13C8E20, bin, bgm/Phonograph_in_Mansion_44] - - [0x13C9AC0, bin, bgm/Toad_Town_00] - - [0x13CE130, bin, bgm/Bill_Blaster_Theme_48] - - [0x13CEF90, bin, bgm/Monty_Mole_Theme_in_Flower_Fields_49] - - [0x13D0590, bin, bgm/Shy_Guys_in_Toad_Town_4A] - - [0x13D18B0, bin, bgm/Whale_s_Problem_4C] - - [0x13D2220, bin, bgm/Toad_Town_Sewers_4B] - - [0x13D3060, bin, bgm/Unused_Theme_4D] - - [0x13D3AA0, bin, bgm/Mario_s_House_Prologue_3E] - - [0x13D3F10, bin, bgm/Peach_s_Party_3F] - - [0x13D54E0, bin, bgm/Goomba_Village_01] - - [0x13D5ED0, bin, bgm/Pleasant_Path_11] - - [0x13D6690, bin, bgm/Fuzzy_s_Took_My_Shell_12] - - [0x13D79E0, bin, bgm/Koopa_Village_13] - - [0x13D8570, bin, bgm/Koopa_Bros_Fortress_14] - - [0x13D9160, bin, bgm/Dry_Dry_Ruins_18] - - [0x13DA0D0, bin, bgm/Dry_Dry_Ruins_Mystery_19] - - [0x13DA450, bin, bgm/Mt_Rugged_16] - - [0x13DAF20, bin, bgm/Dry_Dry_Desert_Oasis_17] - - [0x13DC130, bin, bgm/Dry_Dry_Outpost_15] - - [0x13DCCC0, bin, bgm/Forever_Forest_1A] - - [0x13DE130, bin, bgm/Boo_s_Mansion_1B] - - [0x13DF3E0, bin, bgm/Bow_s_Theme_1C] - - [0x13E0F00, bin, bgm/Gusty_Gulch_Adventure_1D] - - [0x13E2F30, bin, bgm/Tubba_Blubba_s_Castle_1E] - - [0x13E5500, bin, bgm/The_Castle_Crumbles_1F] - - [0x13E65E0, bin, bgm/Shy_Guy_s_Toy_Box_20] - - [0x13E74A0, bin, bgm/Toy_Train_Travel_21] - - [0x13E7E10, bin, bgm/Big_Lantern_Ghost_s_Theme_22] - - [0x13E8410, bin, bgm/Jade_Jungle_24] - - [0x13EA880, bin, bgm/Deep_Jungle_25] - - [0x13EBC00, bin, bgm/Lavalava_Island_26] - - [0x13EE690, bin, bgm/Search_for_the_Fearsome_5_27] - - [0x13F0A00, bin, bgm/Raphael_the_Raven_28] - - [0x13F2520, bin, bgm/Hot_Times_in_Mt_Lavalava_29] - - [0x13F5C80, bin, bgm/Escape_from_Mt_Lavalava_2A] - - [0x13F8ED0, bin, bgm/Cloudy_Climb_32] - - [0x13F92B0, bin, bgm/Puff_Puff_Machine_33] - - [0x13FAFF0, bin, bgm/Flower_Fields_30] - - [0x13FC8D0, bin, bgm/Flower_Fields_Sunny_31] - - [0x13FDF40, bin, bgm/Sun_s_Tower_34] - - [0x13FF500, bin, bgm/Sun_s_Celebration_35] - - [0x1401700, bin, bgm/Shiver_City_38] - - [0x1402E50, bin, bgm/Detective_Mario_39] - - [0x1404220, bin, bgm/Snow_Road_3A] - - [0x1404CB0, bin, bgm/Over_Shiver_Mountain_3B] - - [0x1405B30, bin, bgm/Starborn_Valley_3C] - - [0x1406690, bin, bgm/Sanctuary_3D] - - [0x1406B70, bin, bgm/Crystal_Palace_37] - - [0x1407F80, bin, bgm/Star_Haven_60] - - [0x1409640, bin, bgm/Shooting_Star_Summit_61] - - [0x140A050, bin, bgm/Legendary_Star_Ship_62] - - [0x140C270, bin, bgm/Star_Sanctuary_63] - - [0x140CED0, bin, bgm/Bowser_s_Castle_-_Caves_65] - - [0x140EE40, bin, bgm/Bowser_s_Castle_64] - - [0x1413390, bin, bgm/Star_Elevator_2B] - - [0x14151F0, bin, bgm/Goomba_Bros_Defeated_7E] - - [0x14159C0, bin, bgm/Farewell_Twink_70] - - [0x1417200, bin, bgm/Peach_Cooking_71] - - [0x1417680, bin, bgm/Gourmet_Guy_72] - - [0x1418600, bin, bgm/Hope_on_the_Balcony_Peach_1_73] - - [0x1419070, bin, bgm/Peach_s_Theme_2_74] - - [0x141A0C0, bin, bgm/Peach_Sneaking_75] - - [0x141AA40, bin, bgm/Peach_Captured_76] - - [0x141AD90, bin, bgm/Quiz_Show_Intro_77] - - [0x141BEA0, bin, bgm/Unconscious_Mario_78] - - [0x141C780, bin, bgm/Petunia_s_Theme_89] - - [0x141DC00, bin, bgm/Flower_Fields_Door_appears_8A] - - [0x141E190, bin, bgm/Beanstalk_7B] - - [0x141EE20, bin, bgm/Lakilester_s_Theme_7D] - - [0x1420230, bin, bgm/The_Sun_s_Back_7F] - - [0x1421260, bin, bgm/Shiver_City_in_Crisis_79] - - [0x1422460, bin, bgm/Solved_Shiver_City_Mystery_7A] - - [0x1422D00, bin, bgm/Merlon_s_Spell_7C] - - [0x1423DC0, bin, bgm/Bowser_s_Theme_66] - - [0x1425590, bin, bgm/Train_Travel_80] - - [0x14260E0, bin, bgm/Whale_Trip_81] - - [0x1427000, bin, bgm/Chanterelle_s_Song_8C] - - [0x1427610, bin, bgm/Boo_s_Game_8D] - - [0x1428B30, bin, bgm/Dry_Dry_Ruins_rises_up_83] - - [0x1429570, bin, bgm/End_of_Chapter_40] - - [0x142AAF0, bin, bgm/Beginning_of_Chapter_41] - - [0x142B820, bin, bgm/Hammer_and_Jump_Upgrade_42] - - [0x142BD90, bin, bgm/Found_Baby_Yoshi_s_4E] - - [0x142C360, bin, bgm/New_Partner_JAP_96] - - [0x142D110, bin, bgm/Unused_YI_Fanfare_4F] - - [0x142D3E0, bin, bgm/Unused_YI_Fanfare_2_5D] - - [0x1430880, bin, bgm/Peach_s_Castle_inside_Bubble_5E] - - [0x1432A50, bin, bgm/Angry_Bowser_67] - - [0x1435510, bin, bgm/Bowser_s_Castle_explodes_5F] - - [0x1436280, bin, bgm/Peach_s_Wish_68] - - [0x1438520, bin, bgm/File_Select_69] - - [0x1438F90, bin, bgm/Title_Screen_6A] - - [0x143B830, bin, bgm/Peach_s_Castle_in_Crisis_6B] - - [0x143D3B0, bin, bgm/Mario_falls_from_Bowser_s_Castle_6C] - - [0x143D690, bin, bgm/Peach_s_Arrival_6D] - - [0x143EF30, bin, bgm/Star_Rod_Recovered_6F] - - [0x143FA30, bin, bgm/Mario_s_House_94] - - [0x14408A0, bin, bgm/Bowser_s_Attacks_95] - - [0x1443C60, bin, bgm/End_Parade_1_90] - - [0x14485F0, bin, bgm/End_Parade_2_91] - - [0x144BE90, bin, bgm/Ths_End_6E] - - [0x144CC80, bin, bgm/Koopa_Radio_Station_2D] - - [0x144D210, bin, bgm/Ths_End_Low_Frequency__2E] - - [0x144D8F0, bin, bgm/SMW_Remix_2F] - - [0x144DE70, bin, bgm/New_Partner_82] # BGM end - - [0x144E860, bin, FAE860] # more audio stuff - - [0x1DE25C0, bin, 19425C0] # INIT file -############### -### Sprites ### -############### + - [0x13A0000, pm_sbn, audio] - [0x1DE2C40, bin, 1942C40] - - start: 0x1DF0000 - align: 8 - type: pm_sprites - name: sprites + - { start: 0x1DF0000, align: 8, type: pm_sprites, name: sprites } - [0x202f208, bin] # end of sprite data - todo: figure out what this is - start: 0x2030000 diff --git a/ver/us/splat.yaml b/ver/us/splat.yaml index c862bc12a1..bf4e685b3e 100644 --- a/ver/us/splat.yaml +++ b/ver/us/splat.yaml @@ -15632,155 +15632,7 @@ segments: - [auto, c, flo_19_4_clouds] - [auto, c, flo_19_5_beanstalk] -############# -### Audio ### -############# - - [0xF00000, bin, SBN] - - [0xF007C0, bin, bgm/Battle_Fanfare_02] # BGM start - - [0xF02160, bin, bgm/Hey_You_03] - - [0xF03740, bin, bgm/The_Goomba_King_s_Decree_07] - - [0xF043F0, bin, bgm/Attack_of_the_Koopa_Bros_08] - - [0xF073C0, bin, bgm/Trojan_Bowser_09] - - [0xF08D40, bin, bgm/Chomp_Attack_0A] - - [0xF09600, bin, bgm/Ghost_Gulping_0B] - - [0xF0A550, bin, bgm/Keeping_Pace_0C] - - [0xF0BAE0, bin, bgm/Go_Mario_Go_0D] - - [0xF0DEC0, bin, bgm/Huffin_and_Puffin_0E] - - [0xF0FD20, bin, bgm/Freeze_0F] - - [0xF110D0, bin, bgm/Winning_a_Battle_8B] - - [0xF116C0, bin, bgm/Winning_a_Battle_and_Level_Up_8E] - - [0xF12320, bin, bgm/Jr_Troopa_Battle_04] - - [0xF13C20, bin, bgm/Final_Bowser_Battle_interlude_05] - - [0xF15F40, bin, bgm/Master_Battle_2C] - - [0xF16F80, bin, bgm/Game_Over_87] - - [0xF171D0, bin, bgm/Resting_at_the_Toad_House_88] - - [0xF17370, bin, bgm/Running_around_the_Heart_Pillar_in_Ch1_84] - - [0xF17570, bin, bgm/Tutankoopa_s_Warning_45] - - [0xF18940, bin, bgm/Kammy_Koopa_s_Theme_46] - - [0xF193D0, bin, bgm/Jr_Troopa_s_Theme_47] - - [0xF19BC0, bin, bgm/Goomba_King_s_Theme_50] - - [0xF1A6F0, bin, bgm/Koopa_Bros_Defeated_51] - - [0xF1ABD0, bin, bgm/Koopa_Bros_Theme_52] - - [0xF1C810, bin, bgm/Tutankoopa_s_Warning_2_53] - - [0xF1DBF0, bin, bgm/Tutankoopa_s_Theme_54] - - [0xF1F2E0, bin, bgm/Tubba_Blubba_s_Theme_55] - - [0xF20FF0, bin, bgm/General_Guy_s_Theme_56] - - [0xF21780, bin, bgm/Lava_Piranha_s_Theme_57] - - [0xF22A00, bin, bgm/Huff_N_Puff_s_Theme_58] - - [0xF23A00, bin, bgm/Crystal_King_s_Theme_59] - - [0xF24810, bin, bgm/Blooper_s_Theme_5A] - - [0xF25240, bin, bgm/Midboss_Theme_5B] - - [0xF26260, bin, bgm/Monstar_s_Theme_5C] - - [0xF27840, bin, bgm/Moustafa_s_Theme_86] - - [0xF27E20, bin, bgm/Fuzzy_Searching_Minigame_85] - - [0xF28E20, bin, bgm/Phonograph_in_Mansion_44] - - [0xF29AC0, bin, bgm/Toad_Town_00] - - [0xF2E130, bin, bgm/Bill_Blaster_Theme_48] - - [0xF2EF90, bin, bgm/Monty_Mole_Theme_in_Flower_Fields_49] - - [0xF30590, bin, bgm/Shy_Guys_in_Toad_Town_4A] - - [0xF318B0, bin, bgm/Whale_s_Problem_4C] - - [0xF32220, bin, bgm/Toad_Town_Sewers_4B] - - [0xF33060, bin, bgm/Unused_Theme_4D] - - [0xF33AA0, bin, bgm/Mario_s_House_Prologue_3E] - - [0xF33F10, bin, bgm/Peach_s_Party_3F] - - [0xF354E0, bin, bgm/Goomba_Village_01] - - [0xF35ED0, bin, bgm/Pleasant_Path_11] - - [0xF36690, bin, bgm/Fuzzy_s_Took_My_Shell_12] - - [0xF379E0, bin, bgm/Koopa_Village_13] - - [0xF38570, bin, bgm/Koopa_Bros_Fortress_14] - - [0xF39160, bin, bgm/Dry_Dry_Ruins_18] - - [0xF3A0D0, bin, bgm/Dry_Dry_Ruins_Mystery_19] - - [0xF3A450, bin, bgm/Mt_Rugged_16] - - [0xF3AF20, bin, bgm/Dry_Dry_Desert_Oasis_17] - - [0xF3C130, bin, bgm/Dry_Dry_Outpost_15] - - [0xF3CCC0, bin, bgm/Forever_Forest_1A] - - [0xF3E130, bin, bgm/Boo_s_Mansion_1B] - - [0xF3F3E0, bin, bgm/Bow_s_Theme_1C] - - [0xF40F00, bin, bgm/Gusty_Gulch_Adventure_1D] - - [0xF42F30, bin, bgm/Tubba_Blubba_s_Castle_1E] - - [0xF45500, bin, bgm/The_Castle_Crumbles_1F] - - [0xF465E0, bin, bgm/Shy_Guy_s_Toy_Box_20] - - [0xF474A0, bin, bgm/Toy_Train_Travel_21] - - [0xF47E10, bin, bgm/Big_Lantern_Ghost_s_Theme_22] - - [0xF48410, bin, bgm/Jade_Jungle_24] - - [0xF4A880, bin, bgm/Deep_Jungle_25] - - [0xF4BC00, bin, bgm/Lavalava_Island_26] - - [0xF4E690, bin, bgm/Search_for_the_Fearsome_5_27] - - [0xF50A00, bin, bgm/Raphael_the_Raven_28] - - [0xF52520, bin, bgm/Hot_Times_in_Mt_Lavalava_29] - - [0xF55C80, bin, bgm/Escape_from_Mt_Lavalava_2A] - - [0xF58ED0, bin, bgm/Cloudy_Climb_32] - - [0xF592B0, bin, bgm/Puff_Puff_Machine_33] - - [0xF5AFF0, bin, bgm/Flower_Fields_30] - - [0xF5C8D0, bin, bgm/Flower_Fields_Sunny_31] - - [0xF5DF40, bin, bgm/Sun_s_Tower_34] - - [0xF5F500, bin, bgm/Sun_s_Celebration_35] - - [0xF61700, bin, bgm/Shiver_City_38] - - [0xF62E50, bin, bgm/Detective_Mario_39] - - [0xF64220, bin, bgm/Snow_Road_3A] - - [0xF64CB0, bin, bgm/Over_Shiver_Mountain_3B] - - [0xF65B30, bin, bgm/Starborn_Valley_3C] - - [0xF66690, bin, bgm/Sanctuary_3D] - - [0xF66B70, bin, bgm/Crystal_Palace_37] - - [0xF67F80, bin, bgm/Star_Haven_60] - - [0xF69640, bin, bgm/Shooting_Star_Summit_61] - - [0xF6A050, bin, bgm/Legendary_Star_Ship_62] - - [0xF6C270, bin, bgm/Star_Sanctuary_63] - - [0xF6CED0, bin, bgm/Bowser_s_Castle_-_Caves_65] - - [0xF6EE40, bin, bgm/Bowser_s_Castle_64] - - [0xF73390, bin, bgm/Star_Elevator_2B] - - [0xF751F0, bin, bgm/Goomba_Bros_Defeated_7E] - - [0xF759C0, bin, bgm/Farewell_Twink_70] - - [0xF77200, bin, bgm/Peach_Cooking_71] - - [0xF77680, bin, bgm/Gourmet_Guy_72] - - [0xF78600, bin, bgm/Hope_on_the_Balcony_Peach_1_73] - - [0xF79070, bin, bgm/Peach_s_Theme_2_74] - - [0xF7A0C0, bin, bgm/Peach_Sneaking_75] - - [0xF7AA40, bin, bgm/Peach_Captured_76] - - [0xF7AD90, bin, bgm/Quiz_Show_Intro_77] - - [0xF7BEA0, bin, bgm/Unconscious_Mario_78] - - [0xF7C780, bin, bgm/Petunia_s_Theme_89] - - [0xF7DC00, bin, bgm/Flower_Fields_Door_appears_8A] - - [0xF7E190, bin, bgm/Beanstalk_7B] - - [0xF7EE20, bin, bgm/Lakilester_s_Theme_7D] - - [0xF80230, bin, bgm/The_Sun_s_Back_7F] - - [0xF81260, bin, bgm/Shiver_City_in_Crisis_79] - - [0xF82460, bin, bgm/Solved_Shiver_City_Mystery_7A] - - [0xF82D00, bin, bgm/Merlon_s_Spell_7C] - - [0xF83DC0, bin, bgm/Bowser_s_Theme_66] - - [0xF85590, bin, bgm/Train_Travel_80] - - [0xF860E0, bin, bgm/Whale_Trip_81] - - [0xF87000, bin, bgm/Chanterelle_s_Song_8C] - - [0xF87610, bin, bgm/Boo_s_Game_8D] - - [0xF88B30, bin, bgm/Dry_Dry_Ruins_rises_up_83] - - [0xF89570, bin, bgm/End_of_Chapter_40] - - [0xF8AAF0, bin, bgm/Beginning_of_Chapter_41] - - [0xF8B820, bin, bgm/Hammer_and_Jump_Upgrade_42] - - [0xF8BD90, bin, bgm/Found_Baby_Yoshi_s_4E] - - [0xF8C360, bin, bgm/New_Partner_JAP_96] - - [0xF8D110, bin, bgm/Unused_YI_Fanfare_4F] - - [0xF8D3E0, bin, bgm/Unused_YI_Fanfare_2_5D] - - [0xF90880, bin, bgm/Peach_s_Castle_inside_Bubble_5E] - - [0xF92A50, bin, bgm/Angry_Bowser_67] - - [0xF95510, bin, bgm/Bowser_s_Castle_explodes_5F] - - [0xF96280, bin, bgm/Peach_s_Wish_68] - - [0xF98520, bin, bgm/File_Select_69] - - [0xF98F90, bin, bgm/Title_Screen_6A] - - [0xF9B830, bin, bgm/Peach_s_Castle_in_Crisis_6B] - - [0xF9D3B0, bin, bgm/Mario_falls_from_Bowser_s_Castle_6C] - - [0xF9D690, bin, bgm/Peach_s_Arrival_6D] - - [0xF9EF30, bin, bgm/Star_Rod_Recovered_6F] - - [0xF9FA30, bin, bgm/Mario_s_House_94] - - [0xFA08A0, bin, bgm/Bowser_s_Attacks_95] - - [0xFA3C60, bin, bgm/End_Parade_1_90] - - [0xFA85F0, bin, bgm/End_Parade_2_91] - - [0xFABE90, bin, bgm/Ths_End_6E] - - [0xFACC80, bin, bgm/Koopa_Radio_Station_2D] - - [0xFAD210, bin, bgm/Ths_End_Low_Frequency__2E] - - [0xFAD8F0, bin, bgm/SMW_Remix_2F] - - [0xFADE70, bin, bgm/New_Partner_82] # BGM end - - [0xFAE860, bin] # more audio stuff - - [0x19425C0, bin] # INIT file + - [0xF00000, pm_sbn, audio] - [0x1942C40, bin] - { start: 0x1943000, align: 8, type: pm_sprites, name: sprites } - [0x1B82208, bin] # end of sprite data - todo: figure out what this is