add back games, conform them to new title dispatch

This commit is contained in:
Hay1tsme
2023-02-17 01:02:21 -05:00
parent f18e939dd0
commit 7e3396a7ff
214 changed files with 19412 additions and 23 deletions

18
titles/diva/__init__.py Normal file
View File

@ -0,0 +1,18 @@
from titles.diva.index import DivaServlet
from titles.diva.const import DivaConstants
from titles.diva.database import DivaData
from titles.diva.read import DivaReader
index = DivaServlet
database = DivaData
reader = DivaReader
use_default_title = True
include_protocol = True
title_secure = False
game_codes = [DivaConstants.GAME_CODE]
trailing_slash = True
use_default_host = False
host = ""
current_schema_version = 1

506
titles/diva/base.py Normal file
View File

@ -0,0 +1,506 @@
import datetime
from typing import Any, List, Dict
import logging
import json
import urllib
from core.config import CoreConfig
from titles.diva.config import DivaConfig
from titles.diva.const import DivaConstants
from titles.diva.database import DivaData
class DivaBase():
def __init__(self, cfg: CoreConfig, game_cfg: DivaConfig) -> None:
self.core_cfg = cfg # Config file
self.game_config = game_cfg
self.data = DivaData(cfg) # Database
self.date_time_format = "%Y-%m-%d %H:%M:%S"
self.logger = logging.getLogger("diva")
self.game = DivaConstants.GAME_CODE
self.version = DivaConstants.VER_PROJECT_DIVA_ARCADE_FUTURE_TONE
dt = datetime.datetime.now()
self.time_lut=urllib.parse.quote(dt.strftime("%Y-%m-%d %H:%M:%S:16.0"))
def handle_test_request(self, data: Dict) -> Dict:
return ""
def handle_game_init_request(self, data: Dict) -> Dict:
return ( f'' )
def handle_attend_request(self, data: Dict) -> Dict:
encoded = "&"
params = {
'atnd_prm1': '0,1,1,0,0,0,1,0,100,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1',
'atnd_prm2': '30,10,100,4,1,50,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1',
'atnd_prm3': '100,0,1,1,1,1,1,1,1,1,2,3,4,1,1,1,3,4,5,1,1,1,4,5,6,1,1,1,5,6,7,4,4,4,9,10,14,5,10,10,25,20,50,30,90,5,10,10,25,20,50,30,90,5,10,10,25,20,50,30,90,5,10,10,25,20,50,30,90,5,10,10,25,20,50,30,90,10,30,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0',
'atnd_lut': f'{self.time_lut}',
}
encoded += urllib.parse.urlencode(params)
encoded = encoded.replace("%2C", ",")
return encoded
def handle_ping_request(self, data: Dict) -> Dict:
encoded = "&"
params = {
'ping_b_msg': f'Welcome to {self.core_cfg.server.name} network!',
'ping_m_msg': 'xxx',
'atnd_lut': f'{self.time_lut}',
'fi_lut': f'{self.time_lut}',
'ci_lut': f'{self.time_lut}',
'qi_lut': f'{self.time_lut}',
'pvl_lut': '2021-05-22 12:08:16.0',
'shp_ctlg_lut': '2020-06-10 19:44:16.0',
'cstmz_itm_ctlg_lut': '2019-10-08 20:23:12.0',
'ngwl_lut': '2019-10-08 20:23:12.0',
'rnk_nv_lut': '2020-06-10 19:51:30.0',
'rnk_ps_lut': f'{self.time_lut}',
'bi_lut': '2020-09-18 10:00:00.0',
'cpi_lut': '2020-10-25 09:25:10.0',
'bdlol_lut': '2020-09-18 10:00:00.0',
'p_std_hc_lut': '2019-08-01 04:00:36.0',
'p_std_i_n_lut': '2019-08-01 04:00:36.0',
'pdcl_lut': '2019-08-01 04:00:36.0',
'pnml_lut': '2019-08-01 04:00:36.0',
'cinml_lut': '2019-08-01 04:00:36.0',
'rwl_lut': '2019-08-01 04:00:36.0',
'req_inv_cmd_num': '-1,-1,-1,-1,-1,-1,-1,-1,-1,-1',
'req_inv_cmd_prm1': '-1,-1,-1,-1,-1,-1,-1,-1,-1,-1',
'req_inv_cmd_prm2': '-1,-1,-1,-1,-1,-1,-1,-1,-1,-1',
'req_inv_cmd_prm3': '-1,-1,-1,-1,-1,-1,-1,-1,-1,-1',
'req_inv_cmd_prm4': '-1,-1,-1,-1,-1,-1,-1,-1,-1,-1',
'pow_save_flg': 0,
'nblss_dnt_p': 100,
'nblss_ltt_rl_vp': 1500,
'nblss_ex_ltt_flg': 1,
'nblss_dnt_st_tm': "2019-07-15 12:00:00.0",
'nblss_dnt_ed_tm': "2019-09-17 12:00:00.0",
'nblss_ltt_st_tm': "2019-09-18 12:00:00.0",
'nblss_ltt_ed_tm': "2019-09-22 12:00:00.0",
}
encoded += urllib.parse.urlencode(params)
encoded = encoded.replace("+", "%20")
encoded = encoded.replace("%2C", ",")
return encoded
def handle_pv_list_request(self, data: Dict) -> Dict:
pvlist = ""
with open(r"titles/diva/data/PvList0.dat", encoding="utf-8") as shop:
lines = shop.readlines()
for line in lines:
pvlist += f"{line}"
pvlist += ","
with open(r"titles/diva/data/PvList1.dat", encoding="utf-8") as shop:
lines = shop.readlines()
for line in lines:
pvlist += f"{line}"
pvlist += ","
with open(r"titles/diva/data/PvList2.dat", encoding="utf-8") as shop:
lines = shop.readlines()
for line in lines:
pvlist += f"{line}"
pvlist += ","
with open(r"titles/diva/data/PvList3.dat", encoding="utf-8") as shop:
lines = shop.readlines()
for line in lines:
pvlist += f"{line}"
pvlist += ","
with open(r"titles/diva/data/PvList4.dat", encoding="utf-8") as shop:
lines = shop.readlines()
for line in lines:
pvlist += f"{line}"
response = ""
response += f"&pvl_lut={self.time_lut}"
response += f"&pv_lst={pvlist}"
return ( response )
def handle_shop_catalog_request(self, data: Dict) -> Dict:
catalog = ""
shopList = self.data.static.get_enabled_shop(self.version)
if not shopList:
with open(r"titles/diva/data/ShopCatalog.dat", encoding="utf-8") as shop:
lines = shop.readlines()
for line in lines:
line = urllib.parse.quote(line) + ","
catalog += f"{urllib.parse.quote(line)}"
catalog = catalog.replace("+", "%20")
response = ""
response += f"&shp_ctlg_lut={self.time_lut}"
response += f"&shp_ctlg={catalog[:-3]}"
else:
for shop in shopList:
line = str(shop["shopId"]) + "," + str(shop['unknown_0']) + "," + shop['name'] + "," + str(shop['points']) + "," + shop['start_date'] + "," + shop['end_date'] + "," + str(shop["type"])
line = urllib.parse.quote(line) + ","
catalog += f"{urllib.parse.quote(line)}"
catalog = catalog.replace("+", "%20")
response = ""
response += f"&shp_ctlg_lut={self.time_lut}"
response += f"&shp_ctlg={catalog[:-3]}"
return ( response )
def handle_cstmz_itm_ctlg_request(self, data: Dict) -> Dict:
catalog = ""
itemList = self.data.static.get_enabled_items(self.version)
if not itemList:
with open(r"titles/diva/data/ItemCatalog.dat", encoding="utf-8") as item:
lines = item.readlines()
for line in lines:
line = urllib.parse.quote(line) + ","
catalog += f"{urllib.parse.quote(line)}"
catalog = catalog.replace("+", "%20")
response = ""
response += f"&cstmz_itm_ctlg_lut={self.time_lut}"
response += f"&cstmz_itm_ctlg={catalog[:-3]}"
else:
for item in itemList:
line = str(item["itemId"]) + "," + str(item['unknown_0']) + "," + item['name'] + "," + str(item['points']) + "," + item['start_date'] + "," + item['end_date'] + "," + str(item["type"])
line = urllib.parse.quote(line) + ","
catalog += f"{urllib.parse.quote(line)}"
catalog = catalog.replace("+", "%20")
response = ""
response += f"&cstmz_itm_ctlg_lut={self.time_lut}"
response += f"&cstmz_itm_ctlg={catalog[:-3]}"
return ( response )
def handle_festa_info_request(self, data: Dict) -> Dict:
encoded = "&"
params = {
'fi_id': '1,-1',
'fi_name': f'{self.core_cfg.server.name} Opening,xxx',
'fi_kind': '0,0',
'fi_difficulty': '-1,-1',
'fi_pv_id_lst': 'ALL,ALL',
'fi_attr': '7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',
'fi_add_vp': '10,0',
'fi_mul_vp': '1,1',
'fi_st': '2022-06-17 17:00:00.0,2014-07-08 18:10:11.0',
'fi_et': '2029-01-01 10:00:00.0,2014-07-08 18:10:11.0',
'fi_lut': '{self.time_lut}',
}
encoded += urllib.parse.urlencode(params)
encoded = encoded.replace("+", "%20")
encoded = encoded.replace("%2C", ",")
return encoded
def handle_contest_info_request(self, data: Dict) -> Dict:
response = ""
response += f"&ci_lut={self.time_lut}"
response += "&ci_str=%2A%2A%2A,%2A%2A%2A,%2A%2A%2A,%2A%2A%2A,%2A%2A%2A,%2A%2A%2A,%2A%2A%2A,%2A%2A%2A"
return ( response )
def handle_qst_inf_request(self, data: Dict) -> Dict:
quest = ""
questList = self.data.static.get_enabled_quests(self.version)
if not questList:
with open(r"titles/diva/data/QuestInfo.dat", encoding="utf-8") as shop:
lines = shop.readlines()
for line in lines:
quest += f"{urllib.parse.quote(line)},"
response = ""
response += f"&qi_lut={self.time_lut}"
response += f"&qhi_str={quest[:-1]}"
else:
for quests in questList:
line = str(quests["questId"]) + "," + str(quests['quest_order']) + "," + str(quests['kind']) + "," + str(quests['unknown_0']) + "," + quests['start_datetime'] + "," + quests['end_datetime'] + "," + quests["name"] + "," + str(quests["unknown_1"]) + "," + str(quests["unknown_2"]) + "," + str(quests["quest_enable"])
quest += f"{urllib.parse.quote(line)}%0A,"
responseline = f"{quest[:-1]},"
for i in range(len(questList),59):
responseline += "%2A%2A%2A%0A,"
response = ""
response += f"&qi_lut={self.time_lut}"
response += f"&qhi_str={responseline}%2A%2A%2A"
response += "&qrai_str=%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1,%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1,%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1%2C%2D1"
return ( response )
def handle_nv_ranking_request(self, data: Dict) -> Dict:
return ( f'' )
def handle_ps_ranking_request(self, data: Dict) -> Dict:
return ( f'' )
def handle_ng_word_request(self, data: Dict) -> Dict:
return ( f'' )
def handle_rmt_wp_list_request(self, data: Dict) -> Dict:
return ( f'' )
def handle_pv_def_chr_list_request(self, data: Dict) -> Dict:
return ( f'' )
def handle_pv_ng_mdl_list_request(self, data: Dict) -> Dict:
return ( f'' )
def handle_cstmz_itm_ng_mdl_lst_request(self, data: Dict) -> Dict:
return ( f'' )
def handle_banner_info_request(self, data: Dict) -> Dict:
return ( f'' )
def handle_banner_data_request(self, data: Dict) -> Dict:
return ( f'' )
def handle_cm_ply_info_request(self, data: Dict) -> Dict:
return ( f'' )
def handle_pstd_h_ctrl_request(self, data: Dict) -> Dict:
return ( f'' )
def handle_pstd_item_ng_lst_request(self, data: Dict) -> Dict:
return ( f'' )
def handle_pre_start_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile(data["aime_id"], self.version)
profile_shop = self.data.item.get_shop(data["aime_id"], self.version)
if profile is None:
return ( f"&ps_result=-3")
else:
response = ""
response += "&ps_result=1"
response += f"&pd_id={data['aime_id']}"
response += "&nblss_ltt_stts=-1"
response += "&nblss_ltt_tckt=-1"
response += "&nblss_ltt_is_opn=-1"
response += f"&vcld_pts={profile['vcld_pts']}"
response += f"&player_name={profile['player_name']}"
response += f"&lv_efct_id={profile['lv_efct_id']}"
response += f"&lv_plt_id={profile['lv_plt_id']}"
response += f"&lv_str={profile['lv_str']}"
response += f"&lv_num={profile['lv_num']}"
response += f"&lv_pnt={profile['lv_pnt']}"
#Store stuff to add to rework
response += f"&mdl_eqp_tm={self.time_lut}"
if profile_shop:
response += f"&mdl_eqp_ary={profile_shop['mdl_eqp_ary']}"
response += f"&c_itm_eqp_ary=-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,-999"
response += f"&ms_itm_flg_ary=1,1,1,1,1,1,1,1,1,1,1,1"
return ( response )
def handle_registration_request(self, data: Dict) -> Dict: #DONE
self.data.profile.create_profile(self.version, data["aime_id"], data["player_name"])
return ( f"&cd_adm_result=1&pd_id={data['aime_id']}")
def handle_start_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile(data["pd_id"], self.version)
profile_shop = self.data.item.get_shop(data["pd_id"], self.version)
if profile is None: return
response = ""
response += f"&pd_id={data['pd_id']}"
response += "&start_result=1"
response += "&accept_idx=100"
response += f"&hp_vol={profile['hp_vol']}"
response += f"&btn_se_vol={profile['btn_se_vol']}"
response += f"&btn_se_vol2={profile['btn_se_vol2']}"
response += f"&sldr_se_vol2={profile['sldr_se_vol2']}"
response += f"&sort_kind={profile['sort_kind']}"
response += f"&player_name={profile['player_name']}"
response += f"&lv_num={profile['lv_num']}"
response += f"&lv_pnt={profile['lv_pnt']}"
response += f"&lv_efct_id={profile['lv_efct_id']}"
response += f"&lv_plt_id={profile['lv_plt_id']}"
response += "&mdl_have=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
response += "&cstmz_itm_have=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
response += f"&use_pv_mdl_eqp={profile['use_pv_mdl_eqp']}"
response += f"&use_pv_btn_se_eqp={profile['use_pv_btn_se_eqp']}"
response += f"&use_pv_sld_se_eqp={profile['use_pv_sld_se_eqp']}"
response += f"&use_pv_chn_sld_se_eqp={profile['use_pv_chn_sld_se_eqp']}"
response += f"&use_pv_sldr_tch_se_eqp={profile['use_pv_sldr_tch_se_eqp']}"
response += f"&vcld_pts={profile['lv_efct_id']}"
response += f"&nxt_pv_id={profile['nxt_pv_id']}"
response += f"&nxt_dffclty={profile['nxt_dffclty']}"
response += f"&nxt_edtn={profile['nxt_edtn']}"
response += f"&dsp_clr_brdr={profile['dsp_clr_brdr']}"
response += f"&dsp_intrm_rnk={profile['dsp_intrm_rnk']}"
response += f"&dsp_clr_sts={profile['dsp_clr_sts']}"
response += f"&rgo_sts={profile['rgo_sts']}"
#To be fully fixed
if "my_qst_id" not in profile:
response += f"&my_qst_id=-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"
response += f"&my_qst_sts=0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"
else:
response += f"&my_qst_id={profile['my_qst_id']}"
response += f"&my_qst_sts={profile['my_qst_sts']}"
response += f"&my_qst_prgrs=0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"
response += f"&my_qst_et=2022-06-19%2010%3A28%3A52.0,2022-06-19%2010%3A28%3A52.0,2022-06-19%2010%3A28%3A52.0,2100-01-01%2008%3A59%3A59.0,2100-01-01%2008%3A59%3A59.0,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx"
response += f"&clr_sts=0,0,0,0,0,0,0,0,56,52,35,6,6,3,1,0,0,0,0,0"
#Store stuff to add to rework
response += f"&mdl_eqp_tm={self.time_lut}"
if profile_shop:
response += f"&mdl_eqp_ary={profile_shop['mdl_eqp_ary']}"
response += f"&c_itm_eqp_ary=-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,-999"
response += f"&ms_itm_flg_ary=1,1,1,1,1,1,1,1,1,1,1,1"
return ( response )
def handle_pd_unlock_request(self, data: Dict) -> Dict:
return ( f'' )
def handle_spend_credit_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile(data["pd_id"], self.version)
if profile is None: return
response = ""
response += "&cmpgn_rslt=-1,-1,x,-1,-1,x,x,-1,x,-1,-1,x,-1,-1,x,x,-1,x,-1,-1,x,-1,-1,x,x,-1,x,-1,-1,x,-1,-1,x,x,-1,x,-1,-1,x,-1,-1,x,x,-1,x,-1,-1,x,-1,-1,x,x,-1,x"
response += "&cmpgn_rslt_num=0"
response += f"&vcld_pts={profile['vcld_pts']}"
response += f"&lv_str={profile['lv_str']}"
response += f"&lv_efct_id={profile['lv_efct_id']}"
response += f"&lv_plt_id={profile['lv_plt_id']}"
return ( response )
def handle_get_pv_pd_request(self, data: Dict) -> Dict:
song_id = data["pd_pv_id_lst"].split(",")
pv = ""
for song in song_id:
if int(song) > 0:
pd_db_song = self.data.score.get_best_score(data["pd_id"], int(song), data["difficulty"])
if pd_db_song is not None:
pv += urllib.parse.quote(f"{song},0,{pd_db_song['clr_kind']},{pd_db_song['score']},{pd_db_song['atn_pnt']},{pd_db_song['sort_kind']},-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,1,1,1,1,1,1,1,1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1337,1,1,1,0,0,0")
else:
#self.logger.debug(f"No score saved for ID: {song}!")
pv += urllib.parse.quote(f"{song},0,-1,-1,-1,0,-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,1,1,1,1,1,1,1,1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1,1,1,0,0,0")
#pv_no, edition, rslt, max_score, max_atn_pnt, challenge_kind, module_eqp[-999,-999,-999], customize_eqp[-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,-999], customize_flag[1,1,1,1,1,1,1,1,1,1,1,1], skin, btn_se, sld_se, chsld_se, sldtch_se, rvl_pd_id, rvl_score, rvl_attn_pnt, countrywide_ranking, rgo_hispeed, rgo_hidden, rgo_sudden, rgo_hispeed_cleared, rgo_hidden_cleared, rgo_sudden_cleared, chain_challenge_num, chain_challenge_max, chain_challenge_open, version
else:
pv += urllib.parse.quote(f"{song}***")
pv += ","
response = ""
response += f"&pd_by_pv_id={pv[:-1]}"
response += "&pdddt_flg=0"
response += f"&pdddt_tm={self.time_lut}"
return ( response )
def handle_stage_start_request(self, data: Dict) -> Dict:
return ( f'' )
def handle_stage_result_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile(data["pd_id"], self.version)
pd_song_list = data["stg_ply_pv_id"].split(",")
pd_song_difficulty = data["stg_difficulty"].split(",")
pd_song_max_score = data["stg_score"].split(",")
pd_song_max_atn_pnt = data["stg_atn_pnt"].split(",")
pd_song_ranking = data["stg_clr_kind"].split(",")
pd_song_sort_kind = data["sort_kind"]
pd_song_cool_cnt = data["stg_cool_cnt"].split(",")
pd_song_fine_cnt = data["stg_fine_cnt"].split(",")
pd_song_safe_cnt = data["stg_safe_cnt"].split(",")
pd_song_sad_cnt = data["stg_sad_cnt"].split(",")
pd_song_worst_cnt = data["stg_wt_wg_cnt"].split(",")
pd_song_max_combo = data["stg_max_cmb"].split(",")
for index, value in enumerate(pd_song_list):
if "-1" not in pd_song_list[index]:
profile_pd_db_song = self.data.score.get_best_score(data["pd_id"], pd_song_list[index], pd_song_difficulty[index])
if profile_pd_db_song is None:
self.data.score.put_best_score(data["pd_id"], self.version, pd_song_list[index], pd_song_difficulty[index], pd_song_max_score[index], pd_song_max_atn_pnt[index], pd_song_ranking[index], pd_song_sort_kind, pd_song_cool_cnt[index], pd_song_fine_cnt[index], pd_song_safe_cnt[index], pd_song_sad_cnt[index], pd_song_worst_cnt[index], pd_song_max_combo[index])
self.data.score.put_playlog(data["pd_id"], self.version, pd_song_list[index], pd_song_difficulty[index], pd_song_max_score[index], pd_song_max_atn_pnt[index], pd_song_ranking[index], pd_song_sort_kind, pd_song_cool_cnt[index], pd_song_fine_cnt[index], pd_song_safe_cnt[index], pd_song_sad_cnt[index], pd_song_worst_cnt[index], pd_song_max_combo[index])
elif int(pd_song_max_score[index]) >= int(profile_pd_db_song["score"]):
self.data.score.put_best_score(data["pd_id"], self.version, pd_song_list[index], pd_song_difficulty[index], pd_song_max_score[index], pd_song_max_atn_pnt[index], pd_song_ranking[index], pd_song_sort_kind, pd_song_cool_cnt[index], pd_song_fine_cnt[index], pd_song_safe_cnt[index], pd_song_sad_cnt[index], pd_song_worst_cnt[index], pd_song_max_combo[index])
self.data.score.put_playlog(data["pd_id"], self.version, pd_song_list[index], pd_song_difficulty[index], pd_song_max_score[index], pd_song_max_atn_pnt[index], pd_song_ranking[index], pd_song_sort_kind, pd_song_cool_cnt[index], pd_song_fine_cnt[index], pd_song_safe_cnt[index], pd_song_sad_cnt[index], pd_song_worst_cnt[index], pd_song_max_combo[index])
elif int(pd_song_max_score[index]) != int(profile_pd_db_song["score"]):
self.data.score.put_playlog(data["pd_id"], self.version, pd_song_list[index], pd_song_difficulty[index], pd_song_max_score[index], pd_song_max_atn_pnt[index], pd_song_ranking[index], pd_song_sort_kind, pd_song_cool_cnt[index], pd_song_fine_cnt[index], pd_song_safe_cnt[index], pd_song_sad_cnt[index], pd_song_worst_cnt[index], pd_song_max_combo[index])
# Profile saving based on registration list
old_level = int(profile['lv_num'])
new_level = (int(data["ttl_vp_add"]) + int(profile["lv_pnt"])) / 12
self.data.profile.update_profile(data["pd_id"], int(new_level), int(profile["lv_pnt"]) + int(data["ttl_vp_add"]), int(data["vcld_pts"]), int(data["hp_vol"]), int(data["btn_se_vol"]), int(data["btn_se_vol2"]), int(data["sldr_se_vol2"]), int(data["sort_kind"]), int(data["use_pv_mdl_eqp"]), profile["use_pv_btn_se_eqp"], profile["use_pv_sld_se_eqp"], profile["use_pv_chn_sld_se_eqp"], profile["use_pv_sldr_tch_se_eqp"], int(data["ply_pv_id"]), int(data["nxt_dffclty"]), int(data["nxt_edtn"]), profile["dsp_clr_brdr"], profile["dsp_intrm_rnk"], profile["dsp_clr_sts"], profile["rgo_sts"], profile["lv_efct_id"], profile["lv_plt_id"], data["my_qst_id"], data["my_qst_sts"])
response = ""
response += "&chllng_kind=-1"
response += f"&lv_num_old={int(old_level)}"
response += f"&lv_pnt_old={int(profile['lv_pnt'])}"
response += f"&lv_num={int(profile['lv_num'])}"
response += f"&lv_str={profile['lv_str']}"
response += f"&lv_pnt={int(profile['lv_pnt']) + int(data['ttl_vp_add'])}"
response += f"&lv_efct_id={int(profile['lv_efct_id'])}"
response += f"&lv_plt_id={int(profile['lv_plt_id'])}"
response += f"&vcld_pts={int(data['vcld_pts'])}"
response += f"&prsnt_vcld_pts={int(profile['vcld_pts'])}"
response += "&cerwd_kind=-1"
response += "&cerwd_value=-1"
response += "&cerwd_str_0=***"
response += "&cerwd_str_1=***"
response += "&ttl_str_ary=xxx,xxx,xxx,xxx,xxx"
response += "&ttl_plt_id_ary=-1,-1,-1,-1,-1"
response += "&ttl_desc_ary=xxx,xxx,xxx,xxx,xxx"
response += "&skin_id_ary=xxx,xxx,xxx,xxx,xxx"
response += "&skin_name_ary=xxx,xxx,xxx,xxx,xxx"
response += "&skin_illust_ary=xxx,xxx,xxx,xxx,xxx"
response += "&skin_desc_ary=xxx,xxx,xxx,xxx,xxx"
if "my_qst_id" not in profile:
response += f"&my_qst_id=-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"
else:
response += f"&my_qst_id={profile['my_qst_id']}"
response += "&my_qst_r_qid=-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"
response += "&my_qst_r_knd=-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"
response += "&my_qst_r_vl=-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"
response += "&my_qst_r_nflg=-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"
response += "&my_ccd_r_qid=-1,-1,-1,-1,-1"
response += "&my_ccd_r_hnd=-1,-1,-1,-1,-1"
response += "&my_ccd_r_vp=-1,-1,-1,-1,-1"
return ( response )
def handle_end_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile(data["pd_id"], self.version)
self.data.profile.update_profile(data["pd_id"], profile["lv_num"], profile["lv_pnt"], profile["vcld_pts"], profile["hp_vol"], profile["btn_se_vol"], profile["btn_se_vol2"], profile["sldr_se_vol2"], profile["sort_kind"], profile["use_pv_mdl_eqp"], profile["use_pv_btn_se_eqp"], profile["use_pv_sld_se_eqp"], profile["use_pv_chn_sld_se_eqp"], profile["use_pv_sldr_tch_se_eqp"], profile["nxt_pv_id"], profile["nxt_dffclty"], profile["nxt_edtn"], profile["dsp_clr_brdr"], profile["dsp_intrm_rnk"], profile["dsp_clr_sts"], profile["rgo_sts"], profile["lv_efct_id"], profile["lv_plt_id"], data["my_qst_id"], data["my_qst_sts"])
return ( f'' )
def handle_shop_exit_request(self, data: Dict) -> Dict:
self.data.item.put_shop(data["pd_id"], self.version, data["mdl_eqp_cmn_ary"])
response = ""
response += "&shp_rslt=1"
return ( response )

