import argparse import ctypes import json import struct def read_string(infile, length, encoding="cp932"): string_data = infile.read(length) try: return string_data.decode(encoding).strip("\0") except UnicodeDecodeError: # Can't decode truncated string with half multibyte sequence appended (0x83) return string_data[:-1].decode(encoding).strip("\0") def write_string(outfile, input, length, fill="\0", encoding="cp932"): string_data = input[:length].encode(encoding) outfile.write(string_data) if len(input) < length: outfile.write("".join([fill] * (length - len(string_data))).encode("utf-8")) def reader(data_ver, infile, song_count): song_entries = [] for i in range(song_count): if data_ver >= 32: title = read_string(infile, 0x100, encoding="utf-16-le") title_ascii = read_string(infile, 0x40) genre = read_string(infile, 0x80, encoding="utf-16-le") artist = read_string(infile, 0x100, encoding="utf-16-le") unk_sect0 = infile.read(0x100) else: title = read_string(infile, 0x40) title_ascii = read_string(infile, 0x40) genre = read_string(infile, 0x40) artist = read_string(infile, 0x40) ( texture_title, texture_artist, texture_genre, texture_load, texture_list, ) = struct.unpack("= 32: texture_unk = struct.unpack("= 32: ( other_folder, bemani_folder, unk_folder0, unk_folder1, unk_folder2, splittable_diff, unk_folder3, ) = struct.unpack("= 27: ( SPB_level, SPN_level, SPH_level, SPA_level, SPL_level, DPB_level, DPN_level, DPH_level, DPA_level, DPL_level, ) = struct.unpack("= 27: unk_sect1 = infile.read(0x286) else: unk_sect1 = infile.read(0xA0) song_id, volume = struct.unpack("= 27: ( SPB_ident, SPN_ident, SPH_ident, SPA_ident, SPL_ident, DPB_ident, DPN_ident, DPH_ident, DPA_ident, DPL_ident, ) = struct.unpack("= 22: afp_data = [] for x in range(10): afp_data.append(infile.read(0x20).hex()) else: afp_data = [] for x in range(9): afp_data.append(infile.read(0x20).hex()) if data_ver >= 26: unk_sect4 = infile.read(4) entries = { "song_id": song_id, "title": title, "title_ascii": title_ascii, "genre": genre, "artist": artist, "texture_title": texture_title, "texture_artist": texture_artist, "texture_genre": texture_genre, "texture_load": texture_load, "texture_list": texture_list, "font_idx": font_idx, "game_version": game_version, "other_folder": other_folder, "bemani_folder": bemani_folder, "splittable_diff": splittable_diff, "SPB_level": SPB_level, "SPN_level": SPN_level, "SPH_level": SPH_level, "SPA_level": SPA_level, "SPL_level": SPL_level, "DPB_level": DPB_level, "DPN_level": DPN_level, "DPH_level": DPH_level, "DPA_level": DPA_level, "DPL_level": DPL_level, "volume": volume, "SPB_ident": SPB_ident, "SPN_ident": SPN_ident, "SPH_ident": SPH_ident, "SPA_ident": SPA_ident, "SPL_ident": SPL_ident, "DPB_ident": DPB_ident, "DPN_ident": DPN_ident, "DPH_ident": DPH_ident, "DPA_ident": DPA_ident, "DPL_ident": DPL_ident, "bga_filename": bga_filename, "bga_delay": bga_delay, "afp_flag": afp_flag, "afp_data": afp_data, } if data_ver >= 32: # if data_ver == 80: # unk = { # "unk_sect1": unk_sect1.hex(), # "unk_sect2": unk_sect2.hex(), # "unk_sect3": unk_sect3.hex(), # "unk_sect4": unk_sect4.hex(), # } unk = { "unk_sect0": unk_sect0.hex(), "texture_unk": texture_unk, "unk_folder0": unk_folder0, "unk_folder1": unk_folder1, "unk_folder2": unk_folder2, "unk_folder3": unk_folder3, # "unk_sect1": unk_sect1.hex(), # "unk_sect2": unk_sect2.hex(), # "unk_sect3": unk_sect3.hex(), # "unk_sect4": unk_sect4.hex(), } # elif data_ver >= 27: # unk = { # "unk_sect1": unk_sect1.hex(), # "unk_sect4": unk_sect4.hex(), # } # elif data_ver == 26: # unk = { # "unk_sect1": unk_sect1.hex(), # "unk_sect2": unk_sect2.hex(), # "unk_sect4": unk_sect4.hex(), # } # elif data_ver <= 25: # unk = { # "unk_sect1": unk_sect1.hex(), # "unk_sect2": unk_sect2.hex(), # } entries.update(unk) song_entries.append(entries) return song_entries def writer(data_ver, outfile, data): DATA_VERSION = data_ver MAX_ENTRIES = data_ver * 1000 + 1000 CUR_STYLE_ENTRIES = MAX_ENTRIES - 1000 # Write header outfile.write(b"IIDX") if data_ver >= 32: outfile.write(struct.pack("= 32 else "= CUR_STYLE_ENTRIES: outfile.write(struct.pack(entries_struct_format, 0)) else: outfile.write(struct.pack(entries_struct_format, -1)) # Write song entries for k in sorted(exist_ids.keys()): song_data = data[exist_ids[k]] if data_ver >= 32: write_string(outfile, song_data["title"], 0x100, encoding="utf-16-le") write_string(outfile, song_data["title_ascii"], 0x40) write_string(outfile, song_data["genre"], 0x80, encoding="utf-16-le") write_string(outfile, song_data["artist"], 0x100, encoding="utf-16-le") outfile.write( bytes.fromhex( song_data.get( "unk_sect0", "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", ) ) ) else: write_string(outfile, song_data["title"], 0x40) write_string(outfile, song_data["title_ascii"], 0x40) write_string(outfile, song_data["genre"], 0x40) write_string(outfile, song_data["artist"], 0x40) outfile.write( struct.pack( "= 32: outfile.write(struct.pack("= 32: outfile.write( struct.pack( "= 27: outfile.write( struct.pack( "= 32: outfile.write( bytes.fromhex( "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ) ) elif data_ver >= 27: outfile.write( bytes.fromhex( "00000000000001000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ) ) else: outfile.write( bytes.fromhex( "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ) ) outfile.write(struct.pack("= 27: outfile.write( struct.pack( "= 22: for afp_data in song_data["afp_data"]: outfile.write(bytes.fromhex(afp_data)) if len(song_data["afp_data"]) == 9: outfile.write( bytes.fromhex( "0000000000000000000000000000000000000000000000000000000000000000" ) ) elif len(song_data["afp_data"]) == 10 and data_ver <= 21: for afp_data in song_data["afp_data"][:9]: outfile.write(bytes.fromhex(afp_data)) elif len(song_data["afp_data"]) == 9 and data_ver <= 21: for afp_data in song_data["afp_data"]: outfile.write(bytes.fromhex(afp_data)) if data_ver >= 26: outfile.write(bytes.fromhex("00000000")) def course_reader(infile, total_entries): course_entries = [] for i in range(total_entries): is_DP, course_num, stages = struct.unpack("= 32: available_entries, unk4, total_entries = struct.unpack( "= 32: song_id = struct.unpack("= 32: available_entries, unk4, total_entries = struct.unpack( "= 32: song_id = struct.unpack("= 32: available_entries, unk4, total_entries = struct.unpack( "= 32: song_id = struct.unpack("