mirror of
https://github.com/pmret/papermario.git
synced 2024-11-08 12:02:30 +01:00
Make audio respect the asset stack (#1107)
This commit is contained in:
parent
6e80d188e7
commit
a3f35fe27a
@ -1,25 +1,183 @@
|
|||||||
#! /usr/bin/python3
|
#! /usr/bin/python3
|
||||||
|
|
||||||
from sys import argv, path
|
import argparse
|
||||||
|
from sys import path
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import sys
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
sys.path.append(str(Path(__file__).parent.parent))
|
||||||
|
sys.path.append(str(Path(__file__).parent.parent.parent))
|
||||||
|
sys.path.append(str(Path(__file__).parent.parent.parent))
|
||||||
|
sys.path.append(str(Path(__file__).parent.parent.parent / "splat"))
|
||||||
|
from common import get_asset_path
|
||||||
|
|
||||||
# allow importing splat_ext
|
# allow importing splat_ext
|
||||||
path.append(str(Path(__file__).parent.parent.parent))
|
path.append(str(Path(__file__).parent.parent.parent))
|
||||||
|
|
||||||
from splat_ext.pm_sbn import SBN
|
from splat_ext.pm_sbn import SBN, BufferEntry, InitSongEntry, SBNFile
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
out = argv[1]
|
|
||||||
inputs = [Path(p) for p in argv[2:]]
|
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
|
||||||
|
def from_yaml(yaml_path: Path, asset_stack: Tuple[Path, ...]) -> SBN:
|
||||||
sbn = SBN()
|
sbn = SBN()
|
||||||
|
|
||||||
for input in inputs:
|
with yaml_path.open("r") as f:
|
||||||
# if input is a sbn.yaml, read its directory
|
config = yaml.safe_load(f)
|
||||||
if input.suffix == ".yaml":
|
|
||||||
sbn.read(input.parent)
|
unknown_bin_path = get_asset_path("audio/unknown.bin", asset_stack)
|
||||||
|
with open(unknown_bin_path, "rb") as f:
|
||||||
|
sbn.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(sbn.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_path = get_asset_path(f"audio/{filename}", asset_stack)
|
||||||
|
sbn_file.read(sbn_file_path)
|
||||||
|
|
||||||
|
if fakesize is not None:
|
||||||
|
sbn_file.fakesize = fakesize
|
||||||
|
else:
|
||||||
|
sbn_file.fakesize = sbn_file.size
|
||||||
|
|
||||||
|
# Replace sbn.files[id]
|
||||||
|
if id < len(sbn.files):
|
||||||
|
print("Overwriting file ID {:02X}", id)
|
||||||
|
sbn.files[id] = sbn_file
|
||||||
|
elif id == len(sbn.files):
|
||||||
|
sbn.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(sbn.init.song_entries)
|
||||||
|
assert type(id) == int
|
||||||
|
|
||||||
|
# Lookup file ID
|
||||||
|
file = song.get("file")
|
||||||
|
assert type(file) == str
|
||||||
|
file_id = sbn.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(sbn.lookup_file_id(bk_file))
|
||||||
|
|
||||||
|
init_song_entry = InitSongEntry(file_id, bk_file_ids[0], bk_file_ids[1], bk_file_ids[2])
|
||||||
|
|
||||||
|
# Replace sbn.init.song_entries[id]
|
||||||
|
if id < len(sbn.init.song_entries):
|
||||||
|
print(f"Overwriting song ID {id:02X}")
|
||||||
|
sbn.init.song_entries[id] = init_song_entry
|
||||||
|
elif id == len(sbn.init.song_entries):
|
||||||
|
sbn.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(sbn.init.mseq_entries)
|
||||||
|
assert type(id) == int
|
||||||
|
|
||||||
|
# Lookup file ID
|
||||||
|
file = mseq.get("file")
|
||||||
|
assert type(file) == str
|
||||||
|
file_id = sbn.lookup_file_id(file)
|
||||||
|
|
||||||
|
# Replace sbn.init.mseq_entries[id]
|
||||||
|
if id < len(sbn.init.mseq_entries):
|
||||||
|
print(f"Overwriting MSEQ ID {id:02X}")
|
||||||
|
sbn.init.mseq_entries[id] = file_id
|
||||||
|
elif id == len(sbn.init.mseq_entries):
|
||||||
|
sbn.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(sbn.init.bk_entries)
|
||||||
|
assert type(id) == int
|
||||||
|
|
||||||
|
file = bank.get("file")
|
||||||
|
assert type(file) == str
|
||||||
|
file_id = sbn.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 sbn.init.bk_entries[id]
|
||||||
|
if id < len(sbn.init.bk_entries):
|
||||||
|
print(f"Overwriting bank ID {id:02X}")
|
||||||
|
sbn.init.bk_entries[id] = entry
|
||||||
|
elif id == len(sbn.init.bk_entries):
|
||||||
|
sbn.init.bk_entries.append(entry)
|
||||||
|
else:
|
||||||
|
raise Exception(f"Invalid bank ID: {id:02X} - cannot have gaps")
|
||||||
|
return sbn
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(description="Builds SBN")
|
||||||
|
parser.add_argument("out")
|
||||||
|
parser.add_argument("asset_stack")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
out_path = Path(args.out)
|
||||||
|
asset_stack = tuple(Path(d) for d in args.asset_stack.split(","))
|
||||||
|
|
||||||
|
yaml_path = get_asset_path(Path("audio/sbn.yaml"), asset_stack)
|
||||||
|
sbn = from_yaml(yaml_path, asset_stack)
|
||||||
|
|
||||||
data = sbn.encode()
|
data = sbn.encode()
|
||||||
|
|
||||||
with open(out, "wb") as f:
|
with open(out_path, "wb") as f:
|
||||||
f.write(data)
|
f.write(data)
|
||||||
|
@ -301,7 +301,7 @@ def write_ninja_rules(
|
|||||||
|
|
||||||
ninja.rule("effect_data", command=f"$python {BUILD_TOOLS}/effects.py $in_yaml $out_dir")
|
ninja.rule("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")
|
ninja.rule("pm_sbn", command=f"$python {BUILD_TOOLS}/audio/sbn.py $out $asset_stack")
|
||||||
|
|
||||||
with Path("tools/permuter_settings.toml").open("w") as f:
|
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")
|
f.write(f"compiler_command = \"{cc} {CPPFLAGS.replace('$version', 'pal')} {cflags} -DPERMUTER -fforce-addr\"\n")
|
||||||
@ -1060,7 +1060,15 @@ class Configure:
|
|||||||
build(entry.object_path, [entry.object_path.with_suffix("")], "bin")
|
build(entry.object_path, [entry.object_path.with_suffix("")], "bin")
|
||||||
elif seg.type == "pm_sbn":
|
elif seg.type == "pm_sbn":
|
||||||
sbn_path = entry.object_path.with_suffix("")
|
sbn_path = entry.object_path.with_suffix("")
|
||||||
build(sbn_path, entry.src_paths, "pm_sbn") # could have non-yaml inputs be implicit
|
build(
|
||||||
|
sbn_path,
|
||||||
|
entry.src_paths,
|
||||||
|
"pm_sbn",
|
||||||
|
variables={
|
||||||
|
"asset_stack": ",".join(self.asset_stack),
|
||||||
|
},
|
||||||
|
asset_deps=entry.src_paths,
|
||||||
|
)
|
||||||
build(entry.object_path, [sbn_path], "bin")
|
build(entry.object_path, [sbn_path], "bin")
|
||||||
elif seg.type == "linker" or seg.type == "linker_offset":
|
elif seg.type == "linker" or seg.type == "linker_offset":
|
||||||
pass
|
pass
|
||||||
|
@ -274,139 +274,6 @@ class SBN:
|
|||||||
f.write(f" bank_group: 0x{entry.bankGroup:02X}\n")
|
f.write(f" bank_group: 0x{entry.bankGroup:02X}\n")
|
||||||
f.write("\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:
|
def lookup_file_id(self, filename: str) -> int:
|
||||||
for id, sbn_file in enumerate(self.files):
|
for id, sbn_file in enumerate(self.files):
|
||||||
if sbn_file.file_name() == filename:
|
if sbn_file.file_name() == filename:
|
||||||
@ -689,13 +556,10 @@ if splat_loaded:
|
|||||||
dir = options.opts.asset_path / self.dir / self.name
|
dir = options.opts.asset_path / self.dir / self.name
|
||||||
out = options.opts.asset_path / self.dir / (self.name + ".sbn")
|
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 [
|
return [
|
||||||
LinkerEntry(
|
LinkerEntry(
|
||||||
self,
|
self,
|
||||||
inputs,
|
[dir],
|
||||||
out,
|
out,
|
||||||
".data",
|
".data",
|
||||||
),
|
),
|
||||||
@ -849,12 +713,3 @@ def get_song_symbol_name(song_id: int) -> str:
|
|||||||
0x00000096: "SONG_NEW_PARTNER_JP",
|
0x00000096: "SONG_NEW_PARTNER_JP",
|
||||||
}
|
}
|
||||||
return song_names.get(song_id, f"null")
|
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)
|
|
||||||
|
Loading…
Reference in New Issue
Block a user