17
titles/diva/config.py Normal file
View File

@ -0,0 +1,17 @@
from core.config import CoreConfig
class DivaServerConfig():
def __init__(self, parent_config: "DivaConfig") -> None:
self.__config = parent_config
@property
def enable(self) -> bool:
return CoreConfig.get_config_field(self.__config, 'diva', 'server', 'enable', default=True)
@property
def loglevel(self) -> int:
return CoreConfig.str_to_loglevel(CoreConfig.get_config_field(self.__config, 'diva', 'server', 'loglevel', default="info"))
class DivaConfig(dict):
def __init__(self) -> None:
self.server = DivaServerConfig(self)

11
titles/diva/const.py Normal file
View File

@ -0,0 +1,11 @@
class DivaConstants():
GAME_CODE = "SBZV"
VER_PROJECT_DIVA_ARCADE = 0
VER_PROJECT_DIVA_ARCADE_FUTURE_TONE = 1
VERSION_NAMES = ("Project Diva Arcade", "Project Diva Arcade Future Tone")
@classmethod
def game_ver_to_string(cls, ver: int):
return cls.VERSION_NAMES[ver]

View File

@ -0,0 +1,204 @@
0,0,縁なしメガネ(銀),50,2013-09-13,2029-01-01,0
1,0,縁なしメガネ(赤),50,2013-09-13,2029-01-01,1
2,0,ナイロールメガネ(銀),100,2013-09-13,2029-01-01,2
3,0,ナイロールメガネ(茶),100,2014-03-18,2029-01-01,3
4,0,フルフレームメガネ(橙),100,2014-03-18,2029-01-01,4
5,0,フルフレームメガネ(黒),100,2013-09-13,2029-01-01,5
6,0,アンダーリムメガネ(青),50,2014-02-12,2029-01-01,8
7,0,アンダーリムメガネ(ピンク),50,2014-05-15,2029-01-01,9
8,0,ぐるぐるメガネ,150,2014-09-26,2029-01-01,15
9,0,三角メガネ(黒),100,2013-12-25,2029-01-01,10
10,0,三角メガネ(赤),100,2014-03-18,2029-01-01,11
11,0,サングラス,150,2015-01-09,2029-01-01,16
12,0,ゴーグル,150,2014-12-02,2029-01-01,17
13,0,電脳バイザー,150,2015-01-09,2029-01-01,18
14,0,片眼鏡,150,2015-04-01,2029-01-01,14
15,0,アイマスク,150,2014-10-23,2029-01-01,19
16,0,キラ目マスク,200,2015-05-20,2029-01-01,20
17,0,眼帯(黒),150,2013-09-13,2029-01-01,23
18,0,眼帯(緑),150,2013-12-25,2029-01-01,24
19,0,眼帯(黄),150,2014-02-12,2029-01-01,25
20,0,眼帯(オレンジ),150,2014-05-15,2029-01-01,26
21,0,眼帯(ピンク),150,2014-05-15,2029-01-01,27
22,0,眼帯(青),150,2013-09-13,2029-01-01,28
23,0,眼帯(赤),150,2013-09-13,2029-01-01,29
24,0,眼帯(白),150,2015-07-22,2029-01-01,22
25,0,京劇仮面(青),200,2016-01-08,2029-01-01,33
26,0,京劇仮面(赤),200,2016-01-08,2029-01-01,34
27,0,バタフライマスク,200,2015-07-22,2029-01-01,30
28,0,マスカレードマスク,200,2014-07-24,2029-01-01,31
30,0,ガスマスク,200,2015-07-22,2029-01-01,39
31,0,能面(般若),200,2015-07-22,2029-01-01,40
32,0,能面(女),200,2015-05-20,2029-01-01,41
33,0,能面(翁),200,2015-05-20,2029-01-01,42
34,0,白マスク,150,2014-07-24,2029-01-01,35
35,0,白マスク(ペロ舌),200,2014-09-26,2029-01-01,36
36,0,白マスク(ω),200,2014-12-02,2029-01-01,37
37,0,白マスク(ε),200,2015-05-20,2029-01-01,38
38,0,ネコひげ,50,2015-04-01,2029-01-01,43
39,0,天使の輪,200,2015-07-22,2029-01-01,10
40,0,ひよこ,200,2014-07-24,2029-01-01,11
41,0,ナースキャップ,100,2013-09-13,2029-01-01,0
42,0,メイドカチューシャ,100,2013-12-25,2029-01-01,1
43,0,ネコミミ(黒),200,2013-09-13,2029-01-01,2
44,0,ネコミミ(白),200,2013-12-25,2029-01-01,3
45,0,ネコミミ(トラ),200,2014-03-18,2029-01-01,4
46,0,ウサミミ(黒),200,2014-02-12,2029-01-01,5
47,0,ウサミミ(白),200,2013-09-13,2029-01-01,6
48,0,ウサミミ(ピンク),200,2014-09-26,2029-01-01,7
49,0,一本角,150,2014-03-18,2029-01-01,8
50,0,悪魔の角,200,2015-05-20,2029-01-01,9
51,0,クセ毛 A,150,2014-05-15,2029-01-01,12
52,0,クセ毛 B,150,2014-07-24,2029-01-01,22
53,0,クセ毛 C,150,2014-09-26,2029-01-01,31
54,0,クセ毛 Dピンク,150,2014-10-23,2029-01-01,41
55,0,クセ毛 E,150,2014-12-02,2029-01-01,51
56,0,クセ毛 Fこげ茶,150,2015-01-09,2029-01-01,61
57,0,蝶ネクタイ(金),100,2013-12-25,2029-01-01,0
58,0,蝶ネクタイ(黒),100,2013-09-13,2029-01-01,1
59,0,蝶ネクタイ(赤),100,2013-09-13,2029-01-01,2
60,0,リボン(青),150,2014-02-12,2029-01-01,3
61,0,リボン(黄),150,2013-09-13,2029-01-01,4
62,0,リボン(ピンク),150,2013-09-13,2029-01-01,5
67,0,鈴(金),150,2014-10-23,2029-01-01,6
68,0,鈴(銀),150,2014-10-23,2029-01-01,7
69,0,光の翼,200,2014-12-02,2029-01-01,1
70,0,天使の翼,200,2015-07-22,2029-01-01,0
71,0,蝶の羽根,200,2015-04-01,2029-01-01,3
72,0,悪魔の翼,200,2015-05-20,2029-01-01,2
73,0,ぬいぐるみ,200,2013-09-13,2029-01-01,8
74,0,リュックサック,150,2013-09-13,2029-01-01,4
75,0,ナップサック,100,2013-12-25,2029-01-01,5
76,0,ランドセル(黒),150,2014-02-12,2029-01-01,6
77,0,ランドセル(赤),150,2014-02-12,2029-01-01,7
78,0,ロケット,150,2014-07-24,2029-01-01,9
79,0,ネコしっぽ(黒),200,2013-09-13,2029-01-01,11
80,0,ネコしっぽ(白),200,2013-12-25,2029-01-01,12
81,0,ネコしっぽ(トラ),200,2014-03-18,2029-01-01,13
82,0,ウサしっぽ(黒),200,2014-02-12,2029-01-01,14
83,0,ウサしっぽ(白),200,2013-09-13,2029-01-01,15
84,0,ウサしっぽ(ピンク),200,2014-09-26,2029-01-01,16
85,0,狐しっぽ,350,2014-05-15,2029-01-01,17
86,0,悪魔の尾,200,2015-01-09,2029-01-01,18
87,0,赤ぷよぼう,400,2016-09-21,2029-01-01,66
88,0,緑ぷよのかみどめ,400,2016-09-21,2029-01-01,67
89,0,ゼンマイ,150,2015-07-22,2029-01-01,10
97,0,ゴールドクラウン,350,2016-01-08,2029-01-01,70
98,0,プラチナクラウン,350,2016-01-08,2029-01-01,71
99,0,シャープメガネ(藍),100,2014-09-26,2029-01-01,6
100,0,シャープメガネ(紫),100,2013-12-25,2029-01-01,7
101,0,丸メガネ(銀),150,2013-12-25,2029-01-01,12
102,0,丸メガネ(べっ甲),150,2014-03-18,2029-01-01,13
103,0,たこルカ,400,2016-05-18,2029-01-01,68
104,0,シテヤンヨ,400,2016-03-17,2029-01-01,69
105,0,はちゅねミク,400,2014-12-02,2029-01-01,19
106,0,リンの幼虫,400,2016-01-08,2029-01-01,20
107,0,劇画マスク,200,2016-01-08,2029-01-01,21
115,0,シャープサングラス,150,2014-07-24,2029-01-01,45
116,0,水中メガネ,200,2014-12-02,2029-01-01,44
117,0,パーティー眼鏡,100,2015-04-01,2029-01-01,48
118,0,ピエロ鼻,50,2015-05-20,2029-01-01,46
119,0,キツネ面,200,2016-03-17,2029-01-01,47
120,0,ミニシルクハット,350,2014-05-15,2029-01-01,74
121,0,コック帽,50,2013-09-13,2029-01-01,73
122,0,烏帽子,50,2015-04-01,2029-01-01,72
123,0,ミクダヨー,1500,2015-04-01,2029-01-01,76
124,0,記章,100,2014-10-23,2029-01-01,8
131,0,ポスター付リュックサック,150,2014-03-18,2029-01-01,21
132,0,リコーダー付ランドセル(黒),150,2015-01-09,2029-01-01,22
133,0,リコーダー付ランドセル(赤),150,2015-01-09,2029-01-01,23
134,0,ミクダヨー(ミニ),500,2014-10-23,2029-01-01,75
135,0,クセ毛 A,150,2014-05-15,2029-01-01,13
136,0,クセ毛 Aピンク,150,2014-05-15,2029-01-01,14
137,0,クセ毛 A,150,2014-05-15,2029-01-01,15
138,0,クセ毛 Aこげ茶,150,2014-05-15,2029-01-01,16
139,0,クセ毛 A,150,2014-05-15,2029-01-01,17
140,0,クセ毛 A,150,2014-05-15,2029-01-01,18
141,0,クセ毛 A,150,2014-05-15,2029-01-01,19
142,0,クセ毛 A,150,2014-05-15,2029-01-01,20
143,0,クセ毛 B,150,2014-07-24,2029-01-01,21
144,0,クセ毛 Bピンク,150,2014-07-24,2029-01-01,23
145,0,クセ毛 B,150,2014-07-24,2029-01-01,24
146,0,クセ毛 Bこげ茶,150,2014-07-24,2029-01-01,25
147,0,クセ毛 B,150,2014-07-24,2029-01-01,26
148,0,クセ毛 B,150,2014-07-24,2029-01-01,27
149,0,クセ毛 B,150,2014-07-24,2029-01-01,28
150,0,クセ毛 B,150,2014-07-24,2029-01-01,29
151,0,クセ毛 C,150,2014-09-26,2029-01-01,30
152,0,クセ毛 Cピンク,150,2014-09-26,2029-01-01,32
153,0,クセ毛 C,150,2014-09-26,2029-01-01,33
154,0,クセ毛 Cこげ茶,150,2014-09-26,2029-01-01,34
155,0,クセ毛 C,150,2014-09-26,2029-01-01,35
156,0,クセ毛 C,150,2014-09-26,2029-01-01,36
157,0,クセ毛 C,150,2014-09-26,2029-01-01,37
158,0,クセ毛 C,150,2014-09-26,2029-01-01,38
159,0,クセ毛 D,150,2014-10-23,2029-01-01,39
160,0,クセ毛 D,150,2014-10-23,2029-01-01,40
161,0,クセ毛 D,150,2014-10-23,2029-01-01,42
162,0,クセ毛 Dこげ茶,150,2014-10-23,2029-01-01,43
163,0,クセ毛 D,150,2014-10-23,2029-01-01,44
164,0,クセ毛 D,150,2014-10-23,2029-01-01,45
165,0,クセ毛 D,150,2014-10-23,2029-01-01,46
166,0,クセ毛 D,150,2014-10-23,2029-01-01,47
167,0,クセ毛 E,150,2014-12-02,2029-01-01,48
168,0,クセ毛 E,150,2014-12-02,2029-01-01,49
169,0,クセ毛 Eピンク,150,2014-12-02,2029-01-01,50
170,0,クセ毛 Eこげ茶,150,2014-12-02,2029-01-01,52
171,0,クセ毛 E,150,2014-12-02,2029-01-01,53
172,0,クセ毛 E,150,2014-12-02,2029-01-01,54
173,0,クセ毛 E,150,2014-12-02,2029-01-01,55
174,0,クセ毛 E,150,2014-12-02,2029-01-01,56
175,0,クセ毛 F,150,2015-01-09,2029-01-01,57
176,0,クセ毛 F,150,2015-01-09,2029-01-01,58
177,0,クセ毛 Fピンク,150,2015-01-09,2029-01-01,59
178,0,クセ毛 F,150,2015-01-09,2029-01-01,60
179,0,クセ毛 F,150,2015-01-09,2029-01-01,62
180,0,クセ毛 F,150,2015-01-09,2029-01-01,63
181,0,クセ毛 F,150,2015-01-09,2029-01-01,64
182,0,クセ毛 F,150,2015-01-09,2029-01-01,65
183,0,イヌミミ,200,2015-10-15,2029-01-01,77
184,0,羊角,200,2016-09-21,2029-01-01,78
185,0,悪魔の頭羽,200,2016-09-21,2029-01-01,79
186,0,とりの巣,200,2016-03-17,2029-01-01,80
187,0,おばけの三角ずきん,200,2016-07-20,2029-01-01,81
188,0,パラボラアンテナ,200,2016-11-24,2029-01-01,82
189,0,パーティ帽(赤),100,2016-12-15,2029-01-01,83
190,0,パーティ帽(青),100,2016-12-15,2029-01-01,84
191,0,パーティ帽(黄),100,2016-12-15,2029-01-01,85
192,0,ホホジロザメ,300,2016-07-20,2029-01-01,86
193,0,パンプキンヘッド,300,2015-10-15,2029-01-01,87
194,0,雪だるまヘッド(ノーマル),300,2016-12-15,2029-01-01,88
195,0,雪だるまヘッド(笑顔),300,2016-12-15,2029-01-01,89
196,0,ハート(頭),200,2017-01-18,2029-01-01,90
197,0,ぷんぷん,200,2016-11-24,2029-01-01,91
198,0,汗(右),100,2015-10-15,2029-01-01,92
199,0,汗(左),100,2015-10-15,2029-01-01,93
200,0,,200,2016-11-24,2029-01-01,94
201,0,でかメガネ,150,2016-07-20,2029-01-01,49
204,0,つけひげ,150,2016-12-15,2029-01-01,52
205,0,くちばし,100,2016-03-17,2029-01-01,53
208,0,目隠し線,200,2016-05-18,2029-01-01,56
210,0,つけえり,150,2015-04-01,2029-01-01,15
211,0,ハーモニカ,150,2016-03-17,2029-01-01,16
212,0,金メダル,200,2016-05-18,2029-01-01,17
213,0,銀メダル,150,2016-05-18,2029-01-01,18
214,0,銅メダル,100,2016-05-18,2029-01-01,19
215,0,前かけ(緑),100,2016-11-24,2029-01-01,20
216,0,前かけ(黄),100,2016-11-24,2029-01-01,21
217,0,前かけ(橙),100,2016-11-24,2029-01-01,22
218,0,前かけ(ピンク),100,2016-11-24,2029-01-01,23
219,0,前かけ(青),100,2016-11-24,2029-01-01,24
220,0,前かけ(赤),100,2016-11-24,2029-01-01,25
221,0,初心者マーク,150,2016-07-20,2029-01-01,26
222,0,タイマー(緑),50,2015-10-15,2029-01-01,27
223,0,タイマー(赤),50,2015-10-15,2029-01-01,28
224,0,ハート(胸),200,2017-01-18,2029-01-01,29
225,0,失恋ハート,200,2017-01-18,2029-01-01,30
226,0,機械の翼,200,2016-03-17,2029-01-01,24
227,0,スクールバッグ,150,2017-01-18,2029-01-01,25
228,0,押しボタン,200,2016-05-18,2029-01-01,26
229,0,イヌしっぽ,200,2015-10-15,2029-01-01,27
230,0,恐竜しっぽ,200,2016-07-20,2029-01-01,28
231,0,猫又しっぽ,300,2016-09-21,2029-01-01,29
232,0,九尾しっぽ,300,2016-12-15,2029-01-01,30
233,0,ぶらさがりネコ,300,2017-01-18,2029-01-01,31

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
%2A%2A%2A

