import argparse import json import struct def read_string(infile, length, encoding="cp932"): return infile.read(length).decode(encoding, errors="ignore").rstrip("\0") def write_string(outfile, input, length, encoding="cp932"): string_data = input[:length].encode(encoding) outfile.write(string_data) outfile.write(b"\0" * (length - len(string_data))) def reader(version, infile, song_count): all_song_entries = [] for i in range(song_count): if version >= 32 and version != 80: 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") subtitle = read_string(infile, 0x100, encoding="utf-16-le") 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 and version != 80: texture_subtitle = struct.unpack("= 32 and version != 80: ( other_folder, bemani_folder, beginner_rec_folder, iidx_rec_folder, bemani_rec_folder, splittable_diff, unk_unused, ) = 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 else 9)] if version >= 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 version >= 32 and version != 80: new_version_entries = { "subtitle": subtitle, "texture_subtitle": texture_subtitle, "beginner_rec_folder": beginner_rec_folder, "iidx_rec_folder": iidx_rec_folder, "bemani_rec_folder": bemani_rec_folder, "unk_unused": unk_unused, } entries.update(new_version_entries) all_song_entries.append(entries) return all_song_entries def writer(version, outfile, data): cur_style_entries = version * 1000 max_entries = cur_style_entries + 1000 entries_struct_format = "= 32 and version != 80 else "= 32: outfile.write(struct.pack("= 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): song_data = data[exist_ids[k]] if version >= 32 and version != 80: 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") write_string(outfile, song_data.get("subtitle", ""), 0x100, encoding="utf-16-le") 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 and version != 80: outfile.write(struct.pack("= 32 and version != 80: outfile.write( struct.pack( "= 27: outfile.write( struct.pack( "= 32: outfile.write(bytes.fromhex(f"{0:01292}")) elif version >= 27: outfile.write(bytes.fromhex(f"{1:014}{2:08}{3:0248}{4:08}{0:01014}")) else: outfile.write(bytes.fromhex(f"{0:0320}")) outfile.write(struct.pack("= 27: outfile.write( struct.pack( "= 22 else 9): try: write_string(outfile, song_data["afp_data"][idx], 0x20) except IndexError: write_string(outfile, "", 0x20) if version >= 26: outfile.write(bytes.fromhex("00" * 4)) handlers = { 20, # TRICORO 21, # SPADA 22, # PENDUAL 23, # COPULA 24, # SINOBUZ 25, # CANNON BALLERS 26, # ROOTAGE 27, # HEROIC VERSE 28, # BISTROVER 29, # CASTHOUR 30, # RESIDENT 31, # EPOLIS 32, # PINKY CRUSH 33, # SPARKLE SHOWER 34, # ??? 80, # INFINITAS } def extract_file(input, output, in_memory=False): with open(input, "rb") as infile: if infile.read(4) != b"IIDX": raise SystemExit(f"Input file ({input}) is not valid") version = struct.unpack("= 32 and version != 80 else "= 32: available_entries, unk4, total_entries = struct.unpack("