3
2
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:
Dniel97 2023-03-05 23:54:13 +01:00
parent fe8f40c627
commit 74f3ab7c3f
Signed by untrusted user: Dniel97
GPG Key ID: 6180B3C768FB2E08
16 changed files with 453 additions and 1844 deletions

View File

@ -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

View File

@ -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

View File

@ -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
View 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

View File

@ -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 gachaId cardId rarity weight isPickup isSelect
334 1135 101602 3 1 0 0
335 1135 101603 3 1 0 0
336 1135 101619 2 1 0 0
337 1156 101604 3 1 0 0
338 1156 101605 3 1 0 0
339 1156 101607 3 1 0 0
340 1156 101608 3 1 0 0
341 1156 101596 4 1 0 0
342 1156 101597 4 1 0 0
343 1156 101599 4 1 0 0
344 1156 101600 4 1 0 0
345 1149 100003 2 1 0 0
346 1149 100004 2 1 0 0
347 1149 100012 2 1 0 0
348 1149 100013 2 1 0 0
349 1149 100021 2 1 0 0
350 1149 100022 2 1 0 0
351 1149 100173 2 1 0 0
352 1149 100174 2 1 0 0
353 1149 100175 2 1 0 0
354 1149 100339 2 1 0 0
355 1149 100340 2 1 0 0
356 1149 100341 2 1 0 0
357 1149 100223 2 1 0 0
358 1149 100224 2 1 0 0
359 1149 100225 2 1 0 0
360 1149 100692 2 1 0 0
361 1149 100693 2 1 0 0
362 1149 100694 2 1 0 0
363 1149 101020 2 1 0 0
364 1149 101025 2 1 0 0
365 1149 100418 3 1 0 0
366 1149 101005 3 1 0 0
367 1149 100785 3 1 0 0
368 1149 100786 3 1 0 0
369 1149 101602 3 1 0 0
370 1149 101604 3 1 0 0
371 1149 100760 4 1 0 0
372 1149 100780 4 1 0 0
373 1149 100987 4 1 0 0
374 1149 101295 4 1 0 0
375 1149 101296 4 1 0 0
376 1149 101592 4 1 0 0
377 1163 100008 4 1 0 1
378 1163 100017 4 1 0 1
379 1163 100026 4 1 0 1
380 1163 100034 4 1 0 1
381 1163 100041 4 1 0 1
382 1163 100048 4 1 0 1
383 1163 100054 4 1 0 1
384 1163 100060 4 1 0 1
385 1163 100066 4 1 0 1
386 1163 100078 4 1 0 1
387 1163 100072 4 1 0 1
388 1163 100084 4 1 0 1
389 1163 100090 4 1 0 1
390 1163 100282 4 1 0 1
391 1163 100285 4 1 0 1
392 1163 100284 4 1 0 1
393 1163 100286 4 1 0 1
394 1163 100280 4 1 0 1
395 1163 100276 4 1 0 1
396 1163 100277 4 1 0 1
397 1163 100275 4 1 0 1
398 1163 100278 4 1 0 1
399 1163 100431 4 1 0 1
400 1163 100407 4 1 0 1
401 1163 100432 4 1 0 1
402 1163 100433 4 1 0 1
403 1163 100434 4 1 0 1
404 1163 100435 4 1 0 1
405 1163 100436 4 1 0 1
406 1163 100437 4 1 0 1
407 1163 100438 4 1 0 1
408 1163 100439 4 1 0 1
409 1163 100760 4 1 0 1
410 1163 100761 4 1 0 1
411 1163 100779 4 1 0 1
412 1163 100767 4 1 0 1
413 1163 100780 4 1 0 1
414 1163 100784 4 1 0 1
415 1163 100768 4 1 0 1
416 1163 100725 4 1 0 1
417 1163 100726 4 1 0 1
418 1163 100984 4 1 0 1
419 1163 100985 4 1 0 1
420 1163 100987 4 1 0 1
421 1163 100988 4 1 0 1
422 1163 100986 4 1 0 1
423 1163 100989 4 1 0 1
424 1163 100982 4 1 0 1
425 1163 100983 4 1 0 1
426 1163 100787 4 1 0 1
427 1163 101293 4 1 0 1
428 1163 101294 4 1 0 1
429 1163 101295 4 1 0 1
430 1163 101296 4 1 0 1
431 1163 101297 4 1 0 1
432 1163 101320 4 1 0 1
433 1163 101567 4 1 0 1
434 1164 100008 4 1 0 1
435 1164 100017 4 1 0 1
436 1164 100026 4 1 0 1
437 1164 100034 4 1 0 1
438 1164 100041 4 1 0 1
439 1164 100048 4 1 0 1
440 1164 100054 4 1 0 1
441 1164 100060 4 1 0 1
442 1164 100066 4 1 0 1
443 1164 100078 4 1 0 1
444 1164 100072 4 1 0 1
445 1164 100084 4 1 0 1
446 1164 100090 4 1 0 1
447 1164 100282 4 1 0 1
448 1164 100285 4 1 0 1
449 1164 100284 4 1 0 1
450 1164 100286 4 1 0 1
451 1164 100280 4 1 0 1
452 1164 100276 4 1 0 1
453 1164 100277 4 1 0 1
454 1164 100275 4 1 0 1
455 1164 100278 4 1 0 1
456 1164 100431 4 1 0 1
457 1164 100407 4 1 0 1
458 1164 100432 4 1 0 1
459 1164 100433 4 1 0 1
460 1164 100434 4 1 0 1
461 1164 100435 4 1 0 1
462 1164 100436 4 1 0 1
463 1164 100437 4 1 0 1
464 1164 100438 4 1 0 1
465 1164 100439 4 1 0 1
466 1164 100760 4 1 0 1
467 1164 100761 4 1 0 1
468 1164 100779 4 1 0 1
469 1164 100767 4 1 0 1
470 1164 100780 4 1 0 1
471 1164 100784 4 1 0 1
472 1164 100768 4 1 0 1
473 1164 100725 4 1 0 1
474 1164 100726 4 1 0 1
475 1164 100984 4 1 0 1
476 1164 100985 4 1 0 1
477 1164 100987 4 1 0 1
478 1164 100988 4 1 0 1
479 1164 100986 4 1 0 1
480 1164 100989 4 1 0 1
481 1164 100982 4 1 0 1
482 1164 100983 4 1 0 1
483 1164 100787 4 1 0 1
484 1164 101293 4 1 0 1
485 1164 101294 4 1 0 1
486 1164 101295 4 1 0 1
487 1164 101296 4 1 0 1
488 1164 101297 4 1 0 1
489 1164 101320 4 1 0 1
490 1164 101567 4 1 0 1
491 1164 101592 4 1 0 1
492 1164 101593 4 1 0 1
493 1164 101594 4 1 0 1
494 1164 101595 4 1 0 1
495 1164 101598 4 1 0 1
496 1164 101596 4 1 0 1
497 1164 101597 4 1 0 1
498 1164 101599 4 1 0 1
499 1164 101600 4 1 0 1
500 1141 101600 4 1 0 1
501 1141 101608 3 1 0 1