View File

@ -0,0 +1,60 @@
1001,1,0,0,2022-06-18 09:00:00.0,2022-06-20 09:00:00.0,Play in the Simple Mode or Contest Mode,1,1,1
1010,10,0,0,2022-06-18 09:00:00.0,2022-06-20 09:00:00.0,Play with a randomly selected Miku module,1,1,1
1011,11,0,0,2022-06-18 09:00:00.0,2022-06-20 09:00:00.0,Hit COOL 500 times or more,1,3,1
1012,12,0,0,2022-06-18 09:00:00.0,2022-06-20 09:00:00.0,Take a photo in the PV watching mode,1,1,1
1013,13,0,0,2022-06-18 09:00:00.0,2022-06-20 09:00:00.0,Play with the SE turned OFF,1,1,1
1002,2,0,0,2022-06-18 09:00:00.0,2022-06-20 09:00:00.0,Do some Max Chain Slides,1,1,1
1003,3,0,0,2022-06-18 09:00:00.0,2022-06-20 09:00:00.0,Challenge yourself to beat a trial,1,1,1
1004,4,0,0,2022-06-18 09:00:00.0,2022-06-20 09:00:00.0,Play a song on <EX_EXT>,1,2,1
1005,5,0,0,2022-06-18 09:00:00.0,2022-06-20 09:00:00.0,Play a chart by Misora,1,2,1
1006,6,0,0,2022-06-18 09:00:00.0,2022-06-20 09:00:00.0,Score at least 390390 pts in any song,1,1,1
1007,7,0,0,2022-06-18 09:00:00.0,2022-06-20 09:00:00.0,Play a song chosen at random,1,1,1
1008,8,0,0,2022-06-18 09:00:00.0,2022-06-20 09:00:00.0,Play with at least 100 percent result,1,1,1
1009,9,0,0,2022-06-18 09:00:00.0,2022-06-20 09:00:00.0,Play with the <MDLID>169</MDLID> module,1,1,1
3001,1,2,0,2001-01-01 09:00:00.0,2100-01-01 08:59:59.0,Clear a song with the HISPEED mode,1,2,1
3002,2,2,0,2001-01-01 09:00:00.0,2100-01-01 08:59:59.0,Clear a song with the HIDDEN mode,1,2,1
3003,3,2,0,2001-01-01 09:00:00.0,2100-01-01 08:59:59.0,Clear a song with the SUDDEN mode,1,2,1
3004,4,2,0,2001-01-01 09:00:00.0,2100-01-01 08:59:59.0,Enhance your Chain-Perfect Trial streak,1,4,1
3005,5,2,0,2001-01-01 09:00:00.0,2100-01-01 08:59:59.0,Beat your rival in a song by percentage,1,4,1
2001,1,1,0,2022-06-18 09:00:00.0,2022-06-25 09:00:00.0,Hit COOL at least 1300 times,1,3,1
2002,2,1,0,2022-06-18 09:00:00.0,2022-06-25 09:00:00.0,Hit COOL at least 2000 times,1,3,1
2003,3,1,0,2022-06-18 09:00:00.0,2022-06-25 09:00:00.0,Achieve a sum of MAX COMBO of 730 or more,1,3,1
2004,4,1,0,2022-06-18 09:00:00.0,2022-06-25 09:00:00.0,Achieve a sum of MAX COMBO of 960 or more,1,3,1
2005,5,1,0,2022-06-18 09:00:00.0,2022-06-25 09:00:00.0,Score at least 440000 pts in any song,1,1,1
***
***
***
***
***
***
***
***
***
***
***
***
***
***
***
***
***
***
***
***
***
***
***
***
***
***
***
***
***
***
***
***
***
***
***
***
***

View File

@ -0,0 +1,401 @@
0,0,初音ミク,0,2009-01-01,2029-01-01,1
1,0,弱音ハク,50,2009-01-01,2029-01-01,1
2,0,亞北ネル,50,2009-01-01,2029-01-01,4
3,0,メイコスタイル,100,2009-01-01,2029-01-01,2
4,0,リンスタイル,100,2009-01-01,2029-01-01,3
5,0,スペチャン5,100,2009-01-01,2029-01-01,4
6,0,スペチャン39,50,2009-01-01,2029-01-01,5
7,0,ガリア軍第7小隊,100,2009-01-01,2029-01-01,6
8,0,P-スタイルPB,50,2009-01-01,2029-01-01,31
9,0,P-スタイルCW,50,2009-01-01,2029-01-01,32
10,0,P-スタイルIS,50,2009-01-01,2029-01-01,33
11,0,P-スタイルRP,50,2009-01-01,2029-01-01,34
12,0,P-スタイルLP,50,2009-01-01,2029-01-01,35
13,0,P-スタイルFB,50,2009-01-01,2029-01-01,36
14,0,P-スタイルMG,50,2009-01-01,2029-01-01,37
15,0,P-スタイルCG,300,2009-01-01,2029-01-01,38
16,0,チア,50,2009-01-01,2029-01-01,7
17,0,プラグイン,100,2009-01-01,2029-01-01,8
18,0,ゴシック,100,2009-01-01,2029-01-01,9
19,0,プリンセス,50,2009-01-01,2029-01-01,10
20,0,ミコ,100,2009-01-01,2029-01-01,11
21,0,にゃんこ,50,2009-01-01,2029-01-01,12
22,0,ねむねむ,100,2009-01-01,2029-01-01,13
23,0,ハートハンター,100,2009-01-01,2029-01-01,14
24,0,ボーカル,50,2009-01-01,2029-01-01,15
25,0,パンク,100,2009-01-01,2029-01-01,16
26,0,ダンサー,50,2009-01-01,2029-01-01,17
27,0,スター,100,2009-01-01,2029-01-01,18
28,0,フェアリー,100,2009-01-01,2029-01-01,19
29,0,スクール,100,2009-01-01,2029-01-01,20
30,0,スノウ,50,2009-01-01,2029-01-01,21
31,0,アラビアン,100,2009-01-01,2029-01-01,22
32,0,みやび,50,2009-01-01,2029-01-01,23
33,0,チャイナ,300,2009-01-01,2029-01-01,24
34,0,マジシャン,50,2009-01-01,2029-01-01,25
35,0,ホワイトドレス,100,2009-01-01,2029-01-01,26
36,0,パイレーツ,50,2009-01-01,2029-01-01,27
37,0,VN02,100,2009-01-01,2029-01-01,28
38,0,ギャラクシー,100,2009-01-01,2029-01-01,29
39,0,ハツネミク,100,2009-01-01,2029-01-01,30
40,0,鏡音リン,50,2009-01-01,2029-01-01,1
41,0,鏡音レン,50,2009-01-01,2029-01-01,1
42,0,巡音ルカ,50,2009-01-01,2029-01-01,1
43,0,咲音メイコ,300,2009-01-01,2029-01-01,7
44,0,メイコ,50,2009-01-01,2029-01-01,1
45,0,カイト,50,2009-01-01,2029-01-01,1
46,0,初音ミク スイムウェアS,1000,2009-01-01,2029-01-01,140
47,0,初音ミク スイムウェア,1000,2009-01-01,2029-01-01,141
48,0,鏡音リン スイムウェア,1000,2009-01-01,2029-01-01,39
49,0,鏡音レン スイムウェア,300,2009-01-01,2029-01-01,33
50,0,巡音ルカ スイムウェア,1000,2009-01-01,2029-01-01,31
51,0,メイコ スイムウェア,1000,2009-01-01,2029-01-01,24
52,0,カイト スイムウェア,300,2009-01-01,2029-01-01,31
53,0,初音ミク アペンド,300,2010-08-31,2029-01-01,152
54,0,ホワイトワンピース,100,2010-11-16,2029-01-01,49
55,0,ナチュラル,200,2010-11-16,2029-01-01,48
56,0,スピリチュアル,200,2010-11-16,2029-01-01,41
57,0,カラフルドロップ,200,2010-12-27,2029-01-01,57
58,0,初音ミク 蝶,100,2010-11-16,2029-01-01,40
59,0,チアフルキャンディ,200,2010-12-27,2029-01-01,6
60,0,スクールジャージ,100,2010-12-27,2029-01-01,3
61,0,巡音ルカ 華,100,2010-11-16,2029-01-01,2
62,0,初音ミク クリスマス,150,2010-11-30,2029-01-01,153
63,0,鏡音リン クリスマス,150,2010-11-30,2029-01-01,50
64,0,鏡音レン クリスマス,150,2010-11-30,2029-01-01,41
65,0,巡音ルカ クリスマス,150,2010-11-30,2029-01-01,40
66,0,メイコ クリスマス,150,2010-11-30,2029-01-01,32
67,0,カイト クリスマス,150,2010-11-30,2029-01-01,41
68,0,雪ミク 2010,300,2011-01-31,2029-01-01,154
69,0,ヴィンテージドレス,200,2011-02-17,2029-01-01,39
70,0,ピンクポップス,100,2011-01-31,2029-01-01,55
71,0,ピンクポップス AS,100,2011-01-31,2029-01-01,56
72,0,リアクター,200,2011-02-24,2029-01-01,3
73,0,パンキッシュ,200,2011-02-24,2029-01-01,2
74,0,ハードロック,200,2011-01-31,2029-01-01,5
75,0,クラシック,200,2011-02-17,2029-01-01,2
76,0,スカーレット,200,2011-02-17,2029-01-01,3
77,0,雪ミク 2011,300,2011-01-31,2029-01-01,155
78,0,おさんぽスタイル,150,2011-07-26,2029-01-01,42
79,0,みくずきん,150,2011-05-20,2029-01-01,43
80,0,イエロー,150,2011-11-01,2029-01-01,44
81,0,ジャー★ジ,150,2011-09-08,2029-01-01,45
82,0,ノーブル,150,2011-05-20,2029-01-01,46
83,0,パウダー,150,2011-09-08,2029-01-01,47
84,0,エールダンジュ,150,2011-11-01,2029-01-01,50
85,0,スペイシーナース,200,2012-03-13,2029-01-01,51
86,0,初音ミク キュート,200,2011-07-26,2029-01-01,52
87,0,エンジェル,200,2012-01-10,2029-01-01,53
88,0,サイハテミク,150,2011-07-26,2029-01-01,54
89,0,∞,200,2012-02-14,2029-01-01,58
90,0,初音ミク スイムウェアB,1000,2011-07-26,2029-01-01,143
91,0,アシンメトリーR,200,2011-09-08,2029-01-01,2
92,0,EoEスタイル,200,2011-11-01,2029-01-01,4
93,0,鏡音リン キュート,200,2011-07-26,2029-01-01,5
94,0,鏡音リン スイムウェアT,1000,2011-07-26,2029-01-01,41
95,0,アシンメトリーL,200,2011-09-08,2029-01-01,4
96,0,鏡音レン スイムウェアWS,750,2011-07-26,2029-01-01,35
97,0,シフォンワンピース,150,2011-06-21,2029-01-01,3
98,0,フロイライン,200,2011-09-08,2029-01-01,4
99,0,VFスーツ,200,2012-01-10,2029-01-01,6
100,0,巡音ルカ スイムウェアP,1000,2011-07-26,2029-01-01,32
101,0,キャンパス,200,2012-02-14,2029-01-01,3
102,0,ネコサイバー,300,2011-06-21,2029-01-01,4
103,0,カイト スイムウェアV,750,2011-07-26,2029-01-01,32
104,0,カイト スイムウェアV AS,750,2011-07-26,2029-01-01,34
105,0,ふわふわコート,200,2011-11-01,2029-01-01,2
106,0,モダンガール,150,2012-03-13,2029-01-01,4
107,0,モダンガール AS,150,2012-03-13,2029-01-01,5
108,0,メイコ スイムウェアB,1000,2011-07-26,2029-01-01,26
109,0,エスニック,200,2011-05-20,2029-01-01,5
110,0,亞北ネル スイムウェア,1000,2011-07-26,2029-01-01,12
111,0,サイバーダイブ,200,2011-05-20,2029-01-01,2
112,0,弱音ハク スイムウェア,1000,2011-07-26,2029-01-01,11
113,0,ブラックワンピース,300,2011-05-20,2029-01-01,8
114,0,ブラックワンピース NS,300,2011-05-20,2029-01-01,9
115,0,咲音メイコ スイムウェア,1000,2011-07-26,2029-01-01,13
116,0,メイコ 大正浪漫,250,2011-11-01,2029-01-01,30
117,0,鏡音リン スクールウェア,250,2011-12-06,2029-01-01,45
118,0,鏡音レン スクールウェア,250,2011-12-06,2029-01-01,40
119,0,巡音ルカ 魔女っ娘Style,250,2012-01-31,2029-01-01,37
120,0,カイト ホワイトブレザー,250,2012-02-14,2029-01-01,38
121,0,フェアリーマカロン,300,2011-06-21,2029-01-01,41
122,0,セクシープディング,300,2011-06-21,2029-01-01,33
124,0,ホワイト・イヴ,250,2012-11-05,2029-01-01,61
125,0,Hello World.,250,2012-10-04,2029-01-01,68
126,0,レーシングミク2010ver.,200,2012-11-22,2029-01-01,62
127,0,レーシングミク2011ver.,250,2012-12-06,2029-01-01,63
128,0,フェイ・イェン スタイル,250,2012-11-22,2029-01-01,66
129,0,回転少女,250,2012-11-05,2029-01-01,71
130,0,ラセツトムクロ,250,2012-05-24,2029-01-01,69
131,0,オービット,250,2012-11-05,2029-01-01,60
132,0,パッチワーク,250,2012-04-26,2029-01-01,65
133,0,ソニックスタイル,250,2012-08-31,2029-01-01,59
134,0,チロル,250,2012-10-04,2029-01-01,64
135,0,コンフリクト,200,2012-04-26,2029-01-01,70
136,0,シャイニー,250,2012-07-05,2029-01-01,72
137,0,TYPE2020,250,2012-08-31,2029-01-01,67
138,0,部活少女,250,2012-07-31,2029-01-01,6
139,0,ゴシック・パープル,250,2012-07-31,2029-01-01,3
141,0,鏡音リン アペンド,300,2011-12-27,2029-01-01,51
142,0,ネームレス No.1,200,2012-12-27,2029-01-01,8
143,0,レーシングリン2010ver.,200,2012-11-22,2029-01-01,12
144,0,ブラックスター,250,2012-09-14,2029-01-01,7
145,0,陽炎,250,2012-04-26,2029-01-01,11
146,0,鏡音リン 蘇芳,150,2012-04-05,2029-01-01,9
147,0,鏡音レン アペンド,300,2011-12-27,2029-01-01,42
148,0,ブルームーン,250,2012-09-14,2029-01-01,5
149,0,鏡音レン 藍鉄,150,2012-04-05,2029-01-01,6
150,0,ストレンジダーク,250,2012-07-05,2029-01-01,8
151,0,ネームレス No.7,200,2012-12-27,2029-01-01,9
153,0,サイレンス,250,2012-07-05,2029-01-01,7
154,0,レーシングルカ2010ver.,200,2012-11-22,2029-01-01,10
155,0,サイバーネイション,300,2011-12-15,2029-01-01,42
156,0,ナギサ・レプカ,200,2012-10-04,2029-01-01,8
157,0,ナギサ・レプカ AS,200,2012-10-04,2029-01-01,9
158,0,時雨,250,2012-05-24,2029-01-01,6
159,0,スミレ,250,2012-07-05,2029-01-01,5
160,0,VFニンジャ,200,2013-02-15,2029-01-01,7
161,0,VFニンジャ AS,200,2013-02-15,2029-01-01,8
162,0,怪盗ブラックテール,250,2012-11-05,2029-01-01,6
163,0,紅葉,250,2012-05-24,2029-01-01,8
164,0,レーシングメイコ2010ver.,200,2012-11-22,2029-01-01,9
165,0,ローレライ,250,2012-08-31,2029-01-01,7
166,0,ノスタルジー,250,2012-07-31,2029-01-01,10
167,0,雪ミク 2012,300,2011-12-19,2029-01-01,156
168,0,AMERICANA,300,2012-07-31,2029-01-01,167
169,0,桜ミク,300,2012-04-05,2029-01-01,168
170,0,巡音ルカ コンフリクト,300,2012-04-26,2029-01-01,43
171,0,鏡音リン 蘇芳 妖狐,200,2012-04-05,2029-01-01,10
172,0,鏡音レン 藍鉄 妖狐,200,2012-04-05,2029-01-01,7
188,0,ドリーマー,250,2013-01-30,2029-01-01,120
189,0,初音ミク 妄想ガール,250,2012-12-06,2029-01-01,121
190,0,雪ミク 2013,300,2012-12-17,2029-01-01,157
191,0,鏡音リン 妄想ガール,250,2012-12-06,2029-01-01,29
192,0,ロジカリスト,250,2012-12-06,2029-01-01,25
193,0,オンザロック,250,2013-01-30,2029-01-01,22
194,0,雪ミク 2013 AS,300,2012-12-17,2029-01-01,158
195,0,ディープスカイ,250,2014-02-25,2029-01-01,73
196,0,紫揚羽,250,2015-04-23,2029-01-01,74
197,0,メモリア,250,2015-03-26,2029-01-01,75
198,0,理系少女,250,2014-06-10,2029-01-01,76
199,0,ピエレッタ,250,2013-09-25,2029-01-01,77
200,0,イノセント,250,2014-04-17,2029-01-01,78
201,0,堕悪天使,250,2014-04-17,2029-01-01,79
202,0,サマーメモリー,250,2014-08-28,2029-01-01,80
203,0,初音ミク 翠玉,250,2016-05-26,2029-01-01,81
204,0,ソリチュード,250,2015-04-23,2029-01-01,82
205,0,ホーリーゴッデス,250,2015-05-28,2029-01-01,83
206,0,フォニュエールスタイル,250,2014-07-29,2029-01-01,84
207,0,ねこねこケープ,250,2014-06-10,2029-01-01,85
208,0,アジテーション,250,2013-09-25,2029-01-01,86
209,0,スターヴォイス,200,2013-09-25,2029-01-01,87
210,0,ハートビート,250,2014-09-17,2029-01-01,89
211,0,パンジー,250,2013-09-25,2029-01-01,90
212,0,レーシングミク2012ver.,250,2013-03-12,2029-01-01,91
213,0,わがまま工場長,250,2015-05-28,2029-01-01,92
214,0,HelloGood night.,250,2014-02-25,2029-01-01,93
215,0,初音ミク みずたまビキニ,1000,2013-07-04,2029-01-01,145
216,0,初音ミク スクール競泳,1000,2013-06-06,2029-01-01,144
217,0,初音ミク 浴衣スタイル,200,2013-07-31,2029-01-01,94
218,0,らんみんぐ,250,2013-04-26,2029-01-01,95
219,0,リボンガール,250,2013-01-30,2029-01-01,96
220,0,メランコリー,250,2014-01-15,2029-01-01,13
221,0,トランスミッター,250,2014-04-17,2029-01-01,14
222,0,鏡音リン 桜月,250,2013-12-10,2029-01-01,15
223,0,鏡音リン 雨,250,2014-07-29,2029-01-01,16
224,0,鏡音リン しましまビキニ,1000,2013-07-04,2029-01-01,42
225,0,鏡音リン SW スクール,1000,2013-06-06,2029-01-01,43
226,0,鏡音リン 浴衣スタイル,150,2013-07-31,2029-01-01,17
227,0,魔導師のタマゴ,250,2013-09-11,2029-01-01,19
228,0,スタイリッシュエナジーR,250,2013-04-05,2029-01-01,20
229,0,トラッドスクール,250,2012-12-27,2029-01-01,21
230,0,スターマイン,250,2013-09-25,2029-01-01,10
231,0,レシーバー,250,2014-04-17,2029-01-01,11
232,0,鏡音レン 鳳月,250,2014-07-29,2029-01-01,12
233,0,鏡音レン 鶴,250,2013-12-10,2029-01-01,13
234,0,バッドボーイ,200,2013-09-25,2029-01-01,14
235,0,鏡音レン SW ボクサー,1000,2013-06-06,2029-01-01,36
236,0,鏡音レン 浴衣スタイル,200,2013-07-31,2029-01-01,16
237,0,スタイリッシュエナジーL,250,2013-04-05,2029-01-01,17
238,0,生徒会執行部,250,2012-12-27,2029-01-01,18
239,0,バッドボーイ AS,200,2013-09-25,2029-01-01,15
240,0,ゆるふわコーデ,250,2014-06-25,2029-01-01,11
241,0,エターナルホワイト,250,2013-12-10,2029-01-01,12
242,0,アムール,250,2014-04-17,2029-01-01,13
243,0,巡音ルカ 紅玉,250,2016-05-26,2029-01-01,14
244,0,巡音ルカ リゾートビキニ,1000,2013-07-04,2029-01-01,34
245,0,巡音ルカ 競泳タイプ,1000,2013-06-06,2029-01-01,35
246,0,巡音ルカ 浴衣スタイル,200,2013-07-31,2029-01-01,15
247,0,森の妖精姫,250,2013-09-11,2029-01-01,16
248,0,クイン・ビー,250,2013-04-26,2029-01-01,17
249,0,放課後モード,250,2013-01-30,2029-01-01,18
250,0,レクイエム,250,2013-12-17,2029-01-01,9
251,0,ギルティ,250,2014-04-17,2029-01-01,10
252,0,ジェネラル,200,2013-09-25,2029-01-01,11
253,0,KAITO ハーフスパッツ,1000,2013-06-06,2029-01-01,36
254,0,KAITO 浴衣スタイル,200,2013-07-31,2029-01-01,13
255,0,ジーニアス,250,2013-05-15,2029-01-01,14
256,0,学ラン★パーカー,250,2013-02-15,2029-01-01,15
257,0,ジェネラル AS,200,2013-09-25,2029-01-01,12
258,0,ブルークリスタル,250,2014-11-05,2029-01-01,10
259,0,ノエル・ルージュ,250,2013-12-10,2029-01-01,11
260,0,MEIKO ロングパレオ,1000,2013-07-04,2029-01-01,27
261,0,MEIKO ウォーターポロ,1000,2013-06-06,2029-01-01,28
262,0,MEIKO 浴衣スタイル,200,2013-07-31,2029-01-01,12
263,0,ホイッスル,250,2013-05-15,2029-01-01,13
264,0,グラデュエート,250,2013-02-15,2029-01-01,14
265,0,BBオペレーター,250,2013-03-12,2029-01-01,15
266,0,鏡音リン 浴衣スタイル AS,150,2013-07-31,2029-01-01,18
267,0,カイト V3,300,2013-03-12,2029-01-01,42
268,0,深海少女,250,2013-07-31,2029-01-01,169
269,0,ハニーウィップ,250,2014-10-02,2029-01-01,148
270,0,壱ノ桜・桜花,150,2015-03-26,2029-01-01,149
271,0,リンちゃん愛し隊1号,200,2014-12-18,2029-01-01,151
272,0,シザーズ,250,2014-06-25,2029-01-01,46
273,0,弐ノ桜・胡蝶,200,2015-03-26,2029-01-01,47
274,0,鏡音リン Future Style,300,2014-12-18,2029-01-01,49
275,0,トリッカー,250,2014-12-18,2029-01-01,38
276,0,弐ノ桜・扇舞,200,2015-03-26,2029-01-01,39
277,0,参ノ桜・楓香,200,2015-03-26,2029-01-01,38
278,0,リンちゃん愛し隊2号,200,2014-12-18,2029-01-01,39
279,0,零ノ桜・蒼雪,150,2015-03-26,2029-01-01,39
280,0,零ノ桜・紅椿,200,2015-03-26,2029-01-01,31
281,0,リンケージ,250,2013-12-17,2029-01-01,147
282,0,スターヴォイス AS,200,2013-09-25,2029-01-01,88
283,0,重音テト,300,2013-09-25,2029-01-01,14
284,0,雪ミク 2014,300,2013-12-17,2029-01-01,159
285,0,雪ミク 2014 AS,100,2029-01-01,2029-01-01,160
286,0,CA 初音ミク,250,2014-01-15,2029-01-01,122
287,0,CA 鏡音リン,250,2014-01-15,2029-01-01,30
288,0,CA 巡音ルカ,250,2014-01-15,2029-01-01,26
289,0,CA メイコ,250,2014-01-15,2029-01-01,19
290,0,マジカルミライ,300,2014-02-12,2029-01-01,170
291,0,Cheerful ミク,150,2014-02-25,2029-01-01,123
292,0,Cheerful ミク AS,150,2014-02-25,2029-01-01,124
293,0,Cheerful リン,200,2014-02-25,2029-01-01,31
294,0,Cheerful レン,200,2014-02-25,2029-01-01,26
295,0,Cheerful ルカ,200,2014-02-25,2029-01-01,27
296,0,Cheerful カイト,200,2014-02-25,2029-01-01,23
297,0,Cheerful メイコ,200,2014-02-25,2029-01-01,20
298,0,Cheerful カイト AS,200,2029-01-01,2029-01-01,24
299,0,初音ミク V3,300,2014-08-28,2029-01-01,171
300,0,アバンガード,250,2015-07-30,2029-01-01,97
301,0,ナナイロライン,250,2015-08-28,2029-01-01,98
302,0,ブレス・ユー,250,2015-10-08,2029-01-01,99
303,0,花詞,250,2015-11-05,2029-01-01,100
304,0,華車,250,2015-11-05,2029-01-01,101
305,0,リグレット,250,2015-11-05,2029-01-01,102
306,0,マリオネット,250,2015-11-05,2029-01-01,104
308,0,ライアー,250,2016-01-28,2029-01-01,105
309,0,月光アゲハ,250,2015-12-22,2029-01-01,106
310,0,サイレン,250,2015-10-08,2029-01-01,107
311,0,ローザ・ビアンカ,250,2016-06-30,2029-01-01,108
313,0,メテオライト,250,2015-07-30,2029-01-01,111
314,0,シャノワール,200,2015-06-26,2029-01-01,112
315,0,シャノワール AS,200,2015-06-26,2029-01-01,113
316,0,シュープリーム,250,2016-04-01,2029-01-01,114
317,0,オレンジブロッサム,250,2015-08-28,2029-01-01,115
318,0,ディメンション,250,2015-07-30,2029-01-01,116
319,0,ストリートポップ,250,2016-01-28,2029-01-01,117
320,0,ゆるふわパステル,250,2016-01-28,2029-01-01,118
321,0,夢見るパンダ,250,2015-06-26,2029-01-01,22
322,0,フェイカー,250,2016-01-28,2029-01-01,23
323,0,ヒマワリ,200,2015-12-22,2029-01-01,24
324,0,ソレイユ,250,2015-12-22,2029-01-01,26
325,0,フェアリーワンピース,250,2015-12-22,2029-01-01,27
326,0,恋するシロクマ,250,2015-06-26,2029-01-01,19
327,0,アヤサキ,200,2015-12-22,2029-01-01,20
328,0,シエル,250,2015-12-22,2029-01-01,22
329,0,イレイザー,250,2016-01-28,2029-01-01,23
330,0,ホワイトエッジ,250,2015-12-22,2029-01-01,24
331,0,サクセサー,250,2015-07-30,2029-01-01,19
332,0,テンプテーション,250,2016-01-28,2029-01-01,20
334,0,リクルーター,250,2015-01-27,2029-01-01,22
335,0,フローラル,250,2016-01-28,2029-01-01,23
336,0,ローザ・ブルー,250,2016-06-30,2029-01-01,16
338,0,オリジネイター,250,2016-01-28,2029-01-01,19
339,0,ホリデイ,250,2015-11-05,2029-01-01,20
340,0,ブレイジング,250,2015-11-05,2029-01-01,16
341,0,マリーン・リボン,250,2015-11-05,2029-01-01,17
343,0,うさみみパーカー,250,2016-08-31,2029-01-01,119
344,0,アルパーカー R,250,2016-09-29,2029-01-01,28
345,0,アルパーカー L,250,2016-09-29,2029-01-01,25
346,0,ねこみみパーカー,250,2016-08-31,2029-01-01,24
347,0,おサカナつなぎ,250,2016-10-27,2029-01-01,21
348,0,ひつじさんウェア,250,2016-10-27,2029-01-01,18
349,0,初音ミク スイムウェア/ST,1000,2016-07-28,2029-01-01,142
350,0,鏡音リン スイムウェア/ST,1000,2016-07-28,2029-01-01,40
351,0,鏡音レン スイムウェア/ST,300,2016-07-28,2029-01-01,34
352,0,巡音ルカ スイムウェアP/ST,1000,2016-07-28,2029-01-01,33
353,0,KAITO スイムウェアV/ST,750,2016-07-28,2029-01-01,33
354,0,KAITO スイムウェアV AS/ST,750,2016-07-28,2029-01-01,35
355,0,MEIKO スイムウェア/ST,1000,2016-07-28,2029-01-01,25
356,0,M・S・J,300,2016-04-01,2029-01-01,15
359,0,out of the gravity,250,2014-08-28,2029-01-01,126
360,0,インタビュア ミク,250,2014-10-02,2029-01-01,127
361,0,インタビュア ルカ,250,2014-10-02,2029-01-01,28
362,0,スイートパンプキン,300,2014-10-02,2029-01-01,172
363,0,MEIKO V3,300,2014-11-05,2029-01-01,34
364,0,雪ミク 2015,300,2014-12-18,2029-01-01,161
365,0,壱ノ桜・白桜花,150,2015-03-26,2029-01-01,150
366,0,零ノ桜・白雪,150,2015-03-26,2029-01-01,40
367,0,ダイヤモンドダスト,250,2015-01-27,2029-01-01,25
368,0,アイスフォグ,250,2015-01-27,2029-01-01,27
369,0,テレカクシパーカー 黄色,250,2015-04-23,2029-01-01,28
370,0,テレカクシパーカー 青色,250,2015-04-23,2029-01-01,26
371,0,スチャラカハツネ,250,2015-05-28,2029-01-01,130
372,0,マジックシェフ,250,2015-06-26,2029-01-01,32
373,0,グラデーションリゾート,1000,2015-08-06,2029-01-01,146
374,0,ミラクルスターリゾート,1000,2015-08-06,2029-01-01,44
375,0,ポップスターリゾート,1000,2015-08-06,2029-01-01,37
376,0,トゥインクルリゾート,1000,2015-08-06,2029-01-01,36
377,0,プレイドリゾート,1000,2015-08-06,2029-01-01,37
378,0,バイカラーリボンリゾート,1000,2015-08-06,2029-01-01,29
379,0,ありふれミク,250,2015-08-28,2029-01-01,128
380,0,P4Dスタイル,300,2016-06-23,2029-01-01,173
381,0,PIANOGIRL,250,2015-10-08,2029-01-01,129
382,0,重音テト スイムウェア,1000,2015-11-05,2029-01-01,16
383,0,巡音ルカV4X,300,2016-01-28,2029-01-01,44
385,0,Trip The Light Fantastic,250,2016-04-27,2029-01-01,132
386,0,Poppin Delight,250,2016-04-27,2029-01-01,33
387,0,Bebop Knave,250,2016-04-27,2029-01-01,29
389,0,プランセス・ブランシュ,250,2016-04-27,2029-01-01,133
390,0,プランス・ブラン,250,2016-04-27,2029-01-01,27
391,0,アドレサンスプリンセス,250,2016-04-27,2029-01-01,343
392,0,アドレサンスナイト,250,2016-04-27,2029-01-01,30
393,0,マーチ・ヘイヤ,250,2016-08-31,2029-01-01,136
394,0,アゲアゲアゲイン,250,2016-02-26,2029-01-01,131
395,0,エトワール,250,2016-06-30,2029-01-01,135
398,0,フェアウェル,250,2016-06-30,2029-01-01,134
399,0,天袖,250,2016-12-21,2029-01-01,36
402,0,雪ミク 2016,300,2015-12-22,2029-01-01,162
403,0,パジャマパーティ ミク,250,2017-01-26,2029-01-01,139
404,0,パジャマパーティ リン,250,2017-01-26,2029-01-01,38
405,0,パジャマパーティ レン,250,2017-01-26,2029-01-01,32
406,0,パジャマパーティ ルカ,250,2017-01-26,2029-01-01,30
407,0,パジャマパーティ カイト,250,2017-01-26,2029-01-01,30
408,0,パジャマパーティ メイコ,250,2017-01-26,2029-01-01,23
409,0,Phantom Thief ミク,250,2016-10-27,2029-01-01,137
410,0,Phantom Thief リン,250,2016-10-27,2029-01-01,35
411,0,Phantom Thief メイコ,250,2016-10-27,2029-01-01,21
412,0,Phantom Thief カイト,250,2016-10-27,2029-01-01,28
413,0,鉄道員・鶯,250,2016-12-21,2029-01-01,138
414,0,鉄道員・金糸雀,250,2016-12-21,2029-01-01,37
415,0,鉄道員・銀朱,250,2016-12-21,2029-01-01,31
416,0,鉄道員・薔薇,250,2016-12-21,2029-01-01,29
417,0,鉄道員・空,250,2016-12-21,2029-01-01,29
418,0,鉄道員・紅葡萄,250,2016-12-21,2029-01-01,22
419,0,雪ミク 2017,300,2016-12-21,2029-01-01,163
420,0,ヒマワリ AS,200,2015-12-22,2029-01-01,25
421,0,アヤサキ AS,200,2015-12-22,2029-01-01,21
422,0,みくりすたる☆,300,2016-05-26,2029-01-01,125
423,0,マイディアバニー,1000,2017-08-31,2029-01-01,174
424,0,ローザ・ノッテ,250,2016-12-21,2029-01-01,110
425,0,ローザ・ルーノ,250,2016-12-21,2029-01-01,18
426,0,GHOST,250,2017-12-13,2029-01-01,175
427,0,セレブレーション,300,2017-12-13,2029-01-01,176
428,0,雪ミク 2018 AS,300,2019-04-24,2029-01-01,165
429,0,雪ミク 2019,300,2019-04-24,2029-01-01,166
430,0,雪ミク 2018,300,2019-04-24,2029-01-01,164
431,0,Catch The Wave,900,2020-02-20,2029-01-01,177

