30 Commits

Author SHA1 Message Date
1a616cba41 diva: change how requests are decoded 2023-11-26 20:43:10 -05:00
d2d02f9483 diva: add list decoders 2023-11-26 19:24:36 -05:00
024bc352d7 diva: fix attend response 2023-11-26 02:43:49 -05:00
4831aa5628 diva: add encoder helper methods 2023-11-26 01:41:25 -05:00
dd1c7667e0 diva: fix ip logging 2023-11-20 11:29:10 -05:00
2ec1dcadef Merge branch 'develop' into diva_handler_classes 2023-11-20 10:49:48 -05:00
81c13f5008 diva: fix typing 2023-11-08 21:23:54 -05:00
572b8ddbe5 Merge branch 'develop' into diva_handler_classes 2023-11-08 21:22:24 -05:00
2b02ed8684 diva: add stage_result, end handlers 2023-10-05 23:47:50 -04:00
b2a01d20d5 diva: add configurable banner_msg 2023-10-05 12:29:02 -04:00
5e03193819 diva: fix start, spend_credit, and get_pv_pd requests 2023-10-04 23:58:26 -04:00
f836b5dd21 diva: fix start request 2023-10-04 23:25:10 -04:00
53c74c6511 diva: add register, fixes, start still busted 2023-10-04 02:18:04 -04:00
7322bc60dc Merge branch 'develop' into diva_handler_classes 2023-10-04 00:53:10 -04:00
c2ee09bce0 diva: integrate latest changes 2023-05-02 11:41:35 -04:00
dfea392b03 Merge branch 'develop' into diva_handler_classes 2023-05-02 11:36:14 -04:00
998aa70929 diva: fix for responses that haven't been updated yet 2023-04-01 23:45:28 -04:00
8df1325613 diva: implement StartResponse 2023-04-01 23:19:44 -04:00
6ff7827918 diva: fill out StartResponse 2023-04-01 23:05:10 -04:00
a36170f2c3 Merge branch 'develop' into diva_handler_classes 2023-04-01 22:23:13 -04:00
48144b53f0 fix attend 2023-03-17 01:35:51 -04:00
3b852ea739 Merge branch 'develop' into diva_handler_classes 2023-03-16 22:32:18 -04:00
21c9b23617 diva: add game_init and attend handlers 2023-03-13 04:04:08 -04:00
3076bdf575 Merge branch 'develop' into diva_handler_classes 2023-03-13 02:27:12 -04:00
5be25f89ff format with black 2023-03-09 11:50:11 -05:00
5443d24352 Merge branch 'develop' into diva_handler_classes 2023-03-09 11:49:51 -05:00
56927c049f clean up index.py 2023-02-26 11:40:13 -05:00
2b81ba206c remove unused code from index.py 2023-02-25 23:40:50 -05:00
9718e822f3 add commas to urlencode safe param 2023-02-25 19:12:48 -05:00
b07e3d6a94 add base request/response classes 2023-02-25 18:50:35 -05:00
23 changed files with 888 additions and 728 deletions

3
.gitattributes vendored
View File

@ -1,3 +0,0 @@
*.csv binary
*.txt binary
*.json binary

View File

@ -1,12 +1,9 @@
from typing import Dict, Any, Optional
from typing import Dict, Any
from types import ModuleType
from twisted.web.http import Request
import logging
import importlib
from os import walk
import jwt
from base64 import b64decode
from datetime import datetime, timezone
from .config import CoreConfig
@ -63,35 +60,3 @@ class Utils:
cls.real_title_port_ssl = cfg.title.port_ssl
return cls.real_title_port_ssl
def create_sega_auth_key(aime_id: int, game: str, ver: float, place_id: str, b64_secret: str, err_logger: str = 'aimedb') -> Optional[str]:
logger = logging.getLogger(err_logger)
try:
return jwt.encode({ "aime_id": aime_id, "game": game, "ver": ver, "place_id": place_id, "exp": int(datetime.now(tz=timezone.utc).timestamp()) + 86400 }, b64decode(b64_secret), algorithm="HS256")
except jwt.InvalidKeyError:
logger.error("Failed to encode Sega Auth Key because the secret is invalid!")
return None
except Exception as e:
logger.error(f"Unknown exception occoured when encoding Sega Auth Key! {e}")
return None
def decode_sega_auth_key(token: str, b64_secret: str, err_logger: str = 'aimedb') -> Optional[Dict]:
logger = logging.getLogger(err_logger)
try:
return jwt.decode(token, "secret", b64decode(b64_secret), algorithms=["HS256"], options={"verify_signature": True})
except jwt.ExpiredSignatureError:
logger.error("Sega Auth Key failed to validate due to an expired signature!")
return None
except jwt.InvalidSignatureError:
logger.error("Sega Auth Key failed to validate due to an invalid signature!")
return None
except jwt.DecodeError as e:
logger.error(f"Sega Auth Key failed to decode! {e}")
return None
except jwt.InvalidTokenError as e:
logger.error(f"Sega Auth Key is invalid! {e}")
return None
except Exception as e:
logger.error(f"Unknown exception occoured when decoding Sega Auth Key! {e}")
return None

View File

@ -1,6 +1,7 @@
server:
enable: True
loglevel: "info"
banner_msg: ""
mods:
unlock_all_modules: True

View File

@ -1,21 +1,20 @@
mypy
wheel
twisted
pytz
pyyaml
sqlalchemy==1.4.46
mysqlclient
pyopenssl
service_identity
PyCryptodome
inflection
coloredlogs
pylibmc; platform_system != "Windows"
wacky
Routes
bcrypt
jinja2
protobuf
autobahn
pillow
pyjwt
mypy
wheel
twisted
pytz
pyyaml
sqlalchemy==1.4.46
mysqlclient
pyopenssl
service_identity
PyCryptodome
inflection
coloredlogs
pylibmc; platform_system != "Windows"
wacky
Routes
bcrypt
jinja2
protobuf
autobahn
pillow

View File