View File

@ -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
1 version gachaId gachaName type kind isCeiling maxSelectPoint ceilingCnt changeRateCnt1 changeRateCnt2
2 6 1011 無料ガチャ 0 3 0 0 10 0 0
3 6 1012 無料ガチャ(SR確定) 0 3 0 0 10 0 0
4 6 1043 レギュラーガチャ 0 0 0 0 10 0 0
5 6 1067 例えるなら大人のパッションフルーツ リゾートプールガチャ 0 1 0 0 10 0 0
6 6 1068 柏木 咲姫 ピックアップガチャ 0 2 0 0 10 0 0
7 6 1069 井之原 小星 ピックアップガチャ 0 2 0 0 10 0 0
8 6 1070 目指すは優勝! 炎の体育祭リミテッドガチャ 0 1 1 110 10 0 0
9 6 1071 星咲 あかり ピックアップガチャ 0 2 0 0 10 0 0
10 6 1072 藤沢 柚子 ピックアップガチャ 0 2 0 0 10 0 0
11 6 1073 三角 葵 ピックアップガチャ 0 2 0 0 10 0 0
12 6 1074 おくれてきた Halloweenガチャ 0 1 0 0 10 0 0
13 6 1075 早乙女 彩華 ピックアップガチャ 0 2 0 0 10 0 0
14 6 1076 桜井 春菜 ピックアップガチャ 0 2 0 0 10 0 0
15 6 1077 ふわふわすぺーす お仕事体験リミテッドガチャ 0 1 1 110 10 0 0
16 6 1078 高瀬 梨緒 ピックアップガチャ 0 2 0 0 10 0 0
17 6 1079 結城 莉玖 ピックアップガチャ 0 2 0 0 10 0 0
18 6 1080 藍原 椿 ピックアップガチャ 0 2 0 0 10 0 0
19 6 1081 今夜はおうちでパーティ☆ メリクリガチャ 0 1 0 0 10 0 0
20 6 1082 日向 千夏 ピックアップガチャ 0 2 0 0 10 0 0
21 6 1083 柏木 美亜 ピックアップガチャ 0 2 0 0 10 0 0
22 6 1084 東雲 つむぎ ピックアップガチャ 0 2 0 0 10 0 0
23 6 1085 謹賀新年 福袋ガチャ 0 0 1 33 10 0 0
24 6 1086 逢坂 茜 ピックアップガチャ 0 2 0 0 10 0 0
25 6 1087 珠洲島 有栖 ピックアップガチャ 0 2 0 0 10 0 0
26 6 1088 九條 楓 ピックアップガチャ 0 2 0 0 10 0 0
27 6 1089 冬の魔法 スーパーウルトラウィンターガチャ 0 1 0 0 10 0 0
28 6 1093 高瀬 梨緒ピックアップガチャ 0 2 0 0 10 0 0
29 6 1094 結城 莉玖ピックアップガチャ 0 2 0 0 10 0 0
30 6 1095 藍原 椿ピックアップガチャ 0 2 0 0 10 0 0
31 6 1096 早乙女 彩華ピックアップガチャ 0 2 0 0 10 0 0
32 6 1097 桜井 春菜ピックアップガチャ 0 2 0 0 10 0 0
33 6 1098 逢坂 茜ピックアップガチャ 0 2 0 0 10 0 0
34 6 1099 九條 楓ピックアップガチャ 0 2 0 0 10 0 0
35 6 1100 珠洲島 有栖ピックアップガチャ 0 2 0 0 10 0 0
36 6 1101 LEAF属性オンリーガチャ 0 2 0 0 10 0 0
37 6 1102 AQUA属性オンリーガチャ 0 2 0 0 10 0 0
38 6 1103 FIRE属性オンリーガチャ 0 2 0 0 10 0 0
39 6 1104 夜明け前の双星ガチャ 0 1 0 0 10 0 0
40 6 1105 謎の洞窟 黄金は実在した!!ガチャ 0 1 0 0 10 0 0
41 6 1106 スウィートブライダルリミテッドガチャ 0 1 0 0 10 0 0
42 6 1107 忘れられない、愛(ピュア)とロックがここにある。ガチャ 0 1 0 0 10 0 0
43 6 1108 メルティ夜ふかしガチャ 0 1 0 0 10 0 0
44 6 1109 絵本の国のシューターズガチャ 0 1 0 0 10 0 0
45 6 1110 オンゲキ R.E.D. PLUS 大感謝祭ガチャ 0 1 0 0 10 0 0
46 6 1111 オンゲキ 3rd Anniversaryガチャ 0 1 1 33 10 0 0
47 10 0 0
48 10 0 0
49 10 0 0
50 10 0 0
51 10 0 0
52 10 0 0
53 10 0 0
54 10 0 0
55 10 0 0
56 10 0 0
57 10 0 0
58 10 0 0
59 10 0 0
60 10 0 0
61 10 0 0
62 10 0 0
63 10 0 0
64 10 0 0
65 10 0 0
66 10 0 0
67 10 0 0
68 10 0 0
69 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

View File

@ -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):

View File

@ -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:

View File

@ -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}")

View File

@ -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

View File

@ -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(

View File

@ -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

View File

@ -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}")

View File

@ -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)

View File

@ -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
)) ))