diff --git a/TaikoNus3bankMake/README.md b/TaikoNus3bankMake/README.md index 7a433fd..75b3cfb 100644 --- a/TaikoNus3bankMake/README.md +++ b/TaikoNus3bankMake/README.md @@ -1,11 +1,52 @@ -# Taiko no Tatsujin - Simple Nus3bank Creation Tool +# Taiko no Tatsujin - Nus3bank Creation Tool -Simple Python 3 script to allows you to quickly create nus3bank files and specify in-game audio preview points. +Python 3 scripts that converts audio to a Taiko no Tatsujin compatable `.nus3bank` file. +Accepted file types: `.mp3`, `.wav`, `.flac` and whatever else pydub supports. -`Usage: nus3.py ` +``` +usage: conv.py [-h] [input_audio] [audio_type] [game] [preview_point] [song_id] -Only supports specific output file names, adheering to the game's ID system. -E.g: `song_abs.nus3bank` up to `song_fungus.nus3bank` +Convert audio to nus3bank -By default includes template files for Nijiiro. -`nus3.py` can be modified along with the templates to specifiy offsets for other `.nus3bank` files +positional arguments: + input_audio Input audio file path. + audio_type Type of input audio (e.g., bnsf, at9, idsp, lopus). + game Game type (e.g., nijiiro, ns1, ps4). + preview_point Audio preview point in ms. + song_id Song ID for the nus3bank file. +``` + +By default includes support for Taiko no Tatsujin Wii U 3, NS1, PS4 and Nijiiro. +Support for other Taiko no Tatsujin games that use `.nus3bank` can be added in the future. + +### Prerequisites +[Python 3.12.3](https://www.python.org/downloads/) or newer installed. +Python 3 Module pydub `pip install pydub` + +### Supported Audio Formats + +| Audio Format | NS1 | PS4 | WIIU3 | Nijiiro | +| ------------- | ------------- | ------------- | ------------- | ------------- | +| WAV (PCM) | ✅ | ✅ | ✅ | ✅ | +| BNSF (IS14) | ✅ | ✅ | ❓ | ✅ | +| Nintendo OPUS | ✅ | ❌ | ❌ | ❌ | +| Nintendo IDSP | ✅ | ❌ | ✅ | ✅ | +| Sony AT9 | ❌ | ✅ | ❌ | ❌ | + +### Known Limitations +It seems that if a IS14 BNSF .nus3bank file is too long/too large in size, then it'll fail to play in "Song Select", even if the "Preview Point" is properly set. + +When it comes to Song ID: +Nijiiro can have Song IDs ranging from 3 to 8 characters. +Wii U, PS4 and NS1 can only have Song IDs ranging from 3 to 6 characters. +Exceeding this will result in an error. + +## Tools Used +at9tool - Used to convert audio to the Sony AT9 format. +[VGAudioCli](https://github.com/Thealexbarney/VGAudio) - Used to convert audio to Nintendo IDSP and Nintendo OPUS. +[G.722.1 Reference Tool](https://www.itu.int/rec/T-REC-G.722.1-200505-I/en) - Used to convert audio to Polycom Siren 14 + +### Special Thanks +Steam User [descatal](https://steamcommunity.com/id/descatal) for writing [this](https://exvsfbce.home.blog/2020/02/04/guide-to-encoding-bnsf-is14-audio-files-converting-wav-back-to-bnsf-is14/) guide on how to create/encode `bnsf` files. +[korenkonder](https://github.com/korenkonder) for compiling the G.722.1 tool used in this project. +[Kamui/despairoharmony](https://github.com/despairoharmony) for some of the Nijiiro `.nus3bank` template research. diff --git a/TaikoNus3bankMake/bin/VGAudio.dll b/TaikoNus3bankMake/bin/VGAudio.dll new file mode 100644 index 0000000..815ee24 Binary files /dev/null and b/TaikoNus3bankMake/bin/VGAudio.dll differ diff --git a/TaikoNus3bankMake/bin/VGAudioCli.exe b/TaikoNus3bankMake/bin/VGAudioCli.exe new file mode 100644 index 0000000..91d1dfb Binary files /dev/null and b/TaikoNus3bankMake/bin/VGAudioCli.exe differ diff --git a/TaikoNus3bankMake/bin/at9tool.exe b/TaikoNus3bankMake/bin/at9tool.exe new file mode 100644 index 0000000..c56eaf0 Binary files /dev/null and b/TaikoNus3bankMake/bin/at9tool.exe differ diff --git a/TaikoNus3bankMake/bin/encode.exe b/TaikoNus3bankMake/bin/encode.exe new file mode 100644 index 0000000..d77a33c Binary files /dev/null and b/TaikoNus3bankMake/bin/encode.exe differ diff --git a/TaikoNus3bankMake/conv.py b/TaikoNus3bankMake/conv.py new file mode 100644 index 0000000..76b7159 --- /dev/null +++ b/TaikoNus3bankMake/conv.py @@ -0,0 +1,70 @@ +import argparse +import subprocess +import os +import sys + +def convert_audio_to_nus3bank(input_audio, audio_type, game, preview_point, song_id): + # Determine the output filename for the nus3bank + output_filename = f"song_{song_id}.nus3bank" + converted_audio_file = f"{input_audio}.{audio_type}" + + # Determine the path to the run.py script within the 'script' folder + templates_folder = os.path.join(os.path.dirname(__file__), 'script') + run_py_path = os.path.join(templates_folder, 'run.py') + + # Prepare the command based on the audio type + if audio_type in ["bnsf", "at9", "idsp", "lopus", "wav"]: + # Construct the command to convert input audio to the specified type + conversion_command = ["python", run_py_path, audio_type, input_audio, f"{input_audio}.{audio_type}"] + + # Construct the command to create the nus3bank + nus3_command = ["python", run_py_path, "nus3", game, f"{input_audio}.{audio_type}", str(preview_point), output_filename] + + try: + # Execute the conversion command + subprocess.run(conversion_command, check=True) + + # Execute the nus3 command + subprocess.run(nus3_command, check=True) + + print(f"Conversion successful! Created {output_filename}") + + # Delete the non-nus3bank file after successful conversion + if os.path.exists(converted_audio_file): + os.remove(converted_audio_file) + print(f"Deleted {converted_audio_file}") + except subprocess.CalledProcessError as e: + print(f"Error: {e}") + else: + print(f"Unsupported audio type: {audio_type}") + +def main(): + # Create an argument parser + parser = argparse.ArgumentParser(description="Convert audio to nus3bank") + + # Define command-line arguments + parser.add_argument("input_audio", type=str, nargs="?", help="Input audio file path.") + parser.add_argument("audio_type", type=str, nargs="?", help="Type of input audio (e.g., wav, bnsf, at9, idsp, lopus).") + parser.add_argument("game", type=str, nargs="?", help="Game type (e.g., nijiiro, ns1, ps4, wiiu3).") + parser.add_argument("preview_point", type=int, nargs="?", help="Audio preview point in ms.") + parser.add_argument("song_id", type=str, nargs="?", help="Song ID for the nus3bank file.") + + # Parse the command-line arguments + args = parser.parse_args() + + # If no arguments are provided, display usage information + if len(sys.argv) == 1: + parser.print_help() + sys.exit(0) + + # Validate input audio file path + if not args.input_audio: + print("Error: Input audio file path is required.") + parser.print_help() + sys.exit(1) + + # Call function to convert audio to nus3bank + convert_audio_to_nus3bank(args.input_audio, args.audio_type, args.game, args.preview_point, args.song_id) + +if __name__ == "__main__": + main() diff --git a/TaikoNus3bankMake/nus3.py b/TaikoNus3bankMake/nus3.py deleted file mode 100644 index b91d08f..0000000 --- a/TaikoNus3bankMake/nus3.py +++ /dev/null @@ -1,141 +0,0 @@ -import sys -import os -import struct -import random -import toml - -def generate_random_uint16_hex(): - return format(random.randint(0, 65535), '04X') - -def load_template_config(): - # Load template configurations from config.toml (if needed in the future) - # This function can be expanded to load more template configurations if necessary - # For now, we don't need to use this function directly for selecting templates - return {} - -def select_template_name(output_file): - # Determine the appropriate template name based on the length of the output file name - base_filename = os.path.splitext(output_file)[0] - length = len(base_filename) - - if length == 8: - return "song_ABC" - elif length == 9: - return "song_ABCD" - elif length == 10: - return "song_ABCDE" - elif length == 11: - return "song_ABCDEF" - elif length == 12: - return "song_ABCDEFG" - elif length == 13: - return "song_ABCDEFGH" - else: - raise ValueError("Output file name length (excluding extension) must be between 8 and 12 characters.") - -def modify_nus3bank_template(template_name, audio_file, preview_point, output_file): - # Define template configurations based on the selected template_name - template_configs = { - "song_ABC": { - "unique_id_offset": 176, - "audio_size_offsets": [76, 1568, 1852], - "preview_point_offset": 1724, - "song_placeholder": "song_ABC", - "template_file": "song_ABC.nus3bank" - }, - "song_ABCD": { - "unique_id_offset": 176, - "audio_size_offsets": [76, 1568, 1852], - "preview_point_offset": 1724, - "song_placeholder": "song_ABCD", - "template_file": "song_ABCD.nus3bank" - }, - "song_ABCDE": { - "unique_id_offset": 176, - "audio_size_offsets": [76, 1568, 1852], - "preview_point_offset": 1724, - "song_placeholder": "song_ABCDE", - "template_file": "song_ABCDE.nus3bank" - }, - "song_ABCDEF": { - "unique_id_offset": 180, - "audio_size_offsets": [76, 1576, 1868], - "preview_point_offset": 1732, - "song_placeholder": "song_ABCDEF", - "template_file": "song_ABCDEF.nus3bank" - }, - "song_ABCDEFG": { - "unique_id_offset": 180, - "audio_size_offsets": [76, 1672, 1964], - "preview_point_offset": 1824, - "song_placeholder": "song_ABCDEFG", - "template_file": "song_ABCDEFG.nus3bank" - }, - "song_ABCDEFGH": { - "unique_id_offset": 180, - "audio_size_offsets": [76, 1576, 1868], - "preview_point_offset": 1732, - "song_placeholder": "song_ABCDEFGH", - "template_file": "song_ABCDEFGH.nus3bank" - } - } - - # Retrieve template configurations for the specified template_name - template_config = template_configs[template_name] - - # Read template nus3bank file from the templates folder - template_file = os.path.join("templates", template_config['template_file']) - with open(template_file, 'rb') as f: - template_data = bytearray(f.read()) - - # Generate random UInt16 hex for unique ID - unique_id_hex = generate_random_uint16_hex() - - # Set unique ID in the template data at the specified offset - template_data[template_config['unique_id_offset']:template_config['unique_id_offset']+2] = bytes.fromhex(unique_id_hex) - - # Get size of the audio file in bytes - audio_size = os.path.getsize(audio_file) - - # Convert audio size to UInt32 bytes in little-endian format - size_bytes = audio_size.to_bytes(4, 'little') - - # Set audio size in the template data at the specified offsets - for offset in template_config['audio_size_offsets']: - template_data[offset:offset+4] = size_bytes - - # Convert preview point (milliseconds) to UInt32 bytes in little-endian format - preview_point_ms = int(preview_point) - preview_point_bytes = preview_point_ms.to_bytes(4, 'little') - - # Set preview point in the template data at the specified offset - template_data[template_config['preview_point_offset']:template_config['preview_point_offset']+4] = preview_point_bytes - - # Replace song name placeholder with the output file name in bytes - output_file_bytes = output_file.encode('utf-8') - template_data = template_data.replace(template_config['song_placeholder'].encode('utf-8'), output_file_bytes.replace(b'.nus3bank', b'')) - - # Append the audio file contents to the modified template data - with open(audio_file, 'rb') as audio: - template_data += audio.read() - - # Write the modified data to the output file - with open(output_file, 'wb') as out: - out.write(template_data) - -if __name__ == "__main__": - if len(sys.argv) != 4: - print("Usage: audio.py ") - sys.exit(1) - - audio_file = sys.argv[1] - preview_point = sys.argv[2] - output_file = sys.argv[3] - - try: - template_name = select_template_name(output_file) - modify_nus3bank_template(template_name, audio_file, preview_point, output_file) - print(f"Created {output_file} successfully.") - except ValueError as e: - print(f"Error: {e}") - sys.exit(1) diff --git a/TaikoNus3bankMake/script/at9/at9.py b/TaikoNus3bankMake/script/at9/at9.py new file mode 100644 index 0000000..591a548 --- /dev/null +++ b/TaikoNus3bankMake/script/at9/at9.py @@ -0,0 +1,44 @@ +import subprocess +import os +import sys +import shutil +from pydub import AudioSegment + +def convert_audio_to_at9(input_file, output_file): + # Create a temporary folder to store intermediate files + temp_folder = "temp" + os.makedirs(temp_folder, exist_ok=True) + + try: + # Check if the input file is already in WAV format + if not input_file.lower().endswith('.wav'): + # Load the input audio file using pydub and convert to WAV + temp_wav_file = os.path.join(temp_folder, "temp.wav") + audio = AudioSegment.from_file(input_file) + audio.export(temp_wav_file, format="wav") + input_file = temp_wav_file + + # Path to AT9Tool executable + at9tool_cli_path = os.path.join("bin", "at9tool.exe") + + # Run VGAudioCli to convert WAV to AT9 + subprocess.run([at9tool_cli_path, "-e", "-br", "192", input_file, output_file], check=True) + + finally: + # Clean up temporary folder + shutil.rmtree(temp_folder, ignore_errors=True) + +if __name__ == "__main__": + # Check command-line arguments + if len(sys.argv) != 3: + print("Usage: python at9.py ") + sys.exit(1) + + input_audio_file = sys.argv[1] + output_audio_file = sys.argv[2] + + try: + convert_audio_to_at9(input_audio_file, output_audio_file) + print(f"Conversion successful. Output file: {output_audio_file}") + except Exception as e: + print(f"Error during conversion: {e}") diff --git a/TaikoNus3bankMake/script/bnsf/bnsf.py b/TaikoNus3bankMake/script/bnsf/bnsf.py new file mode 100644 index 0000000..d38dc14 --- /dev/null +++ b/TaikoNus3bankMake/script/bnsf/bnsf.py @@ -0,0 +1,93 @@ +import subprocess +import os +import sys +import shutil +from pydub import AudioSegment +from pydub.exceptions import CouldntDecodeError + +def convert_to_mono_48k(input_file, output_file): + """Convert input audio file to 16-bit mono WAV with 48000 Hz sample rate.""" + try: + audio = AudioSegment.from_file(input_file) + audio = audio.set_channels(1) # Convert to mono + audio = audio.set_frame_rate(48000) # Set frame rate to 48000 Hz + audio = audio.set_sample_width(2) # Set sample width to 16-bit (2 bytes) + audio.export(output_file, format='wav') + except CouldntDecodeError: + print(f"Error: Unable to decode {input_file}. Please provide a valid audio file.") + sys.exit(1) + + +def run_encode_tool(input_wav, output_bs): + """Run external encode tool with specified arguments.""" + subprocess.run(['bin/encode.exe', '0', input_wav, output_bs, '48000', '14000']) + + +def modify_bnsf_template(output_bs, output_bnsf, header_size, total_samples): + """Modify the BNSF template file with calculated values and combine with output.bs.""" + # Calculate the file size of output.bs + bs_file_size = os.path.getsize(output_bs) + + # Create modified BNSF data + new_file_size = bs_file_size + header_size - 0x8 + total_samples_bytes = total_samples.to_bytes(4, 'big') + bs_file_size_bytes = bs_file_size.to_bytes(4, 'big') + + # Read BNSF template data + with open('templates/header.bnsf', 'rb') as template_file: + bnsf_template_data = bytearray(template_file.read()) + + # Modify BNSF template with calculated values + bnsf_template_data[0x4:0x8] = new_file_size.to_bytes(4, 'big') # File size + bnsf_template_data[0x1C:0x20] = total_samples_bytes # Total sample count + bnsf_template_data[0x2C:0x30] = bs_file_size_bytes # Size of output.bs + + # Append output.bs data to modified BNSF template + with open(output_bs, 'rb') as bs_file: + bs_data = bs_file.read() + final_bnsf_data = bnsf_template_data + bs_data + + # Write final BNSF file + with open(output_bnsf, 'wb') as output_file: + output_file.write(final_bnsf_data) + + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: bnsf.py []") + sys.exit(1) + + input_audio = sys.argv[1] + output_bnsf = sys.argv[2] if len(sys.argv) > 2 else 'output.bnsf' + + # Create temp folder if it doesn't exist + temp_folder = 'temp' + os.makedirs(temp_folder, exist_ok=True) + + # Temporary file paths + output_wav = os.path.join(temp_folder, 'output_mono.wav') + output_bs = os.path.join(temp_folder, 'output.bs') + + # Header size (assuming fixed size) + header_size = 0x30 + + try: + # Step 1: Convert input audio to required format (WAV) + convert_to_mono_48k(input_audio, output_wav) + + # Step 2: Run external encoding tool + run_encode_tool(output_wav, output_bs) + + # Step 3: Get sample count from the converted mono WAV + mono_wav = AudioSegment.from_wav(output_wav) + total_samples = len(mono_wav.get_array_of_samples()) + + # Step 4: Modify BNSF template with calculated values and combine with output.bs + modify_bnsf_template(output_bs, output_bnsf, header_size, total_samples) + + print("BNSF file created:", output_bnsf) + + finally: + # Cleanup: Delete temporary files and temp folder + if os.path.exists(temp_folder): + shutil.rmtree(temp_folder) diff --git a/TaikoNus3bankMake/script/idsp/idsp.py b/TaikoNus3bankMake/script/idsp/idsp.py new file mode 100644 index 0000000..8afbcc0 --- /dev/null +++ b/TaikoNus3bankMake/script/idsp/idsp.py @@ -0,0 +1,44 @@ +import subprocess +import os +import sys +import shutil +from pydub import AudioSegment + +def convert_audio_to_idsp(input_file, output_file): + # Create a temporary folder to store intermediate files + temp_folder = "temp" + os.makedirs(temp_folder, exist_ok=True) + + try: + # Check if the input file is already in WAV format + if not input_file.lower().endswith('.wav'): + # Load the input audio file using pydub and convert to WAV + temp_wav_file = os.path.join(temp_folder, "temp.wav") + audio = AudioSegment.from_file(input_file) + audio.export(temp_wav_file, format="wav") + input_file = temp_wav_file + + # Path to VGAudioCli executable + vgaudio_cli_path = os.path.join("bin", "VGAudioCli.exe") + + # Run VGAudioCli to convert WAV to IDSP + subprocess.run([vgaudio_cli_path, "-i", input_file, "-o", output_file], check=True) + + finally: + # Clean up temporary folder + shutil.rmtree(temp_folder, ignore_errors=True) + +if __name__ == "__main__": + # Check command-line arguments + if len(sys.argv) != 3: + print("Usage: python idsp.py ") + sys.exit(1) + + input_audio_file = sys.argv[1] + output_audio_file = sys.argv[2] + + try: + convert_audio_to_idsp(input_audio_file, output_audio_file) + print(f"Conversion successful. Output file: {output_audio_file}") + except Exception as e: + print(f"Error during conversion: {e}") diff --git a/TaikoNus3bankMake/script/lopus/lopus.py b/TaikoNus3bankMake/script/lopus/lopus.py new file mode 100644 index 0000000..e7519db --- /dev/null +++ b/TaikoNus3bankMake/script/lopus/lopus.py @@ -0,0 +1,45 @@ +import subprocess +import os +import sys +import shutil +from pydub import AudioSegment + +def convert_audio_to_opus(input_file, output_file): + # Create a temporary folder to store intermediate files + temp_folder = "temp" + os.makedirs(temp_folder, exist_ok=True) + + try: + # Check if the input file is already in WAV format + if not input_file.lower().endswith('.wav'): + # Load the input audio file using pydub and convert to WAV + temp_wav_file = os.path.join(temp_folder, "temp.wav") + audio = AudioSegment.from_file(input_file) + audio = audio.set_frame_rate(48000) # Set frame rate to 48000 Hz + audio.export(temp_wav_file, format="wav") + input_file = temp_wav_file + + # Path to VGAudioCli executable + vgaudio_cli_path = os.path.join("bin", "VGAudioCli.exe") + + # Run VGAudioCli to convert WAV to Switch OPUS + subprocess.run([vgaudio_cli_path, "-i", input_file, "-o", output_file, "--opusheader", "namco"], check=True) + + finally: + # Clean up temporary folder + shutil.rmtree(temp_folder, ignore_errors=True) + +if __name__ == "__main__": + # Check command-line arguments + if len(sys.argv) != 3: + print("Usage: python opus.py ") + sys.exit(1) + + input_audio_file = sys.argv[1] + output_audio_file = sys.argv[2] + + try: + convert_audio_to_opus(input_audio_file, output_audio_file) + print(f"Conversion successful. Output file: {output_audio_file}") + except Exception as e: + print(f"Error during conversion: {e}") diff --git a/TaikoNus3bankMake/script/nus3/nus3.py b/TaikoNus3bankMake/script/nus3/nus3.py new file mode 100644 index 0000000..90d9b23 --- /dev/null +++ b/TaikoNus3bankMake/script/nus3/nus3.py @@ -0,0 +1,272 @@ +import sys +import os +import struct +import random + +def generate_random_uint16_hex(): + return format(random.randint(0, 65535), '04X') + +def load_template_config(): + # Load template configurations from config.toml (if needed in the future) + # This function can be expanded to load more template configurations if necessary + # For now, we don't need to use this function directly for selecting templates + return {} + +def select_template_name(game, output_file): + # Determine the appropriate template name based on the game and the length of the output file name + base_filename = os.path.splitext(output_file)[0] + length = len(base_filename) + + if game == "nijiiro": + if length == 8: + return "song_ABC" + elif length == 9: + return "song_ABCD" + elif length == 10: + return "song_ABCDE" + elif length == 11: + return "song_ABCDEF" + elif length == 12: + return "song_ABCDEFG" + elif length == 13: + return "song_ABCDEFGH" + elif game == "ps4": + if length == 8: + return "song_ABC" + elif length == 9: + return "song_ABCD" + elif length == 10: + return "song_ABCDE" + elif length == 11: + return "song_ABCDEF" + elif game == "ns1": + if length == 8: + return "song_ABC" + elif length == 9: + return "song_ABCD" + elif length == 10: + return "song_ABCDE" + elif length == 11: + return "song_ABCDEF" + pass + elif game == "wiiu3": + if length == 8: + return "song_ABC" + elif length == 9: + return "song_ABCD" + elif length == 10: + return "song_ABCDE" + elif length == 11: + return "song_ABCDEF" + pass + + raise ValueError("Unsupported game or output file name length.") + +def modify_nus3bank_template(game, template_name, audio_file, preview_point, output_file): + # Define game-specific template configurations + game_templates = { + "nijiiro": { + "template_folder": "nijiiro", + "templates": { + "song_ABC": { + "unique_id_offset": 176, + "audio_size_offsets": [76, 1568, 1852], + "preview_point_offset": 1724, + "song_placeholder": "song_ABC", + "template_file": "song_ABC.nus3bank" + }, + "song_ABCD": { + "unique_id_offset": 176, + "audio_size_offsets": [76, 1568, 1852], + "preview_point_offset": 1724, + "song_placeholder": "song_ABCD", + "template_file": "song_ABCD.nus3bank" + }, + "song_ABCDE": { + "unique_id_offset": 176, + "audio_size_offsets": [76, 1568, 1852], + "preview_point_offset": 1724, + "song_placeholder": "song_ABCDE", + "template_file": "song_ABCDE.nus3bank" + }, + "song_ABCDEF": { + "unique_id_offset": 180, + "audio_size_offsets": [76, 1576, 1868], + "preview_point_offset": 1732, + "song_placeholder": "song_ABCDEF", + "template_file": "song_ABCDEF.nus3bank" + }, + "song_ABCDEFG": { + "unique_id_offset": 180, + "audio_size_offsets": [76, 1672, 1964], + "preview_point_offset": 1824, + "song_placeholder": "song_ABCDEFG", + "template_file": "song_ABCDEFG.nus3bank" + }, + "song_ABCDEFGH": { + "unique_id_offset": 180, + "audio_size_offsets": [76, 1576, 1868], + "preview_point_offset": 1732, + "song_placeholder": "song_ABCDEFGH", + "template_file": "song_ABCDEFGH.nus3bank" + }, + } + }, + "ns1": { + "template_folder": "ns1", + "templates": { + "song_ABC": { + "audio_size_offsets": [76, 5200, 5420], + "preview_point_offset": 5324, + "song_placeholder": "SONG_ABC", + "template_file": "SONG_ABC.nus3bank" + }, + "song_ABCD": { + "audio_size_offsets": [76, 5200, 5420], + "preview_point_offset": 5324, + "song_placeholder": "SONG_ABCD", + "template_file": "SONG_ABCD.nus3bank" + }, + "song_ABCDE": { + "audio_size_offsets": [76, 5200, 5404], + "preview_point_offset": 5320, + "song_placeholder": "SONG_ABCDE", + "template_file": "SONG_ABCDE.nus3bank" + }, + "song_ABCDEF": { + "audio_size_offsets": [76, 5208, 5420], + "preview_point_offset": 5324, + "song_placeholder": "SONG_ABCDEF", + "template_file": "SONG_ABCDEF.nus3bank" + } + } + }, + "ps4": { + "template_folder": "ps4", + "templates": { + "song_ABC": { + "audio_size_offsets": [76, 3220, 3436], + "preview_point_offset": 3344, + "song_placeholder": "SONG_ABC", + "template_file": "SONG_ABC.nus3bank" + }, + "song_ABCD": { + "audio_size_offsets": [76, 3220, 3436], + "preview_point_offset": 3344, + "song_placeholder": "SONG_ABCD", + "template_file": "SONG_ABCD.nus3bank" + }, + "song_ABCDE": { + "audio_size_offsets": [76, 3220, 3436], + "preview_point_offset": 3344, + "song_placeholder": "SONG_ABCDE", + "template_file": "SONG_ABCDE.nus3bank" + }, + "song_ABCDEF": { + "audio_size_offsets": [76, 3228, 3452], + "preview_point_offset": 3360, + "song_placeholder": "SONG_ABCDEF", + "template_file": "SONG_ABCDEF.nus3bank" + } + } + }, + "wiiu3": { + "template_folder": "wiiu3", + "templates": { + "song_ABC": { + "audio_size_offsets": [76, 3420, 3612], + "preview_point_offset": 3540, + "song_placeholder": "SONG_ABC", + "template_file": "SONG_ABC.nus3bank" + }, + "song_ABCD": { + "audio_size_offsets": [76, 3420, 3612], + "preview_point_offset": 3540, + "song_placeholder": "SONG_ABCD", + "template_file": "SONG_ABCD.nus3bank" + }, + "song_ABCDE": { + "audio_size_offsets": [76, 3420, 3612], + "preview_point_offset": 3540, + "song_placeholder": "SONG_ABCDE", + "template_file": "SONG_ABCDE.nus3bank" + }, + "song_ABCDEF": { + "audio_size_offsets": [76, 3428, 3612], + "preview_point_offset": 3548, + "song_placeholder": "SONG_ABCDEF", + "template_file": "SONG_ABCDEF.nus3bank" + } + } + }, + } + + if game not in game_templates: + raise ValueError("Unsupported game.") + + templates_config = game_templates[game] + + if template_name not in templates_config["templates"]: + raise ValueError(f"Unsupported template for {game}.") + + template_config = templates_config["templates"][template_name] + template_folder = templates_config["template_folder"] + + # Read template nus3bank file from the specified game's template folder + template_file = os.path.join("templates", template_folder, template_config['template_file']) + with open(template_file, 'rb') as f: + template_data = bytearray(f.read()) + + # Set unique ID if it exists in the template configuration + if 'unique_id_offset' in template_config: + # Generate random UInt16 hex for unique ID + unique_id_hex = generate_random_uint16_hex() + # Set unique ID in the template data at the specified offset + template_data[template_config['unique_id_offset']:template_config['unique_id_offset']+2] = bytes.fromhex(unique_id_hex) + + # Get size of the audio file in bytes + audio_size = os.path.getsize(audio_file) + + # Convert audio size to UInt32 bytes in little-endian format + size_bytes = audio_size.to_bytes(4, 'little') + + # Set audio size in the template data at the specified offsets + for offset in template_config['audio_size_offsets']: + template_data[offset:offset+4] = size_bytes + + # Convert preview point (milliseconds) to UInt32 bytes in little-endian format + preview_point_ms = int(preview_point) + preview_point_bytes = preview_point_ms.to_bytes(4, 'little') + + # Set preview point in the template data at the specified offset + template_data[template_config['preview_point_offset']:template_config['preview_point_offset']+4] = preview_point_bytes + + # Replace song name placeholder with the output file name in bytes + output_file_bytes = output_file.encode('utf-8') + template_data = template_data.replace(template_config['song_placeholder'].encode('utf-8'), output_file_bytes.replace(b'.nus3bank', b'')) + + # Append the audio file contents to the modified template data + with open(audio_file, 'rb') as audio: + template_data += audio.read() + + # Write the modified data to the output file + with open(output_file, 'wb') as out: + out.write(template_data) + +if __name__ == "__main__": + if len(sys.argv) != 5: + print("Usage: nus3.py ") + sys.exit(1) + + game = sys.argv[1] + audio_file = sys.argv[2] + preview_point = sys.argv[3] + output_file = sys.argv[4] + + try: + template_name = select_template_name(game, output_file) + modify_nus3bank_template(game, template_name, audio_file, preview_point, output_file) + print(f"Created {output_file} successfully.") + except ValueError as e: + print(f"Error: {e}") + sys.exit(1) \ No newline at end of file diff --git a/TaikoNus3bankMake/script/run.py b/TaikoNus3bankMake/script/run.py new file mode 100644 index 0000000..de470cb --- /dev/null +++ b/TaikoNus3bankMake/script/run.py @@ -0,0 +1,22 @@ +import os +import sys +import subprocess + +def run_script(script_name, script_args): + script_path = os.path.join('script', script_name, f'{script_name}.py') + if os.path.exists(script_path): + command = ['python', script_path] + script_args + subprocess.run(command) + else: + print(f"Script '{script_name}' not found.") + sys.exit(1) + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: python launcher.py []") + sys.exit(1) + + script_name = sys.argv[1] + script_args = sys.argv[2:] # Capture all arguments after script_name + + run_script(script_name, script_args) diff --git a/TaikoNus3bankMake/script/wav/wav.py b/TaikoNus3bankMake/script/wav/wav.py new file mode 100644 index 0000000..20d9b86 --- /dev/null +++ b/TaikoNus3bankMake/script/wav/wav.py @@ -0,0 +1,33 @@ +import os +import sys +from pydub import AudioSegment + +def convert_audio_to_wav(input_file, output_file): + try: + # Load the input audio file using pydub + audio = AudioSegment.from_file(input_file) + + # Ensure the output file has a .wav extension + if not output_file.lower().endswith('.wav'): + output_file += '.wav' + + # Export the audio to WAV format + audio.export(output_file, format="wav") + + except Exception as e: + raise RuntimeError(f"Error during WAV conversion: {e}") + +if __name__ == "__main__": + # Check command-line arguments + if len(sys.argv) != 3: + print("Usage: python audio_converter.py ") + sys.exit(1) + + input_audio_file = sys.argv[1] + output_audio_file = sys.argv[2] + + try: + convert_audio_to_wav(input_audio_file, output_audio_file) + print(f"Conversion successful. Output file: {output_audio_file}") + except Exception as e: + print(f"Error during conversion: {e}") diff --git a/TaikoNus3bankMake/templates/header.bnsf b/TaikoNus3bankMake/templates/header.bnsf new file mode 100644 index 0000000..725c81e Binary files /dev/null and b/TaikoNus3bankMake/templates/header.bnsf differ diff --git a/TaikoNus3bankMake/templates/song_ABC.nus3bank b/TaikoNus3bankMake/templates/nijiiro/song_ABC.nus3bank similarity index 100% rename from TaikoNus3bankMake/templates/song_ABC.nus3bank rename to TaikoNus3bankMake/templates/nijiiro/song_ABC.nus3bank diff --git a/TaikoNus3bankMake/templates/song_ABCD.nus3bank b/TaikoNus3bankMake/templates/nijiiro/song_ABCD.nus3bank similarity index 100% rename from TaikoNus3bankMake/templates/song_ABCD.nus3bank rename to TaikoNus3bankMake/templates/nijiiro/song_ABCD.nus3bank diff --git a/TaikoNus3bankMake/templates/song_ABCDE.nus3bank b/TaikoNus3bankMake/templates/nijiiro/song_ABCDE.nus3bank similarity index 100% rename from TaikoNus3bankMake/templates/song_ABCDE.nus3bank rename to TaikoNus3bankMake/templates/nijiiro/song_ABCDE.nus3bank diff --git a/TaikoNus3bankMake/templates/song_ABCDEF.nus3bank b/TaikoNus3bankMake/templates/nijiiro/song_ABCDEF.nus3bank similarity index 100% rename from TaikoNus3bankMake/templates/song_ABCDEF.nus3bank rename to TaikoNus3bankMake/templates/nijiiro/song_ABCDEF.nus3bank diff --git a/TaikoNus3bankMake/templates/song_ABCDEFG.nus3bank b/TaikoNus3bankMake/templates/nijiiro/song_ABCDEFG.nus3bank similarity index 100% rename from TaikoNus3bankMake/templates/song_ABCDEFG.nus3bank rename to TaikoNus3bankMake/templates/nijiiro/song_ABCDEFG.nus3bank diff --git a/TaikoNus3bankMake/templates/song_ABCDEFGH.nus3bank b/TaikoNus3bankMake/templates/nijiiro/song_ABCDEFGH.nus3bank similarity index 100% rename from TaikoNus3bankMake/templates/song_ABCDEFGH.nus3bank rename to TaikoNus3bankMake/templates/nijiiro/song_ABCDEFGH.nus3bank diff --git a/TaikoNus3bankMake/templates/ns1/SONG_ABC.nus3bank b/TaikoNus3bankMake/templates/ns1/SONG_ABC.nus3bank new file mode 100644 index 0000000..1117bc6 Binary files /dev/null and b/TaikoNus3bankMake/templates/ns1/SONG_ABC.nus3bank differ diff --git a/TaikoNus3bankMake/templates/ns1/SONG_ABCD.nus3bank b/TaikoNus3bankMake/templates/ns1/SONG_ABCD.nus3bank new file mode 100644 index 0000000..87385fe Binary files /dev/null and b/TaikoNus3bankMake/templates/ns1/SONG_ABCD.nus3bank differ diff --git a/TaikoNus3bankMake/templates/ns1/SONG_ABCDE.nus3bank b/TaikoNus3bankMake/templates/ns1/SONG_ABCDE.nus3bank new file mode 100644 index 0000000..8043a3b Binary files /dev/null and b/TaikoNus3bankMake/templates/ns1/SONG_ABCDE.nus3bank differ diff --git a/TaikoNus3bankMake/templates/ns1/SONG_ABCDEF.nus3bank b/TaikoNus3bankMake/templates/ns1/SONG_ABCDEF.nus3bank new file mode 100644 index 0000000..dee9098 Binary files /dev/null and b/TaikoNus3bankMake/templates/ns1/SONG_ABCDEF.nus3bank differ diff --git a/TaikoNus3bankMake/templates/ps4/SONG_ABC.nus3bank b/TaikoNus3bankMake/templates/ps4/SONG_ABC.nus3bank new file mode 100644 index 0000000..c611d37 Binary files /dev/null and b/TaikoNus3bankMake/templates/ps4/SONG_ABC.nus3bank differ diff --git a/TaikoNus3bankMake/templates/ps4/SONG_ABCD.nus3bank b/TaikoNus3bankMake/templates/ps4/SONG_ABCD.nus3bank new file mode 100644 index 0000000..3f4b81d Binary files /dev/null and b/TaikoNus3bankMake/templates/ps4/SONG_ABCD.nus3bank differ diff --git a/TaikoNus3bankMake/templates/ps4/SONG_ABCDE.nus3bank b/TaikoNus3bankMake/templates/ps4/SONG_ABCDE.nus3bank new file mode 100644 index 0000000..f32af63 Binary files /dev/null and b/TaikoNus3bankMake/templates/ps4/SONG_ABCDE.nus3bank differ diff --git a/TaikoNus3bankMake/templates/ps4/SONG_ABCDEF.nus3bank b/TaikoNus3bankMake/templates/ps4/SONG_ABCDEF.nus3bank new file mode 100644 index 0000000..08f3f3f Binary files /dev/null and b/TaikoNus3bankMake/templates/ps4/SONG_ABCDEF.nus3bank differ diff --git a/TaikoNus3bankMake/templates/wiiu3/SONG_ABC.nus3bank b/TaikoNus3bankMake/templates/wiiu3/SONG_ABC.nus3bank new file mode 100644 index 0000000..3847cd9 Binary files /dev/null and b/TaikoNus3bankMake/templates/wiiu3/SONG_ABC.nus3bank differ diff --git a/TaikoNus3bankMake/templates/wiiu3/SONG_ABCD.nus3bank b/TaikoNus3bankMake/templates/wiiu3/SONG_ABCD.nus3bank new file mode 100644 index 0000000..31e75ad Binary files /dev/null and b/TaikoNus3bankMake/templates/wiiu3/SONG_ABCD.nus3bank differ diff --git a/TaikoNus3bankMake/templates/wiiu3/SONG_ABCDE.nus3bank b/TaikoNus3bankMake/templates/wiiu3/SONG_ABCDE.nus3bank new file mode 100644 index 0000000..cb1ca82 Binary files /dev/null and b/TaikoNus3bankMake/templates/wiiu3/SONG_ABCDE.nus3bank differ diff --git a/TaikoNus3bankMake/templates/wiiu3/SONG_ABCDEF.nus3bank b/TaikoNus3bankMake/templates/wiiu3/SONG_ABCDEF.nus3bank new file mode 100644 index 0000000..2dfdda0 Binary files /dev/null and b/TaikoNus3bankMake/templates/wiiu3/SONG_ABCDEF.nus3bank differ