forked from Hay1tsme/artemis
chuni: fix encrypted hash, update unlock challenge req
This commit is contained in:
@ -1,20 +1,22 @@
|
|||||||
from starlette.requests import Request
|
import asyncio
|
||||||
from starlette.routing import Route
|
import re
|
||||||
from starlette.responses import Response
|
|
||||||
import logging
|
import logging
|
||||||
import coloredlogs
|
import coloredlogs
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
|
||||||
import zlib
|
import zlib
|
||||||
import yaml
|
import yaml
|
||||||
import json
|
import json
|
||||||
import inflection
|
import inflection
|
||||||
import string
|
import string
|
||||||
|
from os import path
|
||||||
|
from typing import Tuple, Dict, List
|
||||||
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
|
from starlette.requests import Request
|
||||||
|
from starlette.routing import Route
|
||||||
|
from starlette.responses import Response
|
||||||
from Crypto.Cipher import AES
|
from Crypto.Cipher import AES
|
||||||
from Crypto.Util.Padding import pad
|
from Crypto.Util.Padding import pad
|
||||||
from Crypto.Protocol.KDF import PBKDF2
|
from Crypto.Protocol.KDF import PBKDF2
|
||||||
from Crypto.Hash import SHA1
|
from Crypto.Hash import SHA1
|
||||||
from os import path
|
|
||||||
from typing import Tuple, Dict, List
|
|
||||||
|
|
||||||
from core import CoreConfig, Utils
|
from core import CoreConfig, Utils
|
||||||
from core.title import BaseServlet
|
from core.title import BaseServlet
|
||||||
@ -39,6 +41,7 @@ from .luminous import ChuniLuminous
|
|||||||
from .luminousplus import ChuniLuminousPlus
|
from .luminousplus import ChuniLuminousPlus
|
||||||
from .verse import ChuniVerse
|
from .verse import ChuniVerse
|
||||||
|
|
||||||
|
|
||||||
class ChuniServlet(BaseServlet):
|
class ChuniServlet(BaseServlet):
|
||||||
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
||||||
super().__init__(core_cfg, cfg_dir)
|
super().__init__(core_cfg, cfg_dir)
|
||||||
@ -98,15 +101,15 @@ class ChuniServlet(BaseServlet):
|
|||||||
|
|
||||||
known_iter_counts = {
|
known_iter_counts = {
|
||||||
ChuniConstants.VER_CHUNITHM_CRYSTAL_PLUS: 67,
|
ChuniConstants.VER_CHUNITHM_CRYSTAL_PLUS: 67,
|
||||||
f"{ChuniConstants.VER_CHUNITHM_CRYSTAL_PLUS}_int": 25, # SUPERSTAR
|
f"{ChuniConstants.VER_CHUNITHM_CRYSTAL_PLUS}_int": 25, # SUPERSTAR
|
||||||
ChuniConstants.VER_CHUNITHM_PARADISE: 44,
|
ChuniConstants.VER_CHUNITHM_PARADISE: 44,
|
||||||
f"{ChuniConstants.VER_CHUNITHM_PARADISE}_int": 51, # SUPERSTAR PLUS
|
f"{ChuniConstants.VER_CHUNITHM_PARADISE}_int": 51, # SUPERSTAR PLUS
|
||||||
ChuniConstants.VER_CHUNITHM_NEW: 54,
|
ChuniConstants.VER_CHUNITHM_NEW: 54,
|
||||||
f"{ChuniConstants.VER_CHUNITHM_NEW}_int": 49,
|
f"{ChuniConstants.VER_CHUNITHM_NEW}_int": 49,
|
||||||
f"{ChuniConstants.VER_CHUNITHM_NEW}_chn": 37,
|
f"{ChuniConstants.VER_CHUNITHM_NEW}_chn": 37,
|
||||||
ChuniConstants.VER_CHUNITHM_NEW_PLUS: 25,
|
ChuniConstants.VER_CHUNITHM_NEW_PLUS: 25,
|
||||||
f"{ChuniConstants.VER_CHUNITHM_NEW_PLUS}_int": 31,
|
f"{ChuniConstants.VER_CHUNITHM_NEW_PLUS}_int": 31,
|
||||||
f"{ChuniConstants.VER_CHUNITHM_NEW_PLUS}_chn": 35, # NEW
|
f"{ChuniConstants.VER_CHUNITHM_NEW_PLUS}_chn": 35, # NEW
|
||||||
ChuniConstants.VER_CHUNITHM_SUN: 70,
|
ChuniConstants.VER_CHUNITHM_SUN: 70,
|
||||||
f"{ChuniConstants.VER_CHUNITHM_SUN}_int": 35,
|
f"{ChuniConstants.VER_CHUNITHM_SUN}_int": 35,
|
||||||
ChuniConstants.VER_CHUNITHM_SUN_PLUS: 36,
|
ChuniConstants.VER_CHUNITHM_SUN_PLUS: 36,
|
||||||
@ -126,7 +129,7 @@ class ChuniServlet(BaseServlet):
|
|||||||
version_idx = version
|
version_idx = version
|
||||||
else:
|
else:
|
||||||
version_idx = int(version.split("_")[0])
|
version_idx = int(version.split("_")[0])
|
||||||
|
|
||||||
salt = bytes.fromhex(keys[2])
|
salt = bytes.fromhex(keys[2])
|
||||||
|
|
||||||
if len(keys) >= 4:
|
if len(keys) >= 4:
|
||||||
@ -156,12 +159,9 @@ class ChuniServlet(BaseServlet):
|
|||||||
and version_idx >= ChuniConstants.VER_CHUNITHM_NEW
|
and version_idx >= ChuniConstants.VER_CHUNITHM_NEW
|
||||||
):
|
):
|
||||||
method_fixed += "C3Exp"
|
method_fixed += "C3Exp"
|
||||||
elif (
|
elif isinstance(version, str) and version.endswith("_chn"):
|
||||||
isinstance(version, str)
|
|
||||||
and version.endswith("_chn")
|
|
||||||
):
|
|
||||||
method_fixed += "Chn"
|
method_fixed += "Chn"
|
||||||
|
|
||||||
hash = PBKDF2(
|
hash = PBKDF2(
|
||||||
method_fixed,
|
method_fixed,
|
||||||
salt,
|
salt,
|
||||||
@ -170,7 +170,8 @@ class ChuniServlet(BaseServlet):
|
|||||||
hmac_hash_module=SHA1,
|
hmac_hash_module=SHA1,
|
||||||
)
|
)
|
||||||
|
|
||||||
hashed_name = hash.hex()[:32] # truncate unused bytes like the game does
|
# truncate unused bytes like the game does
|
||||||
|
hashed_name = hash.hex()[:32]
|
||||||
self.hash_table[version][hashed_name] = method_fixed
|
self.hash_table[version][hashed_name] = method_fixed
|
||||||
|
|
||||||
self.logger.debug(
|
self.logger.debug(
|
||||||
@ -192,7 +193,9 @@ class ChuniServlet(BaseServlet):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
|
def get_allnet_info(
|
||||||
|
self, game_code: str, game_ver: int, keychip: str
|
||||||
|
) -> Tuple[str, str]:
|
||||||
title_port_int = Utils.get_title_port(self.core_cfg)
|
title_port_int = Utils.get_title_port(self.core_cfg)
|
||||||
title_port_ssl_int = Utils.get_title_port_ssl(self.core_cfg)
|
title_port_ssl_int = Utils.get_title_port_ssl(self.core_cfg)
|
||||||
|
|
||||||
@ -206,7 +209,7 @@ class ChuniServlet(BaseServlet):
|
|||||||
|
|
||||||
if proto == "https":
|
if proto == "https":
|
||||||
t_port = f":{title_port_ssl_int}" if title_port_ssl_int != 443 else ""
|
t_port = f":{title_port_ssl_int}" if title_port_ssl_int != 443 else ""
|
||||||
else:
|
else:
|
||||||
t_port = f":{title_port_int}" if title_port_int != 80 else ""
|
t_port = f":{title_port_int}" if title_port_int != 80 else ""
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -216,8 +219,16 @@ class ChuniServlet(BaseServlet):
|
|||||||
|
|
||||||
def get_routes(self) -> List[Route]:
|
def get_routes(self) -> List[Route]:
|
||||||
return [
|
return [
|
||||||
Route("/{game:str}/{version:int}/ChuniServlet/{endpoint:str}", self.render_POST, methods=['POST']),
|
Route(
|
||||||
Route("/{game:str}/{version:int}/ChuniServlet/MatchingServer/{endpoint:str}", self.render_POST, methods=['POST']),
|
"/{game:str}/{version:int}/ChuniServlet/{endpoint:str}",
|
||||||
|
self.render_POST,
|
||||||
|
methods=["POST"],
|
||||||
|
),
|
||||||
|
Route(
|
||||||
|
"/{game:str}/{version:int}/ChuniServlet/MatchingServer/{endpoint:str}",
|
||||||
|
self.render_POST,
|
||||||
|
methods=["POST"],
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
async def render_POST(self, request: Request) -> bytes:
|
async def render_POST(self, request: Request) -> bytes:
|
||||||
@ -234,67 +245,71 @@ class ChuniServlet(BaseServlet):
|
|||||||
internal_ver = 0
|
internal_ver = 0
|
||||||
client_ip = Utils.get_ip_addr(request)
|
client_ip = Utils.get_ip_addr(request)
|
||||||
|
|
||||||
if game_code == "SDHD" or game_code == "SDBT": # JP
|
if game_code == "SDHD" or game_code == "SDBT": # JP
|
||||||
if version < 105: # 1.0
|
if version < 105: # 1.0
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM
|
internal_ver = ChuniConstants.VER_CHUNITHM
|
||||||
elif version >= 105 and version < 110: # PLUS
|
elif version >= 105 and version < 110: # PLUS
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_PLUS
|
internal_ver = ChuniConstants.VER_CHUNITHM_PLUS
|
||||||
elif version >= 110 and version < 115: # AIR
|
elif version >= 110 and version < 115: # AIR
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_AIR
|
internal_ver = ChuniConstants.VER_CHUNITHM_AIR
|
||||||
elif version >= 115 and version < 120: # AIR PLUS
|
elif version >= 115 and version < 120: # AIR PLUS
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_AIR_PLUS
|
internal_ver = ChuniConstants.VER_CHUNITHM_AIR_PLUS
|
||||||
elif version >= 120 and version < 125: # STAR
|
elif version >= 120 and version < 125: # STAR
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_STAR
|
internal_ver = ChuniConstants.VER_CHUNITHM_STAR
|
||||||
elif version >= 125 and version < 130: # STAR PLUS
|
elif version >= 125 and version < 130: # STAR PLUS
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_STAR_PLUS
|
internal_ver = ChuniConstants.VER_CHUNITHM_STAR_PLUS
|
||||||
elif version >= 130 and version < 135: # AMAZON
|
elif version >= 130 and version < 135: # AMAZON
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_AMAZON
|
internal_ver = ChuniConstants.VER_CHUNITHM_AMAZON
|
||||||
elif version >= 135 and version < 140: # AMAZON PLUS
|
elif version >= 135 and version < 140: # AMAZON PLUS
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_AMAZON_PLUS
|
internal_ver = ChuniConstants.VER_CHUNITHM_AMAZON_PLUS
|
||||||
elif version >= 140 and version < 145: # CRYSTAL
|
elif version >= 140 and version < 145: # CRYSTAL
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_CRYSTAL
|
internal_ver = ChuniConstants.VER_CHUNITHM_CRYSTAL
|
||||||
elif version >= 145 and version < 150: # CRYSTAL PLUS
|
elif version >= 145 and version < 150: # CRYSTAL PLUS
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_CRYSTAL_PLUS
|
internal_ver = ChuniConstants.VER_CHUNITHM_CRYSTAL_PLUS
|
||||||
elif version >= 150 and version < 200: # PARADISE
|
elif version >= 150 and version < 200: # PARADISE
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_PARADISE
|
internal_ver = ChuniConstants.VER_CHUNITHM_PARADISE
|
||||||
elif version >= 200 and version < 205: # NEW!!
|
elif version >= 200 and version < 205: # NEW!!
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_NEW
|
internal_ver = ChuniConstants.VER_CHUNITHM_NEW
|
||||||
elif version >= 205 and version < 210: # NEW PLUS!!
|
elif version >= 205 and version < 210: # NEW PLUS!!
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_NEW_PLUS
|
internal_ver = ChuniConstants.VER_CHUNITHM_NEW_PLUS
|
||||||
elif version >= 210 and version < 215: # SUN
|
elif version >= 210 and version < 215: # SUN
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_SUN
|
internal_ver = ChuniConstants.VER_CHUNITHM_SUN
|
||||||
elif version >= 215 and version < 220: # SUN PLUS
|
elif version >= 215 and version < 220: # SUN PLUS
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_SUN_PLUS
|
internal_ver = ChuniConstants.VER_CHUNITHM_SUN_PLUS
|
||||||
elif version >= 220 and version < 225: # LUMINOUS
|
elif version >= 220 and version < 225: # LUMINOUS
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_LUMINOUS
|
internal_ver = ChuniConstants.VER_CHUNITHM_LUMINOUS
|
||||||
elif version >= 225 and version < 230: # LUMINOUS PLUS
|
elif version >= 225 and version < 230: # LUMINOUS PLUS
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_LUMINOUS_PLUS
|
internal_ver = ChuniConstants.VER_CHUNITHM_LUMINOUS_PLUS
|
||||||
elif version >= 230: # VERSE
|
elif version >= 230: # VERSE
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_VERSE
|
internal_ver = ChuniConstants.VER_CHUNITHM_VERSE
|
||||||
elif game_code == "SDGS": # Int
|
elif game_code == "SDGS": # Int
|
||||||
if version < 105: # SUPERSTAR
|
if version < 105: # SUPERSTAR
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_CRYSTAL_PLUS
|
internal_ver = ChuniConstants.VER_CHUNITHM_CRYSTAL_PLUS
|
||||||
elif version >= 105 and version < 110: # SUPERSTAR PLUS *Cursed but needed due to different encryption key
|
elif (
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_PARADISE
|
version >= 105 and version < 110
|
||||||
elif version >= 110 and version < 115: # NEW
|
): # SUPERSTAR PLUS *Cursed but needed due to different encryption key
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_NEW
|
internal_ver = ChuniConstants.VER_CHUNITHM_PARADISE
|
||||||
elif version >= 115 and version < 120: # NEW PLUS!!
|
elif version >= 110 and version < 115: # NEW
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_NEW_PLUS
|
internal_ver = ChuniConstants.VER_CHUNITHM_NEW
|
||||||
elif version >= 120 and version < 125: # SUN
|
elif version >= 115 and version < 120: # NEW PLUS!!
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_SUN
|
internal_ver = ChuniConstants.VER_CHUNITHM_NEW_PLUS
|
||||||
elif version >= 125 and version < 130: # SUN PLUS
|
elif version >= 120 and version < 125: # SUN
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_SUN_PLUS
|
internal_ver = ChuniConstants.VER_CHUNITHM_SUN
|
||||||
elif version >= 130 and version < 135: # LUMINOUS
|
elif version >= 125 and version < 130: # SUN PLUS
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_LUMINOUS
|
internal_ver = ChuniConstants.VER_CHUNITHM_SUN_PLUS
|
||||||
elif version >= 135: # LUMINOUS PLUS
|
elif version >= 130 and version < 135: # LUMINOUS
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_LUMINOUS_PLUS
|
internal_ver = ChuniConstants.VER_CHUNITHM_LUMINOUS
|
||||||
elif game_code == "SDHJ": # Chn
|
elif version >= 135: # LUMINOUS PLUS
|
||||||
if version < 110: # NEW
|
internal_ver = ChuniConstants.VER_CHUNITHM_LUMINOUS_PLUS
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_NEW
|
elif game_code == "SDHJ": # Chn
|
||||||
elif version >= 110 and version < 120: # NEW *Cursed but needed due to different encryption key
|
if version < 110: # NEW
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_NEW_PLUS
|
internal_ver = ChuniConstants.VER_CHUNITHM_NEW
|
||||||
elif version >= 120: # LUMINOUS
|
elif (
|
||||||
internal_ver = ChuniConstants.VER_CHUNITHM_LUMINOUS
|
version >= 110 and version < 120
|
||||||
|
): # NEW *Cursed but needed due to different encryption key
|
||||||
|
internal_ver = ChuniConstants.VER_CHUNITHM_NEW_PLUS
|
||||||
|
elif version >= 120: # LUMINOUS
|
||||||
|
internal_ver = ChuniConstants.VER_CHUNITHM_LUMINOUS
|
||||||
|
|
||||||
if all(c in string.hexdigits for c in endpoint) and len(endpoint) == 32:
|
if all(c in string.hexdigits for c in endpoint) and len(endpoint) == 32:
|
||||||
# If we get a 32 character long hex string, it's a hash and we're
|
# If we get a 32 character long hex string, it's a hash and we're
|
||||||
@ -381,7 +396,7 @@ class ChuniServlet(BaseServlet):
|
|||||||
else:
|
else:
|
||||||
endpoint = endpoint
|
endpoint = endpoint
|
||||||
|
|
||||||
func_to_find = "handle_" + inflection.underscore(endpoint) + "_request"
|
func_to_find = "handle_" + self.strict_underscore(endpoint) + "_request"
|
||||||
handler_cls = self.versions[internal_ver](self.core_cfg, self.game_cfg)
|
handler_cls = self.versions[internal_ver](self.core_cfg, self.game_cfg)
|
||||||
|
|
||||||
if not hasattr(handler_cls, func_to_find):
|
if not hasattr(handler_cls, func_to_find):
|
||||||
@ -419,3 +434,9 @@ class ChuniServlet(BaseServlet):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return Response(crypt.encrypt(padded))
|
return Response(crypt.encrypt(padded))
|
||||||
|
|
||||||
|
def strict_underscore(self, name: str) -> str:
|
||||||
|
# Insert underscores between *all* capital letters
|
||||||
|
name = re.sub(r"([A-Z])([A-Z])", r"\1_\2", name)
|
||||||
|
return inflection.underscore(name)
|
||||||
|
|
||||||
|
@ -60,8 +60,8 @@ class ChuniVerse(ChuniLuminousPlus):
|
|||||||
"length": len(game_course_level_list),
|
"length": len(game_course_level_list),
|
||||||
"gameCourseLevelList": game_course_level_list,
|
"gameCourseLevelList": game_course_level_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
async def handle_get_game_uc_condition_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_u_c_condition_api_request(self, data: Dict) -> Dict:
|
||||||
unlock_challenges = await self.data.static.get_unlock_challenges(self.version)
|
unlock_challenges = await self.data.static.get_unlock_challenges(self.version)
|
||||||
game_unlock_challenge_condition_list = []
|
game_unlock_challenge_condition_list = []
|
||||||
|
|
||||||
@ -84,8 +84,8 @@ class ChuniVerse(ChuniLuminousPlus):
|
|||||||
unlock_condition = conditions.get(
|
unlock_condition = conditions.get(
|
||||||
unlock_challenge_id,
|
unlock_challenge_id,
|
||||||
{
|
{
|
||||||
"type": 3, # always unlocked
|
"type": MapAreaConditionType.ALWAYS_UNLOCKED.value, # always unlocked
|
||||||
"conditionId": 0,
|
"conditionId": -1,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -114,7 +114,7 @@ class ChuniVerse(ChuniLuminousPlus):
|
|||||||
"gameUnlockChallengeConditionList": game_unlock_challenge_condition_list,
|
"gameUnlockChallengeConditionList": game_unlock_challenge_condition_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
async def handle_get_user_uc_api_request(self, data: Dict) -> Dict:
|
async def handle_get_user_u_c_api_request(self, data: Dict) -> Dict:
|
||||||
user_id = data["userId"]
|
user_id = data["userId"]
|
||||||
|
|
||||||
user_unlock_challenges = await self.data.item.get_unlock_challenges(
|
user_unlock_challenges = await self.data.item.get_unlock_challenges(
|
||||||
@ -167,7 +167,9 @@ class ChuniVerse(ChuniLuminousPlus):
|
|||||||
|
|
||||||
# try adding recommendations in order of: title → artist → genre
|
# try adding recommendations in order of: title → artist → genre
|
||||||
for field in ("title", "artist", "genre"):
|
for field in ("title", "artist", "genre"):
|
||||||
await self._add_recommendations(field, user_rec_music_set, music_info_list, rec_limit)
|
await self._add_recommendations(
|
||||||
|
field, user_rec_music_set, music_info_list, rec_limit
|
||||||
|
)
|
||||||
if len(user_rec_music_set) >= rec_limit:
|
if len(user_rec_music_set) >= rec_limit:
|
||||||
break
|
break
|
||||||
|
|
||||||
@ -220,9 +222,7 @@ class ChuniVerse(ChuniLuminousPlus):
|
|||||||
excluding music IDs already in the user's recent ratings and recommendations.
|
excluding music IDs already in the user's recent ratings and recommendations.
|
||||||
"""
|
"""
|
||||||
# Collect all existing songId to exclude from recommendations
|
# Collect all existing songId to exclude from recommendations
|
||||||
existing_music_ids = {
|
existing_music_ids = {info["songId"] for info in music_info_list}
|
||||||
info["songId"] for info in music_info_list
|
|
||||||
}
|
|
||||||
|
|
||||||
for music_info in music_info_list:
|
for music_info in music_info_list:
|
||||||
if len(user_rec_music_set) >= limit:
|
if len(user_rec_music_set) >= limit:
|
||||||
|
Reference in New Issue
Block a user