@ -313,13 +313,6 @@ class ChuniBase:
"userChargeList": charge_list,
}
def handle_get_user_recent_player_api_request(self, data: Dict) -> Dict:
return {
"userId": data["userId"],
"length": 0,
"userRecentPlayerList": [], # playUserId, playUserName, playDate, friendPoint
}
def handle_get_user_course_api_request(self, data: Dict) -> Dict:
user_course_list = self.data.score.get_courses(data["userId"])
if user_course_list is None:
@ -331,8 +324,8 @@ class ChuniBase:
}
course_list = []
next_idx = int(data.get("nextIndex", 0))
max_ct = int(data.get("maxCount", 300))
next_idx = int(data["nextIndex"])
max_ct = int(data["maxCount"])
for x in range(next_idx, len(user_course_list)):
tmp = user_course_list[x]._asdict()
@ -409,7 +402,6 @@ class ChuniBase:
"userId": data["userId"],
"userRivalData": userRivalData
}
def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict:
rival_id = data["rivalId"]
next_index = int(data["nextIndex"])
@ -457,7 +449,6 @@ class ChuniBase:
}
return result
def handle_get_user_favorite_item_api_request(self, data: Dict) -> Dict:
user_fav_item_list = []
@ -747,7 +738,6 @@ class ChuniBase:
"aggrDate": data["playDate"],
},
}
def handle_get_team_course_setting_api_request(self, data: Dict) -> Dict:
return {
"userId": data["userId"],
@ -915,10 +905,6 @@ class ChuniBase:
self.data.item.put_login_bonus(
user_id, self.version, login["presetId"], isWatched=True
)
if "userRecentPlayerList" in upsert: # TODO: Seen in Air, maybe implement sometime
for rp in upsert["userRecentPlayerList"]:
pass
return {"returnCode": "1"}

View File

@ -142,9 +142,9 @@ class ChuniServlet(BaseServlet):
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80:
return (f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}/{game_ver}/", self.core_cfg.title.hostname)
return (f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}/{game_ver}/", "")
return (f"http://{self.core_cfg.title.hostname}/{game_ver}/", self.core_cfg.title.hostname)
return (f"http://{self.core_cfg.title.hostname}/{game_ver}/", "")
def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
endpoint = matchers['endpoint']

View File

@ -1,9 +0,0 @@
1,1412103600,4070908801,1,
2,1412103600,4070908801,1,
3,1412103600,4070908801,1,
4,1412103600,4070908801,1,
5,1412103600,4070908801,1,
6,1412103600,4070908801,1,
7,1412103600,4070908801,1,
8,1412103600,4070908801,1,
9,1412103600,4070908801,1,
1 1 1412103600 4070908801 1
2 2 1412103600 4070908801 1
3 3 1412103600 4070908801 1
4 4 1412103600 4070908801 1
5 5 1412103600 4070908801 1
6 6 1412103600 4070908801 1
7 7 1412103600 4070908801 1
8 8 1412103600 4070908801 1
9 9 1412103600 4070908801 1

View File

@ -1,272 +0,0 @@
tutori2,10000,100000,10,10,10,10,10,0,0,0,0,0,0,100000,
tutori3,10000,100000,10,10,10,10,10,0,0,0,0,0,0,100000,
tutori4,10000,10100000,10,10,10,10,10,0,0,0,0,0,0,100000,
tutori5,10000,100000,10,10,10,10,10,0,0,0,0,0,0,100000,
tutori6,10050,10100000,10,10,10,10,10,0,0,0,0,0,0,100000,
wannab,10073,800000,90,230,400,650,50,1,1,1,1,1,3,100001,
toucho,10000,802000,90,280,440,650,50,1,1,1,1,1,1,100002,
ameoto,10000,802000,120,260,470,630,60,1,1,1,1,1,1,100003,
kimito,10000,800000,100,260,490,660,70,1,1,1,1,1,1,100004,
landin,10000,802000,260,330,490,670,130,1,1,1,1,1,1,100005,
aquali,10104,800000,160,340,580,0,110,1,1,1,0,1,4,100006,
yiyoyi,10080,800000,140,330,560,700,70,1,1,1,1,1,3,100007,
hontno,10022,801000,120,260,530,660,90,1,1,1,1,1,1,100010,
binary,10080,800000,170,350,640,890,140,1,1,1,1,1,1,100014,
breakd,10000,803000,230,340,570,740,110,1,1,1,1,1,2,100015,
higame,10076,800000,200,300,580,710,130,1,1,1,1,1,3,100016,
freakw,10104,800000,220,420,650,660,130,1,1,1,1,1,0,100018,
giantk,10000,808000,250,530,710,780,110,1,1,1,1,1,3,100021,
theepi,10076,800000,190,400,610,750,140,1,1,1,1,1,3,100022,
anomie,10076,800000,220,380,610,770,150,1,1,1,1,1,0,100023,
azitat,10022,800000,240,550,700,830,140,1,1,1,1,1,0,100024,
dazzlj,10000,802000,350,600,800,900,160,1,1,1,1,1,1,100028,
aerial,10100,800000,110,280,560,710,50,1,1,1,1,1,1,100039,
gingat,10005,800000,130,290,460,610,50,1,1,1,1,1,0,100041,
sundro,10000,807000,240,470,750,830,140,1,1,1,1,1,3,100042,
childr,10069,800000,240,390,560,620,140,1,1,1,1,1,0,100043,
tsukai,10069,802000,190,440,720,760,130,1,1,1,1,1,1,100044,
dennou,10000,801000,290,600,760,870,150,1,1,1,1,1,1,100045,
romanc,10104,800000,280,550,780,0,100,1,1,1,0,1,0,100047,
makaim,10078,2400000,300,500,770,0,230,1,1,1,0,1,3,100054,
gyakut,10078,2400000,150,210,460,640,60,1,1,1,1,1,1,100055,
basara,10078,2400000,190,370,640,730,140,1,1,1,1,1,0,100056,
howtru,10074,800000,120,250,530,740,80,1,1,1,1,1,0,100057,
auflcb,10000,800000,130,430,810,0,80,1,1,1,0,1,0,100063,
daisak,10005,800000,280,360,600,750,120,1,1,1,1,1,0,100066,
rideon,10069,802000,290,410,600,800,160,1,1,1,1,1,1,100067,
hokoro,10000,801000,290,570,710,810,140,1,1,1,1,1,1,100068,
ididid,10000,802000,290,460,720,810,160,1,1,1,1,1,1,100093,
yukiya,10103,800000,190,400,610,0,110,1,1,1,0,1,3,100096,
samalv,10102,800000,190,390,580,770,130,1,1,1,1,1,6,100098,
crissc,10076,800000,370,630,860,910,170,1,1,1,1,1,4,100100,
drivin,10100,800000,230,400,660,760,80,1,1,1,1,1,7,100111,
genzit,10100,800000,180,460,730,820,120,1,1,1,1,1,0,100118,
overlp,10103,800000,170,300,510,0,90,1,1,1,0,1,7,100119,
zankyo,10103,800000,180,380,570,740,100,1,1,1,1,1,5,100124,
megaro,10000,800000,550,800,930,980,0,1,1,1,1,1,0,100129,
sateli,10000,800000,280,490,770,820,170,1,1,1,1,1,0,100300,
nature,10000,40802000,140,240,530,750,50,1,1,1,1,1,3,100301,
reseed3,10000,800000,200,550,760,800,100,1,1,1,1,1,0,100306,
purple,10000,800000,220,540,640,730,140,1,1,1,1,1,0,100307,
hearts,10000,800000,180,380,680,770,80,1,1,1,1,1,0,100308,
syoujo,10003,802000,190,350,530,780,80,1,1,1,1,1,1,100309,
phasea,10000,800000,160,380,650,750,90,1,1,1,1,1,0,100310,
planet,10000,802000,170,360,490,710,100,1,1,1,1,1,1,100311,
touchn,10050,804000,270,460,680,860,150,1,1,1,1,1,8,100312,
upside,10050,802000,180,270,480,670,80,1,1,1,1,1,1,100313,
firefo,10000,800000,130,360,570,830,70,1,1,1,1,1,0,100314,
kounen,10000,808000,210,400,660,780,100,1,1,1,1,1,3,100315,
essenc,10000,800000,250,500,700,760,110,1,1,1,1,1,0,100316,
summer,10000,800000,230,570,790,890,130,1,1,1,1,1,0,100317,
tanosi,10000,802000,250,450,700,800,160,1,1,1,1,1,7,100319,
picora,10000,802000,150,380,660,750,80,1,1,1,1,1,1,100320,
istanb,10050,806000,410,490,900,980,230,1,1,1,1,1,3,100322,
devils,10000,800000,270,400,800,0,150,1,1,1,0,1,0,100323,
speedy,10076,800000,220,550,770,0,110,1,1,1,0,1,8,100324,
irohaa,10008,800000,190,410,550,760,120,1,1,1,1,1,0,100325,
ibelie,10008,800000,270,470,780,820,140,1,1,1,1,1,0,100326,
letmeg,10021,802000,180,320,500,720,120,1,1,1,1,1,1,100327,
techno,10000,802000,160,380,510,740,90,1,1,1,1,1,1,100328,
angeli,10000,800000,330,560,820,900,220,1,1,1,1,1,0,100330,
glowww,10000,40802000,170,280,420,600,80,1,1,1,1,1,7,100335,
powerr,10000,800000,190,380,690,790,120,1,1,1,1,1,0,100336,
laught,10022,801000,200,350,510,710,70,1,1,1,1,1,1,100337,
chaset,10019,803000,150,310,580,760,80,1,1,1,1,1,9999,100338,
amater,10000,800000,210,480,650,790,130,1,1,1,1,1,0,100340,
moonli,10000,803000,140,430,610,730,80,1,1,1,1,1,9999,100342,
sayona,10050,803000,200,340,530,710,100,1,1,1,1,1,9999,100343,
hosita,10000,802000,160,360,480,650,100,1,1,1,1,1,9999,100344,
ascand,10069,800000,320,540,800,900,180,1,1,1,1,1,0,100347,
pinkym,10000,800000,240,440,740,810,160,1,1,1,1,1,0,100348,
advers,10000,802000,150,480,710,830,90,1,1,1,1,1,1,100349,
straye,10050,800000,250,360,610,730,140,1,1,1,1,1,4,100350,
destru,10003,800000,190,410,620,720,100,1,1,1,1,1,0,100352,
venera,10000,800000,150,430,680,750,80,1,1,1,1,1,0,100353,
dazaii,10000,803000,210,430,730,770,120,1,1,1,1,1,3,100357,
rearhy,10050,802000,170,320,520,690,70,1,1,1,1,1,9999,100358,
onlyll,10050,802000,210,320,560,690,130,1,1,1,1,1,9999,100359,
hellom,10022,801000,180,370,580,720,70,1,1,1,1,1,1,100360,
megaro2,10100,800000,0,0,990,1000,0,0,0,1,1,0,4,100361,
tomorr,10003,803000,150,240,440,620,80,1,1,1,1,1,7,100362,
daybyd,10003,803000,130,260,380,590,60,1,1,1,1,1,7,100363,
thesig,10000,802000,210,380,560,730,100,1,1,1,1,1,3,100365,
zonzon,10066,800000,160,330,630,670,50,1,1,1,1,1,1,100367,
yumemi,10000,802000,120,350,590,690,60,1,1,1,1,1,9999,100369,
dynami2,10000,804000,180,510,780,800,80,1,1,1,1,1,0,100370,
memori,10050,803000,260,380,650,840,130,1,1,1,1,1,9999,100371,
bluede,10000,802000,220,410,580,700,120,1,1,1,1,1,5,100372,
emerao,10000,802000,300,530,850,0,190,1,1,1,0,1,1,100373,
auflcb3,10007,802000,160,360,660,860,60,1,1,1,1,1,9999,100374,
paradi,10005,801000,160,280,530,640,100,1,1,1,1,1,3,100376,
pigooo,10005,800000,190,340,590,840,130,1,1,1,1,1,6,100377,
flameu,10050,801000,180,360,570,650,100,1,1,1,1,1,1,100380,
season,10006,1607000,150,280,440,650,80,1,1,1,1,1,1,100386,
canonn,10006,1607000,170,280,500,830,70,1,1,1,1,1,1,100387,
rhapso,10006,1607000,180,340,620,740,60,1,1,1,1,1,1,100388,
turkis,10006,1607000,190,390,640,840,110,1,1,1,1,1,1,100389,
biohaz,10006,1609000,150,300,510,640,60,1,1,1,1,1,9999,100390,
monhan,10006,1609000,100,260,360,540,50,1,1,1,1,1,9999,100391,
gyakut2,10006,1609000,130,350,430,560,50,1,1,1,1,1,9999,100392,
street,10006,1609000,130,340,470,660,70,1,1,1,1,1,9999,100393,
rockma2,10006,1609000,210,340,490,760,140,1,1,1,1,1,9999,100394,
monhan2,10014,1609000,120,240,430,680,60,1,1,1,1,1,9999,100409,
monhan3,10014,1609000,180,280,450,730,80,1,1,1,1,1,9999,100410,
orbita,10050,803000,200,380,620,740,120,1,1,1,1,1,5,100411,
metall,10021,803000,160,280,570,770,80,1,1,1,1,1,1,100412,
xmasss,10017,1607000,180,380,560,770,80,1,1,1,1,1,101,100417,
yejiii,10019,800000,220,360,630,790,100,1,1,1,1,1,0,100418,
histor,10019,800000,200,360,560,820,110,1,1,1,1,1,0,100419,
silbur,10026,801000,200,350,540,750,90,1,1,1,1,1,1,100421,
spicaa,10026,801000,230,360,560,780,100,1,1,1,1,1,1,100422,
street2,10025,1609000,210,370,550,730,140,1,1,1,1,1,9999,100423,
street3,10025,1609000,240,380,570,740,130,1,1,1,1,1,9999,100424,
street4,10025,1609000,170,320,510,780,110,1,1,1,1,1,9999,100425,
bluede2,10024,801000,160,360,560,810,90,1,1,1,1,1,1,100426,
pyroma,10050,800000,220,420,700,840,80,1,1,1,1,1,4,100427,
avemar,10050,1607000,160,310,530,750,80,1,1,1,1,1,5,100428,
mateki,10050,1607000,180,350,540,790,100,1,1,1,1,1,5,100429,
densho,10050,800000,280,420,740,900,170,1,1,1,1,1,5,100430,
sunglo,10050,801000,180,390,590,720,100,1,1,1,1,1,7,100431,
hereco,10050,801000,160,340,510,680,110,1,1,1,1,1,4,100432,
dddddd,10050,800000,130,330,530,690,90,1,1,1,1,1,5,100433,
raidon,10050,801000,300,380,580,870,130,1,1,1,1,1,1,100434,
thisis,10050,800000,230,370,600,740,120,1,1,1,1,1,4,100435,
rising,10050,800000,230,380,660,850,140,1,1,1,1,1,4,100436,
riseup,10050,800000,180,410,670,770,70,1,1,1,1,1,4,100437,
tricko,10050,800000,230,340,560,750,110,1,1,1,1,1,0,100438,
kinbos,10050,800000,220,380,640,770,120,1,1,1,1,1,3,100439,
thesun,10050,800000,250,400,720,870,130,1,1,1,1,1,4,100441,
lovech,10050,801000,160,300,460,680,80,1,1,1,1,1,7,100445,
machup,10061,801000,170,320,410,710,90,1,1,1,1,1,6,100447,
cyberg,10104,801000,230,350,600,0,120,1,1,1,0,1,0,100448,
harmon,10050,801000,200,360,490,650,120,1,1,1,1,1,7,100449,
kansho,10056,801000,130,270,550,740,70,1,1,1,1,1,9999,100450,
cantst,10103,800000,230,420,650,0,110,1,1,1,0,1,0,100455,
takeit,10050,800000,200,380,650,830,120,1,1,1,1,1,4,100457,
lespri,10063,800000,250,570,800,0,130,1,1,1,0,1,5,100465,
notoss,10061,800000,120,420,650,910,80,1,1,1,1,1,4,100466,
codena,10055,801000,180,350,480,680,90,1,1,1,1,1,1,100468,
aiohoo,10050,101000,180,290,420,660,100,1,1,1,1,1,1,100471,
entert,10050,1607000,150,330,540,870,90,1,1,1,1,1,6,100472,
akaihe,10051,101000,210,320,500,690,80,1,1,1,1,1,1,100473,
juicys,10051,101000,180,260,450,830,100,1,1,1,1,1,7,100474,
groove,10055,1601000,220,400,520,730,100,1,1,1,1,1,103,100475,
groove2,10058,1601000,150,400,580,850,90,1,1,1,1,1,9999,100480,
everyt,10063,801000,220,300,740,0,130,1,1,1,0,1,5,100482,
taikoo,10058,1609000,130,390,500,750,60,1,1,1,1,1,104,100483,
overcl2,10057,809000,230,420,580,740,120,1,1,1,1,1,3,100486,
overcl,10059,809000,120,350,570,860,80,1,1,1,1,1,4,100487,
groove3,10062,2009000,180,340,640,850,100,1,1,1,1,1,105,100488,
groove4,10062,2009000,220,350,500,750,120,1,1,1,1,1,106,100489,
honeyo,10064,802000,320,630,880,930,240,1,1,1,1,1,6,100490,
groove5,10064,1609000,240,370,670,0,140,1,1,1,0,1,107,100491,
nekofu,10064,1607000,220,340,570,800,100,1,1,1,1,1,6,100493,
groove6,10065,1609000,300,640,790,0,220,1,1,1,0,1,3,100494,
sunglo2,10065,801000,210,360,670,810,110,1,1,1,1,1,6,100495,
monhan4,10066,1609000,120,400,510,620,50,1,1,1,1,1,9999,100496,
monhan5,10066,1609000,120,350,420,650,100,1,1,1,1,1,9999,100497,
fourte,10066,800000,240,500,740,800,140,1,1,1,1,1,5,100498,
cirnon,10069,409000,240,400,600,790,170,1,1,1,1,1,9999,100499,
marisa,10069,409000,250,410,620,850,180,1,1,1,1,1,9999,100500,
yakini,10069,409000,250,340,580,820,130,1,1,1,1,1,9999,100501,
justic,10069,409000,190,360,570,830,140,1,1,1,1,1,1,100502,
sintyo,10069,401000,250,460,700,830,160,1,1,1,1,1,6,100503,
darkpa,10068,800000,260,390,700,840,160,1,1,1,1,1,3,100504,
hervor,10068,800000,280,390,730,810,180,1,1,1,1,1,5,100505,
blackl,10069,800000,190,410,730,840,120,1,1,1,1,1,3,100506,
minest,10070,800000,210,390,620,760,130,1,1,1,1,1,1,100507,
ordine,10070,800000,250,430,730,820,190,1,1,1,1,1,3,100508,
dreamw,10072,800000,260,370,620,750,160,1,1,1,1,1,0,100509,
minerv,10072,800000,320,610,900,0,250,1,1,1,0,1,4,100510,
sekain,10073,800000,260,390,690,780,160,1,1,1,1,1,1,100511,
farawa,10074,800000,230,360,600,760,180,1,1,1,1,1,7,100512,
xxxrev,10076,800000,210,340,560,730,150,1,1,1,1,1,0,100513,
daybre,10080,201000,160,320,530,720,90,1,1,1,1,1,0,100514,
umiyur,10080,201000,140,280,460,640,80,1,1,1,1,1,1,100515,
chalur,10080,201000,180,400,600,720,140,1,1,1,1,1,9999,100516,
melanc,10080,201000,150,300,500,630,100,1,1,1,1,1,7,100517,
konofu,10080,201000,230,350,620,810,110,1,1,1,1,1,1,100518,
redhea,10081,401000,270,390,590,720,100,1,1,1,1,1,0,100519,
warnin,10081,401000,250,360,610,740,120,1,1,1,1,1,9999,100520,
topsec,10081,401000,240,340,510,640,130,1,1,1,1,1,9999,100521,
dddoll,10081,401000,260,380,550,630,140,1,1,1,1,1,9999,100522,
crocus,10077,800000,260,370,600,720,150,1,1,1,1,1,7,100524,
moonsh,10102,800000,230,360,620,0,100,1,1,1,0,1,3,100525,
bladem,10078,800000,280,380,630,750,170,1,1,1,1,1,4,100526,
primaa,10104,800000,180,350,540,750,120,1,1,1,1,1,0,100527,
fracta,10103,800000,310,520,830,0,190,1,1,1,0,1,3,100529,
regene,10100,800000,200,300,560,700,130,1,1,1,1,1,0,100530,
cthugh,10104,800000,250,480,730,0,140,1,1,1,0,1,0,100531,
einher,10100,800000,290,400,740,800,160,1,1,1,1,1,4,100532,
southw,10078,800000,180,270,570,680,120,1,1,1,1,1,7,100536,
ryuuse,10078,800000,210,320,590,0,130,1,1,1,0,1,1,100537,
ariell,10100,800000,190,320,640,730,150,1,1,1,1,1,7,100540,
chipnn,10077,800000,270,340,610,790,160,1,1,1,1,1,6,100541,
firstl,10100,800000,250,360,650,770,170,1,1,1,1,1,1,100542,
lighto,10100,800000,250,330,600,740,120,1,1,1,1,1,1,100543,
palpit,10102,800000,290,550,840,920,180,1,1,1,1,1,8,100544,
lavien,10077,800000,270,410,710,800,180,1,1,1,1,1,5,100546,
prizmm,10102,800000,210,300,540,0,120,1,1,1,0,1,1,100547,
tracee,10081,401000,190,310,490,650,90,1,1,1,1,1,0,100548,
allelu,10100,800000,280,350,640,750,160,1,1,1,1,1,9999,100549,
heartl,10100,800000,230,300,640,0,110,1,1,1,0,1,1,100550,
erasee,10100,800000,220,350,580,680,120,1,1,1,1,1,0,100551,
termin,10100,800000,240,340,630,790,130,1,1,1,1,1,7,100552,
kokoro,10103,800000,200,430,650,690,120,1,1,1,1,1,1,100554,
nisenn,10103,800000,0,0,760,0,0,0,0,1,0,0,8,100555,
ryuuse2,10101,800000,200,360,620,750,130,1,1,1,1,1,1,100556,
gainen,10102,801000,260,370,630,0,150,1,1,1,0,1,9999,100558,
moonki,10102,800000,250,390,640,0,130,1,1,1,0,1,1,100559,
moonri,10102,800000,210,380,580,850,140,1,1,1,1,1,0,100560,
goaway,10103,800000,230,450,590,700,100,1,1,1,1,1,0,100561,
poweri,10103,800000,260,490,750,910,130,1,1,1,1,1,4,100563,
memorm,10103,800000,260,370,730,0,150,1,1,1,0,1,0,100565,
itback,10103,800000,230,380,710,0,120,1,1,1,0,1,3,100567,
actual,10103,800000,250,380,800,0,140,1,1,1,0,1,0,100568,
redhhh,10103,800000,240,390,770,0,130,1,1,1,0,1,4,100569,
thetaa,10104,800000,210,340,620,0,110,1,1,1,0,1,1,100571,
takesc,10104,800000,270,370,690,0,100,1,1,1,0,1,1,100572,
kotobu,10104,800000,320,710,900,0,250,1,1,1,0,1,0,100573,
galaxy,10000,101000,160,320,430,670,100,1,1,1,1,1,0,100600,
levelf,10000,101000,110,280,450,630,50,1,1,1,1,1,0,100605,
unbeli,10000,101000,130,260,380,620,70,1,1,1,1,1,0,100608,
fiveee,10000,101000,180,370,610,710,100,1,1,1,1,1,0,100614,
replay,10000,101000,180,440,630,700,80,1,1,1,1,1,0,100618,
aheadd,10000,101000,130,250,350,580,70,1,1,1,1,1,0,100620,
musicr1,10000,800000,220,330,580,740,120,1,1,1,1,1,0,100621,
getthe,10000,101000,170,370,490,660,60,1,1,1,1,1,0,100622,
hopesb,10000,101000,100,250,440,610,70,1,1,1,1,1,0,100625,
shooti,10000,101000,150,370,490,690,70,1,1,1,1,1,0,100626,
lightm,10000,101000,260,330,540,710,110,1,1,1,1,1,0,100629,
miiroo,10000,101000,220,390,580,680,110,1,1,1,1,1,0,100630,
vividd,10000,101000,160,350,550,650,90,1,1,1,1,1,0,100633,
donuth,10000,201000,220,400,540,800,110,1,1,1,1,1,100,100635,
senbon,10000,301000,200,280,540,740,120,1,1,1,1,1,0,100636,
kmtyju,10000,101000,240,310,570,740,120,1,1,1,1,1,0,100637,
childe,10000,101000,90,240,340,560,40,1,1,1,1,1,0,100640,
flower,10000,101000,70,200,400,650,60,1,1,1,1,1,0,100643,
musicr4,10000,800000,190,320,580,0,120,1,1,1,0,1,0,100647,
breaka,10000,101000,150,310,450,680,70,1,1,1,1,1,0,100657,
izimed,10000,101000,190,390,690,770,90,1,1,1,1,1,0,100665,
godkno,10000,101000,100,260,450,640,60,1,1,1,1,1,0,100670,
roadof,10000,101000,150,360,500,750,70,1,1,1,1,1,0,100671,
rokuch,10000,301000,210,350,620,810,110,1,1,1,1,1,0,100672,
valent,10000,800000,270,330,590,770,100,1,1,1,1,1,102,100673,
unfini,10000,101000,160,320,500,710,80,1,1,1,1,1,0,100674,
dropou,10000,101000,170,310,460,690,140,1,1,1,1,1,0,100678,
xencou,10000,101000,200,320,520,600,80,1,1,1,1,1,0,100679,
killyk,10000,101000,130,420,630,760,60,1,1,1,1,1,0,100680,
burstt,10000,101000,120,250,460,630,70,1,1,1,1,1,0,100682,
musicr2,10000,800000,220,330,580,0,120,1,1,1,0,1,0,100683,
musicr3,10000,800000,190,320,580,720,120,1,1,1,1,1,0,100687,
sugars,10000,101000,170,300,420,660,60,1,1,1,1,1,0,100689,
wegooo,10000,101000,180,340,540,680,100,1,1,1,1,1,0,100692,
alonee,10000,101000,110,210,360,480,50,1,1,1,1,1,0,100693,
granro,10000,101000,150,280,430,600,80,1,1,1,1,1,0,100695,
sister,10000,101000,100,270,460,630,70,1,1,1,1,1,0,100696,
flawli,10000,101000,170,300,400,590,80,1,1,1,1,1,0,100699,
inneru,10000,101000,220,360,480,670,90,1,1,1,1,1,0,100703,
bokuwa,10000,101000,230,340,550,690,160,1,1,1,1,1,0,100706,
showww,10000,101000,180,350,510,790,150,1,1,1,1,1,0,100707,
nevers,10000,101000,260,320,650,750,150,1,1,1,1,1,0,100708,
bleeze,10000,101000,160,310,470,620,90,1,1,1,1,1,0,100709,
1 tutori2 10000 100000 10 10 10 10 10 0 0 0 0 0 0 100000
2 tutori3 10000 100000 10 10 10 10 10 0 0 0 0 0 0 100000
3 tutori4 10000 10100000 10 10 10 10 10 0 0 0 0 0 0 100000
4 tutori5 10000 100000 10 10 10 10 10 0 0 0 0 0 0 100000
5 tutori6 10050 10100000 10 10 10 10 10 0 0 0 0 0 0 100000
6 wannab 10073 800000 90 230 400 650 50 1 1 1 1 1 3 100001
7 toucho 10000 802000 90 280 440 650 50 1 1 1 1 1 1 100002
8 ameoto 10000 802000 120 260 470 630 60 1 1 1 1 1 1 100003
9 kimito 10000 800000 100 260 490 660 70 1 1 1 1 1 1 100004
10 landin 10000 802000 260 330 490 670 130 1 1 1 1 1 1 100005
11 aquali 10104 800000 160 340 580 0 110 1 1 1 0 1 4 100006
12 yiyoyi 10080 800000 140 330 560 700 70 1 1 1 1 1 3 100007
13 hontno 10022 801000 120 260 530 660 90 1 1 1 1 1 1 100010
14 binary 10080 800000 170 350 640 890 140 1 1 1 1 1 1 100014
15 breakd 10000 803000 230 340 570 740 110 1 1 1 1 1 2 100015
16 higame 10076 800000 200 300 580 710 130 1 1 1 1 1 3 100016
17 freakw 10104 800000 220 420 650 660 130 1 1 1 1 1 0 100018
18 giantk 10000 808000 250 530 710 780 110 1 1 1 1 1 3 100021
19 theepi 10076 800000 190 400 610 750 140 1 1 1 1 1 3 100022
20 anomie 10076 800000 220 380 610 770 150 1 1 1 1 1 0 100023
21 azitat 10022 800000 240 550 700 830 140 1 1 1 1 1 0 100024
22 dazzlj 10000 802000 350 600 800 900 160 1 1 1 1 1 1 100028
23 aerial 10100 800000 110 280 560 710 50 1 1 1 1 1 1 100039
24 gingat 10005 800000 130 290 460 610 50 1 1 1 1 1 0 100041
25 sundro 10000 807000 240 470 750 830 140 1 1 1 1 1 3 100042
26 childr 10069 800000 240 390 560 620 140 1 1 1 1 1 0 100043
27 tsukai 10069 802000 190 440 720 760 130 1 1 1 1 1 1 100044
28 dennou 10000 801000 290 600 760 870 150 1 1 1 1 1 1 100045
29 romanc 10104 800000 280 550 780 0 100 1 1 1 0 1 0 100047
30 makaim 10078 2400000 300 500 770 0 230 1 1 1 0 1 3 100054
31 gyakut 10078 2400000 150 210 460 640 60 1 1 1 1 1 1 100055
32 basara 10078 2400000 190 370 640 730 140 1 1 1 1 1 0 100056
33 howtru 10074 800000 120 250 530 740 80 1 1 1 1 1 0 100057
34 auflcb 10000 800000 130 430 810 0 80 1 1 1 0 1 0 100063
35 daisak 10005 800000 280 360 600 750 120 1 1 1 1 1 0 100066
36 rideon 10069 802000 290 410 600 800 160 1 1 1 1 1 1 100067
37 hokoro 10000 801000 290 570 710 810 140 1 1 1 1 1 1 100068
38 ididid 10000 802000 290 460 720 810 160 1 1 1 1 1 1 100093
39 yukiya 10103 800000 190 400 610 0 110 1 1 1 0 1 3 100096
40 samalv 10102 800000 190 390 580 770 130 1 1 1 1 1 6 100098
41 crissc 10076 800000 370 630 860 910 170 1 1 1 1 1 4 100100
42 drivin 10100 800000 230 400 660 760 80 1 1 1 1 1 7 100111
43 genzit 10100 800000 180 460 730 820 120 1 1 1 1 1 0 100118
44 overlp 10103 800000 170 300 510 0 90 1 1 1 0 1 7 100119
45 zankyo 10103 800000 180 380 570 740 100 1 1 1 1 1 5 100124
46 megaro 10000 800000 550 800 930 980 0 1 1 1 1 1 0 100129
47 sateli 10000 800000 280 490 770 820 170 1 1 1 1 1 0 100300
48 nature 10000 40802000 140 240 530 750 50 1 1 1 1 1 3 100301
49 reseed3 10000 800000 200 550 760 800 100 1 1 1 1 1 0 100306
50 purple 10000 800000 220 540 640 730 140 1 1 1 1 1 0 100307
51 hearts 10000 800000 180 380 680 770 80 1 1 1 1 1 0 100308
52 syoujo 10003 802000 190 350 530 780 80 1 1 1 1 1 1 100309
53 phasea 10000 800000 160 380 650 750 90 1 1 1 1 1 0 100310
54 planet 10000 802000 170 360 490 710 100 1 1 1 1 1 1 100311
55 touchn 10050 804000 270 460 680 860 150 1 1 1 1 1 8 100312
56 upside 10050 802000 180 270 480 670 80 1 1 1 1 1 1 100313
57 firefo 10000 800000 130 360 570 830 70 1 1 1 1 1 0 100314
58 kounen 10000 808000 210 400 660 780 100 1 1 1 1 1 3 100315
59 essenc 10000 800000 250 500 700 760 110 1 1 1 1 1 0 100316
60 summer 10000 800000 230 570 790 890 130 1 1 1 1 1 0 100317
61 tanosi 10000 802000 250 450 700 800 160 1 1 1 1 1 7 100319
62 picora 10000 802000 150 380 660 750 80 1 1 1 1 1 1 100320
63 istanb 10050 806000 410 490 900 980 230 1 1 1 1 1 3 100322
64 devils 10000 800000 270 400 800 0 150 1 1 1 0 1 0 100323
65 speedy 10076 800000 220 550 770 0 110 1 1 1 0 1 8 100324
66 irohaa 10008 800000 190 410 550 760 120 1 1 1 1 1 0 100325
67 ibelie 10008 800000 270 470 780 820 140 1 1 1 1 1 0 100326
68 letmeg 10021 802000 180 320 500 720 120 1 1 1 1 1 1 100327
69 techno 10000 802000 160 380 510 740 90 1 1 1 1 1 1 100328
70 angeli 10000 800000 330 560 820 900 220 1 1 1 1 1 0 100330
71 glowww 10000 40802000 170 280 420 600 80 1 1 1 1 1 7 100335
72 powerr 10000 800000 190 380 690 790 120 1 1 1 1 1 0 100336
73 laught 10022 801000 200 350 510 710 70 1 1 1 1 1 1 100337
74 chaset 10019 803000 150 310 580 760 80 1 1 1 1 1 9999 100338
75 amater 10000 800000 210 480 650 790 130 1 1 1 1 1 0 100340
76 moonli 10000 803000 140 430 610 730 80 1 1 1 1 1 9999 100342
77 sayona 10050 803000 200 340 530 710 100 1 1 1 1 1 9999 100343
78 hosita 10000 802000 160 360 480 650 100 1 1 1 1 1 9999 100344
79 ascand 10069 800000 320 540 800 900 180 1 1 1 1 1 0 100347
80 pinkym 10000 800000 240 440 740 810 160 1 1 1 1 1 0 100348
81 advers 10000 802000 150 480 710 830 90 1 1 1 1 1 1 100349
82 straye 10050 800000 250 360 610 730 140 1 1 1 1 1 4 100350
83 destru 10003 800000 190 410 620 720 100 1 1 1 1 1 0 100352
84 venera 10000 800000 150 430 680 750 80 1 1 1 1 1 0 100353
85 dazaii 10000 803000 210 430 730 770 120 1 1 1 1 1 3 100357
86 rearhy 10050 802000 170 320 520 690 70 1 1 1 1 1 9999 100358
87 onlyll 10050 802000 210 320 560 690 130 1 1 1 1 1 9999 100359
88 hellom 10022 801000 180 370 580 720 70 1 1 1 1 1 1 100360
89 megaro2 10100 800000 0 0 990 1000 0 0 0 1 1 0 4 100361
90 tomorr 10003 803000 150 240 440 620 80 1 1 1 1 1 7 100362
91 daybyd 10003 803000 130 260 380 590 60 1 1 1 1 1 7 100363
92 thesig 10000 802000 210 380 560 730 100 1 1 1 1 1 3 100365
93 zonzon 10066 800000 160 330 630 670 50 1 1 1 1 1 1 100367
94 yumemi 10000 802000 120 350 590 690 60 1 1 1 1 1 9999 100369
95 dynami2 10000 804000 180 510 780 800 80 1 1 1 1 1 0 100370
96 memori 10050 803000 260 380 650 840 130 1 1 1 1 1 9999 100371
97 bluede 10000 802000 220 410 580 700 120 1 1 1 1 1 5 100372
98 emerao 10000 802000 300 530 850 0 190 1 1 1 0 1 1 100373
99 auflcb3 10007 802000 160 360 660 860 60 1 1 1 1 1 9999 100374
100 paradi 10005 801000 160 280 530 640 100 1 1 1 1 1 3 100376
101 pigooo 10005 800000 190 340 590 840 130 1 1 1 1 1 6 100377
102 flameu 10050 801000 180 360 570 650 100 1 1 1 1 1 1 100380
103 season 10006 1607000 150 280 440 650 80 1 1 1 1 1 1 100386
104 canonn 10006 1607000 170 280 500 830 70 1 1 1 1 1 1 100387
105 rhapso 10006 1607000 180 340 620 740 60 1 1 1 1 1 1 100388
106 turkis 10006 1607000 190 390 640 840 110 1 1 1 1 1 1 100389
107 biohaz 10006 1609000 150 300 510 640 60 1 1 1 1 1 9999 100390
108 monhan 10006 1609000 100 260 360 540 50 1 1 1 1 1 9999 100391
109 gyakut2 10006 1609000 130 350 430 560 50 1 1 1 1 1 9999 100392
110 street 10006 1609000 130 340 470 660 70 1 1 1 1 1 9999 100393
111 rockma2 10006 1609000 210 340 490 760 140 1 1 1 1 1 9999 100394
112 monhan2 10014 1609000 120 240 430 680 60 1 1 1 1 1 9999 100409
113 monhan3 10014 1609000 180 280 450 730 80 1 1 1 1 1 9999 100410
114 orbita 10050 803000 200 380 620 740 120 1 1 1 1 1 5 100411
115 metall 10021 803000 160 280 570 770 80 1 1 1 1 1 1 100412
116 xmasss 10017 1607000 180 380 560 770 80 1 1 1 1 1 101 100417
117 yejiii 10019 800000 220 360 630 790 100 1 1 1 1 1 0 100418
118 histor 10019 800000 200 360 560 820 110 1 1 1 1 1 0 100419
119 silbur 10026 801000 200 350 540 750 90 1 1 1 1 1 1 100421
120 spicaa 10026 801000 230 360 560 780 100 1 1 1 1 1 1 100422
121 street2 10025 1609000 210 370 550 730 140 1 1 1 1 1 9999 100423
122 street3 10025 1609000 240 380 570 740 130 1 1 1 1 1 9999 100424
123 street4 10025 1609000 170 320 510 780 110 1 1 1 1 1 9999 100425
124 bluede2 10024 801000 160 360 560 810 90 1 1 1 1 1 1 100426
125 pyroma 10050 800000 220 420 700 840 80 1 1 1 1 1 4 100427
126 avemar 10050 1607000 160 310 530 750 80 1 1 1 1 1 5 100428
127 mateki 10050 1607000 180 350 540 790 100 1 1 1 1 1 5 100429
128 densho 10050 800000 280 420 740 900 170 1 1 1 1 1 5 100430
129 sunglo 10050 801000 180 390 590 720 100 1 1 1 1 1 7 100431
130 hereco 10050 801000 160 340 510 680 110 1 1 1 1 1 4 100432
131 dddddd 10050 800000 130 330 530 690 90 1 1 1 1 1 5 100433
132 raidon 10050 801000 300 380 580 870 130 1 1 1 1 1 1 100434
133 thisis 10050 800000 230 370 600 740 120 1 1 1 1 1 4 100435
134 rising 10050 800000 230 380 660 850 140 1 1 1 1 1 4 100436
135 riseup 10050 800000 180 410 670 770 70 1 1 1 1 1 4 100437
136 tricko 10050 800000 230 340 560 750 110 1 1 1 1 1 0 100438
137 kinbos 10050 800000 220 380 640 770 120 1 1 1 1 1 3 100439
138 thesun 10050 800000 250 400 720 870 130 1 1 1 1 1 4 100441
139 lovech 10050 801000 160 300 460 680 80 1 1 1 1 1 7 100445
140 machup 10061 801000 170 320 410 710 90 1 1 1 1 1 6 100447
141 cyberg 10104 801000 230 350 600 0 120 1 1 1 0 1 0 100448
142 harmon 10050 801000 200 360 490 650 120 1 1 1 1 1 7 100449
143 kansho 10056 801000 130 270 550 740 70 1 1 1 1 1 9999 100450
144 cantst 10103 800000 230 420 650 0 110 1 1 1 0 1 0 100455
145 takeit 10050 800000 200 380 650 830 120 1 1 1 1 1 4 100457
146 lespri 10063 800000 250 570 800 0 130 1 1 1 0 1 5 100465
147 notoss 10061 800000 120 420 650 910 80 1 1 1 1 1 4 100466
148 codena 10055 801000 180 350 480 680 90 1 1 1 1 1 1 100468
149 aiohoo 10050 101000 180 290 420 660 100 1 1 1 1 1 1 100471
150 entert 10050 1607000 150 330 540 870 90 1 1 1 1 1 6 100472
151 akaihe 10051 101000 210 320 500 690 80 1 1 1 1 1 1 100473
152 juicys 10051 101000 180 260 450 830 100 1 1 1 1 1 7 100474
153 groove 10055 1601000 220 400 520 730 100 1 1 1 1 1 103 100475
154 groove2 10058 1601000 150 400 580 850 90 1 1 1 1 1 9999 100480
155 everyt 10063 801000 220 300 740 0 130 1 1 1 0 1 5 100482
156 taikoo 10058 1609000 130 390 500 750 60 1 1 1 1 1 104 100483
157 overcl2 10057 809000 230 420 580 740 120 1 1 1 1 1 3 100486
158 overcl 10059 809000 120 350 570 860 80 1 1 1 1 1 4 100487
159 groove3 10062 2009000 180 340 640 850 100 1 1 1 1 1 105 100488
160 groove4 10062 2009000 220 350 500 750 120 1 1 1 1 1 106 100489
161 honeyo 10064 802000 320 630 880 930 240 1 1 1 1 1 6 100490
162 groove5 10064 1609000 240 370 670 0 140 1 1 1 0 1 107 100491
163 nekofu 10064 1607000 220 340 570 800 100 1 1 1 1 1 6 100493
164 groove6 10065 1609000 300 640 790 0 220 1 1 1 0 1 3 100494
165 sunglo2 10065 801000 210 360 670 810 110 1 1 1 1 1 6 100495
166 monhan4 10066 1609000 120 400 510 620 50 1 1 1 1 1 9999 100496
167 monhan5 10066 1609000 120 350 420 650 100 1 1 1 1 1 9999 100497
168 fourte 10066 800000 240 500 740 800 140 1 1 1 1 1 5 100498
169 cirnon 10069 409000 240 400 600 790 170 1 1 1 1 1 9999 100499
170 marisa 10069 409000 250 410 620 850 180 1 1 1 1 1 9999 100500
171 yakini 10069 409000 250 340 580 820 130 1 1 1 1 1 9999 100501
172 justic 10069 409000 190 360 570 830 140 1 1 1 1 1 1 100502
173 sintyo 10069 401000 250 460 700 830 160 1 1 1 1 1 6 100503
174 darkpa 10068 800000 260 390 700 840 160 1 1 1 1 1 3 100504
175 hervor 10068 800000 280 390 730 810 180 1 1 1 1 1 5 100505
176 blackl 10069 800000 190 410 730 840 120 1 1 1 1 1 3 100506
177 minest 10070 800000 210 390 620 760 130 1 1 1 1 1 1 100507
178 ordine 10070 800000 250 430 730 820 190 1 1 1 1 1 3 100508
179 dreamw 10072 800000 260 370 620 750 160 1 1 1 1 1 0 100509
180 minerv 10072 800000 320 610 900 0 250 1 1 1 0 1 4 100510
181 sekain 10073 800000 260 390 690 780 160 1 1 1 1 1 1 100511
182 farawa 10074 800000 230 360 600 760 180 1 1 1 1 1 7 100512
183 xxxrev 10076 800000 210 340 560 730 150 1 1 1 1 1 0 100513
184 daybre 10080 201000 160 320 530 720 90 1 1 1 1 1 0 100514
185 umiyur 10080 201000 140 280 460 640 80 1 1 1 1 1 1 100515
186 chalur 10080 201000 180 400 600 720 140 1 1 1 1 1 9999 100516
187 melanc 10080 201000 150 300 500 630 100 1 1 1 1 1 7 100517
188 konofu 10080 201000 230 350 620 810 110 1 1 1 1 1 1 100518
189 redhea 10081 401000 270 390 590 720 100 1 1 1 1 1 0 100519
190 warnin 10081 401000 250 360 610 740 120 1 1 1 1 1 9999 100520
191 topsec 10081 401000 240 340 510 640 130 1 1 1 1 1 9999 100521
192 dddoll 10081 401000 260 380 550 630 140 1 1 1 1 1 9999 100522
193 crocus 10077 800000 260 370 600 720 150 1 1 1 1 1 7 100524
194 moonsh 10102 800000 230 360 620 0 100 1 1 1 0 1 3 100525
195 bladem 10078 800000 280 380 630 750 170 1 1 1 1 1 4 100526
196 primaa 10104 800000 180 350 540 750 120 1 1 1 1 1 0 100527
197 fracta 10103 800000 310 520 830 0 190 1 1 1 0 1 3 100529
198 regene 10100 800000 200 300 560 700 130 1 1 1 1 1 0 100530
199 cthugh 10104 800000 250 480 730 0 140 1 1 1 0 1 0 100531
200 einher 10100 800000 290 400 740 800 160 1 1 1 1 1 4 100532
201 southw 10078 800000 180 270 570 680 120 1 1 1 1 1 7 100536
202 ryuuse 10078 800000 210 320 590 0 130 1 1 1 0 1 1 100537
203 ariell 10100 800000 190 320 640 730 150 1 1 1 1 1 7 100540
204 chipnn 10077 800000 270 340 610 790 160 1 1 1 1 1 6 100541
205 firstl 10100 800000 250 360 650 770 170 1 1 1 1 1 1 100542
206 lighto 10100 800000 250 330 600 740 120 1 1 1 1 1 1 100543
207 palpit 10102 800000 290 550 840 920 180 1 1 1 1 1 8 100544
208 lavien 10077 800000 270 410 710 800 180 1 1 1 1 1 5 100546
209 prizmm 10102 800000 210 300 540 0 120 1 1 1 0 1 1 100547
210 tracee 10081 401000 190 310 490 650 90 1 1 1 1 1 0 100548
211 allelu 10100 800000 280 350 640 750 160 1 1 1 1 1 9999 100549
212 heartl 10100 800000 230 300 640 0 110 1 1 1 0 1 1 100550
213 erasee 10100 800000 220 350 580 680 120 1 1 1 1 1 0 100551
214 termin 10100 800000 240 340 630 790 130 1 1 1 1 1 7 100552
215 kokoro 10103 800000 200 430 650 690 120 1 1 1 1 1 1 100554
216 nisenn 10103 800000 0 0 760 0 0 0 0 1 0 0 8 100555
217 ryuuse2 10101 800000 200 360 620 750 130 1 1 1 1 1 1 100556
218 gainen 10102 801000 260 370 630 0 150 1 1 1 0 1 9999 100558
219 moonki 10102 800000 250 390 640 0 130 1 1 1 0 1 1 100559
220 moonri 10102 800000 210 380 580 850 140 1 1 1 1 1 0 100560
221 goaway 10103 800000 230 450 590 700 100 1 1 1 1 1 0 100561
222 poweri 10103 800000 260 490 750 910 130 1 1 1 1 1 4 100563
223 memorm 10103 800000 260 370 730 0 150 1 1 1 0 1 0 100565
224 itback 10103 800000 230 380 710 0 120 1 1 1 0 1 3 100567
225 actual 10103 800000 250 380 800 0 140 1 1 1 0 1 0 100568
226 redhhh 10103 800000 240 390 770 0 130 1 1 1 0 1 4 100569
227 thetaa 10104 800000 210 340 620 0 110 1 1 1 0 1 1 100571
228 takesc 10104 800000 270 370 690 0 100 1 1 1 0 1 1 100572
229 kotobu 10104 800000 320 710 900 0 250 1 1 1 0 1 0 100573
230 galaxy 10000 101000 160 320 430 670 100 1 1 1 1 1 0 100600
231 levelf 10000 101000 110 280 450 630 50 1 1 1 1 1 0 100605
232 unbeli 10000 101000 130 260 380 620 70 1 1 1 1 1 0 100608
233 fiveee 10000 101000 180 370 610 710 100 1 1 1 1 1 0 100614
234 replay 10000 101000 180 440 630 700 80 1 1 1 1 1 0 100618
235 aheadd 10000 101000 130 250 350 580 70 1 1 1 1 1 0 100620
236 musicr1 10000 800000 220 330 580 740 120 1 1 1 1 1 0 100621
237 getthe 10000 101000 170 370 490 660 60 1 1 1 1 1 0 100622
238 hopesb 10000 101000 100 250 440 610 70 1 1 1 1 1 0 100625
239 shooti 10000 101000 150 370 490 690 70 1 1 1 1 1 0 100626
240 lightm 10000 101000 260 330 540 710 110 1 1 1 1 1 0 100629
241 miiroo 10000 101000 220 390 580 680 110 1 1 1 1 1 0 100630
242 vividd 10000 101000 160 350 550 650 90 1 1 1 1 1 0 100633
243 donuth 10000 201000 220 400 540 800 110 1 1 1 1 1 100 100635
244 senbon 10000 301000 200 280 540 740 120 1 1 1 1 1 0 100636
245 kmtyju 10000 101000 240 310 570 740 120 1 1 1 1 1 0 100637
246 childe 10000 101000 90 240 340 560 40 1 1 1 1 1 0 100640
247 flower 10000 101000 70 200 400 650 60 1 1 1 1 1 0 100643
248 musicr4 10000 800000 190 320 580 0 120 1 1 1 0 1 0 100647
249 breaka 10000 101000 150 310 450 680 70 1 1 1 1 1 0 100657
250 izimed 10000 101000 190 390 690 770 90 1 1 1 1 1 0 100665
251 godkno 10000 101000 100 260 450 640 60 1 1 1 1 1 0 100670
252 roadof 10000 101000 150 360 500 750 70 1 1 1 1 1 0 100671
253 rokuch 10000 301000 210 350 620 810 110 1 1 1 1 1 0 100672
254 valent 10000 800000 270 330 590 770 100 1 1 1 1 1 102 100673
255 unfini 10000 101000 160 320 500 710 80 1 1 1 1 1 0 100674
256 dropou 10000 101000 170 310 460 690 140 1 1 1 1 1 0 100678
257 xencou 10000 101000 200 320 520 600 80 1 1 1 1 1 0 100679
258 killyk 10000 101000 130 420 630 760 60 1 1 1 1 1 0 100680
259 burstt 10000 101000 120 250 460 630 70 1 1 1 1 1 0 100682
260 musicr2 10000 800000 220 330 580 0 120 1 1 1 0 1 0 100683
261 musicr3 10000 800000 190 320 580 720 120 1 1 1 1 1 0 100687
262 sugars 10000 101000 170 300 420 660 60 1 1 1 1 1 0 100689
263 wegooo 10000 101000 180 340 540 680 100 1 1 1 1 1 0 100692
264 alonee 10000 101000 110 210 360 480 50 1 1 1 1 1 0 100693
265 granro 10000 101000 150 280 430 600 80 1 1 1 1 1 0 100695
266 sister 10000 101000 100 270 460 630 70 1 1 1 1 1 0 100696
267 flawli 10000 101000 170 300 400 590 80 1 1 1 1 1 0 100699
268 inneru 10000 101000 220 360 480 670 90 1 1 1 1 1 0 100703
269 bokuwa 10000 101000 230 340 550 690 160 1 1 1 1 1 0 100706
270 showww 10000 101000 180 350 510 790 150 1 1 1 1 1 0 100707
271 nevers 10000 101000 260 320 650 750 150 1 1 1 1 1 0 100708
272 bleeze 10000 101000 160 310 470 620 90 1 1 1 1 1 0 100709

View File

@ -175,8 +175,8 @@ class CxbServlet(BaseServlet):
internal_ver = CxbConstants.VER_CROSSBEATS_REV_SUNRISE_S2
if not hasattr(self.versions[internal_ver], func_to_find):
self.logger.warn(f"{version_string} has no handler for filetype {filetype} / {func_to_find}")
return({"data":""})
self.logger.warn(f"{version_string} has no handler for filetype {filetype}")
return ""
self.logger.info(f"{version_string} request for filetype {filetype}")
self.logger.debug(req_json)

View File

@ -122,7 +122,7 @@ class CxbRevSunriseS1(CxbBase):
def handle_data_extra_stage_list_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_exxxxx_request(self, data: Dict) -> Dict:
def handle_data_ex0001_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_one_more_extra_list_request(self, data: Dict) -> Dict:
@ -131,7 +131,7 @@ class CxbRevSunriseS1(CxbBase):
def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_oexxxx_request(self, data: Dict) -> Dict:
def handle_data_oe0001_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_free_coupon_request(self, data: Dict) -> Dict:

View File

@ -22,17 +22,8 @@ class CxbRevSunriseS2(CxbBase):
@cached(lifetime=86400)
def handle_data_music_list_request(self, data: Dict) -> Dict:
version = data["dldate"]["filetype"].split("/")[0]
ret_str = ""
if "10104" in version:
self.logger.warning("Game Version is Season 2 Non-Omni")
file = "titles/cxb/data/rss2/MusicArchiveList-NonOmni.csv"
else:
self.logger.warning("Game Version is Season 2 Omnimix")
file = "titles/cxb/data/rss2/MusicArchiveList.csv"
with open(rf"{file}") as music:
with open(r"titles/cxb/data/rss2/MusicArchiveList.csv") as music:
lines = music.readlines()
for line in lines:
line_split = line.split(",")
@ -129,14 +120,9 @@ class CxbRevSunriseS2(CxbBase):
return {"data": ret_str}
def handle_data_extra_stage_list_request(self, data: Dict) -> Dict:
ret_str=""
with open(r"titles/cxb/data/rss2/ExtraStageList.csv") as extra:
lines = extra.readlines()
for line in lines:
ret_str += f"{line[:-1]}\r\n"
return({"data":ret_str})
return {"data": ""}
def handle_data_exxxxx_request(self, data: Dict) -> Dict:
def handle_data_ex0001_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_one_more_extra_list_request(self, data: Dict) -> Dict:
@ -145,16 +131,11 @@ class CxbRevSunriseS2(CxbBase):
def handle_data_bonus_list10100_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_oexxxx_request(self, data: Dict) -> Dict:
def handle_data_oe0001_request(self, data: Dict) -> Dict:
return {"data": ""}
def handle_data_free_coupon_request(self, data: Dict) -> Dict:
ret_str=""
with open(r"titles/cxb/data/rss2/FreeCoupon.csv") as coupon:
lines = coupon.readlines()
for line in lines:
ret_str += f"{line[:-1]}\r\n"
return({"data":ret_str})
return {"data": ""}
@cached(lifetime=86400)
def handle_data_news_list_request(self, data: Dict) -> Dict:

View File

@ -1,4 +1,4 @@
import datetime
from datetime import datetime
from typing import Any, List, Dict
import logging
import json
@ -9,6 +9,7 @@ from core.config import CoreConfig
from titles.diva.config import DivaConfig
from titles.diva.const import DivaConstants
from titles.diva.database import DivaData
from titles.diva.handlers import *
class DivaBase:
@ -21,33 +22,42 @@ class DivaBase:
self.game = DivaConstants.GAME_CODE
self.version = DivaConstants.VER_PROJECT_DIVA_ARCADE_FUTURE_TONE
dt = datetime.datetime.now()
self.time_lut = urllib.parse.quote(dt.strftime("%Y-%m-%d %H:%M:%S:16.0"))
dt = datetime.now()
self.time_lut = parse.quote(dt.strftime("%Y-%m-%d %H:%M:%S:16.0"))
def handle_test_request(self, data: Dict) -> Dict:
return ""
def handle_test_request(self, data: bytes) -> str:
pass
def handle_game_init_request(self, data: Dict) -> Dict:
return f""
def handle_game_init_request(self, data: bytes) -> str:
req = GameInitRequest(data)
return None
def handle_attend_request(self, data: Dict) -> Dict:
def handle_attend_request(self, data: bytes) -> str:
req = AttendRequest(data)
resp = AttendResponse(req.req_id)
for i in [0, 3, 4, 5, 7, 9, 10, 11, 12, 13]:
resp.atnd_prm1[i] = 0
resp.atnd_prm1[8] = 100
resp.atnd_prm2[:6] = [30, 10, 100, 4, 1, 50]
resp.atnd_prm3[0] = 100
resp.atnd_prm3[1] = 0
resp.atnd_prm3[10:13] = [2, 3, 4]
resp.atnd_prm3[16:19] = [3, 4, 5]
resp.atnd_prm3[22:25] = [4, 5, 6]
resp.atnd_prm3[80:] = [0] * 20
resp.atnd_prm3[28:79] = [5,6,7,4,4,4,9,10,14,5,10,10,25,20,50,30,90,5,
10,10,25,20,50,30,90,5,10,10,25,20,50,30,90,5,10,10,25,20,50,30,90,
5,10,10,25,20,50,30,90,10,30]
return resp.make()
def handle_ping_request(self, data: bytes) -> str:
encoded = "&"
params = {
"atnd_prm1": "0,1,1,0,0,0,1,0,100,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1",
"atnd_prm2": "30,10,100,4,1,50,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1",
"atnd_prm3": "100,0,1,1,1,1,1,1,1,1,2,3,4,1,1,1,3,4,5,1,1,1,4,5,6,1,1,1,5,6,7,4,4,4,9,10,14,5,10,10,25,20,50,30,90,5,10,10,25,20,50,30,90,5,10,10,25,20,50,30,90,5,10,10,25,20,50,30,90,5,10,10,25,20,50,30,90,10,30,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
"atnd_lut": f"{self.time_lut}",
}
encoded += urllib.parse.urlencode(params)
encoded = encoded.replace("%2C", ",")
return encoded
def handle_ping_request(self, data: Dict) -> Dict:
encoded = "&"
params = {
"ping_b_msg": f"Welcome to {self.core_cfg.server.name} network!",
"ping_b_msg": f"Welcome to {self.core_cfg.server.name} network!" if not self.game_config.server.banner_msg else self.game_config.server.banner_msg,
"ping_m_msg": "xxx",
"atnd_lut": f"{self.time_lut}",
"fi_lut": f"{self.time_lut}",
@ -83,13 +93,12 @@ class DivaBase:
"nblss_ltt_ed_tm": "2019-09-22 12:00:00.0",
}
encoded += urllib.parse.urlencode(params)
encoded += parse.urlencode(params, safe=",")
encoded = encoded.replace("+", "%20")
encoded = encoded.replace("%2C", ",")
return encoded
def handle_pv_list_request(self, data: Dict) -> Dict:
def handle_pv_list_request(self, data: bytes) -> str:
pvlist = ""
with open(r"titles/diva/data/PvList0.dat", encoding="utf-8") as shop:
lines = shop.readlines()
@ -126,7 +135,7 @@ class DivaBase:
return response
def handle_shop_catalog_request(self, data: Dict) -> Dict:
def handle_shop_catalog_request(self, data: bytes) -> str:
catalog = ""
shopList = self.data.static.get_enabled_shops(self.version)
@ -134,8 +143,8 @@ class DivaBase:
with open(r"titles/diva/data/ShopCatalog.dat", encoding="utf-8") as shop:
lines = shop.readlines()
for line in lines:
line = urllib.parse.quote(line) + ","
catalog += f"{urllib.parse.quote(line)}"
line = parse.quote(line) + ","
catalog += f"{parse.quote(line)}"
else:
for shop in shopList:
@ -154,8 +163,8 @@ class DivaBase:
+ ","
+ str(shop["type"])
)
line = urllib.parse.quote(line) + ","
catalog += f"{urllib.parse.quote(line)}"
line = parse.quote(line) + ","
catalog += f"{parse.quote(line)}"
catalog = catalog.replace("+", "%20")
@ -164,7 +173,7 @@ class DivaBase:
return response
def handle_buy_module_request(self, data: Dict) -> Dict:
def handle_buy_module_request(self, data: bytes) -> str:
profile = self.data.profile.get_profile(data["pd_id"], self.version)
module = self.data.static.get_enabled_shop(self.version, int(data["mdl_id"]))
@ -191,7 +200,7 @@ class DivaBase:
return response
def handle_cstmz_itm_ctlg_request(self, data: Dict) -> Dict:
def handle_cstmz_itm_ctlg_request(self, data: bytes) -> str:
catalog = ""
itemList = self.data.static.get_enabled_items(self.version)
@ -199,8 +208,8 @@ class DivaBase:
with open(r"titles/diva/data/ItemCatalog.dat", encoding="utf-8") as item:
lines = item.readlines()
for line in lines:
line = urllib.parse.quote(line) + ","
catalog += f"{urllib.parse.quote(line)}"
line = parse.quote(line) + ","
catalog += f"{parse.quote(line)}"
else:
for item in itemList:
@ -219,8 +228,8 @@ class DivaBase:
+ ","
+ str(item["type"])
)
line = urllib.parse.quote(line) + ","
catalog += f"{urllib.parse.quote(line)}"
line = parse.quote(line) + ","
catalog += f"{parse.quote(line)}"
catalog = catalog.replace("+", "%20")
@ -229,7 +238,7 @@ class DivaBase:
return response
def handle_buy_cstmz_itm_request(self, data: Dict) -> Dict:
def handle_buy_cstmz_itm_request(self, data: bytes) -> str:
profile = self.data.profile.get_profile(data["pd_id"], self.version)
item = self.data.static.get_enabled_item(
self.version, int(data["cstmz_itm_id"])
@ -264,7 +273,7 @@ class DivaBase:
return response
def handle_festa_info_request(self, data: Dict) -> Dict:
def handle_festa_info_request(self, data: bytes) -> str:
encoded = "&"
params = {
"fi_id": "1,2",
@ -277,17 +286,17 @@ class DivaBase:
"fi_add_vp": "20,5",
"fi_mul_vp": "1,2",
"fi_st": "2019-01-01 00:00:00.0,2019-01-01 00:00:00.0",
"fi_et": "2029-01-01 00:00:00.0,2029-01-01 00:00:00.0",
"fi_lut": "{self.time_lut}",
"fi_et": "2029-01-01 00:00:00.0,2029-01-01 00:00:00.0", # TODO: make this last longer
"fi_lut": f"{self.time_lut}",
}
encoded += urllib.parse.urlencode(params)
encoded += parse.urlencode(params)
encoded = encoded.replace("+", "%20")
encoded = encoded.replace("%2C", ",")
return encoded
def handle_contest_info_request(self, data: Dict) -> Dict:
def handle_contest_info_request(self, data: bytes) -> str:
response = ""
response += f"&ci_lut={self.time_lut}"
@ -295,7 +304,7 @@ class DivaBase:
return response
def handle_qst_inf_request(self, data: Dict) -> Dict:
def handle_qst_inf_request(self, data: bytes) -> str:
quest = ""
questList = self.data.static.get_enabled_quests(self.version)
@ -303,7 +312,7 @@ class DivaBase:
with open(r"titles/diva/data/QuestInfo.dat", encoding="utf-8") as shop:
lines = shop.readlines()
for line in lines:
quest += f"{urllib.parse.quote(line)},"
quest += f"{parse.quote(line)},"
response = ""
response += f"&qi_lut={self.time_lut}"
@ -331,7 +340,7 @@ class DivaBase:
+ ","
+ str(quests["quest_enable"])
)
quest += f"{urllib.parse.quote(line)}%0A,"
quest += f"{parse.quote(line)}%0A,"
responseline = f"{quest[:-1]},"
for i in range(len(questList), 59):
@ -345,166 +354,108 @@ class DivaBase:
return response
def handle_nv_ranking_request(self, data: Dict) -> Dict:
return f""
def handle_nv_ranking_request(self, data: bytes) -> str:
pass
def handle_ps_ranking_request(self, data: Dict) -> Dict:
return f""
def handle_ps_ranking_request(self, data: bytes) -> str:
pass
def handle_ng_word_request(self, data: Dict) -> Dict:
return f""
def handle_ng_word_request(self, data: bytes) -> str:
pass
def handle_rmt_wp_list_request(self, data: Dict) -> Dict:
return f""
def handle_rmt_wp_list_request(self, data: bytes) -> str:
pass
def handle_pv_def_chr_list_request(self, data: Dict) -> Dict:
return f""
def handle_pv_def_chr_list_request(self, data: bytes) -> str:
pass
def handle_pv_ng_mdl_list_request(self, data: Dict) -> Dict:
return f""
def handle_pv_ng_mdl_list_request(self, data: bytes) -> str:
pass
def handle_cstmz_itm_ng_mdl_lst_request(self, data: Dict) -> Dict:
return f""
def handle_cstmz_itm_ng_mdl_lst_request(self, data: bytes) -> str:
pass
def handle_banner_info_request(self, data: Dict) -> Dict:
return f""
def handle_banner_info_request(self, data: bytes) -> str:
pass
def handle_banner_data_request(self, data: Dict) -> Dict:
return f""
def handle_banner_data_request(self, data: bytes) -> str:
pass
def handle_cm_ply_info_request(self, data: Dict) -> Dict:
return f""
def handle_cm_ply_info_request(self, data: bytes) -> str:
pass
def handle_pstd_h_ctrl_request(self, data: Dict) -> Dict:
return f""
def handle_pstd_h_ctrl_request(self, data: bytes) -> str:
pass
def handle_pstd_item_ng_lst_request(self, data: Dict) -> Dict:
return f""
def handle_pstd_item_ng_lst_request(self, data: bytes) -> str:
pass
def handle_pre_start_request(self, data: Dict) -> str:
profile = self.data.profile.get_profile(data["aime_id"], self.version)
profile_shop = self.data.item.get_shop(data["aime_id"], self.version)
def handle_pre_start_request(self, data: bytes) -> str:
req = PreStartRequest(data)
resp = PreStartResponse(req.req_id, req.aime_id)
profile = self.data.profile.get_profile(req.aime_id, self.version)
profile_shop = self.data.item.get_shop(req.aime_id, self.version)
if profile is None:
return f"&ps_result=-3"
else:
response = "&ps_result=1"
response += "&accept_idx=100"
response += "&nblss_ltt_stts=-1"
response += "&nblss_ltt_tckt=-1"
response += "&nblss_ltt_is_opn=-1"
response += f"&pd_id={data['aime_id']}"
response += f"&player_name={profile['player_name']}"
response += f"&sort_kind={profile['player_name']}"
response += f"&lv_efct_id={profile['lv_efct_id']}"
response += f"&lv_plt_id={profile['lv_plt_id']}"
response += f"&lv_str={profile['lv_str']}"
response += f"&lv_num={profile['lv_num']}"
response += f"&lv_pnt={profile['lv_pnt']}"
response += f"&vcld_pts={profile['vcld_pts']}"
response += f"&skn_eqp={profile['skn_eqp']}"
response += f"&btn_se_eqp={profile['btn_se_eqp']}"
response += f"&sld_se_eqp={profile['sld_se_eqp']}"
response += f"&chn_sld_se_eqp={profile['chn_sld_se_eqp']}"
response += f"&sldr_tch_se_eqp={profile['sldr_tch_se_eqp']}"
response += f"&passwd_stat={profile['passwd_stat']}"
# Store stuff to add to rework
response += f"&mdl_eqp_tm={self.time_lut}"
profile_dict = profile._asdict()
profile_dict.pop("id")
profile_dict.pop("user")
profile_dict.pop("version")
mdl_eqp_ary = "-999,-999,-999"
for k, v in profile_dict.items():
if hasattr(resp, k):
setattr(resp, k, v)
# get the common_modules from the profile shop
if profile_shop:
mdl_eqp_ary = profile_shop["mdl_eqp_ary"]
if profile_shop is not None and profile_shop:
resp.mdl_eqp_ary = profile_shop["mdl_eqp_ary"]
response += f"&mdl_eqp_ary={mdl_eqp_ary}"
return resp.make()
return response
def handle_registration_request(self, data: Dict) -> Dict:
self.data.profile.create_profile(
self.version, data["aime_id"], data["player_name"]
def handle_registration_request(self, data: bytes) -> str:
req = RegisterRequest(data)
pd_id = self.data.profile.create_profile(
self.version, req.aime_id, req.player_name
)
return f"&cd_adm_result=1&pd_id={data['aime_id']}"
if pd_id is None:
return "&cd_adm_result=-1"
return RegisterResponse(req.req_id, req.aime_id).make()
def handle_start_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile(data["pd_id"], self.version)
profile_shop = self.data.item.get_shop(data["pd_id"], self.version)
def handle_start_request(self, data: bytes) -> str:
req = StartRequest(data)
profile = self.data.profile.get_profile(req.pd_id, self.version)
profile_shop = self.data.item.get_shop(req.pd_id, self.version)
if profile is None:
return
resp = StartResponse(req.req_id, req.pd_id, profile['player_name'])
profile_dict = profile._asdict()
profile_dict.pop("id")
profile_dict.pop("user")
profile_dict.pop("version")
for k, v in profile_dict.items():
if hasattr(resp, k):
setattr(resp, k, v)
mdl_have = "F" * 250
# generate the mdl_have string if "unlock_all_modules" is disabled
if not self.game_config.mods.unlock_all_modules:
mdl_have = self.data.module.get_modules_have_string(
data["pd_id"], self.version
resp.mdl_have = self.data.module.get_modules_have_string(
req.pd_id, self.version
)
cstmz_itm_have = "F" * 250
# generate the cstmz_itm_have string if "unlock_all_items" is disabled
if not self.game_config.mods.unlock_all_items:
cstmz_itm_have = self.data.customize.get_customize_items_have_string(
data["pd_id"], self.version
resp.cstmz_itm_have = self.data.customize.get_customize_items_have_string(
req.pd_id, self.version
)
response = f"&pd_id={data['pd_id']}"
response += "&start_result=1"
response += "&accept_idx=100"
response += f"&hp_vol={profile['hp_vol']}"
response += f"&btn_se_vol={profile['btn_se_vol']}"
response += f"&btn_se_vol2={profile['btn_se_vol2']}"
response += f"&sldr_se_vol2={profile['sldr_se_vol2']}"
response += f"&sort_kind={profile['sort_kind']}"
response += f"&player_name={profile['player_name']}"
response += f"&lv_num={profile['lv_num']}"
response += f"&lv_pnt={profile['lv_pnt']}"
response += f"&lv_efct_id={profile['lv_efct_id']}"
response += f"&lv_plt_id={profile['lv_plt_id']}"
response += f"&mdl_have={mdl_have}"
response += f"&cstmz_itm_have={cstmz_itm_have}"
response += f"&use_pv_mdl_eqp={int(profile['use_pv_mdl_eqp'])}"
response += f"&use_mdl_pri={int(profile['use_mdl_pri'])}"
response += f"&use_pv_skn_eqp={int(profile['use_pv_skn_eqp'])}"
response += f"&use_pv_btn_se_eqp={int(profile['use_pv_btn_se_eqp'])}"
response += f"&use_pv_sld_se_eqp={int(profile['use_pv_sld_se_eqp'])}"
response += f"&use_pv_chn_sld_se_eqp={int(profile['use_pv_chn_sld_se_eqp'])}"
response += f"&use_pv_sldr_tch_se_eqp={int(profile['use_pv_sldr_tch_se_eqp'])}"
response += f"&vcld_pts={profile['lv_efct_id']}"
response += f"&nxt_pv_id={profile['nxt_pv_id']}"
response += f"&nxt_dffclty={profile['nxt_dffclty']}"
response += f"&nxt_edtn={profile['nxt_edtn']}"
response += f"&dsp_clr_brdr={profile['dsp_clr_brdr']}"
response += f"&dsp_intrm_rnk={profile['dsp_intrm_rnk']}"
response += f"&dsp_clr_sts={profile['dsp_clr_sts']}"
response += f"&rgo_sts={profile['rgo_sts']}"
# Contest progress
response += f"&cv_cid=-1,-1,-1,-1"
response += f"&cv_sc=-1,-1,-1,-1"
response += f"&cv_bv=-1,-1,-1,-1"
response += f"&cv_bv=-1,-1,-1,-1"
response += f"&cv_bf=-1,-1,-1,-1"
# Contest now playing id, return -1 if no current playing contest
response += f"&cnp_cid={profile['cnp_cid']}"
response += f"&cnp_val={profile['cnp_val']}"
# border can be 0=bronzem 1=silver, 2=gold
response += f"&cnp_rr={profile['cnp_rr']}"
# only show contest specifier if it is not empty
response += f"&cnp_sp={profile['cnp_sp']}" if profile["cnp_sp"] != "" else ""
# To be fully fixed
if "my_qst_id" not in profile:
response += f"&my_qst_id=-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"
response += f"&my_qst_sts=0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"
else:
response += f"&my_qst_id={profile['my_qst_id']}"
response += f"&my_qst_sts={profile['my_qst_sts']}"
response += f"&my_qst_prgrs=0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"
response += f"&my_qst_et=2022-06-19%2010%3A28%3A52.0,2022-06-19%2010%3A28%3A52.0,2022-06-19%2010%3A28%3A52.0,2100-01-01%2008%3A59%3A59.0,2100-01-01%2008%3A59%3A59.0,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx"
if "my_qst_id" in profile:
resp.my_qst_id = profile['my_qst_id']
resp.my_qst_sts = profile['my_qst_sts']
# define a helper class to store all counts for clear, great,
# excellent and perfect
@ -525,7 +476,7 @@ class DivaBase:
}
# get clear status from user scores
pv_records = self.data.score.get_best_scores(data["pd_id"])
pv_records = self.data.score.get_best_scores(req.pd_id)
clear_status = "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"
if pv_records is not None:
@ -561,46 +512,33 @@ class DivaBase:
clear_status = ",".join(map(str, clear_list))
response += f"&clr_sts={clear_status}"
# Store stuff to add to rework
response += f"&mdl_eqp_tm={self.time_lut}"
mdl_eqp_ary = "-999,-999,-999"
c_itm_eqp_ary = "-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,-999,-999"
ms_itm_flg_ary = "1,1,1,1,1,1,1,1,1,1,1,1"
resp.clr_sts = clear_status
# get the common_modules, customize_items and customize_item_flags
# from the profile shop
if profile_shop:
mdl_eqp_ary = profile_shop["mdl_eqp_ary"]
c_itm_eqp_ary = profile_shop["c_itm_eqp_ary"]
ms_itm_flg_ary = profile_shop["ms_itm_flg_ary"]
resp.mdl_eqp_ary = profile_shop["mdl_eqp_ary"]
resp.c_itm_eqp_ary = profile_shop["c_itm_eqp_ary"]
resp.ms_itm_flg_ary = profile_shop["ms_itm_flg_ary"]
response += f"&mdl_eqp_ary={mdl_eqp_ary}"
response += f"&c_itm_eqp_ary={c_itm_eqp_ary}"
response += f"&ms_itm_flg_ary={ms_itm_flg_ary}"
return resp.make()
return response
def handle_pd_unlock_request(self, data: bytes) -> str:
pass
def handle_pd_unlock_request(self, data: Dict) -> Dict:
return f""
def handle_spend_credit_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile(data["pd_id"], self.version)
def handle_spend_credit_request(self, data: bytes) -> str:
req = SpendCreditRequest(data)
profile = self.data.profile.get_profile(req.pd_id, self.version)
if profile is None:
return
response = ""
resp = SpendCreditResponse(req.req_id)
resp.vcld_pts = profile['vcld_pts']
resp.lv_str = profile['lv_str']
resp.lv_efct_id = profile['lv_efct_id']
resp.lv_plt_id = profile['lv_plt_id']
response += "&cmpgn_rslt=-1,-1,x,-1,-1,x,x,-1,x,-1,-1,x,-1,-1,x,x,-1,x,-1,-1,x,-1,-1,x,x,-1,x,-1,-1,x,-1,-1,x,x,-1,x,-1,-1,x,-1,-1,x,x,-1,x,-1,-1,x,-1,-1,x,x,-1,x"
response += "&cmpgn_rslt_num=0"
response += f"&vcld_pts={profile['vcld_pts']}"
response += f"&lv_str={profile['lv_str']}"
response += f"&lv_efct_id={profile['lv_efct_id']}"
response += f"&lv_plt_id={profile['lv_plt_id']}"
return response
return resp.make()
def _get_pv_pd_result(
self,
@ -664,31 +602,31 @@ class DivaBase:
return pv_result
def task_generateScoreData(self, data: Dict, pd_by_pv_id, song):
def task_generateScoreData(self, pd_id: int, difficulty: int, pd_by_pv_id: str, song: int):
if int(song) > 0:
# the request do not send a edition so just perform a query best score and ranking for each edition.
# 0=ORIGINAL, 1=EXTRA
pd_db_song_0 = self.data.score.get_best_user_score(
data["pd_id"], int(song), data["difficulty"], edition=0
pd_id, int(song), difficulty, edition=0
)
pd_db_song_1 = self.data.score.get_best_user_score(
data["pd_id"], int(song), data["difficulty"], edition=1
pd_id, int(song), difficulty, edition=1
)
pd_db_ranking_0, pd_db_ranking_1 = None, None
if pd_db_song_0:
pd_db_ranking_0 = self.data.score.get_global_ranking(
data["pd_id"], int(song), data["difficulty"], edition=0
pd_id, int(song), difficulty, edition=0
)
if pd_db_song_1:
pd_db_ranking_1 = self.data.score.get_global_ranking(
data["pd_id"], int(song), data["difficulty"], edition=1
pd_id, int(song), difficulty, edition=1
)
pd_db_customize = self.data.pv_customize.get_pv_customize(
data["pd_id"], int(song)
pd_id, int(song)
)
# generate the pv_result string with the ORIGINAL edition and the EXTRA edition appended
@ -706,14 +644,14 @@ class DivaBase:
pd_by_pv_id.append(",")
def handle_get_pv_pd_request(self, data: Dict) -> Dict:
song_id = data["pd_pv_id_lst"].split(",")
req = GetPvPdRequest(data)
pv = ""
threads = []
pd_by_pv_id = []
for song in song_id:
thread_ScoreData = Thread(target=self.task_generateScoreData(data, pd_by_pv_id, song))
for song in req.pd_pv_id_lst:
thread_ScoreData = Thread(target=self.task_generateScoreData(req.pd_id, req.difficulty, pd_by_pv_id, song))
threads.append(thread_ScoreData)
for x in threads:
@ -725,44 +663,48 @@ class DivaBase:
for x in pd_by_pv_id:
pv += x
resp = GetPvPdResponse(req.req_id)
resp.pd_by_pv_id = pv[:-1]
response = ""
response += f"&pd_by_pv_id={pv[:-1]}"
response += "&pdddt_flg=0"
response += f"&pdddt_tm={self.time_lut}"
return response
return resp.make()
def handle_stage_start_request(self, data: Dict) -> Dict:
return f""
def handle_stage_start_request(self, data: bytes) -> str:
pass
def handle_stage_result_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile(data["pd_id"], self.version)
def handle_stage_result_request(self, data: bytes) -> str:
req = StageResultRequest(data)
resp = StageResultResponse(req.cmd, req.req_id)
profile = self.data.profile.get_profile(req.pd_id, self.version)
pd_song_list = data["stg_ply_pv_id"].split(",")
pd_song_difficulty = data["stg_difficulty"].split(",")
pd_song_edition = data["stg_edtn"].split(",")
pd_song_max_score = data["stg_score"].split(",")
pd_song_max_atn_pnt = data["stg_atn_pnt"].split(",")
pd_song_ranking = data["stg_clr_kind"].split(",")
pd_song_sort_kind = data["sort_kind"]
pd_song_cool_cnt = data["stg_cool_cnt"].split(",")
pd_song_fine_cnt = data["stg_fine_cnt"].split(",")
pd_song_safe_cnt = data["stg_safe_cnt"].split(",")
pd_song_sad_cnt = data["stg_sad_cnt"].split(",")
pd_song_worst_cnt = data["stg_wt_wg_cnt"].split(",")
pd_song_max_combo = data["stg_max_cmb"].split(",")
pd_song_list = req.stg_ply_pv_id
pd_song_difficulty = req.stg_difficulty
pd_song_edition = req.stg_edtn
pd_song_max_score = req.stg_score
pd_song_max_atn_pnt = req.stg_atn_pnt
pd_song_ranking = req.stg_clr_kind
pd_song_sort_kind = req.sort_kind
pd_song_cool_cnt = req.stg_cool_cnt
pd_song_fine_cnt = req.stg_fine_cnt
pd_song_safe_cnt = req.stg_safe_cnt
pd_song_sad_cnt = req.stg_sad_cnt
pd_song_worst_cnt = req.stg_wt_wg_cnt
pd_song_max_combo = req.stg_max_cmb
for index, value in enumerate(pd_song_list):
if "-1" not in pd_song_list[index]:
if pd_song_list[index] > 0:
profile_pd_db_song = self.data.score.get_best_user_score(
data["pd_id"],
req.pd_id,
pd_song_list[index],
pd_song_difficulty[index],
pd_song_edition[index],
)
if profile_pd_db_song is None:
self.data.score.put_best_score(
data["pd_id"],
req.pd_id,
self.version,
pd_song_list[index],
pd_song_difficulty[index],
@ -779,7 +721,7 @@ class DivaBase:
pd_song_max_combo[index],
)
self.data.score.put_playlog(
data["pd_id"],
req.pd_id,
self.version,
pd_song_list[index],
pd_song_difficulty[index],
@ -797,7 +739,7 @@ class DivaBase:
)
elif int(pd_song_max_score[index]) >= int(profile_pd_db_song["score"]):
self.data.score.put_best_score(
data["pd_id"],
req.pd_id,
self.version,
pd_song_list[index],
pd_song_difficulty[index],
@ -814,7 +756,7 @@ class DivaBase:
pd_song_max_combo[index],
)
self.data.score.put_playlog(
data["pd_id"],
req.pd_id,
self.version,
pd_song_list[index],
pd_song_difficulty[index],
@ -832,7 +774,7 @@ class DivaBase:
)
elif int(pd_song_max_score[index]) != int(profile_pd_db_song["score"]):
self.data.score.put_playlog(
data["pd_id"],
req.pd_id,
self.version,
pd_song_list[index],
pd_song_difficulty[index],
@ -852,7 +794,7 @@ class DivaBase:
# Profile saving based on registration list
# Calculate new level
best_scores = self.data.score.get_best_scores(data["pd_id"])
best_scores = self.data.score.get_best_scores(req.pd_id)
total_atn_pnt = 0
for best_score in best_scores:
@ -861,6 +803,18 @@ class DivaBase:
new_level = (total_atn_pnt // 13979) + 1
new_level_pnt = round((total_atn_pnt % 13979) / 13979 * 100)
resp.lv_num_old = int(profile['lv_num'])
resp.lv_pnt_old = int(profile['lv_pnt'])
resp.lv_num = new_level
resp.lv_str = profile['lv_str']
resp.lv_pnt = new_level_pnt
resp.lv_efct_id = int(profile['lv_efct_id'])
resp.lv_plt_id = int(profile['lv_plt_id'])
resp.vcld_pts = int(profile['vcld_pts'])
resp.prsnt_vcld_pts = int(profile['vcld_pts'])
if "my_qst_id" not in profile:
resp.my_qst_id = profile['my_qst_id']
response = "&chllng_kind=-1"
response += f"&lv_num_old={int(profile['lv_num'])}"
response += f"&lv_pnt_old={int(profile['lv_pnt'])}"
@ -870,16 +824,16 @@ class DivaBase:
profile["user"],
lv_num=new_level,
lv_pnt=new_level_pnt,
vcld_pts=int(data["vcld_pts"]),
hp_vol=int(data["hp_vol"]),
btn_se_vol=int(data["btn_se_vol"]),
sldr_se_vol2=int(data["sldr_se_vol2"]),
sort_kind=int(data["sort_kind"]),
nxt_pv_id=int(data["ply_pv_id"]),
nxt_dffclty=int(data["nxt_dffclty"]),
nxt_edtn=int(data["nxt_edtn"]),
my_qst_id=data["my_qst_id"],
my_qst_sts=data["my_qst_sts"],
vcld_pts=req.vcld_pts,
hp_vol=req.hp_vol,
btn_se_vol=req.btn_se_vol,
sldr_se_vol2=req.sldr_se_vol2,
sort_kind=req.sort_kind,
nxt_pv_id=req.ply_pv_id,
nxt_dffclty=req.nxt_dffclty,
nxt_edtn=req.nxt_edtn,
my_qst_id=req.my_qst_id,
my_qst_sts=req.my_qst_sts,
)
response += f"&lv_num={new_level}"
@ -912,17 +866,18 @@ class DivaBase:
response += "&my_ccd_r_hnd=-1,-1,-1,-1,-1"
response += "&my_ccd_r_vp=-1,-1,-1,-1,-1"
return response
return resp.make()
def handle_end_request(self, data: Dict) -> Dict:
profile = self.data.profile.get_profile(data["pd_id"], self.version)
def handle_end_request(self, data: bytes) -> str:
req = EndRequest(data)
profile = self.data.profile.get_profile(req.pd_id, self.version)
self.data.profile.update_profile(
profile["user"], my_qst_id=data["my_qst_id"], my_qst_sts=data["my_qst_sts"]
profile["user"], my_qst_id=req.my_qst_id, my_qst_sts=req.my_qst_sts
)
return f""
return None
def handle_shop_exit_request(self, data: Dict) -> Dict:
def handle_shop_exit_request(self, data: bytes) -> str:
self.data.item.put_shop(
data["pd_id"],
self.version,

View File

@ -18,6 +18,12 @@ class DivaServerConfig:
self.__config, "diva", "server", "loglevel", default="info"
)
)
@property
def banner_msg(self) -> str:
CoreConfig.get_config_field(
self.__config, "diva", "server", "banner_msg", default=""
)
class DivaModsConfig:

View File

@ -6,6 +6,8 @@ class DivaConstants:
VER_PROJECT_DIVA_ARCADE = 0
VER_PROJECT_DIVA_ARCADE_FUTURE_TONE = 1
LUT_TIME_FMT = "%Y-%m-%d %H:%M:%S:16.0"
VERSION_NAMES = ("Project Diva Arcade", "Project Diva Arcade Future Tone")
@classmethod

View File

@ -0,0 +1,3 @@
from .base import *
from .user import *
from .pv import *

View File

@ -0,0 +1,275 @@
from urllib import parse
from urllib.parse import quote
from datetime import datetime
from typing import Union, Dict, List, Any
from ..const import DivaConstants
def lazy_http_form_parse(src: Union[str, bytes]) -> Dict[bytes, bytes]:
out = {}
if type(src) == str:
src = src.encode()
for param in src.split(b"&"):
kvp = param.split(b"=")
out[parse.unquote(kvp[0])] = parse.unquote(kvp[1])
return out
class DivaRequestParseException(Exception):
"""
Exception raised when there is a fault in parsing a diva request,
either due to a malformed request, or missing required items
"""
def __init__(self, message: str) -> None:
self.message = message
super().__init__(self.message)
class BaseBinaryRequest:
cmd: str
req_id: str
def __init__(self, raw: bytes) -> None:
self.raw = raw
self.raw_dict = dict(parse.parse_qsl(raw))
if "cmd" not in self.raw_dict:
raise DivaRequestParseException(f"cmd not in request data {self.raw_dict}")
if "req_id" not in self.raw_dict:
raise DivaRequestParseException(
f"req_id not in request data {self.raw_dict}"
)
for k, v in self.raw_dict.items():
setattr(self, k, v)
class BaseRequest:
def __init__(self, raw: Union[str, bytes]) -> None:
self.raw = raw
try:
self.raw_dict: Dict[str, str] = lazy_http_form_parse(raw)
except UnicodeDecodeError as e:
raise DivaRequestParseException(f"Could not decode data {raw} - {e}")
if "cmd" not in self.raw_dict:
raise DivaRequestParseException(f"cmd not in request data {self.raw_dict}")
if "req_id" not in self.raw_dict:
raise DivaRequestParseException(
f"req_id not in request data {self.raw_dict}"
)
if "place_id" not in self.raw_dict:
raise DivaRequestParseException(
f"place_id not in request data {self.raw_dict}"
)
if "start_up_mode" not in self.raw_dict:
raise DivaRequestParseException(
f"start_up_mode not in request data {self.raw_dict}"
)
if "cmm_dly_mod" not in self.raw_dict:
raise DivaRequestParseException(
f"cmm_dly_mod not in request data {self.raw_dict}"
)
if "cmm_dly_sec" not in self.raw_dict:
raise DivaRequestParseException(
f"cmm_dly_sec not in request data {self.raw_dict}"
)
if "cmm_err_mod" not in self.raw_dict:
raise DivaRequestParseException(
f"cmm_err_mod not in request data {self.raw_dict}"
)
if "region_code" not in self.raw_dict:
raise DivaRequestParseException(
f"region_code not in request data {self.raw_dict}"
)
if "time_stamp" not in self.raw_dict:
raise DivaRequestParseException(
f"time_stamp not in request data {self.raw_dict}"
)
self.cmd: str = self.raw_dict.get('cmd')
self.req_id: str = self.raw_dict.get('req_id')
self.game_id: str = self.raw_dict.get('game_id')
self.r_rev: str = self.raw_dict.get('r_rev')
self.kc_serial: str = self.raw_dict.get('kc_serial')
self.b_serial: str = self.raw_dict.get('b_serial')
self.country_code: str = self.raw_dict.get('country_code')
self.place_id = int(self.raw_dict['place_id'], 16)
self.start_up_mode = int(self.raw_dict.get('start_up_mode'))
self.cmm_dly_mod = int(self.raw_dict.get('cmm_dly_mod'))
self.cmm_dly_sec = int(self.raw_dict.get('cmm_dly_sec'))
self.cmm_err_mod = int(self.raw_dict.get('cmm_err_mod'))
self.region_code = int(self.raw_dict.get('region_code'))
# datetime.now().astimezone().replace(microsecond=0).isoformat()
self.time_stamp = datetime.strptime(self.raw_dict.get('time_stamp'), "%Y-%m-%dT%H:%M:%S%z")
class BaseResponse:
def __init__(self, cmd_id: str, req_id: int) -> None:
self.cmd = cmd_id
self.req_id = req_id
self.stat = "ok"
def make(self) -> str:
itms: List[str] = []
for k, v in vars(self).items():
if type(v) == int:
itms.append(encode_int(k, v))
elif type(v) == bool:
itms.append(encode_bool(k, v))
elif type(v) == datetime:
itms.append(encode_date(k, v))
elif type(v) == list:
itms.append(encode_list(k, v))
else:
itms.append(encode_str(k, v))
return "&".join(itms)
class GameInitRequest(BaseRequest):
def __init__(self, raw: Union[str, bytes]) -> None:
super().__init__(raw)
class AttendRequest(BaseRequest):
def __init__(self, raw: Union[str, bytes]) -> None:
super().__init__(raw)
if 'power_on' not in self.raw_dict:
raise DivaRequestParseException(
f"power_on not in request data {self.raw_dict}"
)
if 'is_bb' not in self.raw_dict:
raise DivaRequestParseException(
f"is_bb not in request data {self.raw_dict}"
)
self.power_on = int(self.raw_dict.get('power_on'))
self.is_bb = bool(int(self.raw_dict.get('is_bb')))
class AttendResponse(BaseResponse):
def __init__(self, req_id: int) -> None:
super().__init__("attend", req_id)
self.atnd_prm1 = [1] * 100
self.atnd_prm2 = [1] * 100
self.atnd_prm3 = [1] * 100
self.atnd_lut = datetime.now()
def make(self) -> str:
return quote(super().make(), safe=",&=")
class SpendCreditRequest(BaseRequest):
def __init__(self, raw: Union[str, bytes]) -> None:
super().__init__(raw)
if 'pd_id' not in self.raw_dict:
raise DivaRequestParseException(
f"pd_id not in request data {self.raw_dict}"
)
if 'my_qst_id' not in self.raw_dict:
raise DivaRequestParseException(
f"my_qst_id not in request data {self.raw_dict}"
)
if 'my_qst_sts' not in self.raw_dict:
raise DivaRequestParseException(
f"my_qst_sts not in request data {self.raw_dict}"
)
if 'crdt_typ' not in self.raw_dict:
raise DivaRequestParseException(
f"crdt_typ not in request data {self.raw_dict}"
)
if 'cmpgn_id' not in self.raw_dict:
raise DivaRequestParseException(
f"cmpgn_id not in request data {self.raw_dict}"
)
if 'cmpgn_pb' not in self.raw_dict:
raise DivaRequestParseException(
f"cmpgn_pb not in request data {self.raw_dict}"
)
self.pd_id = int(self.raw_dict.get('pd_id'))
self.my_qst_id = decode_list_int(self.raw_dict.get('my_qst_id'))
self.my_qst_sts = decode_list_int(self.raw_dict.get('my_qst_sts'))
self.crdt_typ = int(self.raw_dict.get('crdt_typ'))
self.cmpgn_id = decode_list_int(self.raw_dict.get('cmpgn_id'))
self.cmpgn_pb = decode_list_int(self.raw_dict.get('cmpgn_pb'))
class SpendCreditResponse(BaseResponse):
def __init__(self, req_id: int) -> None:
super().__init__("spend_credit", req_id)
self.cmpgn_rslt = ",".join(["-1,-1,x,-1,-1,x,x,-1,x"] * 6)
self.cmpgn_rslt_num = 0
self.vcld_pts = 0
self.lv_str = ""
self.lv_efct_id = 0
self.lv_plt_id = 0
def encode_int(key: str, val: Union[int, None] = None) -> str:
if type(val) != int:
val = -1
return f"{key}={val}"
def encode_bool(key: str, val: Union[bool, None] = None) -> str:
if not val:
return encode_int(key, 0)
return encode_int(key, 1)
def encode_str(key: str, val: Union[str, None] = None, urlencode_val: bool = False) -> str:
if type(val) != str:
val = "xxx"
if urlencode_val:
val = quote(val)
return f"{key}={val}"
def encode_date(key: str, val: Union[datetime, None], urlencode_val: bool = True, fmt: str = DivaConstants.LUT_TIME_FMT) -> str:
if type(val) != datetime:
val = datetime.now().astimezone()
val = val.replace(microsecond=0)
dt_fmt = val.strftime(fmt)
if urlencode_val:
dt_fmt = quote(dt_fmt)
return f"{key}={dt_fmt}"
def encode_list(key: str, val: Union[List[Any], None], urlencode_final_val: bool = False, urlencode_vals: bool = False) -> str:
if not val:
return f"{key}="
for x in range(len(val)):
if val[x] is None:
val[x] = "x"
if type(val[x]) == datetime:
val[x] = val[x].replace(microsecond=0).strftime(DivaConstants.LUT_TIME_FMT)
elif type(val[x]) == bool:
val[x] = str(int(val[x]))
elif type(val[x]) == int:
val[x] = str(val[x])
if urlencode_vals:
val[x] = quote(val[x])
all_vals = ",".join(val)
if urlencode_final_val:
all_vals = quote(all_vals)
return f"{key}={all_vals}"
def decode_list_int(val: str) -> List[int]:
return [int(x) for x in val.split(",")]
def decode_list(val: str) -> List[str]:
return [x for x in val.split(",")]

156
titles/diva/handlers/pv.py Normal file
View File

@ -0,0 +1,156 @@
from typing import Union, List
from titles.diva.handlers.base import (
BaseRequest,
BaseResponse,
DivaRequestParseException,
decode_list_int
)
from datetime import datetime
from urllib import parse
from ..const import DivaConstants
class GetPvPdRequest(BaseRequest):
def __init__(self, raw: Union[str, bytes]) -> None:
super().__init__(raw)
try:
self.pd_id = int(self.raw_dict.get('pd_id'))
self.accept_idx = int(self.raw_dict.get('accept_idx'))
self.start_idx = int(self.raw_dict.get('start_idx'))
self.difficulty = int(self.raw_dict.get('difficulty'))
self.pd_pv_id_lst: List[int] = [int(x) for x in self.pd_pv_id_lst.split(',')]
except AttributeError as e:
raise DivaRequestParseException(f"GetPvPdRequest: {e}")
class GetPvPdResponse(BaseResponse):
def __init__(self, req_id: int) -> None:
super().__init__("get_pv_pd", req_id)
self.pd_by_pv_id = ""
self.pdddt_flg = 0
self.pdddt_tm = parse.quote(datetime.now().strftime(DivaConstants.LUT_TIME_FMT))
class StageResultRequest(BaseRequest):
def __init__(self, raw: Union[str, bytes]) -> None:
super().__init__(raw)
self.pd_id = int(self.raw_dict.get('pd_id'))
self.accept_idx = int(self.raw_dict.get('accept_idx'))
self.start_idx = int(self.raw_dict.get('start_idx'))
self.hp_vol = int(self.raw_dict.get('hp_vol'))
self.btn_se_vol = int(self.raw_dict.get('btn_se_vol'))
self.btn_se_vol2 = int(self.raw_dict.get('btn_se_vol2'))
self.sldr_se_vol2 = int(self.raw_dict.get('sldr_se_vol2'))
self.use_pv_mdl_eqp = int(self.raw_dict.get('use_pv_mdl_eqp'))
self.vcld_pts = int(self.raw_dict.get('vcld_pts'))
self.nxt_pv_id = int(self.raw_dict.get('nxt_pv_id'))
self.nxt_dffclty = int(self.raw_dict.get('nxt_dffclty'))
self.nxt_edtn = int(self.raw_dict.get('nxt_edtn'))
self.sort_kind = int(self.raw_dict.get('sort_kind'))
self.nblss_ltt_stts = int(self.raw_dict.get('nblss_ltt_stts'))
self.nblss_ltt_tckt = int(self.raw_dict.get('nblss_ltt_tckt'))
self.free_play = int(self.raw_dict.get('free_play'))
self.game_type = int(self.raw_dict.get('game_type'))
self.ply_pv_id = int(self.raw_dict.get('ply_pv_id'))
self.ttl_vp_add = int(self.raw_dict.get('ttl_vp_add'))
self.ttl_vp_sub = int(self.raw_dict.get('ttl_vp_sub'))
self.continue_cnt = int(self.raw_dict.get('continue_cnt'))
self.cr_cid = int(self.raw_dict.get('cr_cid'))
self.cr_sc = int(self.raw_dict.get('cr_sc'))
self.cr_tv = int(self.raw_dict.get('cr_tv'))
self.cr_if = int(self.raw_dict.get('cr_if'))
self.my_qst_id: List[int] = decode_list_int(self.raw_dict.get('my_qst_id'))
self.my_qst_sts: List[int] = decode_list_int(self.raw_dict.get('my_qst_sts'))
self.stg_difficulty: List[int] = decode_list_int(self.raw_dict.get('stg_difficulty'))
self.stg_edtn: List[int] = decode_list_int(self.raw_dict.get('stg_edtn'))
self.stg_ply_pv_id: List[int] = decode_list_int(self.raw_dict.get('stg_ply_pv_id'))
self.stg_sel_pv_id: List[int] = decode_list_int(self.raw_dict.get('stg_sel_pv_id'))
self.stg_scrpt_ver: List[int] = decode_list_int(self.raw_dict.get('stg_scrpt_ver'))
self.stg_score: List[int] = decode_list_int(self.raw_dict.get('stg_score'))
self.stg_chllng_kind: List[int] = decode_list_int(self.raw_dict.get('stg_chllng_kind'))
self.stg_chllng_result: List[int] = decode_list_int(self.raw_dict.get('stg_chllng_result'))
self.stg_clr_kind: List[int] = decode_list_int(self.raw_dict.get('stg_clr_kind'))
self.stg_vcld_pts: List[int] = decode_list_int(self.raw_dict.get('stg_vcld_pts'))
self.stg_cool_cnt: List[int] = decode_list_int(self.raw_dict.get('stg_cool_cnt'))
self.stg_cool_pct: List[int] = decode_list_int(self.raw_dict.get('stg_cool_pct'))
self.stg_fine_cnt: List[int] = decode_list_int(self.raw_dict.get('stg_fine_cnt'))
self.stg_fine_pct: List[int] = decode_list_int(self.raw_dict.get('stg_fine_pct'))
self.stg_safe_cnt: List[int] = decode_list_int(self.raw_dict.get('stg_safe_cnt'))
self.stg_safe_pct: List[int] = decode_list_int(self.raw_dict.get('stg_safe_pct'))
self.stg_sad_cnt: List[int] = decode_list_int(self.raw_dict.get('stg_sad_cnt'))
self.stg_sad_pct: List[int] = decode_list_int(self.raw_dict.get('stg_sad_pct'))
self.stg_wt_wg_cnt: List[int] = decode_list_int(self.raw_dict.get('stg_wt_wg_cnt'))
self.stg_wt_wg_pct: List[int] = decode_list_int(self.raw_dict.get('stg_wt_wg_pct'))
self.stg_max_cmb: List[int] = decode_list_int(self.raw_dict.get('stg_max_cmb'))
self.stg_chance_tm: List[int] = decode_list_int(self.raw_dict.get('stg_chance_tm'))
self.stg_sm_hl: List[int] = decode_list_int(self.raw_dict.get('stg_sm_hl'))
self.stg_atn_pnt: List[int] = decode_list_int(self.raw_dict.get('stg_atn_pnt'))
self.stg_skin_id: List[int] = decode_list_int(self.raw_dict.get('stg_skin_id'))
self.stg_btn_se: List[int] = decode_list_int(self.raw_dict.get('stg_btn_se'))
self.stg_btn_se_vol: List[int] = decode_list_int(self.raw_dict.get('stg_btn_se_vol'))
self.stg_sld_se: List[int] = decode_list_int(self.raw_dict.get('stg_sld_se'))
self.stg_chn_sld_se: List[int] = decode_list_int(self.raw_dict.get('stg_chn_sld_se'))
self.stg_sldr_tch_se: List[int] = decode_list_int(self.raw_dict.get('stg_sldr_tch_se'))
self.stg_mdl_id: List[int] = decode_list_int(self.raw_dict.get('stg_mdl_id'))
self.stg_sel_mdl_id: List[int] = decode_list_int(self.raw_dict.get('stg_sel_mdl_id'))
self.stg_rvl_pd_id: List[int] = decode_list_int(self.raw_dict.get('stg_rvl_pd_id'))
self.stg_rvl_wl: List[int] = decode_list_int(self.raw_dict.get('stg_rvl_wl'))
self.stg_cpt_rslt: List[int] = decode_list_int(self.raw_dict.get('stg_cpt_rslt'))
self.stg_sld_scr: List[int] = decode_list_int(self.raw_dict.get('stg_sld_scr'))
self.stg_is_sr_gm: List[int] = decode_list_int(self.raw_dict.get('stg_is_sr_gm'))
self.stg_pv_brnch_rslt: List[int] = decode_list_int(self.raw_dict.get('stg_pv_brnch_rslt'))
self.stg_vcl_chg: List[int] = decode_list_int(self.raw_dict.get('stg_vcl_chg'))
self.stg_c_itm_id: List[int] = decode_list_int(self.raw_dict.get('stg_c_itm_id'))
self.stg_ms_itm_flg: List[int] = decode_list_int(self.raw_dict.get('stg_ms_itm_flg'))
self.stg_rgo: List[int] = decode_list_int(self.raw_dict.get('stg_rgo'))
self.stg_ss_num: List[int] = decode_list_int(self.raw_dict.get('stg_ss_num'))
self.stg_is_cs_scs: List[int] = decode_list_int(self.raw_dict.get('stg_is_cs_scs'))
self.stg_is_nppg_use: List[int] = decode_list_int(self.raw_dict.get('stg_is_nppg_use'))
self.stg_p_std_lo_id: List[int] = decode_list_int(self.raw_dict.get('stg_p_std_lo_id'))
self.stg_p_std_is_to: List[int] = decode_list_int(self.raw_dict.get('stg_p_std_is_to'))
self.stg_p_std_is_ccu: List[int] = decode_list_int(self.raw_dict.get('stg_p_std_is_ccu'))
self.stg_p_std_is_tiu: List[int] = decode_list_int(self.raw_dict.get('stg_p_std_is_tiu'))
self.stg_p_std_is_iu: List[int] = decode_list_int(self.raw_dict.get('stg_p_std_is_iu'))
self.stg_p_std_is_npu: List[int] = decode_list_int(self.raw_dict.get('stg_p_std_is_npu'))
self.stg_p_std_is_du: List[int] = decode_list_int(self.raw_dict.get('stg_p_std_is_du'))
self.gu_cmd: List[int] = decode_list_int(self.raw_dict.get('gu_cmd'))
self.mdl_eqp_cmn_ary: List[int] = decode_list_int(self.raw_dict.get('mdl_eqp_cmn_ary'))
self.c_itm_eqp_cmn_ary: List[int] = decode_list_int(self.raw_dict.get('c_itm_eqp_cmn_ary'))
self.ms_itm_flg_cmn_ary: List[int] = decode_list_int(self.raw_dict.get('ms_itm_flg_cmn_ary'))
self.mdl_eqp_pv_ary: List[int] = decode_list_int(self.raw_dict.get('mdl_eqp_pv_ary'))
self.c_itm_eqp_pv_ary: List[int] = decode_list_int(self.raw_dict.get('c_itm_eqp_pv_ary'))
self.ms_itm_flg_pv_ary: List[int] = decode_list_int(self.raw_dict.get('ms_itm_flg_pv_ary'))
self.stg_mdl_s_sts: List[int] = decode_list_int(self.raw_dict.get('stg_mdl_s_sts'))
self.cr_sp: List[int] = decode_list_int(parse.unquote(self.raw_dict.get('cr_sp')))
class StageResultResponse(BaseResponse):
def __init__(self, req_id: int) -> None:
super().__init__("stage_result", req_id)
self.chllng_kind = -1
self.lv_num_old = 0
self.lv_pnt_old = 0
self.lv_num = 0
self.lv_str = 0
self.lv_pnt = 0
self.lv_efct_id = 0
self.lv_plt_id = 0
self.vcld_pts = 0
self.prsnt_vcld_pts = 0
self.cerwd_kind = -1
self.cerwd_value = -1
self.cerwd_str_0 = "***"
self.cerwd_str_1 = "***"
self.ttl_str_ary = "xxx,xxx,xxx,xxx,xxx"
self.ttl_plt_id_ary = "xxx,xxx,xxx,xxx,xxx"
self.ttl_desc_ary = "xxx,xxx,xxx,xxx,xxx"
self.skin_id_ary = "xxx,xxx,xxx,xxx,xxx"
self.skin_name_ary = "xxx,xxx,xxx,xxx,xxx"
self.skin_illust_ary = "xxx,xxx,xxx,xxx,xxx"
self.skin_desc_ary = "xxx,xxx,xxx,xxx,xxx"
self.my_qst_id = "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"
self.my_qst_r_qid = "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"
self.my_qst_r_knd = "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"
self.my_qst_r_vl = "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"
self.my_qst_r_nflg = "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"
self.my_ccd_r_qid = "-1,-1,-1,-1,-1"
self.my_ccd_r_hnd = "-1,-1,-1,-1,-1"
self.my_ccd_r_vp = "-1,-1,-1,-1,-1"

View File

@ -0,0 +1,140 @@
from typing import Union
from titles.diva.handlers.base import (
BaseRequest,
BaseResponse,
DivaRequestParseException,
)
from datetime import datetime
from urllib import parse
from ..const import DivaConstants
class PreStartRequest(BaseRequest):
def __init__(self, raw: str) -> None:
super().__init__(raw)
self.pmm: str = self.raw_dict.get('pmm')
self.idm: str = self.raw_dict.get('idm')
self.mmgameid: str = self.raw_dict.get('mmgameid')
self.mmuid: str = self.raw_dict.get('mmuid')
self.a_code: str = self.raw_dict.get('a_code')
self.aime_id: str = self.raw_dict.get('aime_id')
self.aime_a_code: str = self.raw_dict.get('aime_a_code')
self.key_obj_type = int(self.raw_dict.get('key_obj_type'))
self.exec_vu = int(self.raw_dict.get('exec_vu'))
class PreStartResponse(BaseResponse):
def __init__(self, req_id: int, pd_id: int) -> None:
super().__init__("pre_start", req_id)
self.ps_result = 1
self.pd_id = pd_id
self.accept_idx = 100
self.nblss_ltt_stts = -1
self.nblss_ltt_tckt = -1
self.nblss_ltt_is_opn = -1
self.player_name: str = ""
self.sort_kind: str = ""
self.lv_efct_id: str = ""
self.lv_plt_id: str = ""
self.lv_str: str = ""
self.lv_num: str = ""
self.lv_pnt: str = ""
self.vcld_pts: str = ""
self.skn_eqp: str = ""
self.btn_se_eqp: str = ""
self.sld_se_eqp: str = ""
self.chn_sld_se_eqp: str = ""
self.sldr_tch_se_eqp: str = ""
self.passwd_stat: str = ""
self.mdl_eqp_tm: str = ""
# Ideally this would be a real array that would get converted later
# But this is how it's stored in the db, so w/e for now
self.mdl_eqp_ary = "-999,-999,-999"
class StartRequest(BaseRequest):
def __init__(self, raw: str) -> None:
super().__init__(raw)
self.pd_id = int(self.raw_dict.get('pd_id'))
self.accept_idx = int(self.raw_dict.get('accept_idx'))
class StartResponse(BaseResponse):
def __init__(self, req_id: int, pv_id: int, pv_name: str) -> None:
super().__init__("start", req_id)
self.pd_id: int = pv_id
self.start_result: int = 1
self.accept_idx: int = 100
self.hp_vol: int = 0
self.btn_se_vol: int = 1
self.btn_se_vol2: int = 1
self.sldr_se_vol2: int = 1
self.sort_kind: int = 1
self.player_name: str = pv_name
self.lv_num: int = 1
self.lv_pnt: int = 0
self.lv_efct_id: int = 1
self.lv_plt_id: int = 1
self.mdl_have: str = "F" * 250
self.cstmz_itm_have: str = "F" * 250
self.use_pv_mdl_eqp: int = 0
self.use_mdl_pri: int = 0
self.use_pv_skn_eqp: int = 1
self.use_pv_btn_se_eqp: int = 1
self.use_pv_sld_se_eqp: int = 1
self.use_pv_chn_sld_se_eqp: int = 1
self.use_pv_sldr_tch_se_eqp: int = 1
self.vcld_pts: int = 0
self.nxt_pv_id: int = 1
self.nxt_dffclty: int = 1
self.nxt_edtn: int = 0
self.dsp_clr_brdr: int = 0
self.dsp_intrm_rnk: int = 0
self.dsp_clr_sts: int = 0
self.rgo_sts: int = 0
self.cv_cid: str = "-1,-1,-1,-1"
self.cv_sc: str = "-1,-1,-1,-1"
#self.cv_bv: str = "-1,-1,-1,-1"
self.cv_bv: str = "-1,-1,-1,-1"
self.cv_bf: str = "-1,-1,-1,-1"
self.cnp_cid=-1
self.cnp_val=-1
self.cnp_rr=-1
self.my_qst_id: str = ",".join(["-1"] * 25)
self.my_qst_sts: str = ",".join("0" * 5) + "," + ",".join(["-1"] * 20)
self.my_qst_prgrs: str = ",".join("0" * 5) + "," + ",".join(["-1"] * 20)
self.my_qst_et: str = ",".join([parse.quote(datetime.now().strftime(DivaConstants.LUT_TIME_FMT))] * 5) + "," + ",".join(["xxx"] * 20)
self.clr_sts: str = ",".join("0" * 5) + "," + ",".join(["-1"] * 20)
self.mdl_eqp_tm: str = parse.quote(datetime.now().strftime(DivaConstants.LUT_TIME_FMT))
self.mdl_eqp_ary = ",".join(["-999"] * 3)
self.c_itm_eqp_ary = ",".join(["-999"] * 12)
self.ms_itm_flg_ary = ",".join(["1"] * 12)
class RegisterRequest(BaseRequest):
def __init__(self, raw: str) -> None:
super().__init__(raw)
self.pmm: str = self.raw_dict.get('pmm')
self.idm: str = self.raw_dict.get('idm')
self.mmgameid: str = self.raw_dict.get('mmgameid')
self.mmuid: str = self.raw_dict.get('mmuid')
self.a_code: str = self.raw_dict.get('a_code')
self.aime_a_code: str = self.raw_dict.get('aime_a_code')
self.player_name: str = self.raw_dict.get('player_name')
self.passwd: str = self.raw_dict.get('passwd')
self.aime_id = int(self.raw_dict.get('aime_id'))
self.key_obj_type = int(self.raw_dict.get('key_obj_type'))
class RegisterResponse(BaseResponse):
def __init__(self, req_id: int, pv_id: int) -> None:
super().__init__("register", req_id)
self.cd_adm_result: int = 1
self.pd_id: int = pv_id
class EndRequest(BaseRequest):
def __init__(self, raw: Union[str, bytes]) -> None:
self.my_qst_id: str = self.raw_dict.get('my_qst_id')
self.my_qst_sts: str = self.raw_dict.get('my_qst_sts')
super().__init__(raw)
try:
self.pd_id = int(self.raw_dict.get('pd_id'))
except AttributeError as e:
raise DivaRequestParseException(f"EndRequest: {e}")

View File

@ -4,11 +4,11 @@ import logging, coloredlogs
from logging.handlers import TimedRotatingFileHandler
import zlib
import json
import urllib.parse
import base64
from os import path
from typing import Tuple, Dict, List
from titles.diva.handlers.base import *
from core.config import CoreConfig
from core.title import BaseServlet
from core.utils import Utils
@ -59,9 +59,9 @@ class DivaServlet(BaseServlet):
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80:
return (f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}/DivaServlet/", self.core_cfg.title.hostname)
return (f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}/DivaServlet/", "")
return (f"http://{self.core_cfg.title.hostname}/DivaServlet/", self.core_cfg.title.hostname)
return (f"http://{self.core_cfg.title.hostname}/DivaServlet/", "")
@classmethod
def is_game_enabled(
@ -82,86 +82,52 @@ class DivaServlet(BaseServlet):
req_raw = request.content.getvalue()
url_header = request.getAllHeaders()
# Ping Dispatch
if "THIS_STRING_SEPARATES" in str(url_header):
binary_request = req_raw.splitlines()
binary_cmd_decoded = binary_request[3].decode("utf-8")
binary_array = binary_cmd_decoded.split("&")
url_data = binary_cmd_decoded # for logging
req_cls = BaseBinaryRequest(binary_cmd_decoded)
bin_req_data = {}
else:
json_string = json.dumps(
req_raw.decode("utf-8")
) # Take the response and decode as UTF-8 and dump
b64string = json_string.replace(
r"\n", "\n"
) # Remove all \n and separate them as new lines
gz_string = base64.b64decode(b64string) # Decompressing the base64 string
for kvp in binary_array:
split_bin = kvp.split("=")
bin_req_data[split_bin[0]] = split_bin[1]
try:
url_data = zlib.decompress(gz_string) # Decompressing the gzip
except zlib.error as e:
self.logger.error(f"Failed to defalte! {e} -> {gz_string}")
return b"stat=0"
self.logger.info(f"Binary {bin_req_data['cmd']} Request")
self.logger.debug(bin_req_data)
try:
req_cls = BaseRequest(url_data)
except DivaRequestParseException as e:
self.logger.error(e)
return b"stat=0"
handler = getattr(self.base, f"handle_{bin_req_data['cmd']}_request")
resp = handler(bin_req_data)
self.logger.debug(
f"Response cmd={bin_req_data['cmd']}&req_id={bin_req_data['req_id']}&stat=ok{resp}"
)
return f"cmd={bin_req_data['cmd']}&req_id={bin_req_data['req_id']}&stat=ok{resp}".encode(
"utf-8"
)
# Main Dispatch
json_string = json.dumps(
req_raw.decode("utf-8")
) # Take the response and decode as UTF-8 and dump
b64string = json_string.replace(
r"\n", "\n"
) # Remove all \n and separate them as new lines
gz_string = base64.b64decode(b64string) # Decompressing the base64 string
try:
url_data = zlib.decompress(gz_string).decode(
"utf-8"
) # Decompressing the gzip
except zlib.error as e:
self.logger.error(f"Failed to defalte! {e} -> {gz_string}")
return "stat=0"
req_kvp = urllib.parse.unquote(url_data)
req_data = {}
# We then need to split each parts with & so we can reuse them to fill out the requests
splitted_request = str.split(req_kvp, "&")
for kvp in splitted_request:
split = kvp.split("=")
req_data[split[0]] = split[1]
self.logger.info(f"{req_data['cmd']} Request")
self.logger.debug(req_data)
func_to_find = f"handle_{req_data['cmd']}_request"
# Load the requests
try:
handler = getattr(self.base, func_to_find)
resp = handler(req_data)
except AttributeError as e:
self.logger.warning(f"Unhandled {req_data['cmd']} request {e}")
return f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok".encode(
"utf-8"
)
except Exception as e:
self.logger.error(f"Error handling method {func_to_find} {e}")
return f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok".encode(
"utf-8"
)
request.responseHeaders.addRawHeader(b"content-type", b"text/plain")
self.logger.debug(
f"Response cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok{resp}"
self.logger.debug(f"Request: {url_data}\nHeaders: {url_header}")
self.logger.info(
f"{req_cls.cmd} request from {req_cls.kc_serial}/{req_cls.b_serial} at {Utils.get_ip_addr(request)}"
)
return (
f"cmd={req_data['cmd']}&req_id={req_data['req_id']}&stat=ok{resp}".encode(
"utf-8"
)
)
handler_str = f"handle_{req_cls.cmd}_request"
if not hasattr(self.base, handler_str):
self.logger.warn(f"Unhandled cmd {req_cls.cmd}")
return BaseResponse(req_cls.cmd, req_cls.req_id).make().encode()
handler = getattr(self.base, handler_str)
response = handler(req_cls.raw)
if response is None or response == "":
response = BaseResponse(req_cls.cmd, req_cls.req_id).make()
if not response.startswith("cmd="):
response = f"cmd={req_cls.cmd}&req_id={req_cls.req_id}&stat=ok" + response
self.logger.debug(f"Response: {response}")
return response.encode(errors="ignore")

View File

@ -228,8 +228,8 @@ class Mai2Base:
user_id,
charge["chargeId"],
charge["stock"],
charge["purchaseDate"], # Ideally these should be datetimes, but db was
charge["validDate"] # set up with them being str, so str it is for now
datetime.strptime(charge["purchaseDate"], Mai2Constants.DATE_TIME_FORMAT),
datetime.strptime(charge["validDate"], Mai2Constants.DATE_TIME_FORMAT),
)
return {"returnCode": 1, "apiName": "UpsertUserChargelogApi"}
@ -303,8 +303,12 @@ class Mai2Base:
user_id,
charge["chargeId"],
charge["stock"],
charge["purchaseDate"],
charge["validDate"]
datetime.strptime(
charge["purchaseDate"], Mai2Constants.DATE_TIME_FORMAT
),
datetime.strptime(
charge["validDate"], Mai2Constants.DATE_TIME_FORMAT
),
)
if "userCharacterList" in upsert and len(upsert["userCharacterList"]) > 0:
@ -454,6 +458,12 @@ class Mai2Base:
tmp = charge._asdict()
tmp.pop("id")
tmp.pop("user")
tmp["purchaseDate"] = datetime.strftime(
tmp["purchaseDate"], Mai2Constants.DATE_TIME_FORMAT
)
tmp["validDate"] = datetime.strftime(
tmp["validDate"], Mai2Constants.DATE_TIME_FORMAT
)
user_charge_list.append(tmp)

View File

@ -501,8 +501,8 @@ class Mai2ItemData(BaseData):
user_id: int,
charge_id: int,
stock: int,
purchase_date: str,
valid_date: str,
purchase_date: datetime,
valid_date: datetime,
) -> Optional[Row]:
sql = insert(charge).values(
user=user_id,

View File

@ -639,9 +639,8 @@ class OngekiItemData(BaseData):
return None
return result.fetchall()
def put_tech_event(self, aime_id: int, version: int, tech_event_data: Dict) -> Optional[int]:
def put_tech_event(self, aime_id: int, tech_event_data: Dict) -> Optional[int]:
tech_event_data["user"] = aime_id
tech_event_data["version"] = version
sql = insert(tech_event).values(**tech_event_data)
conflict = sql.on_duplicate_key_update(**tech_event_data)
@ -652,7 +651,7 @@ class OngekiItemData(BaseData):
return None
return result.lastrowid
def put_tech_event_ranking(self, aime_id: int, version: int, tech_event_data: Dict) -> Optional[int]:
def put_tech_event_ranking(self, version: int, aime_id: int, tech_event_data: Dict) -> Optional[int]:
tech_event_data["user"] = aime_id
tech_event_data["version"] = version
tech_event_data.pop("isRankingRewarded")

View File

@ -89,10 +89,10 @@ class WaccaServlet:
if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80:
return (
f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}/WaccaServlet",
self.core_cfg.title.hostname,
"",
)
return (f"http://{self.core_cfg.title.hostname}/WaccaServlet", self.core_cfg.title.hostname)
return (f"http://{self.core_cfg.title.hostname}/WaccaServlet", "")
def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
def end(resp: Dict) -> bytes: