mirror of
https://github.com/drmext/MonkeyBusiness.git
synced 2025-02-17 10:48:35 +01:00
IIDX endpoints for frontend
This commit is contained in:
parent
f9214b3ca6
commit
449826e4e7
319
modules/iidx/api.py
Normal file
319
modules/iidx/api.py
Normal file
@ -0,0 +1,319 @@
|
|||||||
|
from fastapi import APIRouter, Request, Response, File, UploadFile
|
||||||
|
|
||||||
|
from core_common import core_process_request, core_prepare_response, E
|
||||||
|
|
||||||
|
from tinydb import Query, where
|
||||||
|
from core_database import get_db
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import config
|
||||||
|
import utils.card as conv
|
||||||
|
import utils.musicdata_tool as mdt
|
||||||
|
from utils.lz77 import EamuseLZ77
|
||||||
|
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
import ujson as json
|
||||||
|
from os import path
|
||||||
|
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/iidx", tags=["api_iidx"])
|
||||||
|
|
||||||
|
|
||||||
|
class IIDX_Profile_Main_Items(BaseModel):
|
||||||
|
card: str
|
||||||
|
pin: str
|
||||||
|
|
||||||
|
|
||||||
|
class IIDX_Profile_Version_Items(BaseModel):
|
||||||
|
djname: Optional[str]
|
||||||
|
region: Optional[int]
|
||||||
|
head: Optional[int]
|
||||||
|
hair: Optional[int]
|
||||||
|
face: Optional[int]
|
||||||
|
hand: Optional[int]
|
||||||
|
body: Optional[int]
|
||||||
|
frame: Optional[int]
|
||||||
|
turntable: Optional[int]
|
||||||
|
explosion: Optional[int]
|
||||||
|
bgm: Optional[int]
|
||||||
|
sudden: Optional[int]
|
||||||
|
categoryvoice: Optional[int]
|
||||||
|
note: Optional[int]
|
||||||
|
fullcombo: Optional[int]
|
||||||
|
keybeam: Optional[int]
|
||||||
|
judgestring: Optional[int]
|
||||||
|
soundpreview: Optional[int]
|
||||||
|
grapharea: Optional[int]
|
||||||
|
effector_lock: Optional[int]
|
||||||
|
effector_type: Optional[int]
|
||||||
|
explosion_size: Optional[int]
|
||||||
|
alternate_hcn: Optional[int]
|
||||||
|
kokokara_start: Optional[int]
|
||||||
|
show_category_grade: Optional[int]
|
||||||
|
show_category_status: Optional[int]
|
||||||
|
show_category_difficulty: Optional[int]
|
||||||
|
show_category_alphabet: Optional[int]
|
||||||
|
show_category_rival_play: Optional[int]
|
||||||
|
show_category_rival_winlose: Optional[int]
|
||||||
|
show_category_all_rival_play: Optional[int]
|
||||||
|
show_category_arena_winlose: Optional[int]
|
||||||
|
show_rival_shop_info: Optional[int]
|
||||||
|
hide_play_count: Optional[int]
|
||||||
|
show_score_graph_cutin: Optional[int]
|
||||||
|
hide_iidx_id: Optional[int]
|
||||||
|
classic_hispeed: Optional[int]
|
||||||
|
beginner_option_swap: Optional[int]
|
||||||
|
show_lamps_as_no_play_in_arena: Optional[int]
|
||||||
|
skin_customize_flag_frame: Optional[int]
|
||||||
|
skin_customize_flag_bgm: Optional[int]
|
||||||
|
skin_customize_flag_lane: Optional[int]
|
||||||
|
sp_rival_1_iidx_id: Optional[int]
|
||||||
|
sp_rival_2_iidx_id: Optional[int]
|
||||||
|
sp_rival_3_iidx_id: Optional[int]
|
||||||
|
sp_rival_4_iidx_id: Optional[int]
|
||||||
|
sp_rival_5_iidx_id: Optional[int]
|
||||||
|
sp_rival_6_iidx_id: Optional[int]
|
||||||
|
dp_rival_1_iidx_id: Optional[int]
|
||||||
|
dp_rival_2_iidx_id: Optional[int]
|
||||||
|
dp_rival_3_iidx_id: Optional[int]
|
||||||
|
dp_rival_4_iidx_id: Optional[int]
|
||||||
|
dp_rival_5_iidx_id: Optional[int]
|
||||||
|
dp_rival_6_iidx_id: Optional[int]
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/profiles")
|
||||||
|
async def iidx_profiles():
|
||||||
|
return get_db().table("iidx_profile").all()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/profiles/{iidx_id}")
|
||||||
|
async def iidx_profile_id(iidx_id: str):
|
||||||
|
iidx_id = int("".join([i for i in iidx_id if i.isnumeric()]))
|
||||||
|
return get_db().table("iidx_profile").get(where("iidx_id") == iidx_id)
|
||||||
|
|
||||||
|
|
||||||
|
@router.patch("/profiles/{iidx_id}")
|
||||||
|
async def iidx_profile_id_patch(iidx_id: str, item: IIDX_Profile_Main_Items):
|
||||||
|
iidx_id = int("".join([i for i in iidx_id if i.isnumeric()]))
|
||||||
|
profile = get_db().table("iidx_profile").get(where("iidx_id") == iidx_id)
|
||||||
|
|
||||||
|
profile["card"] = item.card
|
||||||
|
profile["pin"] = item.pin
|
||||||
|
|
||||||
|
get_db().table("iidx_profile").upsert(profile, where("iidx_id") == iidx_id)
|
||||||
|
return Response(status_code=204)
|
||||||
|
|
||||||
|
|
||||||
|
@router.patch("/profiles/{iidx_id}/{version}")
|
||||||
|
async def iidx_profile_id_version_patch(
|
||||||
|
iidx_id: str, version: int, item: IIDX_Profile_Version_Items
|
||||||
|
):
|
||||||
|
if version != 30:
|
||||||
|
# TODO: differentiate 18, 19, 20, 29, 30
|
||||||
|
return Response(status_code=406)
|
||||||
|
iidx_id = int("".join([i for i in iidx_id if i.isnumeric()]))
|
||||||
|
profile = get_db().table("iidx_profile").get(where("iidx_id") == iidx_id)
|
||||||
|
game_profile = profile["version"].get(str(version), {})
|
||||||
|
|
||||||
|
game_profile["djname"] = item.djname
|
||||||
|
game_profile["region"] = item.region
|
||||||
|
game_profile["head"] = item.head
|
||||||
|
game_profile["hair"] = item.hair
|
||||||
|
game_profile["face"] = item.face
|
||||||
|
game_profile["hand"] = item.hand
|
||||||
|
game_profile["body"] = item.body
|
||||||
|
game_profile["frame"] = item.frame
|
||||||
|
game_profile["turntable"] = item.turntable
|
||||||
|
game_profile["explosion"] = item.explosion
|
||||||
|
game_profile["bgm"] = item.bgm
|
||||||
|
game_profile["sudden"] = item.sudden
|
||||||
|
game_profile["categoryvoice"] = item.categoryvoice
|
||||||
|
game_profile["note"] = item.note
|
||||||
|
game_profile["fullcombo"] = item.fullcombo
|
||||||
|
game_profile["keybeam"] = item.keybeam
|
||||||
|
game_profile["judgestring"] = item.judgestring
|
||||||
|
game_profile["soundpreview"] = item.soundpreview
|
||||||
|
game_profile["grapharea"] = item.grapharea
|
||||||
|
game_profile["effector_lock"] = item.effector_lock
|
||||||
|
game_profile["effector_type"] = item.effector_type
|
||||||
|
game_profile["explosion_size"] = item.explosion_size
|
||||||
|
game_profile["alternate_hcn"] = item.alternate_hcn
|
||||||
|
game_profile["kokokara_start"] = item.kokokara_start
|
||||||
|
game_profile["_show_category_grade"] = item.show_category_grade
|
||||||
|
game_profile["_show_category_status"] = item.show_category_status
|
||||||
|
game_profile["_show_category_difficulty"] = item.show_category_difficulty
|
||||||
|
game_profile["_show_category_alphabet"] = item.show_category_alphabet
|
||||||
|
game_profile["_show_category_rival_play"] = item.show_category_rival_play
|
||||||
|
game_profile["_show_category_rival_winlose"] = item.show_category_rival_winlose
|
||||||
|
game_profile["_show_category_all_rival_play"] = item.show_category_all_rival_play
|
||||||
|
game_profile["_show_category_arena_winlose"] = item.show_category_arena_winlose
|
||||||
|
game_profile["_show_rival_shop_info"] = item.show_rival_shop_info
|
||||||
|
game_profile["_hide_play_count"] = item.hide_play_count
|
||||||
|
game_profile["_show_score_graph_cutin"] = item.show_score_graph_cutin
|
||||||
|
game_profile["_hide_iidx_id"] = item.hide_iidx_id
|
||||||
|
game_profile["_classic_hispeed"] = item.classic_hispeed
|
||||||
|
game_profile["_beginner_option_swap"] = item.beginner_option_swap
|
||||||
|
game_profile[
|
||||||
|
"_show_lamps_as_no_play_in_arena"
|
||||||
|
] = item.show_lamps_as_no_play_in_arena
|
||||||
|
game_profile["skin_customize_flag_frame"] = item.skin_customize_flag_frame
|
||||||
|
game_profile["skin_customize_flag_bgm"] = item.skin_customize_flag_bgm
|
||||||
|
game_profile["skin_customize_flag_lane"] = item.skin_customize_flag_lane
|
||||||
|
game_profile["sp_rival_1_iidx_id"] = item.sp_rival_1_iidx_id
|
||||||
|
game_profile["sp_rival_2_iidx_id"] = item.sp_rival_2_iidx_id
|
||||||
|
game_profile["sp_rival_3_iidx_id"] = item.sp_rival_3_iidx_id
|
||||||
|
game_profile["sp_rival_4_iidx_id"] = item.sp_rival_4_iidx_id
|
||||||
|
game_profile["sp_rival_5_iidx_id"] = item.sp_rival_5_iidx_id
|
||||||
|
game_profile["sp_rival_6_iidx_id"] = item.sp_rival_6_iidx_id
|
||||||
|
game_profile["dp_rival_1_iidx_id"] = item.dp_rival_1_iidx_id
|
||||||
|
game_profile["dp_rival_2_iidx_id"] = item.dp_rival_2_iidx_id
|
||||||
|
game_profile["dp_rival_3_iidx_id"] = item.dp_rival_3_iidx_id
|
||||||
|
game_profile["dp_rival_4_iidx_id"] = item.dp_rival_4_iidx_id
|
||||||
|
game_profile["dp_rival_5_iidx_id"] = item.dp_rival_5_iidx_id
|
||||||
|
game_profile["dp_rival_6_iidx_id"] = item.dp_rival_6_iidx_id
|
||||||
|
|
||||||
|
profile["version"][str(version)] = game_profile
|
||||||
|
get_db().table("iidx_profile").upsert(profile, where("iidx_id") == iidx_id)
|
||||||
|
return Response(status_code=204)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/card/{card}")
|
||||||
|
async def iidx_card_to_profile(card: str):
|
||||||
|
card = card.upper()
|
||||||
|
lookalike = {
|
||||||
|
"I": "1",
|
||||||
|
"O": "0",
|
||||||
|
"Q": "0",
|
||||||
|
"V": "U",
|
||||||
|
}
|
||||||
|
for k, v in lookalike.items():
|
||||||
|
card = card.replace(k, v)
|
||||||
|
if card.startswith("E004") or card.startswith("012E"):
|
||||||
|
card = "".join([c for c in card if c in "0123456789ABCDEF"])
|
||||||
|
uid = card
|
||||||
|
kid = conv.to_konami_id(card)
|
||||||
|
else:
|
||||||
|
card = "".join([c for c in card if c in conv.valid_characters])
|
||||||
|
uid = conv.to_uid(card)
|
||||||
|
kid = card
|
||||||
|
profile = get_db().table("iidx_profile").get(where("card") == uid)
|
||||||
|
return profile
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/scores")
|
||||||
|
async def iidx_scores():
|
||||||
|
return get_db().table("iidx_scores").all()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/scores/{iidx_id}")
|
||||||
|
async def iidx_scores_id(iidx_id: str):
|
||||||
|
iidx_id = int("".join([i for i in iidx_id if i.isnumeric()]))
|
||||||
|
return get_db().table("iidx_scores").search((where("iidx_id") == iidx_id))
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/scores_best")
|
||||||
|
async def iidx_scores_best():
|
||||||
|
return get_db().table("iidx_scores_best").all()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/scores_best/{iidx_id}")
|
||||||
|
async def iidx_scores_best_id(iidx_id: str):
|
||||||
|
iidx_id = int("".join([i for i in iidx_id if i.isnumeric()]))
|
||||||
|
return get_db().table("iidx_scores_best").search((where("iidx_id") == iidx_id))
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/music_id/{music_id}/all")
|
||||||
|
async def iidx_scores_id(music_id: int):
|
||||||
|
return get_db().table("iidx_scores").search((where("music_id") == music_id))
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/music_id/{music_id}/best")
|
||||||
|
async def iidx_scores_id_best(music_id: int):
|
||||||
|
return get_db().table("iidx_scores_best").search((where("music_id") == music_id))
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/class_best/{iidx_id}")
|
||||||
|
async def iidx_class_best(iidx_id: str):
|
||||||
|
iidx_id = int("".join([i for i in iidx_id if i.isnumeric()]))
|
||||||
|
return get_db().table("iidx_class_best").search((where("iidx_id") == iidx_id))
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/score_stats/all")
|
||||||
|
async def iidx_score_stats():
|
||||||
|
return get_db().table("iidx_score_stats").all()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/score_stats/{music_id}")
|
||||||
|
async def iidx_score_stats_song(music_id: int):
|
||||||
|
return get_db().table("iidx_score_stats").search((where("music_id") == music_id))
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/parse_mdb/upload")
|
||||||
|
async def iidx_receive_mdb(file: UploadFile = File(...)) -> bytes:
|
||||||
|
data = await file.read()
|
||||||
|
|
||||||
|
iidx_bin = path.join("webui", "music_data.bin")
|
||||||
|
iidx_vid = path.join("webui", "video_music_list.xml")
|
||||||
|
iidx_metadata = path.join("webui", "iidx.json")
|
||||||
|
|
||||||
|
if data[0:4] == b"IIDX":
|
||||||
|
# data_ver = int.from_bytes(data[4:8], "little")
|
||||||
|
with open(iidx_bin, "wb") as output:
|
||||||
|
output.write(data)
|
||||||
|
try:
|
||||||
|
mdt.extract_file(iidx_bin, iidx_metadata)
|
||||||
|
return Response(status_code=201)
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return Response(status_code=422)
|
||||||
|
else:
|
||||||
|
# video_music_list.xml to fix broken characters in title/artist
|
||||||
|
# (this should be a seperate route)
|
||||||
|
try:
|
||||||
|
with open(iidx_metadata, "r", encoding="utf-8") as f:
|
||||||
|
music_data = json.load(f)
|
||||||
|
|
||||||
|
with open(iidx_vid, "wb") as output:
|
||||||
|
output.write(data)
|
||||||
|
|
||||||
|
with open(iidx_vid, "r", encoding="utf-8") as fp:
|
||||||
|
tree = ET.parse(fp, ET.XMLParser())
|
||||||
|
root = tree.getroot()
|
||||||
|
|
||||||
|
proper_names = {}
|
||||||
|
for entry in root:
|
||||||
|
mid = int(entry.get("id"))
|
||||||
|
proper_names[mid] = {}
|
||||||
|
proper_names[mid]["title"] = entry.find("info/title_name").text
|
||||||
|
proper_names[mid]["artist"] = entry.find("info/artist_name").text
|
||||||
|
|
||||||
|
for m in music_data["data"]:
|
||||||
|
try:
|
||||||
|
mid = m["song_id"]
|
||||||
|
vid_title = proper_names[mid]["title"]
|
||||||
|
bin_title = m["title"]
|
||||||
|
if vid_title != bin_title:
|
||||||
|
m["title"] = vid_title
|
||||||
|
# print(vid_title, bin_title)
|
||||||
|
vid_artist = proper_names[mid]["artist"]
|
||||||
|
bin_artist = m["artist"]
|
||||||
|
if vid_artist != bin_artist:
|
||||||
|
m["artist"] = vid_artist
|
||||||
|
# print(vid_artist, bin_artist)
|
||||||
|
except KeyError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
json.dump(
|
||||||
|
music_data,
|
||||||
|
open(iidx_metadata, "w", encoding="utf8"),
|
||||||
|
indent=4,
|
||||||
|
ensure_ascii=False,
|
||||||
|
escape_forward_slashes=False,
|
||||||
|
)
|
||||||
|
return Response(status_code=201)
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return Response(status_code=422)
|
||||||
|
|
||||||
|
return Response(status_code=406)
|
699
utils/musicdata_tool.py
Normal file
699
utils/musicdata_tool.py
Normal file
@ -0,0 +1,699 @@
|
|||||||
|
import argparse
|
||||||
|
import ctypes
|
||||||
|
import ujson as json
|
||||||
|
import sys
|
||||||
|
import struct
|
||||||
|
|
||||||
|
|
||||||
|
def read_string(infile, length, encoding="cp932"):
|
||||||
|
string_data = infile.read(length)
|
||||||
|
try:
|
||||||
|
return string_data.decode(encoding).strip("\0")
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
# Cannot decode truncated string with half of a 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(encoding))
|
||||||
|
|
||||||
|
|
||||||
|
def reader(data_ver, infile, song_count):
|
||||||
|
song_entries = []
|
||||||
|
|
||||||
|
for i in range(song_count):
|
||||||
|
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("<IIIII", infile.read(20))
|
||||||
|
font_idx, game_version = struct.unpack("<IH", infile.read(6))
|
||||||
|
other_folder, bemani_folder, splittable_diff = struct.unpack(
|
||||||
|
"<HHH", infile.read(6)
|
||||||
|
)
|
||||||
|
|
||||||
|
if data_ver >= 27:
|
||||||
|
(
|
||||||
|
SPB_level,
|
||||||
|
SPN_level,
|
||||||
|
SPH_level,
|
||||||
|
SPA_level,
|
||||||
|
SPL_level,
|
||||||
|
DPB_level,
|
||||||
|
DPN_level,
|
||||||
|
DPH_level,
|
||||||
|
DPA_level,
|
||||||
|
DPL_level,
|
||||||
|
) = struct.unpack("<BBBBBBBBBB", infile.read(10))
|
||||||
|
else:
|
||||||
|
(
|
||||||
|
SPN_level,
|
||||||
|
SPH_level,
|
||||||
|
SPA_level,
|
||||||
|
DPN_level,
|
||||||
|
DPH_level,
|
||||||
|
DPA_level,
|
||||||
|
SPB_level,
|
||||||
|
DPB_level,
|
||||||
|
) = struct.unpack("<BBBBBBBB", infile.read(8))
|
||||||
|
SPL_level = 0
|
||||||
|
DPL_level = 0
|
||||||
|
|
||||||
|
if data_ver == 80:
|
||||||
|
unk_sect1 = infile.read(0x146)
|
||||||
|
elif data_ver >= 27:
|
||||||
|
unk_sect1 = infile.read(0x286)
|
||||||
|
else:
|
||||||
|
unk_sect1 = infile.read(0xA0)
|
||||||
|
|
||||||
|
song_id, volume = struct.unpack("<II", infile.read(8))
|
||||||
|
|
||||||
|
if data_ver >= 27:
|
||||||
|
(
|
||||||
|
SPB_ident,
|
||||||
|
SPN_ident,
|
||||||
|
SPH_ident,
|
||||||
|
SPA_ident,
|
||||||
|
SPL_ident,
|
||||||
|
DPB_ident,
|
||||||
|
DPN_ident,
|
||||||
|
DPH_ident,
|
||||||
|
DPA_ident,
|
||||||
|
DPL_ident,
|
||||||
|
) = struct.unpack("<BBBBBBBBBB", infile.read(10))
|
||||||
|
else:
|
||||||
|
(
|
||||||
|
SPN_ident,
|
||||||
|
SPH_ident,
|
||||||
|
SPA_ident,
|
||||||
|
DPN_ident,
|
||||||
|
DPH_ident,
|
||||||
|
DPA_ident,
|
||||||
|
SPB_ident,
|
||||||
|
DPB_ident,
|
||||||
|
) = struct.unpack("<BBBBBBBB", infile.read(8))
|
||||||
|
SPL_ident = 48
|
||||||
|
DPL_ident = 48
|
||||||
|
|
||||||
|
bga_delay = ctypes.c_short(struct.unpack("<H", infile.read(2))[0]).value
|
||||||
|
|
||||||
|
if data_ver <= 26 or data_ver == 80:
|
||||||
|
unk_sect2 = infile.read(2)
|
||||||
|
|
||||||
|
bga_filename = read_string(infile, 0x20)
|
||||||
|
|
||||||
|
if data_ver == 80:
|
||||||
|
unk_sect3 = infile.read(2)
|
||||||
|
|
||||||
|
afp_flag = struct.unpack("<I", infile.read(4))[0]
|
||||||
|
|
||||||
|
if data_ver >= 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 == 80:
|
||||||
|
# unk = {
|
||||||
|
# 'unk_sect1': unk_sect1.hex(),
|
||||||
|
# 'unk_sect2': unk_sect2.hex(),
|
||||||
|
# 'unk_sect3': unk_sect3.hex(),
|
||||||
|
# 'unk_sect4': unk_sect4.hex(),
|
||||||
|
# }
|
||||||
|
# elif data_ver < 80 and 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 == 80:
|
||||||
|
outfile.write(struct.pack("<IHHI", DATA_VERSION, len(data), 0, MAX_ENTRIES))
|
||||||
|
else:
|
||||||
|
outfile.write(struct.pack("<IHHI", DATA_VERSION, len(data), MAX_ENTRIES, 0))
|
||||||
|
|
||||||
|
# Write song index table
|
||||||
|
exist_ids = {}
|
||||||
|
for i in range(len(data)):
|
||||||
|
exist_ids[data[i]["song_id"]] = i
|
||||||
|
|
||||||
|
cur_song = 0
|
||||||
|
for i in range(MAX_ENTRIES):
|
||||||
|
if i in exist_ids:
|
||||||
|
outfile.write(struct.pack("<H", cur_song))
|
||||||
|
cur_song += 1
|
||||||
|
elif i >= CUR_STYLE_ENTRIES:
|
||||||
|
outfile.write(struct.pack("<H", 0x0000))
|
||||||
|
else:
|
||||||
|
outfile.write(struct.pack("<H", 0xFFFF))
|
||||||
|
|
||||||
|
# Write song entries
|
||||||
|
for k in sorted(exist_ids.keys()):
|
||||||
|
song_data = data[exist_ids[k]]
|
||||||
|
|
||||||
|
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(
|
||||||
|
"<IIIII",
|
||||||
|
song_data["texture_title"],
|
||||||
|
song_data["texture_artist"],
|
||||||
|
song_data["texture_genre"],
|
||||||
|
song_data["texture_load"],
|
||||||
|
song_data["texture_list"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
outfile.write(
|
||||||
|
struct.pack("<IH", song_data["font_idx"], song_data["game_version"])
|
||||||
|
)
|
||||||
|
outfile.write(
|
||||||
|
struct.pack(
|
||||||
|
"<HHH",
|
||||||
|
song_data["other_folder"],
|
||||||
|
song_data["bemani_folder"],
|
||||||
|
song_data["splittable_diff"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if data_ver >= 27:
|
||||||
|
outfile.write(
|
||||||
|
struct.pack(
|
||||||
|
"<BBBBBBBBBB",
|
||||||
|
song_data["SPB_level"],
|
||||||
|
song_data["SPN_level"],
|
||||||
|
song_data["SPH_level"],
|
||||||
|
song_data["SPA_level"],
|
||||||
|
song_data["SPL_level"],
|
||||||
|
song_data["DPB_level"],
|
||||||
|
song_data["DPN_level"],
|
||||||
|
song_data["DPH_level"],
|
||||||
|
song_data["DPA_level"],
|
||||||
|
song_data["DPL_level"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
outfile.write(
|
||||||
|
struct.pack(
|
||||||
|
"<BBBBBBBB",
|
||||||
|
song_data["SPN_level"],
|
||||||
|
song_data["SPH_level"],
|
||||||
|
song_data["SPA_level"],
|
||||||
|
song_data["DPN_level"],
|
||||||
|
song_data["DPH_level"],
|
||||||
|
song_data["DPA_level"],
|
||||||
|
song_data["SPB_level"],
|
||||||
|
song_data["DPB_level"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if data_ver == 80:
|
||||||
|
outfile.write(
|
||||||
|
bytes.fromhex(
|
||||||
|
"0000000000000100000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif data_ver >= 27:
|
||||||
|
outfile.write(
|
||||||
|
bytes.fromhex(
|
||||||
|
"00000000000001000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
outfile.write(
|
||||||
|
bytes.fromhex(
|
||||||
|
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
outfile.write(struct.pack("<II", song_data["song_id"], song_data["volume"]))
|
||||||
|
|
||||||
|
if data_ver >= 27:
|
||||||
|
outfile.write(
|
||||||
|
struct.pack(
|
||||||
|
"<BBBBBBBBBB",
|
||||||
|
song_data["SPB_ident"],
|
||||||
|
song_data["SPN_ident"],
|
||||||
|
song_data["SPH_ident"],
|
||||||
|
song_data["SPA_ident"],
|
||||||
|
song_data["SPL_ident"],
|
||||||
|
song_data["DPB_ident"],
|
||||||
|
song_data["DPN_ident"],
|
||||||
|
song_data["DPH_ident"],
|
||||||
|
song_data["DPA_ident"],
|
||||||
|
song_data["DPL_ident"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
outfile.write(
|
||||||
|
struct.pack(
|
||||||
|
"<BBBBBBBB",
|
||||||
|
song_data["SPN_ident"],
|
||||||
|
song_data["SPH_ident"],
|
||||||
|
song_data["SPA_ident"],
|
||||||
|
song_data["DPN_ident"],
|
||||||
|
song_data["DPH_ident"],
|
||||||
|
song_data["DPA_ident"],
|
||||||
|
song_data["SPB_ident"],
|
||||||
|
song_data["DPB_ident"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
outfile.write(struct.pack("<h", song_data["bga_delay"]))
|
||||||
|
|
||||||
|
if data_ver <= 26 or data_ver == 80:
|
||||||
|
outfile.write(bytes.fromhex("0000"))
|
||||||
|
|
||||||
|
write_string(outfile, song_data["bga_filename"], 0x20)
|
||||||
|
|
||||||
|
if data_ver == 80:
|
||||||
|
outfile.write(bytes.fromhex("0000"))
|
||||||
|
|
||||||
|
outfile.write(struct.pack("<I", song_data["afp_flag"]))
|
||||||
|
|
||||||
|
if data_ver >= 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("<HIH", infile.read(8))
|
||||||
|
|
||||||
|
stage_num = []
|
||||||
|
for i in range(0x20):
|
||||||
|
extract = struct.unpack("<I", infile.read(4))
|
||||||
|
if extract[0] != 0xFFFFFFFF:
|
||||||
|
stage_num.extend(extract)
|
||||||
|
|
||||||
|
song_id = []
|
||||||
|
for i in range(0x20):
|
||||||
|
extract = struct.unpack("<I", infile.read(4))
|
||||||
|
if extract[0] != 0xFFFFFFFF:
|
||||||
|
song_id.extend(extract)
|
||||||
|
|
||||||
|
song_diff = []
|
||||||
|
for i in range(0x20):
|
||||||
|
extract = struct.unpack("<I", infile.read(4))
|
||||||
|
if extract[0] != 0xFFFFFFFF:
|
||||||
|
song_diff.extend(extract)
|
||||||
|
|
||||||
|
course_entries.append(
|
||||||
|
{
|
||||||
|
"is_DP": is_DP,
|
||||||
|
"course_num": course_num,
|
||||||
|
"stages": stages,
|
||||||
|
"stage_num": stage_num,
|
||||||
|
"song_id": song_id,
|
||||||
|
"song_diff": song_diff,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return course_entries
|
||||||
|
|
||||||
|
|
||||||
|
def course_writer(outfile, data, data_ver):
|
||||||
|
TOTAL_ENTRIES = len(data)
|
||||||
|
|
||||||
|
# Write header
|
||||||
|
outfile.write(b"IIDXDANE")
|
||||||
|
outfile.write(struct.pack("<III", data_ver, TOTAL_ENTRIES, 0))
|
||||||
|
|
||||||
|
# Write course entries
|
||||||
|
for song_data in data:
|
||||||
|
outfile.write(
|
||||||
|
struct.pack(
|
||||||
|
"<HIH", song_data["is_DP"], song_data["course_num"], song_data["stages"]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
stage = 0
|
||||||
|
|
||||||
|
for i in range(0x20):
|
||||||
|
if i in range(song_data["stages"]):
|
||||||
|
outfile.write(struct.pack("<I", stage))
|
||||||
|
stage += 1
|
||||||
|
else:
|
||||||
|
outfile.write(struct.pack("<I", 0xFFFFFFFF))
|
||||||
|
|
||||||
|
for i in range(0x20):
|
||||||
|
if i in range(song_data["stages"]):
|
||||||
|
outfile.write(struct.pack("<I", song_data["song_id"][i]))
|
||||||
|
stage += 1
|
||||||
|
else:
|
||||||
|
outfile.write(struct.pack("<I", 0xFFFFFFFF))
|
||||||
|
|
||||||
|
for i in range(0x20):
|
||||||
|
if i in range(song_data["stages"]):
|
||||||
|
outfile.write(struct.pack("<I", song_data["song_diff"][i]))
|
||||||
|
stage += 1
|
||||||
|
else:
|
||||||
|
outfile.write(struct.pack("<I", 0xFFFFFFFF))
|
||||||
|
|
||||||
|
|
||||||
|
read_handlers = {
|
||||||
|
0x14, # TRICORO
|
||||||
|
0x15, # SPADA
|
||||||
|
0x16, # PENDUAL
|
||||||
|
0x17, # COPULA
|
||||||
|
0x18, # SINOBUZ
|
||||||
|
0x19, # CANNON BALLERS
|
||||||
|
0x1A, # ROOTAGE
|
||||||
|
0x1B, # HEROIC VERSE
|
||||||
|
0x1C, # BISTROVER
|
||||||
|
0x1D, # CASTHOUR
|
||||||
|
0x1E, # RESIDENT
|
||||||
|
0x1F, # ???
|
||||||
|
0x50, # INFINITAS
|
||||||
|
}
|
||||||
|
|
||||||
|
write_handlers = {
|
||||||
|
0x14, # TRICORO
|
||||||
|
0x15, # SPADA
|
||||||
|
0x16, # PENDUAL
|
||||||
|
0x17, # COPULA
|
||||||
|
0x18, # SINOBUZ
|
||||||
|
0x19, # CANNON BALLERS
|
||||||
|
0x1A, # ROOTAGE
|
||||||
|
0x1B, # HEROIC VERSE
|
||||||
|
0x1C, # BISTROVER
|
||||||
|
0x1D, # CASTHOUR
|
||||||
|
0x1E, # RESIDENT
|
||||||
|
0x1F, # ???
|
||||||
|
0x50, # INFINITAS
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def extract_file(input, output, in_memory=False):
|
||||||
|
with open(input, "rb") as infile:
|
||||||
|
if infile.read(4) != b"IIDX":
|
||||||
|
print("Invalid", input)
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
|
infile.seek(4, 0)
|
||||||
|
data_ver = int.from_bytes(infile.read(4), "little")
|
||||||
|
|
||||||
|
if data_ver == 80:
|
||||||
|
available_entries, unk4, total_entries = struct.unpack(
|
||||||
|
"<HHI", infile.read(8)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
available_entries, total_entries, unk4 = struct.unpack(
|
||||||
|
"<HIH", infile.read(8)
|
||||||
|
)
|
||||||
|
|
||||||
|
song_ids = {}
|
||||||
|
for i in range(total_entries):
|
||||||
|
song_id = struct.unpack("<H", infile.read(2))[0]
|
||||||
|
|
||||||
|
if song_id != 0xFFFF and (len(song_ids) == 0 or song_id != 0):
|
||||||
|
song_ids[i] = song_id
|
||||||
|
|
||||||
|
if data_ver in read_handlers:
|
||||||
|
output_data = reader(data_ver, infile, available_entries)
|
||||||
|
output_data = {
|
||||||
|
"data_ver": data_ver,
|
||||||
|
"data": output_data,
|
||||||
|
}
|
||||||
|
|
||||||
|
if in_memory:
|
||||||
|
return output_data
|
||||||
|
|
||||||
|
json.dump(
|
||||||
|
output_data,
|
||||||
|
open(output, "w", encoding="utf8"),
|
||||||
|
indent=4,
|
||||||
|
ensure_ascii=False,
|
||||||
|
escape_forward_slashes=False,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print("Couldn't find a handler for this data version")
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def create_file(input, output, data_version):
|
||||||
|
data = json.load(open(input, "r", encoding="utf8"))
|
||||||
|
data_ver = data.get("data_ver", data_version)
|
||||||
|
|
||||||
|
if not data_ver:
|
||||||
|
print("Couldn't find data version")
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
|
if data_ver in write_handlers:
|
||||||
|
writer(data_ver, open(output, "wb"), data["data"])
|
||||||
|
else:
|
||||||
|
print("Couldn't find a handler for this data version")
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
|
|
||||||
|
def convert_file(input, output, data_version):
|
||||||
|
with open(input, "rb") as infile:
|
||||||
|
if infile.read(4) != b"IIDX":
|
||||||
|
print("Invalid", input)
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
|
data_ver, available_entries, total_entries, unk4 = struct.unpack(
|
||||||
|
"<IHIH", infile.read(12)
|
||||||
|
)
|
||||||
|
|
||||||
|
song_ids = {}
|
||||||
|
for i in range(total_entries):
|
||||||
|
song_id = struct.unpack("<H", infile.read(2))[0]
|
||||||
|
|
||||||
|
if song_id != 0xFFFF and (len(song_ids) == 0 or song_id != 0):
|
||||||
|
song_ids[i] = song_id
|
||||||
|
|
||||||
|
if data_ver in read_handlers:
|
||||||
|
output_data = reader(data_ver, infile, available_entries)
|
||||||
|
writer(data_ver, open(output, "wb"), output_data)
|
||||||
|
else:
|
||||||
|
print("Couldn't find a handler for this input data version")
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
|
|
||||||
|
def merge_files(input, basefile, output, diff=False):
|
||||||
|
with open(input, "rb") as infile:
|
||||||
|
if infile.read(4) != b"IIDX":
|
||||||
|
print("Invalid", input)
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
|
data_ver, available_entries, total_entries, unk4 = struct.unpack(
|
||||||
|
"<IHIH", infile.read(12)
|
||||||
|
)
|
||||||
|
|
||||||
|
song_ids = {}
|
||||||
|
for i in range(total_entries):
|
||||||
|
song_id = struct.unpack("<H", infile.read(2))[0]
|
||||||
|
|
||||||
|
if song_id != 0xFFFF and (len(song_ids) == 0 or song_id != 0):
|
||||||
|
song_ids[i] = song_id
|
||||||
|
|
||||||
|
if data_ver in read_handlers:
|
||||||
|
old_data = reader(data_ver, infile, available_entries)
|
||||||
|
else:
|
||||||
|
print("Couldn't find a handler for this input data version")
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
|
with open(basefile, "rb") as infile:
|
||||||
|
if infile.read(4) != b"IIDX":
|
||||||
|
print("Invalid", basefile)
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
|
data_ver, available_entries, total_entries, unk4 = struct.unpack(
|
||||||
|
"<IHIH", infile.read(12)
|
||||||
|
)
|
||||||
|
|
||||||
|
song_ids = {}
|
||||||
|
for i in range(total_entries):
|
||||||
|
song_id = struct.unpack("<H", infile.read(2))[0]
|
||||||
|
|
||||||
|
if song_id != 0xFFFF and (len(song_ids) == 0 or song_id != 0):
|
||||||
|
song_ids[i] = song_id
|
||||||
|
|
||||||
|
if data_ver in read_handlers:
|
||||||
|
new_data = reader(data_ver, infile, available_entries)
|
||||||
|
else:
|
||||||
|
print("Couldn't find a handler for this input data version")
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
|
# Create list of
|
||||||
|
exist_ids_new = {}
|
||||||
|
for song_data in new_data:
|
||||||
|
exist_ids_new[song_data["song_id"]] = True
|
||||||
|
|
||||||
|
for song_data in old_data:
|
||||||
|
if song_data["song_id"] not in exist_ids_new:
|
||||||
|
new_data.append(song_data)
|
||||||
|
|
||||||
|
writer(data_ver, open(output, "wb"), new_data)
|
||||||
|
|
||||||
|
if diff:
|
||||||
|
new_data.clear()
|
||||||
|
|
||||||
|
for song_data in old_data:
|
||||||
|
if song_data["song_id"] not in exist_ids_new:
|
||||||
|
new_data.append(song_data)
|
||||||
|
|
||||||
|
writer(data_ver, open(output[:-4] + "_diff.bin", "wb"), new_data)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("--input", help="Input file", required=True)
|
||||||
|
parser.add_argument("--output", help="Output file", required=True)
|
||||||
|
parser.add_argument(
|
||||||
|
"--extract", help="Extraction mode", default=False, action="store_true"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--create", help="Creation mode", default=False, action="store_true"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--convert", help="Conversion mode", default=False, action="store_true"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--merge", help="Merge mode", default=False, action="store_true"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--data-version",
|
||||||
|
help="Force a data version (usedful for converts)",
|
||||||
|
default=None,
|
||||||
|
type=int,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--diff", help="Create diff file with merge", default=False, action="store_true"
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if (
|
||||||
|
args.create == False
|
||||||
|
and args.extract == False
|
||||||
|
and args.convert == False
|
||||||
|
and args.merge == False
|
||||||
|
):
|
||||||
|
print("You must specify either --extract or --create or --convert or --merge")
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
|
if args.convert == True:
|
||||||
|
if args.data_version == None:
|
||||||
|
print("You must specify a target --data-version with --convert")
|
||||||
|
exit(-1)
|
||||||
|
elif args.data_version not in write_handlers:
|
||||||
|
print("Don't know how to handle specified data version")
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
|
if args.extract:
|
||||||
|
extract_file(args.input, args.output)
|
||||||
|
|
||||||
|
elif args.create:
|
||||||
|
create_file(args.input, args.output, args.data_version)
|
||||||
|
|
||||||
|
elif args.convert:
|
||||||
|
convert_file(args.input, args.output, args.data_version)
|
||||||
|
|
||||||
|
elif args.merge:
|
||||||
|
merge_files(args.input, args.output, args.output, args.diff)
|
Loading…
x
Reference in New Issue
Block a user