12
titles/diva/database.py Normal file
View File

@ -0,0 +1,12 @@
from core.data import Data
from core.config import CoreConfig
from titles.diva.schema import DivaProfileData, DivaScoreData, DivaItemData, DivaStaticData
class DivaData(Data):
def __init__(self, cfg: CoreConfig) -> None:
super().__init__(cfg)
self.profile = DivaProfileData(self.config, self.session)
self.score = DivaScoreData(self.config, self.session)
self.item = DivaItemData(self.config, self.session)
self.static = DivaStaticData(self.config, self.session)

105
titles/diva/index.py Normal file
View File

@ -0,0 +1,105 @@
from twisted.web.http import Request
import yaml
import logging, coloredlogs
from logging.handlers import TimedRotatingFileHandler
import zlib
import json
import urllib.parse
import base64
from core.config import CoreConfig
from titles.diva.config import DivaConfig
from titles.diva.base import DivaBase
class DivaServlet():
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
self.core_cfg = core_cfg
self.game_cfg = DivaConfig()
self.game_cfg.update(yaml.safe_load(open(f"{cfg_dir}/diva.yaml")))
self.base = DivaBase(core_cfg, self.game_cfg)
self.logger = logging.getLogger("diva")
log_fmt_str = "[%(asctime)s] Diva | %(levelname)s | %(message)s"
log_fmt = logging.Formatter(log_fmt_str)
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.core_cfg.server.log_dir, "diva"), encoding='utf8',
when="d", backupCount=10)
fileHandler.setFormatter(log_fmt)
consoleHandler = logging.StreamHandler()
consoleHandler.setFormatter(log_fmt)
self.logger.addHandler(fileHandler)
self.logger.addHandler(consoleHandler)
self.logger.setLevel(self.game_cfg.server.loglevel)
coloredlogs.install(level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str)
def render_POST(self, req: Request, version: int, url_path: str) -> bytes:
req_raw = req.content.getvalue()
url_header = req.getAllHeaders()
#Ping Dispatch
if "THIS_STRING_SEPARATES"in str(url_header):
binary_request = req_raw.splitlines()
binary_cmd_decoded = binary_request[3].decode("utf-8")
binary_array = binary_cmd_decoded.split('&')
bin_req_data = {}
for kvp in binary_array:
split_bin = kvp.split("=")
bin_req_data[split_bin[0]] = split_bin[1]
self.logger.info(f"Binary {bin_req_data['cmd']} Request")
self.logger.debug(bin_req_data)
handler = getattr(self.base, f"handle_{bin_req_data['cmd']}_request")
resp = handler(bin_req_data)
self.logger.debug(f"Response cmd={bin_req_data['cmd']}&req_id={bin_req_data['req_id']}&stat=ok{resp}")
return f"cmd={bin_req_data['cmd']}&req_id={bin_req_data['req_id']}&stat=ok{resp}".encode('utf-8')
#Main Dispatch
json_string = json.dumps(req_raw.decode("utf-8")) #Take the response and decode as UTF-8 and dump
b64string = json_string.replace(r'\n', '\n') # Remove all \n and separate them as new lines
gz_string = base64.b64decode(b64string) # Decompressing the base64 string
try:
url_data = zlib.decompress( gz_string ).decode("utf-8") # Decompressing the gzip
except zlib.error as e:
self.logger.error(f"Failed to defalte! {e} -> {gz_string}")
return "stat=0"
req_kvp = urllib.parse.unquote(url_data)
req_data = {}
# We then need to split each parts with & so we can reuse them to fill out the requests
splitted_request = str.split(req_kvp, "&")
for kvp in splitted_request:
split = kvp.split("=")
req_data[split[0]] = split[1]
self.logger.info(f"{req_data['cmd']} Request")
self.logger.debug(req_data)
func_to_find = f"handle_{req_data['cmd']}_request"
# Load the requests
try:
handler = getattr(self.base, func_to_find)
resp = handler(req_data)
except AttributeError as e:
self.logger.warning(f"Unhandled {req_data['cmd']} request {e}")
return f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok".encode('utf-8')
except Exception as e:
self.logger.error(f"Error handling method {func_to_find} {e}")
return f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok".encode('utf-8')
req.responseHeaders.addRawHeader(b"content-type", b"text/plain")
self.logger.debug(f"Response cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok{resp}")
return f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok{resp}".encode('utf-8')

