From 0bc8ffa932800cb73a1f86f583539177c77664ef Mon Sep 17 00:00:00 2001 From: drmext <71258889+drmext@users.noreply.github.com> Date: Wed, 11 Mar 2026 13:45:33 +0000 Subject: [PATCH 01/11] Fix --- README.md | 2 +- core_common.py | 2 + modules/__init__.py | 2 +- modules/ddr/eventlog_3.py | 25 ++ modules/ddr/playdata_3.py | 870 ++++++++++++++++++++++++++++++++++++ modules/ddr/playerdata.py | 49 +- modules/ddr/playerdata_2.py | 17 +- modules/ddr/system_3.py | 24 + modules/ddr/wordcheck_3.py | 23 + 9 files changed, 976 insertions(+), 38 deletions(-) create mode 100644 modules/ddr/eventlog_3.py create mode 100644 modules/ddr/playdata_3.py create mode 100644 modules/ddr/system_3.py create mode 100644 modules/ddr/wordcheck_3.py diff --git a/README.md b/README.md index a56cade..84b66ed 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Run [start.bat (Windows)](start.bat) or [start.sh (Linux, MacOS)](start.sh) ## Playable Games - IIDX 18-20, 29-33 (Online Arena/BPL support) -- DDR A20P, A3 (OmniMIX/GF, BPL, and [Fake PFREE](https://github.com/drmext/BemaniPatcher/blob/nopr/ddra3.html#L133) support) +- DDR A20P, A3, World (OmniMIX/GF, BPL, and [Fake PFREE](https://github.com/drmext/BemaniPatcher/blob/nopr/ddra3.html#L133) support) - GD 6-10 DELTA (Battle Mode support) - DRS - NOST 3 diff --git a/core_common.py b/core_common.py index 41a1c00..30d5497 100644 --- a/core_common.py +++ b/core_common.py @@ -119,6 +119,8 @@ async def core_get_game_version_from_software_version(software_version): return 1 elif model == "MDX": + if ext >= 2024061200 and ext not in (2024042069, 2025042069): # GF + return 20 if ext >= 2019022600: # ??? return 19 diff --git a/modules/__init__.py b/modules/__init__.py index ae34c23..3d22bed 100644 --- a/modules/__init__.py +++ b/modules/__init__.py @@ -44,7 +44,7 @@ async def forward_slashless( try: game_code = model.split(":")[0] # TODO: check for more edge cases - if game_code == "MDX" and module == "eventlog" or module == "eventlog_2": + if game_code == "MDX" and module.startswith("eventlo"): find_response = globals()[f"ddr_{module}_{method}"] elif game_code == "REC": find_response = globals()[f"drs_{module}_{method}"] diff --git a/modules/ddr/eventlog_3.py b/modules/ddr/eventlog_3.py new file mode 100644 index 0000000..c7e3ea3 --- /dev/null +++ b/modules/ddr/eventlog_3.py @@ -0,0 +1,25 @@ +import config + +from fastapi import APIRouter, Request, Response + +from core_common import core_process_request, core_prepare_response, E + +router = APIRouter(prefix="/local2", tags=["local2"]) +router.model_whitelist = ["MDX"] + + +@router.post("/{gameinfo}/eventlog_3/write") +async def ddr_eventlog_3_write(request: Request): + request_info = await core_process_request(request) + + response = E.response( + E.eventlog_3( + E.gamesession(9999999, __type="s64"), + E.logsendflg(1, __type="s32"), + E.logerrlevel(0, __type="s32"), + E.evtidnosendflg(0, __type="s32"), + ) + ) + + response_body, response_headers = await core_prepare_response(request, response) + return Response(content=response_body, headers=response_headers) diff --git a/modules/ddr/playdata_3.py b/modules/ddr/playdata_3.py new file mode 100644 index 0000000..51f5157 --- /dev/null +++ b/modules/ddr/playdata_3.py @@ -0,0 +1,870 @@ +import random +import time + +from tinydb import Query, where + +import config + +from fastapi import APIRouter, Request, Response + +from core_common import core_process_request, core_prepare_response, E +from core_database import get_db + +from base64 import b64decode, b64encode + +router = APIRouter(prefix="/local2", tags=["local2"]) +router.model_whitelist = ["MDX"] + + +def get_profile(cid): + return get_db().table("ddr_profile").get(where("card") == cid) + + +def get_game_profile(cid, game_version): + profile = get_profile(cid) + + return profile["version"].get(str(game_version), None) + +common = [ + "dancername", + "area", + "extrastar", + "playcount", + "today_cal", + "is_subscribed", + "popup_subscribe_enable", + "popup_subscribe_disable", +] + +option = [ + "hispeed", + "gauge", + "fastslow", + "guideline", + "stepzone", + "timing_disp", + "visibility", + "visible_time", + "lane", + "lane_hiddenpos", + "lane_suddenpos", + "lane_hidsudpos", + "lane_filter", + "scroll_direction", + "scroll_moving", + "arrow_priority", + "arrow_placement", + "arrow_color", + "arrow_design", + "cut_timing", + "cut_freeze", + "cut_jump", + "speed_type", + "real_speed", + "lane_preview", + "combo_priority", + "judge_priority", + "judge_position", +] + +lastplay = [ + "mode", + "folder", + "mcode", + "style", + "difficulty", + "window_main", + "window_sub", + "target", + "tab_main", + "tab_sub", + "tab_main_graph_type", + "tab_main_graph_disp", + "tab_sub_graph_type", + "tab_sub_graph_disp", +] + +filtersort = [ + "title", + "version", + "genre", + "bpm", + "event", + "level", + "flare_rank", + "clear_rank", + "flare_skill_target", + "rival_flare_skill", + "rival_score_rank", + "sort_type", + "order_type", + "is_quickmode", + "cleartype", + "difficulty", +] + +checkguide = [ + "tips_basic", + "tips_option", + "tips_event", + "tips_gimmick", + "tips_advance", + "guide_scene", +] + +brave = [ + "last_braveid", + "last_window_btn", +] + + +@router.post("/{gameinfo}/playdata_3/musicdata_load") +async def playdata_3_musicdata_load(request: Request): + request_info = await core_process_request(request) + + response = E.response( + E.playdata_3( + E.result(0, __type="s32"), + E.servertime(round(time.time() * 1000), __type="u64"), + E.music( + E.music_str("", __type="str"), + ), + ) + ) + + response_body, response_headers = await core_prepare_response(request, response) + return Response(content=response_body, headers=response_headers) + +@router.post("/{gameinfo}/playdata_3/playerdata_load") +async def playdata_3_playerdata_load(request: Request): + request_info = await core_process_request(request) + game_version = request_info["game_version"] + + data = request_info["root"][0].find("data") + #mode = data.find("mode").text + #gamesession = data.find("gamesession").text + refid = data.find("refid").text + + default = "X0000000000000000000000000000000" + + if refid != default: + p = get_profile(refid) + all_scores = {} + if p is not None: + ddr_id = p["ddr_id"] + profile = get_game_profile(refid, game_version) + + for record in get_db().table("ddr_scores_best").search(where("ddr_id") == ddr_id): + mcode = record["mcode"] + difficulty = record["difficulty"] + if mcode not in all_scores: + all_scores[mcode] = {} + if difficulty > 4: + all_scores[mcode][difficulty] = f"{int(difficulty) - 4},1,{record["rank"]},{record["lamp"]},{record["score"]},{record["ghostid"]},0,0,0" + else: + all_scores[mcode][difficulty] = f"{difficulty},1,{record["rank"]},{record["lamp"]},{record["score"]},{record["ghostid"]},0,0,0" + + + else: + profile = None + + if profile == None: + response = E.response( + E.playdata_3( + E.result(0, __type="s32"), + E.refid(refid, __type="str"), + E.gamesession(1, __type="s64"), + E.servertime(round(time.time() * 1000), __type="u64"), + E.is_locked(0, __type="bool"), + E.common( + E.ddrcode(0, __type="s32"), + E.dancername("", __type="str"), + E.is_new(1, __type="bool"), + E.is_registering(0, __type="bool"), + E.area(0, __type="s32"), + E.extrastar(0, __type="s32"), + E.playcount(0, __type="s32"), + E.weight(0, __type="s32"), + E.today_cal(0, __type="u64"), + E.is_disp_weight(0, __type="bool"), + E.is_takeover(0, __type="bool"), + E.pre_playable_num(0, __type="s32"), + E.is_subscribed(0, __type="bool"), + E.popup_subscribe_enable(0, __type="bool"), + E.popup_subscribe_disable(0, __type="bool"), + ), + E.option( + E.hispeed(0, __type="s32"), + E.gauge(0, __type="s32"), + E.fastslow(0, __type="s32"), + E.guideline(0, __type="s32"), + E.stepzone(0, __type="s32"), + E.timing_disp(0, __type="s32"), + E.visibility(0, __type="s32"), + E.visible_time(0, __type="s32"), + E.lane(0, __type="s32"), + E.lane_hiddenpos(0, __type="s32"), + E.lane_suddenpos(0, __type="s32"), + E.lane_hidsudpos(0, __type="s32"), + E.lane_filter(0, __type="s32"), + E.scroll_direction(0, __type="s32"), + E.scroll_moving(0, __type="s32"), + E.arrow_priority(0, __type="s32"), + E.arrow_placement(0, __type="s32"), + E.arrow_color(0, __type="s32"), + E.arrow_design(0, __type="s32"), + E.cut_timing(0, __type="s32"), + E.cut_freeze(0, __type="s32"), + E.cut_jump(0, __type="s32"), + E.speed_type(0, __type="s32"), + E.real_speed(0, __type="s32"), + E.lane_preview(0, __type="s32"), + E.combo_priority(0, __type="s32"), + E.judge_priority(0, __type="s32"), + E.judge_position(0, __type="s32"), + ), + E.lastplay( + E.mode(0, __type="s32"), + E.folder(0, __type="s32"), + E.mcode(0, __type="s32"), + E.style(0, __type="s32"), + E.difficulty(0, __type="s32"), + E.window_main(0, __type="s32"), + E.window_sub(0, __type="s32"), + E.target(0, __type="s32"), + E.tab_main(0, __type="s32"), + E.tab_sub(0, __type="s32"), + E.tab_main_graph_type(0, __type="s32"), + E.tab_main_graph_disp(0, __type="s32"), + E.tab_sub_graph_type(0, __type="s32"), + E.tab_sub_graph_disp(0, __type="s32"), + ), + E.filtersort( + E.title(0, __type="u64"), + E.version(0, __type="u64"), + E.genre(0, __type="u64"), + E.bpm(0, __type="u64"), + E.event(0, __type="u64"), + E.level(0, __type="u64"), + E.flare_rank(0, __type="u64"), + E.clear_rank(0, __type="u64"), + E.flare_skill_target(0, __type="u64"), + E.rival_flare_skill(0, __type="u64"), + E.rival_score_rank(0, __type="u64"), + E.sort_type(0, __type="u64"), + E.order_type(0, __type="s32"), + E.is_quickmode(0, __type="bool"), + E.cleartype(0, __type="u64"), + E.difficulty(0, __type="u64"), + ), + E.checkguide( + E.tips_basic(0, __type="u64"), + E.tips_option(0, __type="u64"), + E.tips_event(0, __type="u64"), + E.tips_gimmick(0, __type="u64"), + E.tips_advance(0, __type="u64"), + E.guide_scene(0, __type="u64"), + ), + E.rival( + E.slot(1, __type="s32"), + E.rivalcode(0, __type="s32"), + ), + E.rival( + E.slot(2, __type="s32"), + E.rivalcode(0, __type="s32"), + ), + E.rival( + E.slot(3, __type="s32"), + E.rivalcode(0, __type="s32"), + ), + E.score( + E.mcode(0, __type="s32"), + E.score_single( + E.score_str("", __type="str"), + ), + E.score_double( + E.score_str("", __type="str"), + ), + ), + E.event( + E.event_str("1,101,0,0,14,0,0", __type="str"), + ), + #E.league(), + #E.current(), + #E.result(), + #E.customize(), + #E.brave(), + ) + ) + else: + response = E.response( + E.playdata_3( + E.result(0, __type="s32"), + E.refid(refid, __type="str"), + E.gamesession(1, __type="s64"), + E.servertime(round(time.time() * 1000), __type="u64"), + E.is_locked(0, __type="bool"), + E.common( + E.ddrcode(p["ddr_id"], __type="s32"), + E.dancername(profile["common_dancername"], __type="str"), + E.is_new(0, __type="bool"), + E.is_registering(0, __type="bool"), + E.area(profile["common_area"], __type="s32"), + E.extrastar(profile["common_extrastar"], __type="s32"), + E.playcount(profile["common_playcount"], __type="s32"), + E.weight(0, __type="s32"), + E.today_cal(profile["common_today_cal"], __type="u64"), + E.is_disp_weight(0, __type="bool"), + E.is_takeover(0, __type="bool"), + E.pre_playable_num(1, __type="s32"), + E.is_subscribed(1, __type="bool"), + E.popup_subscribe_enable(0, __type="bool"), + E.popup_subscribe_disable(0, __type="bool"), + ), + E.option( + E.hispeed(profile["option_hispeed"], __type="s32"), + E.gauge(profile["option_gauge"], __type="s32"), + E.fastslow(profile["option_fastslow"], __type="s32"), + E.guideline(profile["option_guideline"], __type="s32"), + E.stepzone(profile["option_stepzone"], __type="s32"), + E.timing_disp(profile["option_timing_disp"], __type="s32"), + E.visibility(profile["option_visibility"], __type="s32"), + E.visible_time(profile["option_visible_time"], __type="s32"), + E.lane(profile["option_lane"], __type="s32"), + E.lane_hiddenpos(profile["option_lane_hiddenpos"], __type="s32"), + E.lane_suddenpos(profile["option_lane_suddenpos"], __type="s32"), + E.lane_hidsudpos(profile["option_lane_hidsudpos"], __type="s32"), + E.lane_filter(profile["option_lane_filter"], __type="s32"), + E.scroll_direction(profile["option_scroll_direction"], __type="s32"), + E.scroll_moving(profile["option_scroll_moving"], __type="s32"), + E.arrow_priority(profile["option_arrow_priority"], __type="s32"), + E.arrow_placement(profile["option_arrow_placement"], __type="s32"), + E.arrow_color(profile["option_arrow_color"], __type="s32"), + E.arrow_design(profile["option_arrow_design"], __type="s32"), + E.cut_timing(profile["option_cut_timing"], __type="s32"), + E.cut_freeze(profile["option_cut_freeze"], __type="s32"), + E.cut_jump(profile["option_cut_jump"], __type="s32"), + E.speed_type(profile["option_speed_type"], __type="s32"), + E.real_speed(profile["option_real_speed"], __type="s32"), + E.lane_preview(profile["option_lane_preview"], __type="s32"), + E.combo_priority(profile["option_combo_priority"], __type="s32"), + E.judge_priority(profile["option_judge_priority"], __type="s32"), + E.judge_position(profile["option_judge_position"], __type="s32"), + ), + E.lastplay( + E.mode(profile["lastplay_mode"], __type="s32"), + E.folder(profile["lastplay_folder"], __type="s32"), + E.mcode(profile["lastplay_mcode"], __type="s32"), + E.style(profile["lastplay_style"], __type="s32"), + E.difficulty(profile["lastplay_difficulty"], __type="s32"), + E.window_main(profile["lastplay_window_main"], __type="s32"), + E.window_sub(profile["lastplay_window_sub"], __type="s32"), + E.target(profile["lastplay_target"], __type="s32"), + E.tab_main(profile["lastplay_tab_main"], __type="s32"), + E.tab_sub(profile["lastplay_tab_sub"], __type="s32"), + E.tab_main_graph_type(profile["lastplay_tab_main_graph_type"], __type="s32"), + E.tab_main_graph_disp(profile["lastplay_tab_main_graph_disp"], __type="s32"), + E.tab_sub_graph_type(profile["lastplay_tab_sub_graph_type"], __type="s32"), + E.tab_sub_graph_disp(profile["lastplay_tab_sub_graph_disp"], __type="s32"), + ), + E.filtersort( + E.title(profile["filtersort_title"], __type="u64"), + E.version(profile["filtersort_version"], __type="u64"), + E.genre(profile["filtersort_genre"], __type="u64"), + E.bpm(profile["filtersort_bpm"], __type="u64"), + E.event(profile["filtersort_event"], __type="u64"), + E.level(profile["filtersort_level"], __type="u64"), + E.flare_rank(profile["filtersort_flare_rank"], __type="u64"), + E.clear_rank(profile["filtersort_clear_rank"], __type="u64"), + E.flare_skill_target(profile["filtersort_flare_skill_target"], __type="u64"), + E.rival_flare_skill(profile["filtersort_rival_flare_skill"], __type="u64"), + E.rival_score_rank(profile["filtersort_rival_score_rank"], __type="u64"), + E.sort_type(profile["filtersort_sort_type"], __type="u64"), + E.order_type(profile["filtersort_order_type"], __type="s32"), + E.is_quickmode(profile["filtersort_is_quickmode"], __type="bool"), + E.cleartype(profile["filtersort_cleartype"], __type="u64"), + E.difficulty(profile["filtersort_difficulty"], __type="u64"), + ), + E.checkguide( + E.tips_basic(profile["checkguide_tips_basic"], __type="u64"), + E.tips_option(profile["checkguide_tips_option"], __type="u64"), + E.tips_event(profile["checkguide_tips_event"], __type="u64"), + E.tips_gimmick(profile["checkguide_tips_gimmick"], __type="u64"), + E.tips_advance(profile["checkguide_tips_advance"], __type="u64"), + E.guide_scene(profile["checkguide_guide_scene"], __type="u64"), + ), + E.rival( + E.slot(1, __type="s32"), + E.rivalcode(profile.get("rival_1_ddr_id", 0), __type="s32"), + ), + E.rival( + E.slot(2, __type="s32"), + E.rivalcode(profile.get("rival_2_ddr_id", 0), __type="s32"), + ), + E.rival( + E.slot(3, __type="s32"), + E.rivalcode(profile.get("rival_3_ddr_id", 0), __type="s32"), + ), + *[ + E.score( + E.mcode(int(mcode), __type="s32"), + *[ + E.score_single(E.score_str(all_scores[mcode][difficulty], __type="str")) + for difficulty in all_scores[mcode] if difficulty < 5 + ], + *[ + E.score_double(E.score_str(all_scores[mcode][difficulty], __type="str")) + for difficulty in all_scores[mcode] if difficulty > 4 + ], + + ) + for mcode in all_scores.keys() + ], + E.event( + E.event_str("1,101,0,0,14,0,0", __type="str"), + ), + *[ + E.event( + E.event_str(f"{x},9999,0,0,0,0,0", __type="str"), + ) + for x in [ + e + for e in range(101, 1, -1) + if e not in [4, 6, 7, 8, 14, 47, 90] + ] + ], + #E.league(), + #E.current(), + #E.result(), + #appeal_board + E.customize( + E.category(1, __type="s32"), + E.key(100006, __type="s32"), + E.pattern(0, __type="s32"), + ), + #character_left + E.customize( + E.category(2, __type="s32"), + E.key(4, __type="s32"), + E.pattern(1, __type="s32"), + ), + #character_right + E.customize( + E.category(2, __type="s32"), + E.key(1, __type="s32"), + E.pattern(2, __type="s32"), + ), + ##game_bg_system + #E.customize( + # E.category(3, __type="s32"), + # E.key(2, __type="s32"), + # E.pattern(1, __type="s32"), + #), + ##game_bg_play + #E.customize( + # E.category(3, __type="s32"), + # E.key(24, __type="s32"), + # E.pattern(2, __type="s32"), + #), + ##lane_bg_single + #E.customize( + # E.category(4, __type="s32"), + # E.key(20, __type="s32"), + # E.pattern(0, __type="s32"), + #), + ##lane_bg_double + #E.customize( + # E.category(5, __type="s32"), + # E.key(20, __type="s32"), + # E.pattern(0, __type="s32"), + #), + ##lane_cover_single + #E.customize( + # E.category(6, __type="s32"), + # E.key(1, __type="s32"), + # E.pattern(0, __type="s32"), + #), + ##lane_cover_double + #E.customize( + # E.category(7, __type="s32"), + # E.key(1, __type="s32"), + # E.pattern(0, __type="s32"), + #), + #song_vid + E.customize( + E.category(8, __type="s32"), + E.key(2, __type="s32"), + E.pattern(0, __type="s32"), + ), + #E.brave(), + ) + ) + + response_body, response_headers = await core_prepare_response(request, response) + return Response(content=response_body, headers=response_headers) + +@router.post("/{gameinfo}/playdata_3/rivaldata_load") +async def playdata_3_rivaldata_load(request: Request): + request_info = await core_process_request(request) + game_version = request_info["game_version"] + + data = request_info["root"][0].find("data") + loadflag = int(data.find("loadkind").text) + country = data.find("country").text + region = data.find("region").text + customercode = data.find("customercode").text + companycode = data.find("companycode").text + locationid = data.find("locationid").text + pcbid = data.find("pcbid").text + targettime = data.find("targettime").text + ddrcode = int(data.find("ddrcode").text) + + db = get_db() + + if loadflag == 1: + all_scores = {} + for record in db.table("ddr_scores").search(where("ddr_id") != 0): + ddr_id = record["ddr_id"] + mcode = record["mcode"] + difficulty = record["difficulty"] + score = record["score"] + + if (mcode, difficulty) not in all_scores or score > all_scores[ + (mcode, difficulty) + ].get("score"): + all_scores[mcode, difficulty] = { + "game_version": game_version, + "ddr_id": ddr_id, + "mcode": mcode, + "difficulty": difficulty, + "rank": record["rank"], + "lamp": record["lamp"], + "score": score, + "exscore": record["exscore"], + "ghostid": record.doc_id, + } + scores = list(all_scores.values()) + + elif loadflag == 2: + all_scores = {} + for record in db.table("ddr_scores").search( + (where("shoparea") == region) + & (where("ddr_id") != 0) + ): + ddr_id = record["ddr_id"] + mcode = record["mcode"] + difficulty = record["difficulty"] + score = record["score"] + + if (mcode, difficulty) not in all_scores or score > all_scores[ + (mcode, difficulty) + ].get("score"): + all_scores[mcode, difficulty] = { + "game_version": game_version, + "ddr_id": ddr_id, + "mcode": mcode, + "difficulty": difficulty, + "rank": record["rank"], + "lamp": record["lamp"], + "score": score, + "exscore": record["exscore"], + "ghostid": record.doc_id, + } + scores = list(all_scores.values()) + + elif loadflag == 3: + all_scores = {} + for record in db.table("ddr_scores").search( + (where("pcbid") == pcbid) + & (where("ddr_id") != 0) + ): + ddr_id = record["ddr_id"] + mcode = record["mcode"] + difficulty = record["difficulty"] + score = record["score"] + + if (mcode, difficulty) not in all_scores or score > all_scores[ + (mcode, difficulty) + ].get("score"): + all_scores[mcode, difficulty] = { + "game_version": game_version, + "ddr_id": ddr_id, + "mcode": mcode, + "difficulty": difficulty, + "rank": record["rank"], + "lamp": record["lamp"], + "score": score, + "exscore": record["exscore"], + "ghostid": record.doc_id, + } + scores = list(all_scores.values()) + + elif loadflag == 4: + scores = [] + for s in db.table("ddr_scores_best").search(where("ddr_id") == ddrcode): + scores.append(s) + + load = [] + names = {} + + profiles = db.table("ddr_profile") + for p in profiles: + names[p["ddr_id"]] = {} + try: + names[p["ddr_id"]]["name"] = p["version"][str(20)]["common_dancername"] + names[p["ddr_id"]]["area"] = int(p["version"][str(20)]["common_area"]) + except KeyError: + try: + names[p["ddr_id"]]["name"] = p["version"][str(19)]["common"].split(",")[27] + names[p["ddr_id"]]["area"] = int(str(p["version"][str(19)]["common"].split(",")[3]), 16) + except KeyError: + names[p["ddr_id"]]["name"] = "UNKNOWN" + names[p["ddr_id"]]["area"] = 13 + + for r in scores: + diffi = r["difficulty"] + if diffi > 4: + style = 1 + diffi -= 4 + else: + style = 0 + load.append(f"{r["mcode"]},{style},{diffi},0,{names[r["ddr_id"]]["name"] if r["ddr_id"] in names else "UNKNOWN"},0,0,1,{r["score"]},{r["ghostid"]}") + + response = E.response( + E.playdata_3( + E.result(0, __type="s32"), + *[ + E.record( + E.record_str(s, __type="str") + ) + for s in load + ] + ) + ) + + response_body, response_headers = await core_prepare_response(request, response) + return Response(content=response_body, headers=response_headers) + + +@router.post("/{gameinfo}/playdata_3/playerdata_new") +async def playdata_3_playerdata_new(request: Request): + request_info = await core_process_request(request) + game_version = request_info["game_version"] + + data = request_info["root"][0].find("data") + refid = data.find("refid").text + + db = get_db() + all_profiles_for_card = db.table("ddr_profile").get(Query().card == refid) + if "ddr_id" not in all_profiles_for_card: + ddr_id = random.randint(10000000, 99999999) + all_profiles_for_card["ddr_id"] = ddr_id + else: + ddr_id = all_profiles_for_card["ddr_id"] + tmp = {"game_version": game_version} + for k in common: + tmp["common_" + k] = 0 + for k in option: + tmp["option_" + k] = 0 + for k in lastplay: + tmp["lastplay_" + k] = 0 + for k in filtersort: + tmp["filtersort_" + k] = 0 + for k in checkguide: + tmp["checkguide_" + k] = 0 + for k in brave: + tmp["brave_" + k] = 0 + tmp["rival_1_ddr_id"] = 0 + tmp["rival_2_ddr_id"] = 0 + tmp["rival_3_ddr_id"] = 0 + all_profiles_for_card["version"][str(game_version)] = tmp + + db.table("ddr_profile").upsert(all_profiles_for_card, where("card") == refid) + + + response = E.response( + E.playdata_3( + E.result(0, __type="s32"), + E.refid(refid, __type="str"), + E.ddrcode(ddr_id, __type="s32"), + E.istakeover(0, __type="bool"), + ) + ) + + response_body, response_headers = await core_prepare_response(request, response) + return Response(content=response_body, headers=response_headers) + + +@router.post("/{gameinfo}/playdata_3/playerdata_save") +async def playdata_3_playerdata_save(request: Request): + request_info = await core_process_request(request) + game_version = request_info["game_version"] + + data = request_info["root"][0].find("data") + + refid = data.find("refid").text + savekind = int(data.find("savekind").text) + + profile = get_profile(refid) + game_profile = get_game_profile(refid, game_version) + + db = get_db() + + if not refid.startswith("X000"): + if savekind in (1, 3): + for k in common: + if k == "playcount": + game_profile["common_playcount"] += 1 + elif k.startswith("popup_subscribe"): + game_profile["common_" + k] = "0" + else: + game_profile["common_" + k] = data.find("common").find(k).text + for k in option: + game_profile["option_" + k] = data.find("option").find(k).text + for k in lastplay: + game_profile["lastplay_" + k] = data.find("lastplay").find(k).text + for k in filtersort: + game_profile["filtersort_" + k] = data.find("filtersort").find(k).text + for k in checkguide: + game_profile["checkguide_" + k] = data.find("checkguide").find(k).text + for k in brave: + game_profile["brave_" + k] = data.find("brave").find(k).text + profile["version"][str(game_version)] = game_profile + get_db().table("ddr_profile").upsert(profile, where("card") == refid) + + elif savekind == 2: + timestamp = time.time() + + ddr_id = int(data.find("common").find("ddrcode").text) + pcbid = data.find("pcbid").text + shoparea = data.find("region").text + + n = data.find("result") + + playstyle = int(n.find("style").text) + mcode = int(n.find("mcode").text) + diffi = int(n.find("difficulty").text) + difficulty = diffi + 4 if playstyle == 1 else diffi + rank = int(n.find("rank").text) + lamp = int(n.find("clearkind").text) + score = int(n.find("score").text) + exscore = int(n.find("exscore").text) + maxcombo = int(n.find("maxcombo").text) + #life = int(n.find("life").text) + fastcount = int(n.find("fastcount").text) + slowcount = int(n.find("slowcount").text) + judge_marvelous = int(n.find("judge_marv").text) + judge_perfect = int(n.find("judge_perf").text) + judge_great = int(n.find("judge_great").text) + judge_good = int(n.find("judge_good").text) + #judge_boo = int(n.find("judge_boo").text) + judge_miss = int(n.find("judge_miss").text) + judge_ok = int(n.find("judge_ok").text) + judge_ng = int(n.find("judge_ng").text) + calorie = int(n.find("calorie").text) + ghostsize = int(n.find("ghostsize").text) + ghost = n.find("ghost").text + + db.table("ddr_scores").insert( + { + "timestamp": timestamp, + "pcbid": pcbid, + "shoparea": shoparea, + "game_version": game_version, + "ddr_id": ddr_id, + "playstyle": playstyle, + "mcode": mcode, + "difficulty": difficulty, + "rank": rank, + "lamp": lamp, + "score": score, + "exscore": exscore, + "maxcombo": maxcombo, + "life": -1, + "fastcount": fastcount, + "slowcount": slowcount, + "judge_marvelous": judge_marvelous, + "judge_perfect": judge_perfect, + "judge_great": judge_great, + "judge_good": judge_good, + "judge_boo": 0, + "judge_miss": judge_miss, + "judge_ok": judge_ok, + "judge_ng": judge_ng, + "calorie": calorie, + "ghostsize": ghostsize, + "ghost": ghost, + }, + ) + + best = db.table("ddr_scores_best").get( + (where("ddr_id") == ddr_id) + & (where("mcode") == mcode) + & (where("difficulty") == difficulty) + ) + best = {} if best is None else best + + best_score_data = { + "game_version": game_version, + "ddr_id": ddr_id, + "playstyle": playstyle, + "mcode": mcode, + "difficulty": difficulty, + "rank": min(rank, best.get("rank", rank)), + "lamp": max(lamp, best.get("lamp", lamp)), + "score": max(score, best.get("score", score)), + "exscore": max(exscore, best.get("exscore", exscore)), + } + + ghostid = db.table("ddr_scores").get( + (where("ddr_id") == ddr_id) + & (where("mcode") == mcode) + & (where("difficulty") == difficulty) + & (where("score") == max(score, best.get("score", score))) + ) + best_score_data["ghostid"] = ghostid.doc_id + + db.table("ddr_scores_best").upsert( + best_score_data, + (where("ddr_id") == ddr_id) + & (where("mcode") == mcode) + & (where("difficulty") == difficulty), + ) + + response = E.response( + E.playdata_3( + E.result(0, __type="s32"), + ) + ) + + else: + response = E.response( + E.playdata_3( + E.result(1, __type="s32"), + ) + ) + + + response_body, response_headers = await core_prepare_response(request, response) + return Response(content=response_body, headers=response_headers) + + +@router.post("/{gameinfo}/playdata_3/ghostdata_load") +async def playdata_3_ghostdata_load(request: Request): + request_info = await core_process_request(request) + + data = request_info["root"][0].find("data") + ghostid = int(data.find("ghostid").text) + record = get_db().table("ddr_scores").get(doc_id=ghostid) + + response = E.response( + E.playdata_3( + E.result(0, __type="s32"), + E.ghostsize(record["ghostsize"], __type="s32"), + E.ghost(record["ghost"], __type="str"), + ) + ) + + response_body, response_headers = await core_prepare_response(request, response) + return Response(content=response_body, headers=response_headers) + diff --git a/modules/ddr/playerdata.py b/modules/ddr/playerdata.py index a644f2e..3c74f1a 100644 --- a/modules/ddr/playerdata.py +++ b/modules/ddr/playerdata.py @@ -95,13 +95,11 @@ async def playerdata_usergamedata_advanced(request: Request): single_grade = profile.get("single_grade", 0) double_grade = profile.get("double_grade", 0) - for record in db.table("ddr_scores_best").search( - (where("game_version") == game_version) & (where("ddr_id") == ddr_id) - ): + for record in db.table("ddr_scores_best").search(where("ddr_id") == ddr_id): mcode = record["mcode"] difficulty = record["difficulty"] if mcode not in all_scores: - all_scores[mcode] = [[0, 0, 0, 0, 0] for x in range(10)] + all_scores[mcode] = [[0, 0, 0, 0, 0] for x in range(9)] # not 10, there is no dp beginner all_scores[mcode][difficulty] = [ 1, record["rank"], @@ -110,6 +108,25 @@ async def playerdata_usergamedata_advanced(request: Request): record["ghostid"], ] + + f = {} + for mcode in all_scores.keys(): + if mcode not in f: + f[mcode] = [] + for s in [score for score in all_scores.get(mcode)]: + if s[0] == 0: + f[mcode].append(E.note()) + else: + f[mcode].append( + E.note( + E.count(s[0], __type="u16"), + E.rank(s[1], __type="u8"), + E.clearkind(s[2], __type="u8"), + E.score(s[3], __type="s32"), + E.ghostid(s[4], __type="s32"), + ) + ) + response = E.response( E.playerdata( E.result(0, __type="s32"), @@ -120,17 +137,10 @@ async def playerdata_usergamedata_advanced(request: Request): E.music( E.mcode(int(mcode), __type="u32"), *[ - E.note( - E.count(s[0], __type="u16"), - E.rank(s[1], __type="u8"), - E.clearkind(s[2], __type="u8"), - E.score(s[3], __type="s32"), - E.ghostid(s[4], __type="s32"), - ) - for s in [score for score in all_scores.get(mcode)] + s for s in f.get(mcode) ], ) - for mcode in all_scores.keys() + for mcode in f.keys() ], *[ E.eventdata( @@ -306,7 +316,6 @@ async def playerdata_usergamedata_advanced(request: Request): best = db.table("ddr_scores_best").get( (where("ddr_id") == ddr_id) - & (where("game_version") == game_version) & (where("mcode") == mcode) & (where("difficulty") == difficulty) ) @@ -326,7 +335,6 @@ async def playerdata_usergamedata_advanced(request: Request): ghostid = db.table("ddr_scores").get( (where("ddr_id") == ddr_id) - & (where("game_version") == game_version) & (where("mcode") == mcode) & (where("difficulty") == difficulty) & (where("score") == max(score, best.get("score", score))) @@ -336,7 +344,6 @@ async def playerdata_usergamedata_advanced(request: Request): db.table("ddr_scores_best").upsert( best_score_data, (where("ddr_id") == ddr_id) - & (where("game_version") == game_version) & (where("mcode") == mcode) & (where("difficulty") == difficulty), ) @@ -386,8 +393,7 @@ async def playerdata_usergamedata_advanced(request: Request): if loadflag == 1: all_scores = {} for record in db.table("ddr_scores").search( - (where("game_version") == game_version) - & (where("pcbid") == pcbid) + (where("pcbid") == pcbid) & (where("ddr_id") != 0) ): ddr_id = record["ddr_id"] @@ -414,8 +420,7 @@ async def playerdata_usergamedata_advanced(request: Request): elif loadflag == 2: all_scores = {} for record in db.table("ddr_scores").search( - (where("game_version") == game_version) - & (where("shoparea") == shoparea) + (where("shoparea") == shoparea) & (where("ddr_id") != 0) ): ddr_id = record["ddr_id"] @@ -441,9 +446,7 @@ async def playerdata_usergamedata_advanced(request: Request): elif loadflag == 4: all_scores = {} - for record in db.table("ddr_scores").search( - (where("game_version") == game_version) & (where("ddr_id") != 0) - ): + for record in db.table("ddr_scores").search(where("ddr_id") != 0): ddr_id = record["ddr_id"] mcode = record["mcode"] difficulty = record["difficulty"] diff --git a/modules/ddr/playerdata_2.py b/modules/ddr/playerdata_2.py index 5311ea1..100a5f7 100644 --- a/modules/ddr/playerdata_2.py +++ b/modules/ddr/playerdata_2.py @@ -93,9 +93,7 @@ async def playerdata_2_usergamedata_advanced(request: Request): ddr_id = all_profiles_for_card["ddr_id"] profile = get_game_profile(refid, game_version) - for record in db.table("ddr_scores_best").search( - (where("game_version") == game_version) & (where("ddr_id") == ddr_id) - ): + for record in db.table("ddr_scores_best").search(where("ddr_id") == ddr_id): mcode = record["mcode"] difficulty = record["difficulty"] if mcode not in all_scores: @@ -376,7 +374,6 @@ async def playerdata_2_usergamedata_advanced(request: Request): best = db.table("ddr_scores_best").get( (where("ddr_id") == ddr_id) - & (where("game_version") == game_version) & (where("mcode") == mcode) & (where("difficulty") == difficulty) ) @@ -396,7 +393,6 @@ async def playerdata_2_usergamedata_advanced(request: Request): ghostid = db.table("ddr_scores").get( (where("ddr_id") == ddr_id) - & (where("game_version") == game_version) & (where("mcode") == mcode) & (where("difficulty") == difficulty) & (where("score") == max(score, best.get("score", score))) @@ -406,7 +402,6 @@ async def playerdata_2_usergamedata_advanced(request: Request): db.table("ddr_scores_best").upsert( best_score_data, (where("ddr_id") == ddr_id) - & (where("game_version") == game_version) & (where("mcode") == mcode) & (where("difficulty") == difficulty), ) @@ -468,8 +463,7 @@ async def playerdata_2_usergamedata_advanced(request: Request): if loadflag == 1: all_scores = {} for record in db.table("ddr_scores").search( - (where("game_version") == game_version) - & (where("pcbid") == pcbid) + (where("pcbid") == pcbid) & (where("ddr_id") != 0) ): ddr_id = record["ddr_id"] @@ -496,8 +490,7 @@ async def playerdata_2_usergamedata_advanced(request: Request): elif loadflag == 2: all_scores = {} for record in db.table("ddr_scores").search( - (where("game_version") == game_version) - & (where("shoparea") == shoparea) + (where("shoparea") == shoparea) & (where("ddr_id") != 0) ): ddr_id = record["ddr_id"] @@ -523,9 +516,7 @@ async def playerdata_2_usergamedata_advanced(request: Request): elif loadflag == 4: all_scores = {} - for record in db.table("ddr_scores").search( - (where("game_version") == game_version) & (where("ddr_id") != 0) - ): + for record in db.table("ddr_scores").search(where("ddr_id") != 0): ddr_id = record["ddr_id"] mcode = record["mcode"] difficulty = record["difficulty"] diff --git a/modules/ddr/system_3.py b/modules/ddr/system_3.py new file mode 100644 index 0000000..08acce0 --- /dev/null +++ b/modules/ddr/system_3.py @@ -0,0 +1,24 @@ +from fastapi import APIRouter, Request, Response + +from core_common import core_process_request, core_prepare_response, E + +import utils.card as conv + +router = APIRouter(prefix="/local2", tags=["local2"]) +router.model_whitelist = ["MDX"] + + +@router.post("/{gameinfo}/system_3/convcardnumber") +async def system_3_convcardnumber(request: Request): + request_info = await core_process_request(request) + cid = request_info["root"][0].find("data/card_id").text + + response = E.response( + E.system_3( + E.data(E.card_number(conv.to_konami_id(cid), __type="str")), + E.result(0, __type="s32"), + ) + ) + + response_body, response_headers = await core_prepare_response(request, response) + return Response(content=response_body, headers=response_headers) diff --git a/modules/ddr/wordcheck_3.py b/modules/ddr/wordcheck_3.py new file mode 100644 index 0000000..42f9f08 --- /dev/null +++ b/modules/ddr/wordcheck_3.py @@ -0,0 +1,23 @@ +import config + +from fastapi import APIRouter, Request, Response + +from core_common import core_process_request, core_prepare_response, E + +router = APIRouter(prefix="/local2", tags=["local2"]) +router.model_whitelist = ["MDX"] + + +@router.post("/{gameinfo}/wordcheck_3/tabooword_check") +async def wordcheck_3_tabooword_check(request: Request): + request_info = await core_process_request(request) + + response = E.response( + E.wordcheck_3( + E.result(0, __type="s32"), + E.is_taboo(0, __type="bool"), + ) + ) + + response_body, response_headers = await core_prepare_response(request, response) + return Response(content=response_body, headers=response_headers) From 5eda3463e649616c9af50a808a78a45458f1f956 Mon Sep 17 00:00:00 2001 From: drmext <71258889+drmext@users.noreply.github.com> Date: Fri, 13 Mar 2026 02:55:58 +0000 Subject: [PATCH 02/11] Fix --- modules/iidx/iidx33gamesystem.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/iidx/iidx33gamesystem.py b/modules/iidx/iidx33gamesystem.py index 6d6619b..7573e30 100644 --- a/modules/iidx/iidx33gamesystem.py +++ b/modules/iidx/iidx33gamesystem.py @@ -191,7 +191,8 @@ async def iidx33gamesystem_systeminfo(request: Request): E.WorldTourismOpenList(val=-1), E.OldBPLBattleOpenPhase(val=1), E.BPLBattleOpenPhase(val=3), - E.beat(val=0), + E.VocaloidEvent(val=1), + # E.beat(val=5293), ) ) From 87ea60fd99fd20e912c2828cfa308cf8a45d4058 Mon Sep 17 00:00:00 2001 From: drmext <71258889+drmext@users.noreply.github.com> Date: Fri, 13 Mar 2026 11:00:20 +0000 Subject: [PATCH 03/11] Fix --- modules/ddr/playdata_3.py | 674 +++++++++++++------------------------- 1 file changed, 219 insertions(+), 455 deletions(-) diff --git a/modules/ddr/playdata_3.py b/modules/ddr/playdata_3.py index 51f5157..564fde9 100644 --- a/modules/ddr/playdata_3.py +++ b/modules/ddr/playdata_3.py @@ -10,8 +10,6 @@ from fastapi import APIRouter, Request, Response from core_common import core_process_request, core_prepare_response, E from core_database import get_db -from base64 import b64decode, b64encode - router = APIRouter(prefix="/local2", tags=["local2"]) router.model_whitelist = ["MDX"] @@ -25,98 +23,128 @@ def get_game_profile(cid, game_version): return profile["version"].get(str(game_version), None) -common = [ - "dancername", - "area", - "extrastar", - "playcount", - "today_cal", - "is_subscribed", - "popup_subscribe_enable", - "popup_subscribe_disable", -] -option = [ - "hispeed", - "gauge", - "fastslow", - "guideline", - "stepzone", - "timing_disp", - "visibility", - "visible_time", - "lane", - "lane_hiddenpos", - "lane_suddenpos", - "lane_hidsudpos", - "lane_filter", - "scroll_direction", - "scroll_moving", - "arrow_priority", - "arrow_placement", - "arrow_color", - "arrow_design", - "cut_timing", - "cut_freeze", - "cut_jump", - "speed_type", - "real_speed", - "lane_preview", - "combo_priority", - "judge_priority", - "judge_position", -] - -lastplay = [ - "mode", - "folder", - "mcode", - "style", - "difficulty", - "window_main", - "window_sub", - "target", - "tab_main", - "tab_sub", - "tab_main_graph_type", - "tab_main_graph_disp", - "tab_sub_graph_type", - "tab_sub_graph_disp", -] - -filtersort = [ - "title", - "version", - "genre", - "bpm", - "event", - "level", - "flare_rank", - "clear_rank", - "flare_skill_target", - "rival_flare_skill", - "rival_score_rank", - "sort_type", - "order_type", - "is_quickmode", - "cleartype", - "difficulty", -] - -checkguide = [ - "tips_basic", - "tips_option", - "tips_event", - "tips_gimmick", - "tips_advance", - "guide_scene", -] - -brave = [ - "last_braveid", - "last_window_btn", -] +load_settings = { + "common": { + "dancername": "str", + "area": "s32", + "extrastar": "s32", + "playcount": "s32", + "weight": "s32", + "today_cal": "u64", + "is_disp_weight": "bool", + "is_takeover": "bool", + "pre_playable_num": "s32", + "is_subscribed": "bool", + "popup_subscribe_enable": "bool", + "popup_subscribe_disable": "bool", + }, + "option": { + "hispeed": "s32", + "gauge": "s32", + "fastslow": "s32", + "guideline": "s32", + "stepzone": "s32", + "timing_disp": "s32", + "visibility": "s32", + "visible_time": "s32", + "lane": "s32", + "lane_hiddenpos": "s32", + "lane_suddenpos": "s32", + "lane_hidsudpos": "s32", + "lane_filter": "s32", + "scroll_direction": "s32", + "scroll_moving": "s32", + "arrow_priority": "s32", + "arrow_placement": "s32", + "arrow_color": "s32", + "arrow_design": "s32", + "cut_timing": "s32", + "cut_freeze": "s32", + "cut_jump": "s32", + "speed_type": "s32", + "real_speed": "s32", + "lane_preview": "s32", + "combo_priority": "s32", + "judge_priority": "s32", + "judge_position": "s32", + }, + "lastplay": { + "mode": "s32", + "folder": "s32", + "mcode": "s32", + "style": "s32", + "difficulty": "s32", + "window_main": "s32", + "window_sub": "s32", + "target": "s32", + "tab_main": "s32", + "tab_sub": "s32", + "tab_main_graph_type": "s32", + "tab_main_graph_disp": "s32", + "tab_sub_graph_type": "s32", + "tab_sub_graph_disp": "s32", + }, + "filtersort": { + "title": "u64", + "version": "u64", + "genre": "u64", + "bpm": "u64", + "event": "u64", + "level": "u64", + "flare_rank": "u64", + "clear_rank": "u64", + "flare_skill_target": "u64", + "rival_flare_skill": "u64", + "rival_score_rank": "u64", + "sort_type": "u64", + "order_type": "s32", + "is_quickmode": "bool", + "cleartype": "u64", + "difficulty": "u64", + }, + "checkguide": { + "tips_basic": "u64", + "tips_option": "u64", + "tips_event": "u64", + "tips_gimmick": "u64", + "tips_advance": "u64", + "guide_scene": "u64", + }, + "brave": { + "last_braveid": "s32", + "last_window_btn": "s32", + }, +} +customize_settings = { + "1": { + "0": -1, #appeal_board + }, + "2": { + "1": -1, #character_left + "2": -1, #character_right + }, + "3": { + "1": -1, #game_bg_system + "2": -1, #game_bg_play + }, + "4": { + "0": -1, #lane_bg_single + }, + "5": { + "0": -1, #lane_bg_double + }, + "6": { + "0": -1, #lane_cover_single + }, + "7": { + "0": -1, #lane_cover_double + }, + "8": { + "0": -1, #song_vid + }, + } @router.post("/{gameinfo}/playdata_3/musicdata_load") async def playdata_3_musicdata_load(request: Request): @@ -147,9 +175,9 @@ async def playdata_3_playerdata_load(request: Request): default = "X0000000000000000000000000000000" + all_scores = {} if refid != default: p = get_profile(refid) - all_scores = {} if p is not None: ddr_id = p["ddr_id"] profile = get_game_profile(refid, game_version) @@ -163,342 +191,91 @@ async def playdata_3_playerdata_load(request: Request): all_scores[mcode][difficulty] = f"{int(difficulty) - 4},1,{record["rank"]},{record["lamp"]},{record["score"]},{record["ghostid"]},0,0,0" else: all_scores[mcode][difficulty] = f"{difficulty},1,{record["rank"]},{record["lamp"]},{record["score"]},{record["ghostid"]},0,0,0" - - else: - profile = None + p = {} + profile = {} - if profile == None: - response = E.response( - E.playdata_3( - E.result(0, __type="s32"), - E.refid(refid, __type="str"), - E.gamesession(1, __type="s64"), - E.servertime(round(time.time() * 1000), __type="u64"), - E.is_locked(0, __type="bool"), - E.common( - E.ddrcode(0, __type="s32"), - E.dancername("", __type="str"), - E.is_new(1, __type="bool"), - E.is_registering(0, __type="bool"), - E.area(0, __type="s32"), - E.extrastar(0, __type="s32"), - E.playcount(0, __type="s32"), - E.weight(0, __type="s32"), - E.today_cal(0, __type="u64"), - E.is_disp_weight(0, __type="bool"), - E.is_takeover(0, __type="bool"), - E.pre_playable_num(0, __type="s32"), - E.is_subscribed(0, __type="bool"), - E.popup_subscribe_enable(0, __type="bool"), - E.popup_subscribe_disable(0, __type="bool"), - ), - E.option( - E.hispeed(0, __type="s32"), - E.gauge(0, __type="s32"), - E.fastslow(0, __type="s32"), - E.guideline(0, __type="s32"), - E.stepzone(0, __type="s32"), - E.timing_disp(0, __type="s32"), - E.visibility(0, __type="s32"), - E.visible_time(0, __type="s32"), - E.lane(0, __type="s32"), - E.lane_hiddenpos(0, __type="s32"), - E.lane_suddenpos(0, __type="s32"), - E.lane_hidsudpos(0, __type="s32"), - E.lane_filter(0, __type="s32"), - E.scroll_direction(0, __type="s32"), - E.scroll_moving(0, __type="s32"), - E.arrow_priority(0, __type="s32"), - E.arrow_placement(0, __type="s32"), - E.arrow_color(0, __type="s32"), - E.arrow_design(0, __type="s32"), - E.cut_timing(0, __type="s32"), - E.cut_freeze(0, __type="s32"), - E.cut_jump(0, __type="s32"), - E.speed_type(0, __type="s32"), - E.real_speed(0, __type="s32"), - E.lane_preview(0, __type="s32"), - E.combo_priority(0, __type="s32"), - E.judge_priority(0, __type="s32"), - E.judge_position(0, __type="s32"), - ), - E.lastplay( - E.mode(0, __type="s32"), - E.folder(0, __type="s32"), - E.mcode(0, __type="s32"), - E.style(0, __type="s32"), - E.difficulty(0, __type="s32"), - E.window_main(0, __type="s32"), - E.window_sub(0, __type="s32"), - E.target(0, __type="s32"), - E.tab_main(0, __type="s32"), - E.tab_sub(0, __type="s32"), - E.tab_main_graph_type(0, __type="s32"), - E.tab_main_graph_disp(0, __type="s32"), - E.tab_sub_graph_type(0, __type="s32"), - E.tab_sub_graph_disp(0, __type="s32"), - ), - E.filtersort( - E.title(0, __type="u64"), - E.version(0, __type="u64"), - E.genre(0, __type="u64"), - E.bpm(0, __type="u64"), - E.event(0, __type="u64"), - E.level(0, __type="u64"), - E.flare_rank(0, __type="u64"), - E.clear_rank(0, __type="u64"), - E.flare_skill_target(0, __type="u64"), - E.rival_flare_skill(0, __type="u64"), - E.rival_score_rank(0, __type="u64"), - E.sort_type(0, __type="u64"), - E.order_type(0, __type="s32"), - E.is_quickmode(0, __type="bool"), - E.cleartype(0, __type="u64"), - E.difficulty(0, __type="u64"), - ), - E.checkguide( - E.tips_basic(0, __type="u64"), - E.tips_option(0, __type="u64"), - E.tips_event(0, __type="u64"), - E.tips_gimmick(0, __type="u64"), - E.tips_advance(0, __type="u64"), - E.guide_scene(0, __type="u64"), - ), + response = E.response( + E.playdata_3( + E.result(0, __type="s32"), + E.refid(refid, __type="str"), + E.gamesession(1, __type="s64"), + E.servertime(round(time.time() * 1000), __type="u64"), + E.is_locked(0, __type="bool"), + E.common( + E.ddrcode(p.get("ddr_id", 0), __type="s32"), + E.dancername(profile.get("common_dancername", ""), __type="str"), + E.is_new(not profile, __type="bool"), + E.is_registering(0, __type="bool"), + E.area(profile.get("common_area", 13), __type="s32"), + E.extrastar(profile.get("common_extrastar", 0), __type="s32"), + E.playcount(profile.get("common_playcount", 0), __type="s32"), + E.weight(profile.get("common_weight", 0), __type="s32"), + E.today_cal(profile.get("common_today_cal", 0), __type="u64"), + E.is_disp_weight(profile.get("common_is_disp_weight", 0), __type="bool"), + E.is_takeover(0, __type="bool"), + E.pre_playable_num(1, __type="s32"), + E.is_subscribed(1, __type="bool"), + E.popup_subscribe_enable(0, __type="bool"), + E.popup_subscribe_disable(0, __type="bool"), + ), + *[ + E(k, + *[ + E(v, profile.get(f"{k}_{v}", 0), __type=load_settings[k][v]) + for v in load_settings[k] + ], + ) + for k in load_settings if k != "common" + ], + *[ E.rival( - E.slot(1, __type="s32"), - E.rivalcode(0, __type="s32"), - ), - E.rival( - E.slot(2, __type="s32"), - E.rivalcode(0, __type="s32"), - ), - E.rival( - E.slot(3, __type="s32"), - E.rivalcode(0, __type="s32"), - ), + E.slot(i, __type="s32"), + E.rivalcode(profile.get(f"rival_{i}_ddr_id", 0), __type="s32"), + ) + for i in range (1, 4) + ], + *[ E.score( - E.mcode(0, __type="s32"), - E.score_single( - E.score_str("", __type="str"), - ), - E.score_double( - E.score_str("", __type="str"), - ), - ), + E.mcode(int(mcode), __type="s32"), + *[ + E.score_single(E.score_str(all_scores[mcode][difficulty], __type="str")) + for difficulty in all_scores[mcode] if difficulty < 5 + ], + *[ + E.score_double(E.score_str(all_scores[mcode][difficulty], __type="str")) + for difficulty in all_scores[mcode] if difficulty > 4 + ], + + ) + for mcode in all_scores.keys() + ], + E.event( + E.event_str("1,101,0,0,14,0,0", __type="str"), + ), + *[ E.event( - E.event_str("1,101,0,0,14,0,0", __type="str"), - ), - #E.league(), - #E.current(), - #E.result(), - #E.customize(), - #E.brave(), - ) - ) - else: - response = E.response( - E.playdata_3( - E.result(0, __type="s32"), - E.refid(refid, __type="str"), - E.gamesession(1, __type="s64"), - E.servertime(round(time.time() * 1000), __type="u64"), - E.is_locked(0, __type="bool"), - E.common( - E.ddrcode(p["ddr_id"], __type="s32"), - E.dancername(profile["common_dancername"], __type="str"), - E.is_new(0, __type="bool"), - E.is_registering(0, __type="bool"), - E.area(profile["common_area"], __type="s32"), - E.extrastar(profile["common_extrastar"], __type="s32"), - E.playcount(profile["common_playcount"], __type="s32"), - E.weight(0, __type="s32"), - E.today_cal(profile["common_today_cal"], __type="u64"), - E.is_disp_weight(0, __type="bool"), - E.is_takeover(0, __type="bool"), - E.pre_playable_num(1, __type="s32"), - E.is_subscribed(1, __type="bool"), - E.popup_subscribe_enable(0, __type="bool"), - E.popup_subscribe_disable(0, __type="bool"), - ), - E.option( - E.hispeed(profile["option_hispeed"], __type="s32"), - E.gauge(profile["option_gauge"], __type="s32"), - E.fastslow(profile["option_fastslow"], __type="s32"), - E.guideline(profile["option_guideline"], __type="s32"), - E.stepzone(profile["option_stepzone"], __type="s32"), - E.timing_disp(profile["option_timing_disp"], __type="s32"), - E.visibility(profile["option_visibility"], __type="s32"), - E.visible_time(profile["option_visible_time"], __type="s32"), - E.lane(profile["option_lane"], __type="s32"), - E.lane_hiddenpos(profile["option_lane_hiddenpos"], __type="s32"), - E.lane_suddenpos(profile["option_lane_suddenpos"], __type="s32"), - E.lane_hidsudpos(profile["option_lane_hidsudpos"], __type="s32"), - E.lane_filter(profile["option_lane_filter"], __type="s32"), - E.scroll_direction(profile["option_scroll_direction"], __type="s32"), - E.scroll_moving(profile["option_scroll_moving"], __type="s32"), - E.arrow_priority(profile["option_arrow_priority"], __type="s32"), - E.arrow_placement(profile["option_arrow_placement"], __type="s32"), - E.arrow_color(profile["option_arrow_color"], __type="s32"), - E.arrow_design(profile["option_arrow_design"], __type="s32"), - E.cut_timing(profile["option_cut_timing"], __type="s32"), - E.cut_freeze(profile["option_cut_freeze"], __type="s32"), - E.cut_jump(profile["option_cut_jump"], __type="s32"), - E.speed_type(profile["option_speed_type"], __type="s32"), - E.real_speed(profile["option_real_speed"], __type="s32"), - E.lane_preview(profile["option_lane_preview"], __type="s32"), - E.combo_priority(profile["option_combo_priority"], __type="s32"), - E.judge_priority(profile["option_judge_priority"], __type="s32"), - E.judge_position(profile["option_judge_position"], __type="s32"), - ), - E.lastplay( - E.mode(profile["lastplay_mode"], __type="s32"), - E.folder(profile["lastplay_folder"], __type="s32"), - E.mcode(profile["lastplay_mcode"], __type="s32"), - E.style(profile["lastplay_style"], __type="s32"), - E.difficulty(profile["lastplay_difficulty"], __type="s32"), - E.window_main(profile["lastplay_window_main"], __type="s32"), - E.window_sub(profile["lastplay_window_sub"], __type="s32"), - E.target(profile["lastplay_target"], __type="s32"), - E.tab_main(profile["lastplay_tab_main"], __type="s32"), - E.tab_sub(profile["lastplay_tab_sub"], __type="s32"), - E.tab_main_graph_type(profile["lastplay_tab_main_graph_type"], __type="s32"), - E.tab_main_graph_disp(profile["lastplay_tab_main_graph_disp"], __type="s32"), - E.tab_sub_graph_type(profile["lastplay_tab_sub_graph_type"], __type="s32"), - E.tab_sub_graph_disp(profile["lastplay_tab_sub_graph_disp"], __type="s32"), - ), - E.filtersort( - E.title(profile["filtersort_title"], __type="u64"), - E.version(profile["filtersort_version"], __type="u64"), - E.genre(profile["filtersort_genre"], __type="u64"), - E.bpm(profile["filtersort_bpm"], __type="u64"), - E.event(profile["filtersort_event"], __type="u64"), - E.level(profile["filtersort_level"], __type="u64"), - E.flare_rank(profile["filtersort_flare_rank"], __type="u64"), - E.clear_rank(profile["filtersort_clear_rank"], __type="u64"), - E.flare_skill_target(profile["filtersort_flare_skill_target"], __type="u64"), - E.rival_flare_skill(profile["filtersort_rival_flare_skill"], __type="u64"), - E.rival_score_rank(profile["filtersort_rival_score_rank"], __type="u64"), - E.sort_type(profile["filtersort_sort_type"], __type="u64"), - E.order_type(profile["filtersort_order_type"], __type="s32"), - E.is_quickmode(profile["filtersort_is_quickmode"], __type="bool"), - E.cleartype(profile["filtersort_cleartype"], __type="u64"), - E.difficulty(profile["filtersort_difficulty"], __type="u64"), - ), - E.checkguide( - E.tips_basic(profile["checkguide_tips_basic"], __type="u64"), - E.tips_option(profile["checkguide_tips_option"], __type="u64"), - E.tips_event(profile["checkguide_tips_event"], __type="u64"), - E.tips_gimmick(profile["checkguide_tips_gimmick"], __type="u64"), - E.tips_advance(profile["checkguide_tips_advance"], __type="u64"), - E.guide_scene(profile["checkguide_guide_scene"], __type="u64"), - ), - E.rival( - E.slot(1, __type="s32"), - E.rivalcode(profile.get("rival_1_ddr_id", 0), __type="s32"), - ), - E.rival( - E.slot(2, __type="s32"), - E.rivalcode(profile.get("rival_2_ddr_id", 0), __type="s32"), - ), - E.rival( - E.slot(3, __type="s32"), - E.rivalcode(profile.get("rival_3_ddr_id", 0), __type="s32"), - ), - *[ - E.score( - E.mcode(int(mcode), __type="s32"), - *[ - E.score_single(E.score_str(all_scores[mcode][difficulty], __type="str")) - for difficulty in all_scores[mcode] if difficulty < 5 - ], - *[ - E.score_double(E.score_str(all_scores[mcode][difficulty], __type="str")) - for difficulty in all_scores[mcode] if difficulty > 4 - ], - - ) - for mcode in all_scores.keys() - ], - E.event( - E.event_str("1,101,0,0,14,0,0", __type="str"), - ), - *[ - E.event( - E.event_str(f"{x},9999,0,0,0,0,0", __type="str"), - ) - for x in [ - e - for e in range(101, 1, -1) - if e not in [4, 6, 7, 8, 14, 47, 90] - ] - ], - #E.league(), - #E.current(), - #E.result(), - #appeal_board - E.customize( - E.category(1, __type="s32"), - E.key(100006, __type="s32"), - E.pattern(0, __type="s32"), - ), - #character_left - E.customize( - E.category(2, __type="s32"), - E.key(4, __type="s32"), - E.pattern(1, __type="s32"), - ), - #character_right - E.customize( - E.category(2, __type="s32"), - E.key(1, __type="s32"), - E.pattern(2, __type="s32"), - ), - ##game_bg_system - #E.customize( - # E.category(3, __type="s32"), - # E.key(2, __type="s32"), - # E.pattern(1, __type="s32"), - #), - ##game_bg_play - #E.customize( - # E.category(3, __type="s32"), - # E.key(24, __type="s32"), - # E.pattern(2, __type="s32"), - #), - ##lane_bg_single - #E.customize( - # E.category(4, __type="s32"), - # E.key(20, __type="s32"), - # E.pattern(0, __type="s32"), - #), - ##lane_bg_double - #E.customize( - # E.category(5, __type="s32"), - # E.key(20, __type="s32"), - # E.pattern(0, __type="s32"), - #), - ##lane_cover_single - #E.customize( - # E.category(6, __type="s32"), - # E.key(1, __type="s32"), - # E.pattern(0, __type="s32"), - #), - ##lane_cover_double - #E.customize( - # E.category(7, __type="s32"), - # E.key(1, __type="s32"), - # E.pattern(0, __type="s32"), - #), - #song_vid - E.customize( - E.category(8, __type="s32"), - E.key(2, __type="s32"), - E.pattern(0, __type="s32"), - ), - #E.brave(), - ) + E.event_str(f"{x},9999,0,0,0,0,0", __type="str"), + ) + for x in [ + e + for e in range(101, 1, -1) + if e not in [4, 6, 7, 8, 14, 47, 90] + ] + ], + #E.league(), + #E.current(), + *[ + E.customize( + E.category(c, __type="s32"), + E.key(profile["customize"][c][p], __type="s32"), + E.pattern(p, __type="s32") + ) + for c in profile.get("customize", {}) + for p in profile.get("customize", {}).get(c, {}) + ], ) + ) response_body, response_headers = await core_prepare_response(request, response) return Response(content=response_body, headers=response_headers) @@ -604,8 +381,8 @@ async def playdata_3_rivaldata_load(request: Request): for s in db.table("ddr_scores_best").search(where("ddr_id") == ddrcode): scores.append(s) - load = [] names = {} + rival_records = [] profiles = db.table("ddr_profile") for p in profiles: @@ -628,7 +405,7 @@ async def playdata_3_rivaldata_load(request: Request): diffi -= 4 else: style = 0 - load.append(f"{r["mcode"]},{style},{diffi},0,{names[r["ddr_id"]]["name"] if r["ddr_id"] in names else "UNKNOWN"},0,0,1,{r["score"]},{r["ghostid"]}") + rival_records.append(f"{r["mcode"]},{style},{diffi},0,{names[r["ddr_id"]]["name"] if r["ddr_id"] in names else "UNKNOWN"},0,0,1,{r["score"]},{r["ghostid"]}") response = E.response( E.playdata_3( @@ -637,7 +414,7 @@ async def playdata_3_rivaldata_load(request: Request): E.record( E.record_str(s, __type="str") ) - for s in load + for s in rival_records ] ) ) @@ -662,21 +439,13 @@ async def playdata_3_playerdata_new(request: Request): else: ddr_id = all_profiles_for_card["ddr_id"] tmp = {"game_version": game_version} - for k in common: - tmp["common_" + k] = 0 - for k in option: - tmp["option_" + k] = 0 - for k in lastplay: - tmp["lastplay_" + k] = 0 - for k in filtersort: - tmp["filtersort_" + k] = 0 - for k in checkguide: - tmp["checkguide_" + k] = 0 - for k in brave: - tmp["brave_" + k] = 0 + for k in load_settings: + for v in load_settings[k]: + tmp[f"{k}_" + v] = 0 tmp["rival_1_ddr_id"] = 0 tmp["rival_2_ddr_id"] = 0 tmp["rival_3_ddr_id"] = 0 + tmp["customize"] = customize_settings all_profiles_for_card["version"][str(game_version)] = tmp db.table("ddr_profile").upsert(all_profiles_for_card, where("card") == refid) @@ -712,23 +481,17 @@ async def playdata_3_playerdata_save(request: Request): if not refid.startswith("X000"): if savekind in (1, 3): - for k in common: - if k == "playcount": - game_profile["common_playcount"] += 1 - elif k.startswith("popup_subscribe"): - game_profile["common_" + k] = "0" - else: - game_profile["common_" + k] = data.find("common").find(k).text - for k in option: - game_profile["option_" + k] = data.find("option").find(k).text - for k in lastplay: - game_profile["lastplay_" + k] = data.find("lastplay").find(k).text - for k in filtersort: - game_profile["filtersort_" + k] = data.find("filtersort").find(k).text - for k in checkguide: - game_profile["checkguide_" + k] = data.find("checkguide").find(k).text - for k in brave: - game_profile["brave_" + k] = data.find("brave").find(k).text + for k in load_settings: + for v in load_settings[k]: + profile_setting = data.find(k).find(v) + if v == "playcount": + game_profile["common_playcount"] += 1 + elif v.startswith("popup_subscribe"): + game_profile["common_" + v] = "0" + elif profile_setting is not None: + game_profile[f"{k}_" + v] = profile_setting.text + if "customize" not in game_profile: + game_profile["customize"] = customize_settings profile["version"][str(game_version)] = game_profile get_db().table("ddr_profile").upsert(profile, where("card") == refid) @@ -855,6 +618,7 @@ async def playdata_3_ghostdata_load(request: Request): data = request_info["root"][0].find("data") ghostid = int(data.find("ghostid").text) + record = get_db().table("ddr_scores").get(doc_id=ghostid) response = E.response( From a3f559f5a4fb3ad14437a0fc0eb3dbfc704f681b Mon Sep 17 00:00:00 2001 From: drmext <71258889+drmext@users.noreply.github.com> Date: Fri, 13 Mar 2026 13:31:24 +0000 Subject: [PATCH 04/11] Fix --- modules/ddr/playdata_3.py | 102 +++++++++++++++++++++++++------------- 1 file changed, 67 insertions(+), 35 deletions(-) diff --git a/modules/ddr/playdata_3.py b/modules/ddr/playdata_3.py index 564fde9..a1d25df 100644 --- a/modules/ddr/playdata_3.py +++ b/modules/ddr/playdata_3.py @@ -10,6 +10,9 @@ from fastapi import APIRouter, Request, Response from core_common import core_process_request, core_prepare_response, E from core_database import get_db +from os import path +import json + router = APIRouter(prefix="/local2", tags=["local2"]) router.model_whitelist = ["MDX"] @@ -24,6 +27,19 @@ def get_game_profile(cid, game_version): return profile["version"].get(str(game_version), None) +mdb = {} +ddr_metadata = path.join("webui", "ddr.json") +if path.exists(ddr_metadata): + with open(ddr_metadata, "r", encoding="utf-8") as fp: + mdb = json.load(fp) + + music_load = [] + for i in sorted(list(mdb.keys()), reverse=True): + info = mdb[i] + for idx, lvl in enumerate(info["diffLv"]): + if int(lvl) != 0: + music_load.append(f"{i},{1 if idx > 4 else 0},{idx % 5},0,{lvl}") + load_settings = { "common": { "dancername": "str", @@ -118,47 +134,63 @@ load_settings = { } customize_settings = { - "1": { - "0": -1, #appeal_board - }, - "2": { - "1": -1, #character_left - "2": -1, #character_right - }, - "3": { - "1": -1, #game_bg_system - "2": -1, #game_bg_play - }, - "4": { - "0": -1, #lane_bg_single - }, - "5": { - "0": -1, #lane_bg_double - }, - "6": { - "0": -1, #lane_cover_single - }, - "7": { - "0": -1, #lane_cover_double - }, - "8": { - "0": -1, #song_vid - }, - } + "1": { + "0": -1, #appeal_board + }, + "2": { + "1": -1, #character_left + "2": -1, #character_right + }, + "3": { + "1": -1, #game_bg_system + "2": -1, #game_bg_play + }, + "4": { + "0": -1, #lane_bg_single + }, + "5": { + "0": -1, #lane_bg_double + }, + "6": { + "0": -1, #lane_cover_single + }, + "7": { + "0": -1, #lane_cover_double + }, + "8": { + "0": -1, #song_vid + }, +} @router.post("/{gameinfo}/playdata_3/musicdata_load") async def playdata_3_musicdata_load(request: Request): request_info = await core_process_request(request) - response = E.response( - E.playdata_3( - E.result(0, __type="s32"), - E.servertime(round(time.time() * 1000), __type="u64"), - E.music( - E.music_str("", __type="str"), - ), + if mdb: + response = E.response( + E.playdata_3( + E.result(0, __type="s32"), + E.servertime(round(time.time() * 1000), __type="u64"), + *[ + E.music( + E.music_str(s, __type="str"), + ) + for s in music_load + ], + ) + ) + + + else: + response = E.response( + E.playdata_3( + E.result(0, __type="s32"), + E.servertime(round(time.time() * 1000), __type="u64"), + E.music( + E.music_str("", __type="str"), + ), + ) ) - ) response_body, response_headers = await core_prepare_response(request, response) return Response(content=response_body, headers=response_headers) From a74de5ffa5aedb30820880350e44e205280c6f9c Mon Sep 17 00:00:00 2001 From: drmext <71258889+drmext@users.noreply.github.com> Date: Sat, 14 Mar 2026 23:57:02 +0000 Subject: [PATCH 05/11] Fix --- modules/ddr/playdata_3.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/ddr/playdata_3.py b/modules/ddr/playdata_3.py index a1d25df..ef38e3f 100644 --- a/modules/ddr/playdata_3.py +++ b/modules/ddr/playdata_3.py @@ -501,6 +501,7 @@ async def playdata_3_playerdata_save(request: Request): request_info = await core_process_request(request) game_version = request_info["game_version"] + retrycnt = int(request_info["root"][0].find("retrycnt").text) data = request_info["root"][0].find("data") refid = data.find("refid").text @@ -527,7 +528,7 @@ async def playdata_3_playerdata_save(request: Request): profile["version"][str(game_version)] = game_profile get_db().table("ddr_profile").upsert(profile, where("card") == refid) - elif savekind == 2: + elif savekind == 2 and retrycnt == 0: timestamp = time.time() ddr_id = int(data.find("common").find("ddrcode").text) From 327a982742543a34bc81e6c2013ae2cc8e998f7f Mon Sep 17 00:00:00 2001 From: drmext <71258889+drmext@users.noreply.github.com> Date: Thu, 19 Mar 2026 11:33:15 +0000 Subject: [PATCH 06/11] Fix --- modules/ddr/playdata_3.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/ddr/playdata_3.py b/modules/ddr/playdata_3.py index ef38e3f..f59f35b 100644 --- a/modules/ddr/playdata_3.py +++ b/modules/ddr/playdata_3.py @@ -217,12 +217,13 @@ async def playdata_3_playerdata_load(request: Request): for record in get_db().table("ddr_scores_best").search(where("ddr_id") == ddr_id): mcode = record["mcode"] difficulty = record["difficulty"] + flare = record.get("flare_force", 0) if mcode not in all_scores: all_scores[mcode] = {} if difficulty > 4: - all_scores[mcode][difficulty] = f"{int(difficulty) - 4},1,{record["rank"]},{record["lamp"]},{record["score"]},{record["ghostid"]},0,0,0" + all_scores[mcode][difficulty] = f"{int(difficulty) - 4},1,{record["rank"]},{record["lamp"]},{record["score"]},{record["ghostid"]},{flare},{flare},0" else: - all_scores[mcode][difficulty] = f"{difficulty},1,{record["rank"]},{record["lamp"]},{record["score"]},{record["ghostid"]},0,0,0" + all_scores[mcode][difficulty] = f"{difficulty},1,{record["rank"]},{record["lamp"]},{record["score"]},{record["ghostid"]},{flare},{flare},0" else: p = {} profile = {} @@ -560,6 +561,7 @@ async def playdata_3_playerdata_save(request: Request): calorie = int(n.find("calorie").text) ghostsize = int(n.find("ghostsize").text) ghost = n.find("ghost").text + flare_force = int(n.find("flare_force").text) db.table("ddr_scores").insert( { @@ -590,6 +592,7 @@ async def playdata_3_playerdata_save(request: Request): "calorie": calorie, "ghostsize": ghostsize, "ghost": ghost, + "flare_force": flare_force, }, ) @@ -610,6 +613,7 @@ async def playdata_3_playerdata_save(request: Request): "lamp": max(lamp, best.get("lamp", lamp)), "score": max(score, best.get("score", score)), "exscore": max(exscore, best.get("exscore", exscore)), + "flare_force": max(flare_force, best.get("flare_force", flare_force)), } ghostid = db.table("ddr_scores").get( From 9fa4ba7a1128a03dddcee59da8a0d77772e4841d Mon Sep 17 00:00:00 2001 From: drmext <71258889+drmext@users.noreply.github.com> Date: Fri, 20 Mar 2026 15:31:45 +0000 Subject: [PATCH 07/11] Fix --- modules/ddr/playdata_3.py | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/modules/ddr/playdata_3.py b/modules/ddr/playdata_3.py index f59f35b..38b4f35 100644 --- a/modules/ddr/playdata_3.py +++ b/modules/ddr/playdata_3.py @@ -162,6 +162,19 @@ customize_settings = { }, } +flares = [ + (995000, 10), + (990000, 9), + (980000, 8), + (970000, 7), + (960000, 6), + (955000, 5), + (930000, 4), + (900000, 3), + (850000, 2), + (800000, 1), +] + @router.post("/{gameinfo}/playdata_3/musicdata_load") async def playdata_3_musicdata_load(request: Request): request_info = await core_process_request(request) @@ -216,14 +229,22 @@ async def playdata_3_playerdata_load(request: Request): for record in get_db().table("ddr_scores_best").search(where("ddr_id") == ddr_id): mcode = record["mcode"] - difficulty = record["difficulty"] - flare = record.get("flare_force", 0) + difficulty = int(record["difficulty"]) + score = int(record["score"]) + flare = int(record.get("flare_force", 0)) + if flare == 0: + for k, v in flares: + if score >= k: + flare = v + break + if mcode not in all_scores: all_scores[mcode] = {} - if difficulty > 4: - all_scores[mcode][difficulty] = f"{int(difficulty) - 4},1,{record["rank"]},{record["lamp"]},{record["score"]},{record["ghostid"]},{flare},{flare},0" - else: - all_scores[mcode][difficulty] = f"{difficulty},1,{record["rank"]},{record["lamp"]},{record["score"]},{record["ghostid"]},{flare},{flare},0" + all_scores[mcode][difficulty] = ( + f"{difficulty - 4 if difficulty > 4 else difficulty}," + f"1,{record['rank']},{record['lamp']},{score}," + f"{record['ghostid']},{flare},{flare},0" + ) else: p = {} profile = {} From 965d0b561af38dfcb2a3964219dafa9c5bc4fd52 Mon Sep 17 00:00:00 2001 From: drmext <71258889+drmext@users.noreply.github.com> Date: Sat, 21 Mar 2026 18:52:41 +0000 Subject: [PATCH 08/11] Fix --- modules/ddr/playdata_3.py | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/ddr/playdata_3.py b/modules/ddr/playdata_3.py index 38b4f35..d54f26b 100644 --- a/modules/ddr/playdata_3.py +++ b/modules/ddr/playdata_3.py @@ -84,6 +84,7 @@ load_settings = { "combo_priority": "s32", "judge_priority": "s32", "judge_position": "s32", + "timing_music": "s32", }, "lastplay": { "mode": "s32", From cb98a20d2278d7000faff0a338cb9d120822bced Mon Sep 17 00:00:00 2001 From: drmext <71258889+drmext@users.noreply.github.com> Date: Sun, 22 Mar 2026 01:08:26 +0000 Subject: [PATCH 09/11] Fix --- modules/ddr/playdata_3.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/modules/ddr/playdata_3.py b/modules/ddr/playdata_3.py index d54f26b..519e239 100644 --- a/modules/ddr/playdata_3.py +++ b/modules/ddr/playdata_3.py @@ -691,3 +691,19 @@ async def playdata_3_ghostdata_load(request: Request): response_body, response_headers = await core_prepare_response(request, response) return Response(content=response_body, headers=response_headers) + +@router.post("/{gameinfo}/playdata_3/mergeddata_load") +async def playdata_3_mergeddata_load(request: Request): + request_info = await core_process_request(request) + + response = E.response( + E.playdata_3( + E.result(0, __type="s32"), + E.league_class(0, __type="s32"), + E.is_advance_border_exceeded(0, __type="bool"), + E.is_exists_subscribed_user(0, __type="bool"), + ) + ) + + response_body, response_headers = await core_prepare_response(request, response) + return Response(content=response_body, headers=response_headers) From 57d050273e3986eaee6772f99bfa5739abdd4553 Mon Sep 17 00:00:00 2001 From: drmext <71258889+drmext@users.noreply.github.com> Date: Tue, 24 Mar 2026 22:50:16 +0000 Subject: [PATCH 10/11] Fix --- activate-venv.bat | 5 ++ modules/ddr/playdata_3.py | 7 +- modules/ddr/playerdata.py | 2 + modules/ddr/playerdata_2.py | 2 + utils/db/README.md | 4 +- utils/db/import_ddr_spice_automap.py | 96 ++++++++++++++++++---------- 6 files changed, 78 insertions(+), 38 deletions(-) create mode 100644 activate-venv.bat diff --git a/activate-venv.bat b/activate-venv.bat new file mode 100644 index 0000000..e81c3ed --- /dev/null +++ b/activate-venv.bat @@ -0,0 +1,5 @@ +@echo off + +cd /d %~dp0 + +start .venv\Scripts\activate.bat diff --git a/modules/ddr/playdata_3.py b/modules/ddr/playdata_3.py index 519e239..36b442c 100644 --- a/modules/ddr/playdata_3.py +++ b/modules/ddr/playdata_3.py @@ -233,7 +233,7 @@ async def playdata_3_playerdata_load(request: Request): difficulty = int(record["difficulty"]) score = int(record["score"]) flare = int(record.get("flare_force", 0)) - if flare == 0: + if flare in (-1, 0): for k, v in flares: if score >= k: flare = v @@ -644,7 +644,10 @@ async def playdata_3_playerdata_save(request: Request): & (where("difficulty") == difficulty) & (where("score") == max(score, best.get("score", score))) ) - best_score_data["ghostid"] = ghostid.doc_id + if ghostid == None: + best_score_data["ghostid"] = -1 + else: + best_score_data["ghostid"] = ghostid.doc_id db.table("ddr_scores_best").upsert( best_score_data, diff --git a/modules/ddr/playerdata.py b/modules/ddr/playerdata.py index 3c74f1a..dbcbcdb 100644 --- a/modules/ddr/playerdata.py +++ b/modules/ddr/playerdata.py @@ -58,6 +58,8 @@ async def playerdata_usergamedata_advanced(request: Request): if "ddr_id" not in all_profiles_for_card: ddr_id = random.randint(10000000, 99999999) all_profiles_for_card["ddr_id"] = ddr_id + else: + ddr_id = all_profiles_for_card["ddr_id"] all_profiles_for_card["version"][str(game_version)] = { "game_version": game_version, diff --git a/modules/ddr/playerdata_2.py b/modules/ddr/playerdata_2.py index 100a5f7..1d6eac1 100644 --- a/modules/ddr/playerdata_2.py +++ b/modules/ddr/playerdata_2.py @@ -58,6 +58,8 @@ async def playerdata_2_usergamedata_advanced(request: Request): if "ddr_id" not in all_profiles_for_card: ddr_id = random.randint(10000000, 99999999) all_profiles_for_card["ddr_id"] = ddr_id + else: + ddr_id = all_profiles_for_card["ddr_id"] all_profiles_for_card["version"][str(game_version)] = { "game_version": game_version, diff --git a/utils/db/README.md b/utils/db/README.md index ceda409..0f86231 100644 --- a/utils/db/README.md +++ b/utils/db/README.md @@ -29,9 +29,9 @@ Instructions: ### [import_ddr_spice_automap.py](import_ddr_spice_automap.py) -Example: `python utils\db\import_ddr_spice_automap.py --automap_xml automap_0.xml --version 19 --monkey_db db.json --ddr_id 12345678` +Example: `python utils\db\import_ddr_spice_automap.py --automap_xml automap_0.xml --version 3 --monkey_db db.json --ddr_id 12345678` -- `--version` 19 for A20P or 20 for A3 +- `--version` 1 for A20P, 2 for A3, 3 for WORLD (automap source version, not destination) - `--ddr_id` destination profile in db.json diff --git a/utils/db/import_ddr_spice_automap.py b/utils/db/import_ddr_spice_automap.py index 2f45a88..b6e0413 100644 --- a/utils/db/import_ddr_spice_automap.py +++ b/utils/db/import_ddr_spice_automap.py @@ -24,43 +24,72 @@ def main(automap_xml, version, monkey_db, ddr_id): if profile == None: raise SystemExit(f"ERROR: DDR profile {ddr_id} not in {monkey_db}") - game_version = 19 - if profile["version"].get(str(game_version), None) == None: - raise SystemExit( - f"ERROR: DDR profile {ddr_id} version {game_version} not in {monkey_db}" - ) - - scores = [] with open(automap_xml, "rb") as fp: automap_0 = fp.read().split(b"\n\n") - if version == 19: - playerdata = "playerdata" - else: + if version == 3: + playerdata = "playdata_3" + game_version = 20 + elif version == 2: playerdata = "playerdata_2" + game_version = 19 + elif version == 1: + playerdata = "playerdata" + game_version = 19 + scores = [] scores_xml = False for xml in automap_0: - tree = ET.ElementTree(ET.fromstring(xml.decode(encoding="shift-jis"))) - root = tree.getroot() - if scores_xml: - for music in root.findall(f"{playerdata}/music"): - mcode = int(music.find("mcode").text) - for difficulty, chart in enumerate(music.findall("note")): - if int(chart.find("count").text) > 0: - rank = int(chart.find("rank").text) - clearkind = int(chart.find("clearkind").text) - score = int(chart.find("score").text) + try: + tree = ET.ElementTree(ET.fromstring(xml.decode(encoding="shift-jis"))) + root = tree.getroot() + except: + continue + if version in (1, 2): + if scores_xml: + for music in root.findall(f"{playerdata}/music"): + mcode = int(music.find("mcode").text) + for difficulty, chart in enumerate(music.findall("note")): + c = chart.find("count") + if c == None: + continue + if int(c.text) > 0: + rank = int(chart.find("rank").text) + clearkind = int(chart.find("clearkind").text) + score = int(chart.find("score").text) + scores.append([mcode, difficulty, rank, clearkind, score, -1]) + break + else: + try: + if root.find(f"{playerdata}/data/mode").text == "userload": + if len(root.find(f"{playerdata}/data/refid").text) == 16: + scores_xml = True + except AttributeError: + continue + elif version == 3: + if scores_xml: + for music in root.findall(f"{playerdata}/score"): + mcode = int(music.find("mcode").text) + for x in music.findall("score_single") + music.findall("score_double"): + s = x.find("score_str").text.split(",") + s = [int(val) for val in s] + difficulty = s[0] + 4 if x.tag == "score_double" else s[0] + rank = s[2] + clearkind = s[3] + score = s[4] + # flare = s[6] scores.append([mcode, difficulty, rank, clearkind, score]) - break - else: - try: - if root.find(f"{playerdata}/data/mode").text == "userload": - if len(root.find(f"{playerdata}/data/refid").text) == 16: - scores_xml = True - except AttributeError: - continue + break + else: + try: + a = root.find(f"{playerdata}") + if "method" in a.attrib: + if a.attrib["method"] == "playerdata_load": + if len(root.find(f"{playerdata}/data/refid").text) == 16: + scores_xml = True + except AttributeError: + continue total_count = len(scores) @@ -73,6 +102,7 @@ def main(automap_xml, version, monkey_db, ddr_id): rank = s[2] lamp = s[3] score = s[4] + # flare = s[5] exscore = 0 print( @@ -81,7 +111,6 @@ def main(automap_xml, version, monkey_db, ddr_id): best = db.table("ddr_scores_best").get( (where("ddr_id") == ddr_id) - & (where("game_version") == game_version) & (where("mcode") == mcode) & (where("difficulty") == difficulty) ) @@ -97,14 +126,14 @@ def main(automap_xml, version, monkey_db, ddr_id): "lamp": max(lamp, best.get("lamp", lamp)), "score": max(score, best.get("score", score)), "exscore": max(exscore, best.get("exscore", exscore)), + # "flare_force": max(flare, best.get("flare_force", flare)), } ghostid = db.table("ddr_scores").get( (where("ddr_id") == ddr_id) - & (where("game_version") == game_version) & (where("mcode") == mcode) & (where("difficulty") == difficulty) - & (where("exscore") == best.get("exscore", exscore)) + & (where("score") == max(score, best.get("score", score))) ) if ghostid: best_score_data["ghostid"] = ghostid.doc_id @@ -114,7 +143,6 @@ def main(automap_xml, version, monkey_db, ddr_id): db.table("ddr_scores_best").upsert( best_score_data, (where("ddr_id") == ddr_id) - & (where("game_version") == game_version) & (where("mcode") == mcode) & (where("difficulty") == difficulty), ) @@ -129,8 +157,8 @@ if __name__ == "__main__": parser.add_argument("--automap_xml", help="Input xml file", required=True) parser.add_argument( "--version", - help="19 is A20P, 20 is A3", - default=19, + help="1=A20P, 2=A3, 3=WORLD (automap_xml source version, not destination)", + default=3, type=int, ) parser.add_argument("--monkey_db", help="Output json file", required=True) From 08cf21d73048f9a4806bb1638f8d24498dcc8735 Mon Sep 17 00:00:00 2001 From: drmext <71258889+drmext@users.noreply.github.com> Date: Wed, 25 Mar 2026 02:30:24 +0000 Subject: [PATCH 11/11] Fix --- README.md | 2 +- modules/ddr/api.py | 107 ++++++++++++++++++++++-------------- modules/ddr/playdata_3.py | 6 +- modules/ddr/playerdata.py | 5 +- modules/ddr/playerdata_2.py | 5 +- 5 files changed, 78 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 84b66ed..66e5058 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Run [start.bat (Windows)](start.bat) or [start.sh (Linux, MacOS)](start.sh) ## Playable Games - IIDX 18-20, 29-33 (Online Arena/BPL support) -- DDR A20P, A3, World (OmniMIX/GF, BPL, and [Fake PFREE](https://github.com/drmext/BemaniPatcher/blob/nopr/ddra3.html#L133) support) +- DDR A20P, A3, WORLD (OmniMIX/GF, BPL, and [Fake PFREE](https://github.com/drmext/BemaniPatcher/blob/nopr/ddra3.html#L133) support) - GD 6-10 DELTA (Battle Mode support) - DRS - NOST 3 diff --git a/modules/ddr/api.py b/modules/ddr/api.py index 3be10d8..202d449 100644 --- a/modules/ddr/api.py +++ b/modules/ddr/api.py @@ -13,7 +13,7 @@ from utils.lz77 import lz77_decode import lxml.etree as ET import json import struct -from typing import Dict, List, Tuple +from typing import Optional, Dict, List, Tuple from os import path @@ -25,26 +25,35 @@ class DDR_Profile_Main_Items(BaseModel): pin: str -class DDR_Profile_Version_Items(BaseModel): - game_version: int - calories_disp: bool - character: str - arrow_skin: str - filter: str - guideline: str - priority: str - timing_disp: bool - common: str - option: str - last: str - rival: str - rival_1_ddr_id: int - rival_2_ddr_id: int - rival_3_ddr_id: int - single_grade: int - double_grade: int +class DDR_Profile_19_Items(BaseModel): + game_version: Optional[int] + calories_disp: Optional[bool] + character: Optional[str] + arrow_skin: Optional[str] + filter: Optional[str] + guideline: Optional[str] + priority: Optional[str] + timing_disp: Optional[bool] + common: Optional[str] + option: Optional[str] + last: Optional[str] + rival: Optional[str] + rival_1_ddr_id: Optional[int] + rival_2_ddr_id: Optional[int] + rival_3_ddr_id: Optional[int] + single_grade: Optional[int] + double_grade: Optional[int] +class DDR_Profile_20_Items(BaseModel): + game_version: Optional[int] + common_dancername: Optional[str] + common_area: Optional[str] + rival_1_ddr_id: Optional[int] + rival_2_ddr_id: Optional[int] + rival_3_ddr_id: Optional[int] + customize: Optional[dict] + @router.get("/profiles") async def ddr_profiles(): return get_db().table("ddr_profile").all() @@ -68,32 +77,48 @@ async def ddr_profile_id_patch(ddr_id: str, item: DDR_Profile_Main_Items): return Response(status_code=204) -@router.patch("/profiles/{ddr_id}/{version}") -async def ddr_profile_id_version_patch( - ddr_id: str, version: int, item: DDR_Profile_Version_Items -): +@router.patch("/profiles/{ddr_id}/19") +async def ddr_profile_id_19_patch(ddr_id: str, item: DDR_Profile_19_Items): ddr_id = int("".join([i for i in ddr_id if i.isnumeric()])) profile = get_db().table("ddr_profile").get(where("ddr_id") == ddr_id) - game_profile = profile["version"].get(str(version), {}) + game_profile = profile["version"].get("19", {}) - if version >= 19: - game_profile["game_version"] = item.game_version - game_profile["calories_disp"] = "On" if item.calories_disp else "Off" - game_profile["character"] = item.character - game_profile["arrow_skin"] = item.arrow_skin - game_profile["filter"] = item.filter - game_profile["guideline"] = item.guideline - game_profile["priority"] = item.priority - game_profile["timing_disp"] = "On" if item.timing_disp else "Off" - game_profile["common"] = item.common - game_profile["option"] = item.option - game_profile["last"] = item.last - game_profile["rival"] = item.rival - game_profile["rival_1_ddr_id"] = item.rival_1_ddr_id - game_profile["rival_2_ddr_id"] = item.rival_2_ddr_id - game_profile["rival_3_ddr_id"] = item.rival_3_ddr_id + game_profile["game_version"] = item.game_version + game_profile["calories_disp"] = "On" if item.calories_disp else "Off" + game_profile["character"] = item.character + game_profile["arrow_skin"] = item.arrow_skin + game_profile["filter"] = item.filter + game_profile["guideline"] = item.guideline + game_profile["priority"] = item.priority + game_profile["timing_disp"] = "On" if item.timing_disp else "Off" + game_profile["common"] = item.common + game_profile["option"] = item.option + game_profile["last"] = item.last + game_profile["rival"] = item.rival + game_profile["rival_1_ddr_id"] = item.rival_1_ddr_id + game_profile["rival_2_ddr_id"] = item.rival_2_ddr_id + game_profile["rival_3_ddr_id"] = item.rival_3_ddr_id - profile["version"][str(version)] = game_profile + profile["version"]["19"] = game_profile + get_db().table("ddr_profile").upsert(profile, where("ddr_id") == ddr_id) + return Response(status_code=204) + + +@router.patch("/profiles/{ddr_id}/20") +async def ddr_profile_id_20_patch(ddr_id: str, item: DDR_Profile_20_Items): + ddr_id = int("".join([i for i in ddr_id if i.isnumeric()])) + profile = get_db().table("ddr_profile").get(where("ddr_id") == ddr_id) + game_profile = profile["version"].get("20", {}) + + game_profile["game_version"] = item.game_version + game_profile["common_dancername"] = item.common_dancername + game_profile["common_area"] = item.common_area + game_profile["rival_1_ddr_id"] = item.rival_1_ddr_id + game_profile["rival_2_ddr_id"] = item.rival_2_ddr_id + game_profile["rival_3_ddr_id"] = item.rival_3_ddr_id + game_profile["customize"] = item.customize + + profile["version"]["20"] = game_profile get_db().table("ddr_profile").upsert(profile, where("ddr_id") == ddr_id) return Response(status_code=204) diff --git a/modules/ddr/playdata_3.py b/modules/ddr/playdata_3.py index 36b442c..6a7902a 100644 --- a/modules/ddr/playdata_3.py +++ b/modules/ddr/playdata_3.py @@ -644,10 +644,10 @@ async def playdata_3_playerdata_save(request: Request): & (where("difficulty") == difficulty) & (where("score") == max(score, best.get("score", score))) ) - if ghostid == None: - best_score_data["ghostid"] = -1 - else: + if ghostid != None: best_score_data["ghostid"] = ghostid.doc_id + else: + best_score_data["ghostid"] = -1 db.table("ddr_scores_best").upsert( best_score_data, diff --git a/modules/ddr/playerdata.py b/modules/ddr/playerdata.py index dbcbcdb..adc52ca 100644 --- a/modules/ddr/playerdata.py +++ b/modules/ddr/playerdata.py @@ -341,7 +341,10 @@ async def playerdata_usergamedata_advanced(request: Request): & (where("difficulty") == difficulty) & (where("score") == max(score, best.get("score", score))) ) - best_score_data["ghostid"] = ghostid.doc_id + if ghostid != None: + best_score_data["ghostid"] = ghostid.doc_id + else: + best_score_data["ghostid"] = -1 db.table("ddr_scores_best").upsert( best_score_data, diff --git a/modules/ddr/playerdata_2.py b/modules/ddr/playerdata_2.py index 1d6eac1..7645069 100644 --- a/modules/ddr/playerdata_2.py +++ b/modules/ddr/playerdata_2.py @@ -399,7 +399,10 @@ async def playerdata_2_usergamedata_advanced(request: Request): & (where("difficulty") == difficulty) & (where("score") == max(score, best.get("score", score))) ) - best_score_data["ghostid"] = ghostid.doc_id + if ghostid != None: + best_score_data["ghostid"] = ghostid.doc_id + else: + best_score_data["ghostid"] = -1 db.table("ddr_scores_best").upsert( best_score_data,