forked from Dniel97/artemis
cm: added support for 1.36, fixed importer
- Added support for Card Maker 1.36.xx - Added cards importer to ONGEKI importer - Added 4 new 1.36 gachas (requires importing them from opt files) - Fixed version for Card Maker opt importer
This commit is contained in:
parent
fe8f40c627
commit
74f3ab7c3f
@ -23,4 +23,9 @@ gachas:
|
|||||||
- 1089
|
- 1089
|
||||||
- 1104
|
- 1104
|
||||||
- 1111
|
- 1111
|
||||||
- 1135
|
- 1135
|
||||||
|
# can be used for Card Maker 1.35 and up, else will be ignored
|
||||||
|
- 1149
|
||||||
|
- 1156
|
||||||
|
- 1163
|
||||||
|
- 1164
|
||||||
|
@ -15,6 +15,10 @@ Games listed below have been tested and confirmed working. Only game versions ol
|
|||||||
+ Hatsune Miku Arcade
|
+ Hatsune Miku Arcade
|
||||||
+ All versions
|
+ All versions
|
||||||
|
|
||||||
|
+ Card Maker
|
||||||
|
+ 1.34.xx
|
||||||
|
+ 1.36.xx
|
||||||
|
|
||||||
+ Ongeki
|
+ Ongeki
|
||||||
+ All versions up to Bright Memory
|
+ All versions up to Bright Memory
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ include_protocol = True
|
|||||||
title_secure = False
|
title_secure = False
|
||||||
game_codes = [CardMakerConstants.GAME_CODE]
|
game_codes = [CardMakerConstants.GAME_CODE]
|
||||||
trailing_slash = True
|
trailing_slash = True
|
||||||
use_default_host = True
|
use_default_host = False
|
||||||
|
host = ""
|
||||||
|
|
||||||
current_schema_version = 1
|
current_schema_version = 1
|
50
titles/cm/cm136.py
Normal file
50
titles/cm/cm136.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
from datetime import date, datetime, timedelta
|
||||||
|
from typing import Any, Dict, List
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
from core.config import CoreConfig
|
||||||
|
from core.data.cache import cached
|
||||||
|
from titles.cm.base import CardMakerBase
|
||||||
|
from titles.cm.const import CardMakerConstants
|
||||||
|
from titles.cm.config import CardMakerConfig
|
||||||
|
|
||||||
|
|
||||||
|
class CardMaker136(CardMakerBase):
|
||||||
|
def __init__(self, core_cfg: CoreConfig, game_cfg: CardMakerConfig) -> None:
|
||||||
|
super().__init__(core_cfg, game_cfg)
|
||||||
|
self.version = CardMakerConstants.VER_CARD_MAKER_136
|
||||||
|
|
||||||
|
def handle_get_game_connect_api_request(self, data: Dict) -> Dict:
|
||||||
|
uri = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}"
|
||||||
|
|
||||||
|
# CHUNITHM = 0, maimai = 1, ONGEKI = 2
|
||||||
|
return {
|
||||||
|
"length": 3,
|
||||||
|
"gameConnectList": [
|
||||||
|
{
|
||||||
|
"modelKind": 0,
|
||||||
|
"type": 1,
|
||||||
|
"titleUri": f"{uri}/SDHD/205/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"modelKind": 1,
|
||||||
|
"type": 1,
|
||||||
|
"titleUri": f"{uri}/SDEZ/125/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"modelKind": 2,
|
||||||
|
"type": 1,
|
||||||
|
"titleUri": f"{uri}/SDDT/135/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||||
|
ret = super().handle_get_game_setting_api_request(data)
|
||||||
|
ret["gameSetting"]["dataVersion"] = "1.35.00"
|
||||||
|
ret["gameSetting"]["ongekiCmVersion"] = "1.35.04"
|
||||||
|
ret["gameSetting"]["chuniCmVersion"] = "2.05.00"
|
||||||
|
ret["gameSetting"]["maimaiCmVersion"] = "1.25.00"
|
||||||
|
return ret
|
File diff suppressed because it is too large
Load Diff
@ -334,3 +334,168 @@
|
|||||||
1135,101602,3,1,0,0
|
1135,101602,3,1,0,0
|
||||||
1135,101603,3,1,0,0
|
1135,101603,3,1,0,0
|
||||||
1135,101619,2,1,0,0
|
1135,101619,2,1,0,0
|
||||||
|
1156,101604,3,1,0,0
|
||||||
|
1156,101605,3,1,0,0
|
||||||
|
1156,101607,3,1,0,0
|
||||||
|
1156,101608,3,1,0,0
|
||||||
|
1156,101596,4,1,0,0
|
||||||
|
1156,101597,4,1,0,0
|
||||||
|
1156,101599,4,1,0,0
|
||||||
|
1156,101600,4,1,0,0
|
||||||
|
1149,100003,2,1,0,0
|
||||||
|
1149,100004,2,1,0,0
|
||||||
|
1149,100012,2,1,0,0
|
||||||
|
1149,100013,2,1,0,0
|
||||||
|
1149,100021,2,1,0,0
|
||||||
|
1149,100022,2,1,0,0
|
||||||
|
1149,100173,2,1,0,0
|
||||||
|
1149,100174,2,1,0,0
|
||||||
|
1149,100175,2,1,0,0
|
||||||
|
1149,100339,2,1,0,0
|
||||||
|
1149,100340,2,1,0,0
|
||||||
|
1149,100341,2,1,0,0
|
||||||
|
1149,100223,2,1,0,0
|
||||||
|
1149,100224,2,1,0,0
|
||||||
|
1149,100225,2,1,0,0
|
||||||
|
1149,100692,2,1,0,0
|
||||||
|
1149,100693,2,1,0,0
|
||||||
|
1149,100694,2,1,0,0
|
||||||
|
1149,101020,2,1,0,0
|
||||||
|
1149,101025,2,1,0,0
|
||||||
|
1149,100418,3,1,0,0
|
||||||
|
1149,101005,3,1,0,0
|
||||||
|
1149,100785,3,1,0,0
|
||||||
|
1149,100786,3,1,0,0
|
||||||
|
1149,101602,3,1,0,0
|
||||||
|
1149,101604,3,1,0,0
|
||||||
|
1149,100760,4,1,0,0
|
||||||
|
1149,100780,4,1,0,0
|
||||||
|
1149,100987,4,1,0,0
|
||||||
|
1149,101295,4,1,0,0
|
||||||
|
1149,101296,4,1,0,0
|
||||||
|
1149,101592,4,1,0,0
|
||||||
|
1163,100008,4,1,0,1
|
||||||
|
1163,100017,4,1,0,1
|
||||||
|
1163,100026,4,1,0,1
|
||||||
|
1163,100034,4,1,0,1
|
||||||
|
1163,100041,4,1,0,1
|
||||||
|
1163,100048,4,1,0,1
|
||||||
|
1163,100054,4,1,0,1
|
||||||
|
1163,100060,4,1,0,1
|
||||||
|
1163,100066,4,1,0,1
|
||||||
|
1163,100078,4,1,0,1
|
||||||
|
1163,100072,4,1,0,1
|
||||||
|
1163,100084,4,1,0,1
|
||||||
|
1163,100090,4,1,0,1
|
||||||
|
1163,100282,4,1,0,1
|
||||||
|
1163,100285,4,1,0,1
|
||||||
|
1163,100284,4,1,0,1
|
||||||
|
1163,100286,4,1,0,1
|
||||||
|
1163,100280,4,1,0,1
|
||||||
|
1163,100276,4,1,0,1
|
||||||
|
1163,100277,4,1,0,1
|
||||||
|
1163,100275,4,1,0,1
|
||||||
|
1163,100278,4,1,0,1
|
||||||
|
1163,100431,4,1,0,1
|
||||||
|
1163,100407,4,1,0,1
|
||||||
|
1163,100432,4,1,0,1
|
||||||
|
1163,100433,4,1,0,1
|
||||||
|
1163,100434,4,1,0,1
|
||||||
|
1163,100435,4,1,0,1
|
||||||
|
1163,100436,4,1,0,1
|
||||||
|
1163,100437,4,1,0,1
|
||||||
|
1163,100438,4,1,0,1
|
||||||
|
1163,100439,4,1,0,1
|
||||||
|
1163,100760,4,1,0,1
|
||||||
|
1163,100761,4,1,0,1
|
||||||
|
1163,100779,4,1,0,1
|
||||||
|
1163,100767,4,1,0,1
|
||||||
|
1163,100780,4,1,0,1
|
||||||
|
1163,100784,4,1,0,1
|
||||||
|
1163,100768,4,1,0,1
|
||||||
|
1163,100725,4,1,0,1
|
||||||
|
1163,100726,4,1,0,1
|
||||||
|
1163,100984,4,1,0,1
|
||||||
|
1163,100985,4,1,0,1
|
||||||
|
1163,100987,4,1,0,1
|
||||||
|
1163,100988,4,1,0,1
|
||||||
|
1163,100986,4,1,0,1
|
||||||
|
1163,100989,4,1,0,1
|
||||||
|
1163,100982,4,1,0,1
|
||||||
|
1163,100983,4,1,0,1
|
||||||
|
1163,100787,4,1,0,1
|
||||||
|
1163,101293,4,1,0,1
|
||||||
|
1163,101294,4,1,0,1
|
||||||
|
1163,101295,4,1,0,1
|
||||||
|
1163,101296,4,1,0,1
|
||||||
|
1163,101297,4,1,0,1
|
||||||
|
1163,101320,4,1,0,1
|
||||||
|
1163,101567,4,1,0,1
|
||||||
|
1164,100008,4,1,0,1
|
||||||
|
1164,100017,4,1,0,1
|
||||||
|
1164,100026,4,1,0,1
|
||||||
|
1164,100034,4,1,0,1
|
||||||
|
1164,100041,4,1,0,1
|
||||||
|
1164,100048,4,1,0,1
|
||||||
|
1164,100054,4,1,0,1
|
||||||
|
1164,100060,4,1,0,1
|
||||||
|
1164,100066,4,1,0,1
|
||||||
|
1164,100078,4,1,0,1
|
||||||
|
1164,100072,4,1,0,1
|
||||||
|
1164,100084,4,1,0,1
|
||||||
|
1164,100090,4,1,0,1
|
||||||
|
1164,100282,4,1,0,1
|
||||||
|
1164,100285,4,1,0,1
|
||||||
|
1164,100284,4,1,0,1
|
||||||
|
1164,100286,4,1,0,1
|
||||||
|
1164,100280,4,1,0,1
|
||||||
|
1164,100276,4,1,0,1
|
||||||
|
1164,100277,4,1,0,1
|
||||||
|
1164,100275,4,1,0,1
|
||||||
|
1164,100278,4,1,0,1
|
||||||
|
1164,100431,4,1,0,1
|
||||||
|
1164,100407,4,1,0,1
|
||||||
|
1164,100432,4,1,0,1
|
||||||
|
1164,100433,4,1,0,1
|
||||||
|
1164,100434,4,1,0,1
|
||||||
|
1164,100435,4,1,0,1
|
||||||
|
1164,100436,4,1,0,1
|
||||||
|
1164,100437,4,1,0,1
|
||||||
|
1164,100438,4,1,0,1
|
||||||
|
1164,100439,4,1,0,1
|
||||||
|
1164,100760,4,1,0,1
|
||||||
|
1164,100761,4,1,0,1
|
||||||
|
1164,100779,4,1,0,1
|
||||||
|
1164,100767,4,1,0,1
|
||||||
|
1164,100780,4,1,0,1
|
||||||
|
1164,100784,4,1,0,1
|
||||||
|
1164,100768,4,1,0,1
|
||||||
|
1164,100725,4,1,0,1
|
||||||
|
1164,100726,4,1,0,1
|
||||||
|
1164,100984,4,1,0,1
|
||||||
|
1164,100985,4,1,0,1
|
||||||
|
1164,100987,4,1,0,1
|
||||||
|
1164,100988,4,1,0,1
|
||||||
|
1164,100986,4,1,0,1
|
||||||
|
1164,100989,4,1,0,1
|
||||||
|
1164,100982,4,1,0,1
|
||||||
|
1164,100983,4,1,0,1
|
||||||
|
1164,100787,4,1,0,1
|
||||||
|
1164,101293,4,1,0,1
|
||||||
|
1164,101294,4,1,0,1
|
||||||
|
1164,101295,4,1,0,1
|
||||||
|
1164,101296,4,1,0,1
|
||||||
|
1164,101297,4,1,0,1
|
||||||
|
1164,101320,4,1,0,1
|
||||||
|
1164,101567,4,1,0,1
|
||||||
|
1164,101592,4,1,0,1
|
||||||
|
1164,101593,4,1,0,1
|
||||||
|
1164,101594,4,1,0,1
|
||||||
|
1164,101595,4,1,0,1
|
||||||
|
1164,101598,4,1,0,1
|
||||||
|
1164,101596,4,1,0,1
|
||||||
|
1164,101597,4,1,0,1
|
||||||
|
1164,101599,4,1,0,1
|
||||||
|
1164,101600,4,1,0,1
|
||||||
|
1141,101600,4,1,0,1
|
||||||
|
1141,101608,3,1,0,1
|
||||||
|
|
@ -1,104 +1,69 @@
|
|||||||
"version","gachaId","gachaName","type","kind","isCeiling","maxSelectPoint","ceilingCnt","changeRateCnt1","changeRateCnt2"
|
"version","gachaId","gachaName","type","kind","isCeiling","maxSelectPoint"
|
||||||
6,1011,"無料ガチャ",0,3,0,0,10,0,0
|
6,1011,"無料ガチャ",0,3,0,0
|
||||||
6,1012,"無料ガチャ(SR確定)",0,3,0,0,10,0,0
|
6,1012,"無料ガチャ(SR確定)",0,3,0,0
|
||||||
6,1043,"レギュラーガチャ",0,0,0,0,10,0,0
|
6,1043,"レギュラーガチャ",0,0,0,0
|
||||||
6,1067,"例えるなら大人のパッションフルーツ
|
6,1067,"例えるなら大人のパッションフルーツ
|
||||||
リゾートプールガチャ",0,1,0,0,10,0,0
|
リゾートプールガチャ",0,1,0,0
|
||||||
6,1068,"柏木 咲姫
|
6,1068,"柏木 咲姫
|
||||||
ピックアップガチャ",0,2,0,0,10,0,0
|
ピックアップガチャ",0,2,0,0
|
||||||
6,1069,"井之原 小星
|
6,1069,"井之原 小星
|
||||||
ピックアップガチャ",0,2,0,0,10,0,0
|
ピックアップガチャ",0,2,0,0
|
||||||
6,1070,"目指すは優勝!
|
6,1070,"目指すは優勝!
|
||||||
炎の体育祭リミテッドガチャ",0,1,1,110,10,0,0
|
炎の体育祭リミテッドガチャ",0,1,1,110
|
||||||
6,1071,"星咲 あかり
|
6,1071,"星咲 あかり
|
||||||
ピックアップガチャ",0,2,0,0,10,0,0
|
ピックアップガチャ",0,2,0,0
|
||||||
6,1072,"藤沢 柚子
|
6,1072,"藤沢 柚子
|
||||||
ピックアップガチャ",0,2,0,0,10,0,0
|
ピックアップガチャ",0,2,0,0
|
||||||
6,1073,"三角 葵
|
6,1073,"三角 葵
|
||||||
ピックアップガチャ",0,2,0,0,10,0,0
|
ピックアップガチャ",0,2,0,0
|
||||||
6,1074,"おくれてきた
|
6,1074,"おくれてきた
|
||||||
Halloweenガチャ",0,1,0,0,10,0,0
|
Halloweenガチャ",0,1,0,0
|
||||||
6,1075,"早乙女 彩華
|
6,1075,"早乙女 彩華
|
||||||
ピックアップガチャ",0,2,0,0,10,0,0
|
ピックアップガチャ",0,2,0,0
|
||||||
6,1076,"桜井 春菜
|
6,1076,"桜井 春菜
|
||||||
ピックアップガチャ",0,2,0,0,10,0,0
|
ピックアップガチャ",0,2,0,0
|
||||||
6,1077,"ふわふわすぺーす
|
6,1077,"ふわふわすぺーす
|
||||||
お仕事体験リミテッドガチャ",0,1,1,110,10,0,0
|
お仕事体験リミテッドガチャ",0,1,1,110
|
||||||
6,1078,"高瀬 梨緒
|
6,1078,"高瀬 梨緒
|
||||||
ピックアップガチャ",0,2,0,0,10,0,0
|
ピックアップガチャ",0,2,0,0
|
||||||
6,1079,"結城 莉玖
|
6,1079,"結城 莉玖
|
||||||
ピックアップガチャ",0,2,0,0,10,0,0
|
ピックアップガチャ",0,2,0,0
|
||||||
6,1080,"藍原 椿
|
6,1080,"藍原 椿
|
||||||
ピックアップガチャ",0,2,0,0,10,0,0
|
ピックアップガチャ",0,2,0,0
|
||||||
6,1081,"今夜はおうちでパーティ☆
|
6,1081,"今夜はおうちでパーティ☆
|
||||||
メリクリガチャ",0,1,0,0,10,0,0
|
メリクリガチャ",0,1,0,0
|
||||||
6,1082,"日向 千夏
|
6,1082,"日向 千夏
|
||||||
ピックアップガチャ",0,2,0,0,10,0,0
|
ピックアップガチャ",0,2,0,0
|
||||||
6,1083,"柏木 美亜
|
6,1083,"柏木 美亜
|
||||||
ピックアップガチャ",0,2,0,0,10,0,0
|
ピックアップガチャ",0,2,0,0
|
||||||
6,1084,"東雲 つむぎ
|
6,1084,"東雲 つむぎ
|
||||||
ピックアップガチャ",0,2,0,0,10,0,0
|
ピックアップガチャ",0,2,0,0
|
||||||
6,1085,"謹賀新年
|
6,1085,"謹賀新年
|
||||||
福袋ガチャ",0,0,1,33,10,0,0
|
福袋ガチャ",0,0,1,33
|
||||||
6,1086,"逢坂 茜
|
6,1086,"逢坂 茜
|
||||||
ピックアップガチャ",0,2,0,0,10,0,0
|
ピックアップガチャ",0,2,0,0
|
||||||
6,1087,"珠洲島 有栖
|
6,1087,"珠洲島 有栖
|
||||||
ピックアップガチャ",0,2,0,0,10,0,0
|
ピックアップガチャ",0,2,0,0
|
||||||
6,1088,"九條 楓
|
6,1088,"九條 楓
|
||||||
ピックアップガチャ",0,2,0,0,10,0,0
|
ピックアップガチャ",0,2,0,0
|
||||||
6,1089,"冬の魔法
|
6,1089,"冬の魔法
|
||||||
スーパーウルトラウィンターガチャ",0,1,0,0,10,0,0
|
スーパーウルトラウィンターガチャ",0,1,0,0
|
||||||
6,1093,"高瀬 梨緒ピックアップガチャ",0,2,0,0,10,0,0
|
6,1093,"高瀬 梨緒ピックアップガチャ",0,2,0,0
|
||||||
6,1094,"結城 莉玖ピックアップガチャ",0,2,0,0,10,0,0
|
6,1094,"結城 莉玖ピックアップガチャ",0,2,0,0
|
||||||
6,1095,"藍原 椿ピックアップガチャ",0,2,0,0,10,0,0
|
6,1095,"藍原 椿ピックアップガチャ",0,2,0,0
|
||||||
6,1096,"早乙女 彩華ピックアップガチャ",0,2,0,0,10,0,0
|
6,1096,"早乙女 彩華ピックアップガチャ",0,2,0,0
|
||||||
6,1097,"桜井 春菜ピックアップガチャ",0,2,0,0,10,0,0
|
6,1097,"桜井 春菜ピックアップガチャ",0,2,0,0
|
||||||
6,1098,"逢坂 茜ピックアップガチャ",0,2,0,0,10,0,0
|
6,1098,"逢坂 茜ピックアップガチャ",0,2,0,0
|
||||||
6,1099,"九條 楓ピックアップガチャ",0,2,0,0,10,0,0
|
6,1099,"九條 楓ピックアップガチャ",0,2,0,0
|
||||||
6,1100,"珠洲島 有栖ピックアップガチャ",0,2,0,0,10,0,0
|
6,1100,"珠洲島 有栖ピックアップガチャ",0,2,0,0
|
||||||
6,1101,"LEAF属性オンリーガチャ",0,2,0,0,10,0,0
|
6,1101,"LEAF属性オンリーガチャ",0,2,0,0
|
||||||
6,1102,"AQUA属性オンリーガチャ",0,2,0,0,10,0,0
|
6,1102,"AQUA属性オンリーガチャ",0,2,0,0
|
||||||
6,1103,"FIRE属性オンリーガチャ",0,2,0,0,10,0,0
|
6,1103,"FIRE属性オンリーガチャ",0,2,0,0
|
||||||
6,1104,"夜明け前の双星ガチャ",0,1,0,0,10,0,0
|
6,1104,"夜明け前の双星ガチャ",0,1,0,0
|
||||||
6,1105,"謎の洞窟 黄金は実在した!!ガチャ",0,1,0,0,10,0,0
|
6,1105,"謎の洞窟 黄金は実在した!!ガチャ",0,1,0,0
|
||||||
6,1106,"スウィートブライダルリミテッドガチャ",0,1,0,0,10,0,0
|
6,1106,"スウィートブライダルリミテッドガチャ",0,1,0,0
|
||||||
6,1107,"忘れられない、愛(ピュア)とロックがここにある。ガチャ",0,1,0,0,10,0,0
|
6,1107,"忘れられない、愛(ピュア)とロックがここにある。ガチャ",0,1,0,0
|
||||||
6,1108,"メルティ夜ふかしガチャ",0,1,0,0,10,0,0
|
6,1108,"メルティ夜ふかしガチャ",0,1,0,0
|
||||||
6,1109,"絵本の国のシューターズガチャ",0,1,0,0,10,0,0
|
6,1109,"絵本の国のシューターズガチャ",0,1,0,0
|
||||||
6,1110,"オンゲキ R.E.D. PLUS 大感謝祭ガチャ",0,1,0,0,10,0,0
|
6,1110,"オンゲキ R.E.D. PLUS 大感謝祭ガチャ",0,1,0,0
|
||||||
6,1111,"オンゲキ 3rd Anniversaryガチャ",0,1,1,33,10,0,0
|
6,1111,"オンゲキ 3rd Anniversaryガチャ",0,1,1,33
|
||||||
6,1113,"柏木 咲姫ピックアップガチャ",0,2,0,0,10,0,0
|
|
||||||
6,1114,"井之原 小星ピックアップガチャ",0,2,0,0,10,0,0
|
|
||||||
6,1115,"星咲 あかりピックアップガチャ",0,2,0,0,10,0,0
|
|
||||||
6,1116,"藤沢 柚子ピックアップガチャ",0,2,0,0,10,0,0
|
|
||||||
6,1117,"三角 葵ピックアップガチャ",0,2,0,0,10,0,0
|
|
||||||
6,1118,"日向 千夏ピックアップガチャ",0,2,0,0,10,0,0
|
|
||||||
6,1119,"柏木 美亜ピックアップガチャ",0,2,0,0,10,0,0
|
|
||||||
6,1120,"東雲 つむぎピックアップガチャ",0,2,0,0,10,0,0
|
|
||||||
6,1121,"LEAF属性オンリーガチャ",0,2,0,0,10,0,0
|
|
||||||
6,1122,"FIRE属性オンリーガチャ",0,2,0,0,10,0,0
|
|
||||||
6,1123,"AQUA属性オンリーガチャ",0,2,0,0,10,0,0
|
|
||||||
6,1125,"Let`s SHOOT!ガチャ",0,1,0,0,10,0,0
|
|
||||||
6,1126,"ぽかぽか""温""ゲキ!いい湯だな リミテッドガチャ",0,1,0,0,10,0,0
|
|
||||||
6,1127,"聖夜に煌めく イルミネーションガチャ",0,1,0,0,10,0,0
|
|
||||||
6,1128,"bitter chocolate kiss ガチャ",0,1,0,0,10,0,0
|
|
||||||
6,1134,"謹賀新年福袋ガチャ",0,1,0,0,10,0,0
|
|
||||||
6,1135,"オンゲキ bright 大感謝祭ガチャ",0,1,0,0,10,0,0
|
|
||||||
7,1140,"カラフルアンブレラガチャ",0,0,0,0,10,0,0
|
|
||||||
7,1141,"It's Showtime!ワンダフルサーカスガチャ",0,0,0,0,10,0,0
|
|
||||||
7,1147,"R.B.P. ピックアップガチャ",0,0,0,0,10,0,0
|
|
||||||
7,1148,"皇城 セツナ ピックアップガチャ",0,0,0,0,10,0,0
|
|
||||||
7,1149,"ASTERISM ピックアップガチャ",0,0,0,0,10,0,0
|
|
||||||
7,1153,"Memories of O.N.G.E.K.I.打ち上げガチャ",0,0,0,0,10,0,0
|
|
||||||
7,1156,"bright memory振り返りガチャ",0,0,0,0,10,0,0
|
|
||||||
7,1158,"レギュラーガチャ",0,0,0,100,0,0,0
|
|
||||||
7,1159,"オンゲキ&オンゲキ PLUS ピックアップガチャ",0,2,0,100,0,0,0
|
|
||||||
7,1160,"SUMMER & SUMMER PLUS ピックアップガチャ",0,2,0,100,0,0,0
|
|
||||||
7,1161,"R.E.D. & R.E.D. PLUS ピックアップガチャ",0,2,0,100,0,0,0
|
|
||||||
7,1162,"bright & bright MEMORY ピックアップガチャ",0,2,0,100,0,0,0
|
|
||||||
7,1163,"4周年記念!! 4rd Anniversaryセレクトガチャ",0,1,0,100,0,0,0
|
|
||||||
7,1164,"2023謹賀新年福袋ガチャ",0,1,0,100,0,0,0
|
|
||||||
7,1165,"5周年記念!! 5rd Anniversaryセレクトガチャ",0,1,0,100,0,0,0
|
|
||||||
7,1166,"2024謹賀新年福袋ガチャ",0,1,0,100,0,0,0
|
|
||||||
7,1167,"6周年記念!! 6rd Anniversaryセレクトガチャ",0,1,0,100,0,0,0
|
|
||||||
7,1168,"2025謹賀新年福袋ガチャ",0,1,0,100,0,0,0
|
|
|
@ -2,8 +2,9 @@ class CardMakerConstants():
|
|||||||
GAME_CODE = "SDED"
|
GAME_CODE = "SDED"
|
||||||
|
|
||||||
VER_CARD_MAKER = 0
|
VER_CARD_MAKER = 0
|
||||||
|
VER_CARD_MAKER_136 = 1
|
||||||
|
|
||||||
VERSION_NAMES = ["Card Maker 1.34"]
|
VERSION_NAMES = ("Card Maker 1.34", "Card Maker 1.36")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def game_ver_to_string(cls, ver: int):
|
def game_ver_to_string(cls, ver: int):
|
||||||
|
@ -12,6 +12,7 @@ from core.config import CoreConfig
|
|||||||
from titles.cm.config import CardMakerConfig
|
from titles.cm.config import CardMakerConfig
|
||||||
from titles.cm.const import CardMakerConstants
|
from titles.cm.const import CardMakerConstants
|
||||||
from titles.cm.base import CardMakerBase
|
from titles.cm.base import CardMakerBase
|
||||||
|
from titles.cm.cm136 import CardMaker136
|
||||||
|
|
||||||
|
|
||||||
class CardMakerServlet():
|
class CardMakerServlet():
|
||||||
@ -21,14 +22,15 @@ class CardMakerServlet():
|
|||||||
self.game_cfg.update(yaml.safe_load(open(f"{cfg_dir}/cardmaker.yaml")))
|
self.game_cfg.update(yaml.safe_load(open(f"{cfg_dir}/cardmaker.yaml")))
|
||||||
|
|
||||||
self.versions = [
|
self.versions = [
|
||||||
CardMakerBase(core_cfg, self.game_cfg)
|
CardMakerBase(core_cfg, self.game_cfg),
|
||||||
|
CardMaker136(core_cfg, self.game_cfg)
|
||||||
]
|
]
|
||||||
|
|
||||||
self.logger = logging.getLogger("cardmaker")
|
self.logger = logging.getLogger("cardmaker")
|
||||||
log_fmt_str = "[%(asctime)s] Card Maker | %(levelname)s | %(message)s"
|
log_fmt_str = "[%(asctime)s] Card Maker | %(levelname)s | %(message)s"
|
||||||
log_fmt = logging.Formatter(log_fmt_str)
|
log_fmt = logging.Formatter(log_fmt_str)
|
||||||
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.core_cfg.server.log_dir, "cardmaker"), encoding='utf8',
|
fileHandler = TimedRotatingFileHandler("{0}/{1}.log".format(self.core_cfg.server.log_dir, "cardmaker"), encoding='utf8',
|
||||||
when="d", backupCount=10)
|
when="d", backupCount=10)
|
||||||
|
|
||||||
fileHandler.setFormatter(log_fmt)
|
fileHandler.setFormatter(log_fmt)
|
||||||
|
|
||||||
@ -39,7 +41,8 @@ class CardMakerServlet():
|
|||||||
self.logger.addHandler(consoleHandler)
|
self.logger.addHandler(consoleHandler)
|
||||||
|
|
||||||
self.logger.setLevel(self.game_cfg.server.loglevel)
|
self.logger.setLevel(self.game_cfg.server.loglevel)
|
||||||
coloredlogs.install(level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str)
|
coloredlogs.install(level=self.game_cfg.server.loglevel,
|
||||||
|
logger=self.logger, fmt=log_fmt_str)
|
||||||
|
|
||||||
def render_POST(self, request: Request, version: int, url_path: str) -> bytes:
|
def render_POST(self, request: Request, version: int, url_path: str) -> bytes:
|
||||||
req_raw = request.content.getvalue()
|
req_raw = request.content.getvalue()
|
||||||
@ -51,18 +54,21 @@ class CardMakerServlet():
|
|||||||
|
|
||||||
if version >= 130 and version < 135: # Card Maker
|
if version >= 130 and version < 135: # Card Maker
|
||||||
internal_ver = CardMakerConstants.VER_CARD_MAKER
|
internal_ver = CardMakerConstants.VER_CARD_MAKER
|
||||||
|
elif version >= 135 and version < 140: # Card Maker
|
||||||
|
internal_ver = CardMakerConstants.VER_CARD_MAKER_136
|
||||||
|
|
||||||
if all(c in string.hexdigits for c in endpoint) and len(endpoint) == 32:
|
if all(c in string.hexdigits for c in endpoint) and len(endpoint) == 32:
|
||||||
# If we get a 32 character long hex string, it's a hash and we're
|
# If we get a 32 character long hex string, it's a hash and we're
|
||||||
# doing encrypted. The likelyhood of false positives is low but
|
# doing encrypted. The likelyhood of false positives is low but
|
||||||
# technically not 0
|
# technically not 0
|
||||||
self.logger.error("Encryption not supported at this time")
|
self.logger.error("Encryption not supported at this time")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
unzip = zlib.decompress(req_raw)
|
unzip = zlib.decompress(req_raw)
|
||||||
|
|
||||||
except zlib.error as e:
|
except zlib.error as e:
|
||||||
self.logger.error(f"Failed to decompress v{version} {endpoint} request -> {e}")
|
self.logger.error(
|
||||||
|
f"Failed to decompress v{version} {endpoint} request -> {e}")
|
||||||
return zlib.compress("{\"stat\": \"0\"}".encode("utf-8"))
|
return zlib.compress("{\"stat\": \"0\"}".encode("utf-8"))
|
||||||
|
|
||||||
req_data = json.loads(unzip)
|
req_data = json.loads(unzip)
|
||||||
@ -76,11 +82,13 @@ class CardMakerServlet():
|
|||||||
resp = handler(req_data)
|
resp = handler(req_data)
|
||||||
|
|
||||||
except AttributeError as e:
|
except AttributeError as e:
|
||||||
self.logger.warning(f"Unhandled v{version} request {endpoint} - {e}")
|
self.logger.warning(
|
||||||
|
f"Unhandled v{version} request {endpoint} - {e}")
|
||||||
return zlib.compress("{\"stat\": \"0\"}".encode("utf-8"))
|
return zlib.compress("{\"stat\": \"0\"}".encode("utf-8"))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"Error handling v{version} method {endpoint} - {e}")
|
self.logger.error(
|
||||||
|
f"Error handling v{version} method {endpoint} - {e}")
|
||||||
return zlib.compress("{\"stat\": \"0\"}".encode("utf-8"))
|
return zlib.compress("{\"stat\": \"0\"}".encode("utf-8"))
|
||||||
|
|
||||||
if resp is None:
|
if resp is None:
|
||||||
|
@ -24,16 +24,17 @@ class CardMakerReader(BaseReader):
|
|||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"Start importer for {CardMakerConstants.game_ver_to_string(version)}")
|
f"Start importer for {CardMakerConstants.game_ver_to_string(version)}")
|
||||||
except IndexError:
|
except IndexError:
|
||||||
self.logger.error(f"Invalid ongeki version {version}")
|
self.logger.error(f"Invalid Card Maker version {version}")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
def read(self) -> None:
|
def read(self) -> None:
|
||||||
static_datas = {
|
static_datas = {
|
||||||
"static_cards.csv": "read_ongeki_card_csv",
|
|
||||||
"static_gachas.csv": "read_ongeki_gacha_csv",
|
"static_gachas.csv": "read_ongeki_gacha_csv",
|
||||||
"static_gacha_cards.csv": "read_ongeki_gacha_card_csv"
|
"static_gacha_cards.csv": "read_ongeki_gacha_card_csv"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data_dirs = []
|
||||||
|
|
||||||
if self.bin_dir is not None:
|
if self.bin_dir is not None:
|
||||||
for file, func in static_datas.items():
|
for file, func in static_datas.items():
|
||||||
if os.path.exists(f"{self.bin_dir}/MU3/{file}"):
|
if os.path.exists(f"{self.bin_dir}/MU3/{file}"):
|
||||||
@ -43,36 +44,12 @@ class CardMakerReader(BaseReader):
|
|||||||
self.logger.warn(f"Couldn't find {file} file in {self.bin_dir}, skipping")
|
self.logger.warn(f"Couldn't find {file} file in {self.bin_dir}, skipping")
|
||||||
|
|
||||||
if self.opt_dir is not None:
|
if self.opt_dir is not None:
|
||||||
dir = self.get_data_directories(self.opt_dir)
|
data_dirs += self.get_data_directories(self.opt_dir)
|
||||||
|
|
||||||
# ONGEKI (MU3) cnnot easily access the bin data(A000.pac)
|
# ONGEKI (MU3) cnnot easily access the bin data(A000.pac)
|
||||||
# so only opt_dir will work for now
|
# so only opt_dir will work for now
|
||||||
self.read_gacha(f"{dir}/MU3/gacha")
|
for dir in data_dirs:
|
||||||
self.read_card(f"{dir}/MU3/card")
|
self.read_ongeki_gacha(f"{dir}/MU3/gacha")
|
||||||
|
|
||||||
def read_ongeki_card_csv(self, file_path: str) -> None:
|
|
||||||
self.logger.info(f"Reading cards from {file_path}...")
|
|
||||||
|
|
||||||
with open(file_path, encoding="utf-8") as f:
|
|
||||||
reader = csv.DictReader(f)
|
|
||||||
for row in reader:
|
|
||||||
self.ongeki_data.static.put_card(
|
|
||||||
row["version"],
|
|
||||||
row["cardId"],
|
|
||||||
name=row["name"],
|
|
||||||
charaId=row["charaId"],
|
|
||||||
nickName=row["nickName"] if row["nickName"] != "" else None,
|
|
||||||
school=row["school"],
|
|
||||||
attribute=row["attribute"],
|
|
||||||
gakunen=row["gakunen"],
|
|
||||||
rarity=row["rarity"],
|
|
||||||
levelParam=row["levelParam"],
|
|
||||||
skillId=row["skillId"],
|
|
||||||
choKaikaSkillId=row["choKaikaSkillId"],
|
|
||||||
cardNumber=row["cardNumber"] if row["cardNumber"] != "" else None
|
|
||||||
)
|
|
||||||
|
|
||||||
self.logger.info(f"Added card {row['cardId']}")
|
|
||||||
|
|
||||||
def read_ongeki_gacha_csv(self, file_path: str) -> None:
|
def read_ongeki_gacha_csv(self, file_path: str) -> None:
|
||||||
self.logger.info(f"Reading gachas from {file_path}...")
|
self.logger.info(f"Reading gachas from {file_path}...")
|
||||||
@ -87,10 +64,7 @@ class CardMakerReader(BaseReader):
|
|||||||
row["kind"],
|
row["kind"],
|
||||||
type=row["type"],
|
type=row["type"],
|
||||||
isCeiling=True if row["isCeiling"] == "1" else False,
|
isCeiling=True if row["isCeiling"] == "1" else False,
|
||||||
maxSelectPoint=row["maxSelectPoint"],
|
maxSelectPoint=row["maxSelectPoint"]
|
||||||
ceilingCnt=row["ceilingCnt"],
|
|
||||||
changeRateCnt1=row["changeRateCnt1"],
|
|
||||||
changeRateCnt2=row["changeRateCnt2"]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.logger.info(f"Added gacha {row['gachaId']}")
|
self.logger.info(f"Added gacha {row['gachaId']}")
|
||||||
@ -112,64 +86,6 @@ class CardMakerReader(BaseReader):
|
|||||||
|
|
||||||
self.logger.info(f"Added card {row['cardId']} to gacha")
|
self.logger.info(f"Added card {row['cardId']} to gacha")
|
||||||
|
|
||||||
def read_ongeki_card(self, base_dir: str) -> None:
|
|
||||||
self.logger.info(f"Reading cards from {base_dir}...")
|
|
||||||
|
|
||||||
version_ids = {
|
|
||||||
'1000': OngekiConstants.VER_ONGEKI,
|
|
||||||
'1005': OngekiConstants.VER_ONGEKI_PLUS,
|
|
||||||
'1010': OngekiConstants.VER_ONGEKI_SUMMER,
|
|
||||||
'1015': OngekiConstants.VER_ONGEKI_SUMMER_PLUS,
|
|
||||||
'1020': OngekiConstants.VER_ONGEKI_RED,
|
|
||||||
'1025': OngekiConstants.VER_ONGEKI_RED_PLUS,
|
|
||||||
'1030': OngekiConstants.VER_ONGEKI_BRIGHT,
|
|
||||||
'1035': OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY
|
|
||||||
}
|
|
||||||
|
|
||||||
for root, dirs, files in os.walk(base_dir):
|
|
||||||
for dir in dirs:
|
|
||||||
if os.path.exists(f"{root}/{dir}/Card.xml"):
|
|
||||||
with open(f"{root}/{dir}/Card.xml", "r", encoding="utf-8") as f:
|
|
||||||
troot = ET.fromstring(f.read())
|
|
||||||
|
|
||||||
card_id = int(troot.find('Name').find('id').text)
|
|
||||||
name = troot.find('Name').find('str').text
|
|
||||||
chara_id = int(troot.find('CharaID').find('id').text)
|
|
||||||
nick_name = troot.find('NickName').text
|
|
||||||
school = troot.find('School').find('str').text
|
|
||||||
attribute = troot.find('Attribute').text
|
|
||||||
gakunen = troot.find('Gakunen').find('str').text
|
|
||||||
rarity = OngekiConstants.RARITY_TYPES[
|
|
||||||
troot.find('Rarity').text].value
|
|
||||||
|
|
||||||
level_param = []
|
|
||||||
for lvl in troot.find('LevelParam').findall('int'):
|
|
||||||
level_param.append(lvl.text)
|
|
||||||
|
|
||||||
skill_id = int(troot.find('SkillID').find('id').text)
|
|
||||||
cho_kai_ka_skill_id = int(troot.find('ChoKaikaSkillID').find('id').text)
|
|
||||||
|
|
||||||
version = version_ids[
|
|
||||||
troot.find('VersionID').find('id').text]
|
|
||||||
card_number = troot.find('CardNumberString').text
|
|
||||||
|
|
||||||
self.ongeki_data.static.put_card(
|
|
||||||
version,
|
|
||||||
card_id,
|
|
||||||
name=name,
|
|
||||||
charaId=chara_id,
|
|
||||||
nickName=nick_name,
|
|
||||||
school=school,
|
|
||||||
attribute=attribute,
|
|
||||||
gakunen=gakunen,
|
|
||||||
rarity=rarity,
|
|
||||||
levelParam=','.join(level_param),
|
|
||||||
skillId=skill_id,
|
|
||||||
choKaikaSkillId=cho_kai_ka_skill_id,
|
|
||||||
cardNumber=card_number
|
|
||||||
)
|
|
||||||
self.logger.info(f"Added card {card_id}")
|
|
||||||
|
|
||||||
def read_ongeki_gacha(self, base_dir: str) -> None:
|
def read_ongeki_gacha(self, base_dir: str) -> None:
|
||||||
self.logger.info(f"Reading gachas from {base_dir}...")
|
self.logger.info(f"Reading gachas from {base_dir}...")
|
||||||
|
|
||||||
@ -189,11 +105,34 @@ class CardMakerReader(BaseReader):
|
|||||||
troot = ET.fromstring(f.read())
|
troot = ET.fromstring(f.read())
|
||||||
|
|
||||||
name = troot.find('Name').find('str').text
|
name = troot.find('Name').find('str').text
|
||||||
id = int(troot.find('Name').find('id').text)
|
gacha_id = int(troot.find('Name').find('id').text)
|
||||||
|
|
||||||
|
# skip already existing gachas
|
||||||
|
if self.ongeki_data.static.get_gacha(
|
||||||
|
OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY, gacha_id) is not None:
|
||||||
|
self.logger.info(f"Gacha {gacha_id} already added, skipping")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 1140 is the first bright memory gacha
|
||||||
|
if gacha_id < 1140:
|
||||||
|
version = OngekiConstants.VER_ONGEKI_BRIGHT
|
||||||
|
else:
|
||||||
|
version = OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY
|
||||||
|
|
||||||
gacha_kind = OngekiConstants.CM_GACHA_KINDS[
|
gacha_kind = OngekiConstants.CM_GACHA_KINDS[
|
||||||
type_to_kind[troot.find('Type').text]].value
|
type_to_kind[troot.find('Type').text]].value
|
||||||
|
|
||||||
|
# hardcode which gachas get "Select Gacha" with 33 points
|
||||||
|
is_ceiling, max_select_point = 0, 0
|
||||||
|
if gacha_id in {1163, 1164, 1165, 1166, 1167, 1168}:
|
||||||
|
is_ceiling = 1
|
||||||
|
max_select_point = 33
|
||||||
|
|
||||||
self.ongeki_data.static.put_gacha(
|
self.ongeki_data.static.put_gacha(
|
||||||
self.version, id, name, gacha_kind)
|
version,
|
||||||
self.logger.info(f"Added gacha {id}")
|
gacha_id,
|
||||||
|
name,
|
||||||
|
gacha_kind,
|
||||||
|
isCeiling=is_ceiling,
|
||||||
|
maxSelectPoint=max_select_point)
|
||||||
|
self.logger.info(f"Added gacha {gacha_id}")
|
||||||
|
@ -15,4 +15,4 @@ trailing_slash = True
|
|||||||
use_default_host = False
|
use_default_host = False
|
||||||
host = ""
|
host = ""
|
||||||
|
|
||||||
current_schema_version = 2
|
current_schema_version = 2
|
||||||
|
@ -22,10 +22,8 @@ class OngekiBright(OngekiBase):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict:
|
def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict:
|
||||||
# first check for a bright memory profile after that check for a
|
# check for a bright profile
|
||||||
# bright profile
|
p = self.data.profile.get_profile_data(data["userId"], self.version)
|
||||||
p = (self.data.profile.get_profile_data(data["userId"], self.version+1)
|
|
||||||
or self.data.profile.get_profile_data(data["userId"], self.version))
|
|
||||||
if p is None:
|
if p is None:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@ -50,6 +48,8 @@ class OngekiBright(OngekiBase):
|
|||||||
user_data["accessCode"] = cards[0]["access_code"]
|
user_data["accessCode"] = cards[0]["access_code"]
|
||||||
|
|
||||||
# hardcode Card Maker version for now
|
# hardcode Card Maker version for now
|
||||||
|
# Card Maker 1.34.00 = 1.30.01
|
||||||
|
# Card Maker 1.36.00 = 1.35.04
|
||||||
user_data["compatibleCmVersion"] = "1.30.01"
|
user_data["compatibleCmVersion"] = "1.30.01"
|
||||||
|
|
||||||
return {"userId": data["userId"], "userData": user_data}
|
return {"userId": data["userId"], "userData": user_data}
|
||||||
@ -195,7 +195,9 @@ class OngekiBright(OngekiBase):
|
|||||||
|
|
||||||
# make sure to only show gachas for the current version
|
# make sure to only show gachas for the current version
|
||||||
# so only up to bright, 1140 is the first bright memory gacha
|
# so only up to bright, 1140 is the first bright memory gacha
|
||||||
if tmp["gachaId"] < 1140:
|
if self.version == OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY:
|
||||||
|
game_gacha_list.append(tmp)
|
||||||
|
elif self.version == OngekiConstants.VER_ONGEKI_BRIGHT and tmp["gachaId"] < 1140:
|
||||||
game_gacha_list.append(tmp)
|
game_gacha_list.append(tmp)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -379,11 +381,11 @@ class OngekiBright(OngekiBase):
|
|||||||
|
|
||||||
if "userData" in upsert and len(upsert["userData"]) > 0:
|
if "userData" in upsert and len(upsert["userData"]) > 0:
|
||||||
# check if the profile is a bright memory profile
|
# check if the profile is a bright memory profile
|
||||||
p = self.data.profile.get_profile_data(data["userId"], self.version+1)
|
p = self.data.profile.get_profile_data(data["userId"], self.version)
|
||||||
if p is not None:
|
if p is not None:
|
||||||
# save the bright memory profile
|
# save the bright memory profile
|
||||||
self.data.profile.put_profile_data(
|
self.data.profile.put_profile_data(
|
||||||
user_id, self.version+1, upsert["userData"][0])
|
user_id, self.version, upsert["userData"][0])
|
||||||
else:
|
else:
|
||||||
# save the bright profile
|
# save the bright profile
|
||||||
self.data.profile.put_profile_data(
|
self.data.profile.put_profile_data(
|
||||||
@ -413,11 +415,11 @@ class OngekiBright(OngekiBase):
|
|||||||
|
|
||||||
if "userData" in upsert and len(upsert["userData"]) > 0:
|
if "userData" in upsert and len(upsert["userData"]) > 0:
|
||||||
# check if the profile is a bright memory profile
|
# check if the profile is a bright memory profile
|
||||||
p = self.data.profile.get_profile_data(data["userId"], self.version+1)
|
p = self.data.profile.get_profile_data(data["userId"], self.version)
|
||||||
if p is not None:
|
if p is not None:
|
||||||
# save the bright memory profile
|
# save the bright memory profile
|
||||||
self.data.profile.put_profile_data(
|
self.data.profile.put_profile_data(
|
||||||
user_id, self.version+1, upsert["userData"][0])
|
user_id, self.version, upsert["userData"][0])
|
||||||
else:
|
else:
|
||||||
# save the bright profile
|
# save the bright profile
|
||||||
self.data.profile.put_profile_data(
|
self.data.profile.put_profile_data(
|
||||||
@ -601,11 +603,11 @@ class OngekiBright(OngekiBase):
|
|||||||
|
|
||||||
if "userData" in upsert and len(upsert["userData"]) > 0:
|
if "userData" in upsert and len(upsert["userData"]) > 0:
|
||||||
# check if the profile is a bright memory profile
|
# check if the profile is a bright memory profile
|
||||||
p = self.data.profile.get_profile_data(data["userId"], self.version+1)
|
p = self.data.profile.get_profile_data(data["userId"], self.version)
|
||||||
if p is not None:
|
if p is not None:
|
||||||
# save the bright memory profile
|
# save the bright memory profile
|
||||||
self.data.profile.put_profile_data(
|
self.data.profile.put_profile_data(
|
||||||
user_id, self.version+1, upsert["userData"][0])
|
user_id, self.version, upsert["userData"][0])
|
||||||
else:
|
else:
|
||||||
# save the bright profile
|
# save the bright profile
|
||||||
self.data.profile.put_profile_data(
|
self.data.profile.put_profile_data(
|
||||||
|
@ -5,11 +5,12 @@ import json
|
|||||||
|
|
||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
from titles.ongeki.base import OngekiBase
|
from titles.ongeki.base import OngekiBase
|
||||||
|
from titles.ongeki.bright import OngekiBright
|
||||||
from titles.ongeki.const import OngekiConstants
|
from titles.ongeki.const import OngekiConstants
|
||||||
from titles.ongeki.config import OngekiConfig
|
from titles.ongeki.config import OngekiConfig
|
||||||
|
|
||||||
class OngekiBrightMemory(OngekiBase):
|
|
||||||
|
|
||||||
|
class OngekiBrightMemory(OngekiBright):
|
||||||
def __init__(self, core_cfg: CoreConfig, game_cfg: OngekiConfig) -> None:
|
def __init__(self, core_cfg: CoreConfig, game_cfg: OngekiConfig) -> None:
|
||||||
super().__init__(core_cfg, game_cfg)
|
super().__init__(core_cfg, game_cfg)
|
||||||
self.version = OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY
|
self.version = OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY
|
||||||
@ -28,7 +29,7 @@ class OngekiBrightMemory(OngekiBase):
|
|||||||
|
|
||||||
def handle_get_user_memory_chapter_api_request(self, data: Dict) -> Dict:
|
def handle_get_user_memory_chapter_api_request(self, data: Dict) -> Dict:
|
||||||
memories = self.data.item.get_memorychapters(data["userId"])
|
memories = self.data.item.get_memorychapters(data["userId"])
|
||||||
if not memories:
|
if not memories:
|
||||||
return {"userId": data["userId"], "length":6, "userMemoryChapterList":[
|
return {"userId": data["userId"], "length":6, "userMemoryChapterList":[
|
||||||
{"gaugeId":0, "isClear": False, "gaugeNum": 0, "chapterId": 70001, "jewelCount": 0, "isBossWatched": False, "isStoryWatched": False, "isDialogWatched": False, "isEndingWatched": False, "lastPlayMusicId": 0, "lastPlayMusicLevel": 0, "lastPlayMusicCategory": 0},
|
{"gaugeId":0, "isClear": False, "gaugeNum": 0, "chapterId": 70001, "jewelCount": 0, "isBossWatched": False, "isStoryWatched": False, "isDialogWatched": False, "isEndingWatched": False, "lastPlayMusicId": 0, "lastPlayMusicLevel": 0, "lastPlayMusicCategory": 0},
|
||||||
{"gaugeId":0, "isClear": False, "gaugeNum": 0, "chapterId": 70002, "jewelCount": 0, "isBossWatched": False, "isStoryWatched": False, "isDialogWatched": False, "isEndingWatched": False, "lastPlayMusicId": 0, "lastPlayMusicLevel": 0, "lastPlayMusicCategory": 0},
|
{"gaugeId":0, "isClear": False, "gaugeNum": 0, "chapterId": 70002, "jewelCount": 0, "isBossWatched": False, "isStoryWatched": False, "isDialogWatched": False, "isEndingWatched": False, "lastPlayMusicId": 0, "lastPlayMusicLevel": 0, "lastPlayMusicCategory": 0},
|
||||||
@ -37,17 +38,17 @@ class OngekiBrightMemory(OngekiBase):
|
|||||||
{"gaugeId":0, "isClear": False, "gaugeNum": 0, "chapterId": 70005, "jewelCount": 0, "isBossWatched": False, "isStoryWatched": False, "isDialogWatched": False, "isEndingWatched": False, "lastPlayMusicId": 0, "lastPlayMusicLevel": 0, "lastPlayMusicCategory": 0},
|
{"gaugeId":0, "isClear": False, "gaugeNum": 0, "chapterId": 70005, "jewelCount": 0, "isBossWatched": False, "isStoryWatched": False, "isDialogWatched": False, "isEndingWatched": False, "lastPlayMusicId": 0, "lastPlayMusicLevel": 0, "lastPlayMusicCategory": 0},
|
||||||
{"gaugeId":0, "isClear": False, "gaugeNum": 0, "chapterId": 70099, "jewelCount": 0, "isBossWatched": False, "isStoryWatched": False, "isDialogWatched": False, "isEndingWatched": False, "lastPlayMusicId": 0, "lastPlayMusicLevel": 0, "lastPlayMusicCategory": 0}
|
{"gaugeId":0, "isClear": False, "gaugeNum": 0, "chapterId": 70099, "jewelCount": 0, "isBossWatched": False, "isStoryWatched": False, "isDialogWatched": False, "isEndingWatched": False, "lastPlayMusicId": 0, "lastPlayMusicLevel": 0, "lastPlayMusicCategory": 0}
|
||||||
]}
|
]}
|
||||||
|
|
||||||
memory_chp = []
|
memory_chp = []
|
||||||
for chp in memories:
|
for chp in memories:
|
||||||
tmp = chp._asdict()
|
tmp = chp._asdict()
|
||||||
tmp.pop("id")
|
tmp.pop("id")
|
||||||
tmp.pop("user")
|
tmp.pop("user")
|
||||||
memory_chp.append(tmp)
|
memory_chp.append(tmp)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"userId": data["userId"],
|
"userId": data["userId"],
|
||||||
"length": len(memory_chp),
|
"length": len(memory_chp),
|
||||||
"userMemoryChapterList": memory_chp
|
"userMemoryChapterList": memory_chp
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,4 +56,15 @@ class OngekiBrightMemory(OngekiBase):
|
|||||||
return {
|
return {
|
||||||
"techScore": 0,
|
"techScore": 0,
|
||||||
"cardNum": 0
|
"cardNum": 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict:
|
||||||
|
# check for a bright memory profile
|
||||||
|
user_data = super().handle_cm_get_user_data_api_request(data)
|
||||||
|
|
||||||
|
# hardcode Card Maker version for now
|
||||||
|
# Card Maker 1.34.00 = 1.30.01
|
||||||
|
# Card Maker 1.36.00 = 1.35.04
|
||||||
|
user_data["userData"]["compatibleCmVersion"] = "1.35.04"
|
||||||
|
|
||||||
|
return user_data
|
||||||
|
@ -11,29 +11,99 @@ from titles.ongeki.database import OngekiData
|
|||||||
from titles.ongeki.const import OngekiConstants
|
from titles.ongeki.const import OngekiConstants
|
||||||
from titles.ongeki.config import OngekiConfig
|
from titles.ongeki.config import OngekiConfig
|
||||||
|
|
||||||
|
|
||||||
class OngekiReader(BaseReader):
|
class OngekiReader(BaseReader):
|
||||||
def __init__(self, config: CoreConfig, version: int, bin_dir: Optional[str], opt_dir: Optional[str], extra: Optional[str]) -> None:
|
def __init__(self, config: CoreConfig, version: int, bin_dir: Optional[str],
|
||||||
|
opt_dir: Optional[str], extra: Optional[str]) -> None:
|
||||||
super().__init__(config, version, bin_dir, opt_dir, extra)
|
super().__init__(config, version, bin_dir, opt_dir, extra)
|
||||||
self.data = OngekiData(config)
|
self.data = OngekiData(config)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.logger.info(f"Start importer for {OngekiConstants.game_ver_to_string(version)}")
|
self.logger.info(
|
||||||
|
f"Start importer for {OngekiConstants.game_ver_to_string(version)}")
|
||||||
except IndexError:
|
except IndexError:
|
||||||
self.logger.error(f"Invalid ongeki version {version}")
|
self.logger.error(f"Invalid ongeki version {version}")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
def read(self) -> None:
|
def read(self) -> None:
|
||||||
data_dirs = []
|
data_dirs = []
|
||||||
if self.bin_dir is not None:
|
if self.bin_dir is not None:
|
||||||
data_dirs += self.get_data_directories(self.bin_dir)
|
data_dirs += self.get_data_directories(self.bin_dir)
|
||||||
|
|
||||||
if self.opt_dir is not None:
|
if self.opt_dir is not None:
|
||||||
data_dirs += self.get_data_directories(self.opt_dir)
|
data_dirs += self.get_data_directories(self.opt_dir)
|
||||||
|
|
||||||
for dir in data_dirs:
|
for dir in data_dirs:
|
||||||
self.read_events(f"{dir}/event")
|
self.read_events(f"{dir}/event")
|
||||||
self.read_music(f"{dir}/music")
|
self.read_music(f"{dir}/music")
|
||||||
|
self.read_card(f"{dir}/card")
|
||||||
|
|
||||||
|
def read_card(self, base_dir: str) -> None:
|
||||||
|
self.logger.info(f"Reading cards from {base_dir}...")
|
||||||
|
|
||||||
|
version_ids = {
|
||||||
|
'1000': OngekiConstants.VER_ONGEKI,
|
||||||
|
'1005': OngekiConstants.VER_ONGEKI_PLUS,
|
||||||
|
'1010': OngekiConstants.VER_ONGEKI_SUMMER,
|
||||||
|
'1015': OngekiConstants.VER_ONGEKI_SUMMER_PLUS,
|
||||||
|
'1020': OngekiConstants.VER_ONGEKI_RED,
|
||||||
|
'1025': OngekiConstants.VER_ONGEKI_RED_PLUS,
|
||||||
|
'1030': OngekiConstants.VER_ONGEKI_BRIGHT,
|
||||||
|
'1035': OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY
|
||||||
|
}
|
||||||
|
|
||||||
|
for root, dirs, files in os.walk(base_dir):
|
||||||
|
for dir in dirs:
|
||||||
|
if os.path.exists(f"{root}/{dir}/Card.xml"):
|
||||||
|
with open(f"{root}/{dir}/Card.xml", "r", encoding="utf-8") as f:
|
||||||
|
troot = ET.fromstring(f.read())
|
||||||
|
|
||||||
|
card_id = int(troot.find('Name').find('id').text)
|
||||||
|
|
||||||
|
# skip already existing cards
|
||||||
|
if self.data.static.get_card(
|
||||||
|
OngekiConstants.VER_ONGEKI_BRIGHT_MEMORY, card_id) is not None:
|
||||||
|
|
||||||
|
self.logger.info(f"Card {card_id} already added, skipping")
|
||||||
|
continue
|
||||||
|
|
||||||
|
name = troot.find('Name').find('str').text
|
||||||
|
chara_id = int(troot.find('CharaID').find('id').text)
|
||||||
|
nick_name = troot.find('NickName').text
|
||||||
|
school = troot.find('School').find('str').text
|
||||||
|
attribute = troot.find('Attribute').text
|
||||||
|
gakunen = troot.find('Gakunen').find('str').text
|
||||||
|
rarity = OngekiConstants.RARITY_TYPES[
|
||||||
|
troot.find('Rarity').text].value
|
||||||
|
|
||||||
|
level_param = []
|
||||||
|
for lvl in troot.find('LevelParam').findall('int'):
|
||||||
|
level_param.append(lvl.text)
|
||||||
|
|
||||||
|
skill_id = int(troot.find('SkillID').find('id').text)
|
||||||
|
cho_kai_ka_skill_id = int(troot.find('ChoKaikaSkillID').find('id').text)
|
||||||
|
|
||||||
|
version = version_ids[
|
||||||
|
troot.find('VersionID').find('id').text]
|
||||||
|
card_number = troot.find('CardNumberString').text
|
||||||
|
|
||||||
|
self.data.static.put_card(
|
||||||
|
version,
|
||||||
|
card_id,
|
||||||
|
name=name,
|
||||||
|
charaId=chara_id,
|
||||||
|
nickName=nick_name,
|
||||||
|
school=school,
|
||||||
|
attribute=attribute,
|
||||||
|
gakunen=gakunen,
|
||||||
|
rarity=rarity,
|
||||||
|
levelParam=','.join(level_param),
|
||||||
|
skillId=skill_id,
|
||||||
|
choKaikaSkillId=cho_kai_ka_skill_id,
|
||||||
|
cardNumber=card_number
|
||||||
|
)
|
||||||
|
self.logger.info(f"Added card {card_id}")
|
||||||
|
|
||||||
def read_events(self, base_dir: str) -> None:
|
def read_events(self, base_dir: str) -> None:
|
||||||
self.logger.info(f"Reading events from {base_dir}...")
|
self.logger.info(f"Reading events from {base_dir}...")
|
||||||
|
|
||||||
@ -45,12 +115,13 @@ class OngekiReader(BaseReader):
|
|||||||
|
|
||||||
name = troot.find('Name').find('str').text
|
name = troot.find('Name').find('str').text
|
||||||
id = int(troot.find('Name').find('id').text)
|
id = int(troot.find('Name').find('id').text)
|
||||||
event_type = OngekiConstants.EVT_TYPES[troot.find('EventType').text].value
|
event_type = OngekiConstants.EVT_TYPES[troot.find(
|
||||||
|
'EventType').text].value
|
||||||
|
|
||||||
self.data.static.put_event(self.version, id, event_type, name)
|
self.data.static.put_event(
|
||||||
|
self.version, id, event_type, name)
|
||||||
self.logger.info(f"Added event {id}")
|
self.logger.info(f"Added event {id}")
|
||||||
|
|
||||||
def read_music(self, base_dir: str) -> None:
|
def read_music(self, base_dir: str) -> None:
|
||||||
self.logger.info(f"Reading music from {base_dir}...")
|
self.logger.info(f"Reading music from {base_dir}...")
|
||||||
|
|
||||||
@ -72,9 +143,9 @@ class OngekiReader(BaseReader):
|
|||||||
title = name.find('str').text
|
title = name.find('str').text
|
||||||
artist = troot.find('ArtistName').find('str').text
|
artist = troot.find('ArtistName').find('str').text
|
||||||
genre = troot.find('Genre').find('str').text
|
genre = troot.find('Genre').find('str').text
|
||||||
|
|
||||||
fumens = troot.find("FumenData")
|
fumens = troot.find("FumenData")
|
||||||
for fumens_data in fumens.findall('FumenData'):
|
for fumens_data in fumens.findall('FumenData'):
|
||||||
path = fumens_data.find('FumenFile').find('path').text
|
path = fumens_data.find('FumenFile').find('path').text
|
||||||
if path is None or not os.path.exists(f"{root}/{dir}/{path}"):
|
if path is None or not os.path.exists(f"{root}/{dir}/{path}"):
|
||||||
continue
|
continue
|
||||||
@ -82,8 +153,9 @@ class OngekiReader(BaseReader):
|
|||||||
chart_id = int(path.split(".")[0].split("_")[1])
|
chart_id = int(path.split(".")[0].split("_")[1])
|
||||||
level = float(
|
level = float(
|
||||||
f"{fumens_data.find('FumenConstIntegerPart').text}.{fumens_data.find('FumenConstFractionalPart').text}"
|
f"{fumens_data.find('FumenConstIntegerPart').text}.{fumens_data.find('FumenConstFractionalPart').text}"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.data.static.put_chart(self.version, song_id, chart_id, title, artist, genre, level)
|
|
||||||
self.logger.info(f"Added song {song_id} chart {chart_id}")
|
|
||||||
|
|
||||||
|
self.data.static.put_chart(
|
||||||
|
self.version, song_id, chart_id, title, artist, genre, level)
|
||||||
|
self.logger.info(
|
||||||
|
f"Added song {song_id} chart {chart_id}")
|
||||||
|
@ -180,7 +180,7 @@ region = Table(
|
|||||||
mysql_charset='utf8mb4'
|
mysql_charset='utf8mb4'
|
||||||
)
|
)
|
||||||
|
|
||||||
training_room = Table (
|
training_room = Table(
|
||||||
"ongeki_profile_training_room",
|
"ongeki_profile_training_room",
|
||||||
metadata,
|
metadata,
|
||||||
Column("id", Integer, primary_key=True, nullable=False),
|
Column("id", Integer, primary_key=True, nullable=False),
|
||||||
@ -193,7 +193,7 @@ training_room = Table (
|
|||||||
mysql_charset='utf8mb4'
|
mysql_charset='utf8mb4'
|
||||||
)
|
)
|
||||||
|
|
||||||
kop = Table (
|
kop = Table(
|
||||||
"ongeki_profile_kop",
|
"ongeki_profile_kop",
|
||||||
metadata,
|
metadata,
|
||||||
Column("id", Integer, primary_key=True, nullable=False),
|
Column("id", Integer, primary_key=True, nullable=False),
|
||||||
@ -219,6 +219,7 @@ rival = Table(
|
|||||||
mysql_charset='utf8mb4'
|
mysql_charset='utf8mb4'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class OngekiProfileData(BaseData):
|
class OngekiProfileData(BaseData):
|
||||||
def __init__(self, cfg: CoreConfig, conn: Connection) -> None:
|
def __init__(self, cfg: CoreConfig, conn: Connection) -> None:
|
||||||
super().__init__(cfg, conn)
|
super().__init__(cfg, conn)
|
||||||
@ -287,14 +288,14 @@ class OngekiProfileData(BaseData):
|
|||||||
result = self.execute(sql)
|
result = self.execute(sql)
|
||||||
if result is None: return None
|
if result is None: return None
|
||||||
return result.fetchall()
|
return result.fetchall()
|
||||||
|
|
||||||
def get_kop(self, aime_id: int) -> Optional[List[Row]]:
|
def get_kop(self, aime_id: int) -> Optional[List[Row]]:
|
||||||
sql = select(kop).where(kop.c.user == aime_id)
|
sql = select(kop).where(kop.c.user == aime_id)
|
||||||
|
|
||||||
result = self.execute(sql)
|
result = self.execute(sql)
|
||||||
if result is None: return None
|
if result is None: return None
|
||||||
return result.fetchall()
|
return result.fetchall()
|
||||||
|
|
||||||
def get_rivals(self, aime_id: int) -> Optional[List[Row]]:
|
def get_rivals(self, aime_id: int) -> Optional[List[Row]]:
|
||||||
sql = select(rival.c.rivalUserId).where(rival.c.user == aime_id)
|
sql = select(rival.c.rivalUserId).where(rival.c.user == aime_id)
|
||||||
|
|
||||||
|
@ -192,7 +192,7 @@ class OngekiStaticData(BaseData):
|
|||||||
|
|
||||||
def get_gacha(self, version: int, gacha_id: int) -> Optional[Dict]:
|
def get_gacha(self, version: int, gacha_id: int) -> Optional[Dict]:
|
||||||
sql = gachas.select(and_(
|
sql = gachas.select(and_(
|
||||||
gachas.c.version == version,
|
gachas.c.version <= version,
|
||||||
gachas.c.gachaId == gacha_id
|
gachas.c.gachaId == gacha_id
|
||||||
))
|
))
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user