203
titles/diva/read.py Normal file
View File

@ -0,0 +1,203 @@
from typing import Optional, Dict, List
from os import walk, path
import urllib
from read import BaseReader
from core.config import CoreConfig
from titles.diva.database import DivaData
from titles.diva.const import DivaConstants
class DivaReader(BaseReader):
def __init__(self, config: CoreConfig, version: int, bin_dir: Optional[str], opt_dir: Optional[str], extra: Optional[str]) -> None:
super().__init__(config, version, bin_dir, opt_dir, extra)
self.data = DivaData(config)
try:
self.logger.info(f"Start importer for {DivaConstants.game_ver_to_string(version)}")
except IndexError:
self.logger.error(f"Invalid project diva version {version}")
exit(1)
def read(self) -> None:
pull_bin_ram = True
pull_bin_rom = True
pull_opt_rom = True
if not path.exists(f"{self.bin_dir}/ram"):
self.logger.warn(f"Couldn't find ram folder in {self.bin_dir}, skipping")
pull_bin_ram = False
if not path.exists(f"{self.bin_dir}/rom"):
self.logger.warn(f"Couldn't find rom folder in {self.bin_dir}, skipping")
pull_bin_rom = False
if self.opt_dir is not None:
opt_dirs = self.get_data_directories(self.opt_dir)
else:
pull_opt_rom = False
self.logger.warn("No option directory specified, skipping")
if pull_bin_ram:
self.read_ram(f"{self.bin_dir}/ram")
if pull_bin_rom:
self.read_rom(f"{self.bin_dir}/rom")
if pull_opt_rom:
for dir in opt_dirs:
self.read_rom(f"{dir}/rom")
def read_ram(self, ram_root_dir: str) -> None:
self.logger.info(f"Read RAM from {ram_root_dir}")
if path.exists(f"{ram_root_dir}/databank"):
for root, dirs, files in walk(f"{ram_root_dir}/databank"):
for file in files:
if file.startswith("ShopCatalog_") or file.startswith("CustomizeItemCatalog_") or \
(file.startswith("QuestInfo") and not file.startswith("QuestInfoTm")):
with open(f"{root}/{file}", "r") as f:
file_data: str = urllib.parse.unquote(urllib.parse.unquote(f.read()))
if file_data == "***":
self.logger.info(f"{file} is empty, skipping")
continue
file_lines: List[str] = file_data.split("\n")
for line in file_lines:
split = line.split(",")
if not split[0]:
split.pop(0)
if file.startswith("ShopCatalog_"):
for x in range(0, len(split), 7):
self.logger.info(f"Added shop item {split[x+0]}")
self.data.static.put_shop(self.version, split[x+0], split[x+2], split[x+6], split[x+3],
split[x+1], split[x+4], split[x+5])
elif file.startswith("CustomizeItemCatalog_") and len(split) >= 7:
for x in range(0, len(split), 7):
self.logger.info(f"Added item {split[x+0]}")
self.data.static.put_items(self.version, split[x+0], split[x+2], split[x+6], split[x+3],
split[x+1], split[x+4], split[x+5])
elif file.startswith("QuestInfo") and len(split) >= 9:
self.logger.info(f"Added quest {split[0]}")
self.data.static.put_quests(self.version, split[0], split[6], split[2], split[3],
split[7], split[8], split[1], split[4], split[5])
else:
continue
else:
self.logger.warn(f"Databank folder not found in {ram_root_dir}, skipping")
def read_rom(self, rom_root_dir: str) -> None:
self.logger.info(f"Read ROM from {rom_root_dir}")
pv_list: Dict[str, Dict] = {}
if path.exists(f"{rom_root_dir}/mdata_pv_db.txt"):
file_path = f"{rom_root_dir}/mdata_pv_db.txt"
elif path.exists(f"{rom_root_dir}/pv_db.txt"):
file_path = f"{rom_root_dir}/pv_db.txt"
else:
self.logger.warn(f"Cannot find pv_db.txt or mdata_pv_db.txt in {rom_root_dir}, skipping")
return
with open(file_path, "r", encoding="utf-8") as f:
for line in f.readlines():
if line.startswith("#") or not line:
continue
line_split = line.split("=")
if len(line_split) != 2:
continue
key = line_split[0]
val = line_split[1]
if val.endswith("\n"):
val = val[:-1]
key_split = key.split(".")
pv_id = key_split[0]
key_args = []
for x in range(1, len(key_split)):
key_args.append(key_split[x])
try:
pv_list[pv_id] = self.add_branch(pv_list[pv_id], key_args, val)
except KeyError:
pv_list[pv_id] = {}
pv_list[pv_id] = self.add_branch(pv_list[pv_id], key_args, val)
for pv_id, pv_data in pv_list.items():
song_id = int(pv_id.split("_")[1])
if "songinfo" not in pv_data:
continue
if "illustrator" not in pv_data["songinfo"]:
pv_data["songinfo"]["illustrator"] = "-"
if "arranger" not in pv_data["songinfo"]:
pv_data["songinfo"]["arranger"] = "-"
if "lyrics" not in pv_data["songinfo"]:
pv_data["songinfo"]["lyrics"] = "-"
if "music" not in pv_data["songinfo"]:
pv_data["songinfo"]["music"] = "-"
if "easy" in pv_data['difficulty'] and '0' in pv_data['difficulty']['easy']:
diff = pv_data['difficulty']['easy']['0']['level'].split('_')
self.logger.info(f"Added song {song_id} chart 0")
self.data.static.put_music(self.version, song_id, 0, pv_data["song_name"], pv_data["songinfo"]["arranger"],
pv_data["songinfo"]["illustrator"], pv_data["songinfo"]["lyrics"], pv_data["songinfo"]["music"],
float(f"{diff[2]}.{diff[3]}"), pv_data["bpm"], pv_data["date"])
if "normal" in pv_data['difficulty'] and '0' in pv_data['difficulty']['normal']:
diff = pv_data['difficulty']['normal']['0']['level'].split('_')
self.logger.info(f"Added song {song_id} chart 1")
self.data.static.put_music(self.version, song_id, 1, pv_data["song_name"], pv_data["songinfo"]["arranger"],
pv_data["songinfo"]["illustrator"], pv_data["songinfo"]["lyrics"], pv_data["songinfo"]["music"],
float(f"{diff[2]}.{diff[3]}"), pv_data["bpm"], pv_data["date"])
if "hard" in pv_data['difficulty'] and '0' in pv_data['difficulty']['hard']:
diff = pv_data['difficulty']['hard']['0']['level'].split('_')
self.logger.info(f"Added song {song_id} chart 2")
self.data.static.put_music(self.version, song_id, 2, pv_data["song_name"], pv_data["songinfo"]["arranger"],
pv_data["songinfo"]["illustrator"], pv_data["songinfo"]["lyrics"], pv_data["songinfo"]["music"],
float(f"{diff[2]}.{diff[3]}"), pv_data["bpm"], pv_data["date"])
if "extreme" in pv_data['difficulty']:
if "0" in pv_data['difficulty']['extreme']:
diff = pv_data['difficulty']['extreme']['0']['level'].split('_')
self.logger.info(f"Added song {song_id} chart 3")
self.data.static.put_music(self.version, song_id, 3, pv_data["song_name"], pv_data["songinfo"]["arranger"],
pv_data["songinfo"]["illustrator"], pv_data["songinfo"]["lyrics"], pv_data["songinfo"]["music"],
float(f"{diff[2]}.{diff[3]}"), pv_data["bpm"], pv_data["date"])
if "1" in pv_data['difficulty']['extreme']:
diff = pv_data['difficulty']['extreme']['1']['level'].split('_')
self.logger.info(f"Added song {song_id} chart 4")
self.data.static.put_music(self.version, song_id, 4, pv_data["song_name"], pv_data["songinfo"]["arranger"],
pv_data["songinfo"]["illustrator"], pv_data["songinfo"]["lyrics"], pv_data["songinfo"]["music"],
float(f"{diff[2]}.{diff[3]}"), pv_data["bpm"], pv_data["date"])
def add_branch(self, tree: Dict, vector: List, value: str):
"""
Recursivly adds nodes to a dictionary
Author: iJames on StackOverflow
"""
key = vector[0]
tree[key] = value \
if len(vector) == 1 \
else self.add_branch(tree[key] if key in tree else {},
vector[1:],
value)
return tree

View File

@ -0,0 +1,6 @@
from titles.diva.schema.profile import DivaProfileData
from titles.diva.schema.score import DivaScoreData
from titles.diva.schema.item import DivaItemData
from titles.diva.schema.static import DivaStaticData
__all__ = [DivaProfileData, DivaScoreData, DivaItemData, DivaStaticData]

View File

@ -0,0 +1,50 @@
from typing import Optional, Dict, List
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_, case
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean
from sqlalchemy.schema import ForeignKey
from sqlalchemy.sql import func
from sqlalchemy.dialects.mysql import insert
from core.data.schema import BaseData, metadata
shop = Table(
"diva_profile_shop",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
Column("version", Integer, nullable=False),
Column("mdl_eqp_ary", String(32)),
UniqueConstraint("user", "version", name="diva_profile_shop_uk"),
mysql_charset='utf8mb4'
)
class DivaItemData(BaseData):
def put_shop(self, aime_id: int, version: int, mdl_eqp_ary: str) -> None:
sql = insert(shop).values(
version=version,
user=aime_id,
mdl_eqp_ary=mdl_eqp_ary
)
conflict = sql.on_duplicate_key_update(
mdl_eqp_ary = sql.inserted.mdl_eqp_ary
)
result = self.execute(conflict)
if result is None:
self.logger.error(f"{__name__} Failed to insert diva profile! aime id: {aime_id} array: {mdl_eqp_ary}")
return None
return result.lastrowid
def get_shop(self, aime_id: int, version: int) -> Optional[List[Dict]]:
"""
Given a game version and either a profile or aime id, return the profile
"""
sql = shop.select(and_(
shop.c.version == version,
shop.c.user == aime_id
))
result = self.execute(sql)
if result is None: return None
return result.fetchone()

View File

@ -0,0 +1,113 @@
from typing import Optional, Dict, List
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON
from sqlalchemy.schema import ForeignKey
from sqlalchemy.sql import func, select
from sqlalchemy.dialects.mysql import insert
from core.data.schema import BaseData, metadata
profile = Table(
"diva_profile",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
Column("version", Integer, nullable=False),
Column("player_name", String(8), nullable=False),
Column("lv_str", String(24), nullable=False, server_default="Dab on 'em"),
Column("lv_num", Integer, nullable=False, server_default="0"),
Column("lv_pnt", Integer, nullable=False, server_default="0"),
Column("vcld_pts", Integer, nullable=False, server_default="0"),
Column("hp_vol", Integer, nullable=False, server_default="100"),
Column("btn_se_vol", Integer, nullable=False, server_default="100"),
Column("btn_se_vol2", Integer, nullable=False, server_default="100"),
Column("sldr_se_vol2", Integer, nullable=False, server_default="100"),
Column("sort_kind", Integer, nullable=False, server_default="2"),
Column("use_pv_mdl_eqp", String(8), nullable=False, server_default="true"),
Column("use_pv_btn_se_eqp", String(8), nullable=False, server_default="true"),
Column("use_pv_sld_se_eqp", String(8), nullable=False, server_default="false"),
Column("use_pv_chn_sld_se_eqp", String(8), nullable=False, server_default="false"),
Column("use_pv_sldr_tch_se_eqp", String(8), nullable=False, server_default="false"),
Column("nxt_pv_id", Integer, nullable=False, server_default="708"),
Column("nxt_dffclty", Integer, nullable=False, server_default="2"),
Column("nxt_edtn", Integer, nullable=False, server_default="0"),
Column("dsp_clr_brdr", Integer, nullable=False, server_default="7"),
Column("dsp_intrm_rnk", Integer, nullable=False, server_default="1"),
Column("dsp_clr_sts", Integer, nullable=False, server_default="1"),
Column("rgo_sts", Integer, nullable=False, server_default="1"),
Column("lv_efct_id", Integer, nullable=False, server_default="0"),
Column("lv_plt_id", Integer, nullable=False, server_default="1"),
Column("my_qst_id", String(128), server_default="-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"),
Column("my_qst_sts", String(128), server_default="-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"),
UniqueConstraint("user", "version", name="diva_profile_uk"),
mysql_charset='utf8mb4'
)
class DivaProfileData(BaseData):
def create_profile(self, version: int, aime_id: int, player_name: str) -> Optional[int]:
"""
Given a game version, aime id, and player_name, create a profile and return it's ID
"""
sql = insert(profile).values(
version=version,
user=aime_id,
player_name=player_name
)
conflict = sql.on_duplicate_key_update(
player_name = sql.inserted.player_name
)
result = self.execute(conflict)
if result is None:
self.logger.error(f"{__name__} Failed to insert diva profile! aime id: {aime_id} username: {player_name}")
return None
return result.lastrowid
def update_profile(self, profile_id: int, lv_num: int, lv_pnt: int, vcld_pts: int, hp_vol: int, btn_se_vol: int, btn_se_vol2: int, sldr_se_vol2: int, sort_kind: int, use_pv_mdl_eqp: str, use_pv_btn_se_eqp: str, use_pv_sld_se_eqp: str, use_pv_chn_sld_se_eqp: str, use_pv_sldr_tch_se_eqp: str, nxt_pv_id: int, nxt_dffclty: int, nxt_edtn: int, dsp_clr_brdr: int, dsp_intrm_rnk: int, dsp_clr_sts: int, rgo_sts: int, lv_efct_id: int, lv_plt_id: int, my_qst_id: str, my_qst_sts: str) -> None:
sql = profile.update(profile.c.user == profile_id).values(
lv_num = lv_num,
lv_pnt = lv_pnt,
vcld_pts = vcld_pts,
hp_vol = hp_vol,
btn_se_vol = btn_se_vol,
btn_se_vol2 = btn_se_vol2,
sldr_se_vol2 = sldr_se_vol2,
sort_kind = sort_kind,
use_pv_mdl_eqp = use_pv_mdl_eqp,
use_pv_btn_se_eqp = use_pv_btn_se_eqp,
use_pv_sld_se_eqp = use_pv_sld_se_eqp,
use_pv_chn_sld_se_eqp = use_pv_chn_sld_se_eqp,
use_pv_sldr_tch_se_eqp = use_pv_sldr_tch_se_eqp,
nxt_pv_id = nxt_pv_id,
nxt_dffclty = nxt_dffclty,
nxt_edtn = nxt_edtn,
dsp_clr_brdr = dsp_clr_brdr,
dsp_intrm_rnk = dsp_intrm_rnk,
dsp_clr_sts = dsp_clr_sts,
rgo_sts = rgo_sts,
lv_efct_id = lv_efct_id,
lv_plt_id = lv_plt_id,
my_qst_id = my_qst_id,
my_qst_sts = my_qst_sts
)
result = self.execute(sql)
if result is None:
self.logger.error(f"update_profile: failed to update profile! profile: {profile_id}")
return None
def get_profile(self, aime_id: int, version: int) -> Optional[List[Dict]]:
"""
Given a game version and either a profile or aime id, return the profile
"""
sql = profile.select(and_(
profile.c.version == version,
profile.c.user == aime_id
))
result = self.execute(sql)
if result is None: return None
return result.fetchone()

141
titles/diva/schema/score.py Normal file
View File

@ -0,0 +1,141 @@
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_
from sqlalchemy.types import Integer, String, TIMESTAMP, JSON, Boolean
from sqlalchemy.schema import ForeignKey
from sqlalchemy.sql import func
from sqlalchemy.dialects.mysql import insert
from typing import Optional, List, Dict, Any
from core.data.schema import BaseData, metadata
from core.data import cached
score = Table(
"diva_score",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade"), nullable=False),
Column("version", Integer),
Column("pv_id", Integer),
Column("difficulty", Integer),
Column("score", Integer),
Column("atn_pnt", Integer),
Column("clr_kind", Integer),
Column("sort_kind", Integer),
Column("cool", Integer),
Column("fine", Integer),
Column("safe", Integer),
Column("sad", Integer),
Column("worst", Integer),
Column("max_combo", Integer),
UniqueConstraint("user", "pv_id", "difficulty", name="diva_score_uk"),
mysql_charset='utf8mb4'
)
playlog = Table(
"diva_playlog",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("user", ForeignKey("aime_user.id", ondelete="cascade"), nullable=False),
Column("version", Integer),
Column("pv_id", Integer),
Column("difficulty", Integer),
Column("score", Integer),
Column("atn_pnt", Integer),
Column("clr_kind", Integer),
Column("sort_kind", Integer),
Column("cool", Integer),
Column("fine", Integer),
Column("safe", Integer),
Column("sad", Integer),
Column("worst", Integer),
Column("max_combo", Integer),
Column("date_scored", TIMESTAMP, server_default=func.now()),
mysql_charset='utf8mb4'
)
class DivaScoreData(BaseData):
def put_best_score(self, user_id: int, game_version: int, song_id: int, difficulty: int, song_score: int, atn_pnt: int,
clr_kind: int, sort_kind:int, cool: int, fine: int, safe: int, sad: int, worst: int, max_combo: int) -> Optional[int]:
"""
Update the user's best score for a chart
"""
sql = insert(score).values(
user=user_id,
version=game_version,
pv_id = song_id,
difficulty=difficulty,
score=song_score,
atn_pnt = atn_pnt,
clr_kind = clr_kind,
sort_kind = sort_kind,
cool = cool,
fine = fine,
safe = safe,
sad = sad,
worst = worst,
max_combo = max_combo,
)
conflict = sql.on_duplicate_key_update(
score=song_score,
atn_pnt = atn_pnt,
clr_kind = clr_kind,
sort_kind = sort_kind,
cool = cool,
fine = fine,
safe = safe,
sad = sad,
worst = worst,
max_combo = max_combo,
)
result = self.execute(conflict)
if result is None:
self.logger.error(f"{__name__} failed to insert best score! profile: {user_id}, song: {song_id}")
return None
return result.lastrowid
def put_playlog(self, user_id: int, game_version: int, song_id: int, difficulty: int, song_score: int, atn_pnt: int,
clr_kind: int, sort_kind:int, cool: int, fine: int, safe: int, sad: int, worst: int, max_combo: int) -> Optional[int]:
"""
Add an entry to the user's play log
"""
sql = playlog.insert().values(
user=user_id,
version=game_version,
pv_id = song_id,
difficulty=difficulty,
score=song_score,
atn_pnt = atn_pnt,
clr_kind = clr_kind,
sort_kind = sort_kind,
cool = cool,
fine = fine,
safe = safe,
sad = sad,
worst = worst,
max_combo = max_combo
)
result = self.execute(sql)
if result is None:
self.logger.error(f"{__name__} failed to insert playlog! profile: {user_id}, song: {song_id}, chart: {difficulty}")
return None
return result.lastrowid
def get_best_score(self, user_id: int, pv_id: int, chart_id: int) -> Optional[Dict]:
sql = score.select(
and_(score.c.user == user_id, score.c.pv_id == pv_id, score.c.difficulty == chart_id)
)
result = self.execute(sql)
if result is None: return None
return result.fetchone()
def get_best_scores(self, user_id: int) -> Optional[Dict]:
sql = score.select(score.c.user == user_id)
result = self.execute(sql)
if result is None: return None
return result.fetchall()

View File

@ -0,0 +1,222 @@
from typing import Dict, List, Optional
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, Float
from sqlalchemy.engine.base import Connection
from sqlalchemy.engine import Row
from sqlalchemy.schema import ForeignKey
from sqlalchemy.sql import func, select
from sqlalchemy.dialects.mysql import insert
from core.data.schema import BaseData, metadata
music = Table(
"diva_static_music",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("version", Integer, nullable=False),
Column("songId", Integer),
Column("chartId", Integer),
Column("title", String(255)),
Column("vocaloid_arranger", String(255)),
Column("pv_illustrator", String(255)),
Column("lyrics", String(255)),
Column("bg_music", String(255)),
Column("level", Float),
Column("bpm", Integer),
Column("date", String(255)),
UniqueConstraint("version", "songId", "chartId", name="diva_static_music_uk"),
mysql_charset='utf8mb4'
)
quests = Table(
"diva_static_quests",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("version", Integer, nullable=False),
Column("questId", Integer),
Column("name", String(255)),
Column("quest_enable", Boolean, server_default="1"),
Column("kind", Integer),
Column("unknown_0", Integer),
Column("unknown_1", Integer),
Column("unknown_2", Integer),
Column("quest_order", Integer),
Column("start_datetime", String(255)),
Column("end_datetime", String(255)),
UniqueConstraint("version", "questId", name="diva_static_quests_uk"),
mysql_charset='utf8mb4'
)
shop = Table(
"diva_static_shop",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("version", Integer, nullable=False),
Column("shopId", Integer),
Column("name", String(255)),
Column("type", Integer),
Column("points", Integer),
Column("unknown_0", Integer),
Column("start_date", String(255)),
Column("end_date", String(255)),
Column("enabled", Boolean, server_default="1"),
UniqueConstraint("version", "shopId", name="diva_static_shop_uk"),
mysql_charset='utf8mb4'
)
items = Table(
"diva_static_items",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("version", Integer, nullable=False),
Column("itemId", Integer),
Column("name", String(255)),
Column("type", Integer),
Column("points", Integer),
Column("unknown_0", Integer),
Column("start_date", String(255)),
Column("end_date", String(255)),
Column("enabled", Boolean, server_default="1"),
UniqueConstraint("version", "itemId", name="diva_static_items_uk"),
mysql_charset='utf8mb4'
)
class DivaStaticData(BaseData):
def put_quests(self, version: int, questId: int, name: str, kind: int, unknown_0: int, unknown_1: int, unknown_2: int, quest_order: int, start_datetime: str, end_datetime: str) -> Optional[int]:
sql = insert(quests).values(
version = version,
questId = questId,
name = name,
kind = kind,
unknown_0 = unknown_0,
unknown_1 = unknown_1,
unknown_2 = unknown_2,
quest_order = quest_order,
start_datetime = start_datetime,
end_datetime = end_datetime
)
conflict = sql.on_duplicate_key_update(
name = name
)
result = self.execute(conflict)
if result is None: return None
return result.lastrowid
def get_enabled_quests(self, version: int) -> Optional[List[Row]]:
sql = select(quests).where(and_(quests.c.version == version, quests.c.quest_enable == True))
result = self.execute(sql)
if result is None: return None
return result.fetchall()
def put_shop(self, version: int, shopId: int, name: str, type: int, points: int, unknown_0: int, start_date: str, end_date: str) -> Optional[int]:
sql = insert(shop).values(
version = version,
shopId = shopId,
name = name,
type = type,
points = points,
unknown_0 = unknown_0,
start_date = start_date,
end_date = end_date
)
conflict = sql.on_duplicate_key_update(
name = name
)
result = self.execute(conflict)
if result is None: return None
return result.lastrowid
def get_enabled_shop(self, version: int) -> Optional[List[Row]]:
sql = select(shop).where(and_(shop.c.version == version, shop.c.enabled == True))
result = self.execute(sql)
if result is None: return None
return result.fetchall()
def put_items(self, version: int, itemId: int, name: str, type: int, points: int, unknown_0: int, start_date: str, end_date: str) -> Optional[int]:
sql = insert(items).values(
version = version,
itemId = itemId,
name = name,
type = type,
points = points,
unknown_0 = unknown_0,
start_date = start_date,
end_date = end_date
)
conflict = sql.on_duplicate_key_update(
name = name
)
result = self.execute(conflict)
if result is None: return None
return result.lastrowid
def get_enabled_items(self, version: int) -> Optional[List[Row]]:
sql = select(items).where(and_(items.c.version == version, items.c.enabled == True))
result = self.execute(sql)
if result is None: return None
return result.fetchall()
def put_music(self, version: int, song: int, chart: int, title: str, arranger: str, illustrator: str,
lyrics: str, music_comp: str, level: float, bpm: int, date: str) -> Optional[int]:
sql = insert(music).values(
version = version,
songId = song,
chartId = chart,
title = title,
vocaloid_arranger = arranger,
pv_illustrator = illustrator,
lyrics = lyrics,
bg_music = music_comp,
level = level,
bpm = bpm,
date = date
)
conflict = sql.on_duplicate_key_update(
title = title,
vocaloid_arranger = arranger,
pv_illustrator = illustrator,
lyrics = lyrics,
bg_music = music_comp,
level = level,
bpm = bpm,
date = date
)
result = self.execute(conflict)
if result is None: return None
return result.lastrowid
def get_music(self, version: int, song_id: Optional[int] = None) -> Optional[List[Row]]:
if song_id is None:
sql = select(music).where(music.c.version == version)
else:
sql = select(music).where(and_(
music.c.version == version,
music.c.songId == song_id,
))
result = self.execute(sql)
if result is None: return None
return result.fetchall()
def get_music_chart(self, version: int, song_id: int, chart_id: int) -> Optional[List[Row]]:
sql = select(music).where(and_(
music.c.version == version,
music.c.songId == song_id,
music.c.chartId == chart_id
))
result = self.execute(sql)
if result is None: return None
return result.fetchone()