Merge branch 'prism_support' into develop

This commit is contained in:
2025-04-08 00:31:31 +00:00
10 changed files with 233 additions and 6 deletions

View File

@ -0,0 +1,50 @@
"""Mai2 Kaleidx Scope Support
Revision ID: 16f34bf7b968
Revises: d0f1c7fa9505
Create Date: 2025-04-02 07:06:15.829591
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '16f34bf7b968'
down_revision = 'd0f1c7fa9505'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('mai2_score_kaleidx_scope',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user', sa.Integer(), nullable=False),
sa.Column('gateId', sa.Integer(), nullable=True),
sa.Column('isGateFound', sa.Boolean(), nullable=True),
sa.Column('isKeyFound', sa.Boolean(), nullable=True),
sa.Column('isClear', sa.Boolean(), nullable=True),
sa.Column('totalRestLife', sa.Integer(), nullable=True),
sa.Column('totalAchievement', sa.Integer(), nullable=True),
sa.Column('totalDeluxscore', sa.Integer(), nullable=True),
sa.Column('bestAchievement', sa.Integer(), nullable=True),
sa.Column('bestDeluxscore', sa.Integer(), nullable=True),
sa.Column('bestAchievementDate', sa.String(length=25), nullable=True),
sa.Column('bestDeluxscoreDate', sa.String(length=25), nullable=True),
sa.Column('playCount', sa.Integer(), nullable=True),
sa.Column('clearDate', sa.String(length=25), nullable=True),
sa.Column('lastPlayDate', sa.String(length=25), nullable=True),
sa.Column('isInfoWatched', sa.Boolean(), nullable=True),
sa.ForeignKeyConstraint(['user'], ['aime_user.id'], onupdate='cascade', ondelete='cascade'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('user', 'gateId', name='mai2_score_best_uk'),
mysql_charset='utf8mb4'
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('mai2_score_kaleidx_scope')
# ### end Alembic commands ###

View File

@ -0,0 +1,28 @@
"""Mai2 add PRiSM support
Revision ID: d0f1c7fa9505
Revises: 1d0014d35220
Create Date: 2025-04-02 06:37:10.657372
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'd0f1c7fa9505'
down_revision = '1d0014d35220'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('mai2_playlog', sa.Column('extBool2', sa.Boolean(), nullable=True,server_default=sa.text("NULL")))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('mai2_playlog', 'extBool2')
# ### end Alembic commands ###

View File

@ -203,7 +203,7 @@ Presents are items given to the user when they login, with a little animation (f
### Versions ### Versions
| Game Code | Version ID | Version Name | | Game Code | Version ID | Version Name |
| --------- | ---------- | ----------------------- | |-----------|------------|-------------------------|
| SBXL | 0 | maimai | | SBXL | 0 | maimai |
| SBXL | 1 | maimai PLUS | | SBXL | 1 | maimai PLUS |
| SBZF | 2 | maimai GreeN | | SBZF | 2 | maimai GreeN |
@ -227,6 +227,8 @@ Presents are items given to the user when they login, with a little animation (f
| SDEZ | 20 | maimai DX FESTiVAL PLUS | | SDEZ | 20 | maimai DX FESTiVAL PLUS |
| SDEZ | 21 | maimai DX BUDDiES | | SDEZ | 21 | maimai DX BUDDiES |
| SDEZ | 22 | maimai DX BUDDiES PLUS | | SDEZ | 22 | maimai DX BUDDiES PLUS |
| SDEZ | 23 | maimai DX PRiSM |
### Importer ### Importer

View File

@ -52,6 +52,7 @@ Games listed below have been tested and confirmed working. Only game versions ol
+ FESTiVAL PLUS + FESTiVAL PLUS
+ BUDDiES + BUDDiES
+ BUDDiES PLUS + BUDDiES PLUS
+ PRiSM
+ O.N.G.E.K.I. + O.N.G.E.K.I.
+ SUMMER + SUMMER

View File

@ -207,7 +207,8 @@ class CardMakerReader(BaseReader):
"1.30": Mai2Constants.VER_MAIMAI_DX_FESTIVAL, "1.30": Mai2Constants.VER_MAIMAI_DX_FESTIVAL,
"1.35": Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS, "1.35": Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS,
"1.40": Mai2Constants.VER_MAIMAI_DX_BUDDIES, "1.40": Mai2Constants.VER_MAIMAI_DX_BUDDIES,
"1.45": Mai2Constants.VER_MAIMAI_DX_BUDDIES_PLUS "1.45": Mai2Constants.VER_MAIMAI_DX_BUDDIES_PLUS,
"1.50": Mai2Constants.VER_MAIMAI_DX_PRISM
} }
for root, dirs, files in os.walk(base_dir): for root, dirs, files in os.walk(base_dir):

View File

@ -59,6 +59,7 @@ class Mai2Constants:
VER_MAIMAI_DX_FESTIVAL_PLUS = 20 VER_MAIMAI_DX_FESTIVAL_PLUS = 20
VER_MAIMAI_DX_BUDDIES = 21 VER_MAIMAI_DX_BUDDIES = 21
VER_MAIMAI_DX_BUDDIES_PLUS = 22 VER_MAIMAI_DX_BUDDIES_PLUS = 22
VER_MAIMAI_DX_PRISM = 23
VERSION_STRING = ( VERSION_STRING = (
"maimai", "maimai",
@ -83,7 +84,8 @@ class Mai2Constants:
"maimai DX FESTiVAL", "maimai DX FESTiVAL",
"maimai DX FESTiVAL PLUS", "maimai DX FESTiVAL PLUS",
"maimai DX BUDDiES", "maimai DX BUDDiES",
"maimai DX BUDDiES PLUS" "maimai DX BUDDiES PLUS",
"maimai DX PRiSM"
) )
MAI_VERSION_LUT = { MAI_VERSION_LUT = {

View File

@ -112,6 +112,17 @@ class Mai2DX(Mai2Base):
return {"returnCode": 1, "apiName": "UploadUserPlaylogApi"} return {"returnCode": 1, "apiName": "UploadUserPlaylogApi"}
# Exp version use this instead of UploadUserPlaylogApi in 1.50
async def handle_upload_user_playlog_list_api_request(self, data: Dict) -> Dict:
user_id = data["userId"]
playlog_list = data["userPlaylogList"]
for playlog in playlog_list:
await self.data.score.put_playlog(user_id, playlog)
return {"returnCode": 1, "apiName": "UploadUserPlaylogApi"}
async def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict: async def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict:
user_id = data["userId"] user_id = data["userId"]
charge = data["userCharge"] charge = data["userCharge"]
@ -271,6 +282,12 @@ class Mai2DX(Mai2Base):
for intimate in upsert["userIntimateList"]: for intimate in upsert["userIntimateList"]:
await self.data.profile.put_intimacy(user_id, intimate["partnerId"], intimate["intimateLevel"], intimate["intimateCountRewarded"]) await self.data.profile.put_intimacy(user_id, intimate["partnerId"], intimate["intimateLevel"], intimate["intimateCountRewarded"])
# added in PRiSM
if "userKaleidxScopeList" in upsert and len(upsert["userKaleidxScopeList"]) > 0:
for kaleidxscope in upsert["userKaleidxScopeList"]:
await self.data.score.put_user_kaleidxscope(user_id, kaleidxscope)
return {"returnCode": 1, "apiName": "UpsertUserAllApi"} return {"returnCode": 1, "apiName": "UpsertUserAllApi"}
async def handle_get_user_data_api_request(self, data: Dict) -> Dict: async def handle_get_user_data_api_request(self, data: Dict) -> Dict:

View File

@ -31,6 +31,7 @@ from .festival import Mai2Festival
from .festivalplus import Mai2FestivalPlus from .festivalplus import Mai2FestivalPlus
from .buddies import Mai2Buddies from .buddies import Mai2Buddies
from .buddiesplus import Mai2BuddiesPlus from .buddiesplus import Mai2BuddiesPlus
from .prism import Mai2Prism
class Mai2Servlet(BaseServlet): class Mai2Servlet(BaseServlet):
@ -66,7 +67,8 @@ class Mai2Servlet(BaseServlet):
Mai2Festival, Mai2Festival,
Mai2FestivalPlus, Mai2FestivalPlus,
Mai2Buddies, Mai2Buddies,
Mai2BuddiesPlus Mai2BuddiesPlus,
Mai2Prism
] ]
self.logger = logging.getLogger("mai2") self.logger = logging.getLogger("mai2")
@ -306,8 +308,11 @@ class Mai2Servlet(BaseServlet):
internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS
elif version >= 140 and version < 145: # BUDDiES elif version >= 140 and version < 145: # BUDDiES
internal_ver = Mai2Constants.VER_MAIMAI_DX_BUDDIES internal_ver = Mai2Constants.VER_MAIMAI_DX_BUDDIES
elif version >= 145: # BUDDiES PLUS elif version >= 145 and version <150: # BUDDiES PLUS
internal_ver = Mai2Constants.VER_MAIMAI_DX_BUDDIES_PLUS internal_ver = Mai2Constants.VER_MAIMAI_DX_BUDDIES_PLUS,
elif version >=150:
internal_ver = Mai2Constants.VER_MAIMAI_DX_PRISM
elif game_code == "SDGA": # Int elif game_code == "SDGA": # Int
if version < 105: # 1.0 if version < 105: # 1.0
internal_ver = Mai2Constants.VER_MAIMAI_DX internal_ver = Mai2Constants.VER_MAIMAI_DX
@ -325,6 +330,12 @@ class Mai2Servlet(BaseServlet):
internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL
elif version >= 135 and version < 140: # FESTiVAL PLUS elif version >= 135 and version < 140: # FESTiVAL PLUS
internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS
elif version >= 140 and version < 145: # BUDDiES
internal_ver = Mai2Constants.VER_MAIMAI_DX_BUDDIES
elif version >= 145 and version <150: # BUDDiES PLUS
internal_ver = Mai2Constants.VER_MAIMAI_DX_BUDDIES_PLUS,
elif version >=150:
internal_ver = Mai2Constants.VER_MAIMAI_DX_PRISM
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

66
titles/mai2/prism.py Normal file
View File

@ -0,0 +1,66 @@
from typing import Dict
from core.config import CoreConfig
from titles.mai2.buddiesplus import Mai2BuddiesPlus
from titles.mai2.const import Mai2Constants
from titles.mai2.config import Mai2Config
class Mai2Prism(Mai2BuddiesPlus):
def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None:
super().__init__(cfg, game_cfg)
self.version = Mai2Constants.VER_MAIMAI_DX_PRISM
async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
user_data = await super().handle_cm_get_user_preview_api_request(data)
# hardcode lastDataVersion for CardMaker
user_data["lastDataVersion"] = "1.50.00"
return user_data
async def handle_get_user_new_item_list_api_request(self, data: Dict) -> Dict:
return {
"user_id": data["userId"],
"userItemList": []
}
#seems to be used for downloading music scores online
async def handle_get_game_music_score_api_request(self, data: Dict) -> Dict:
return {
"gameMusicScore": {
"musicId": data["musicId"],
"level": data["level"],
"type": data["type"],
"scoreData": ""
}
}
async def handle_get_game_kaleidx_scope_api_request(self, data: Dict) -> Dict:
return {
"gameKaleidxScopeList": [
{"gateId": 1, "phaseId": 6},
{"gateId": 2, "phaseId": 6},
{"gateId": 3, "phaseId": 6},
{"gateId": 4, "phaseId": 6},
{"gateId": 5, "phaseId": 6},
{"gateId": 6, "phaseId": 6}
]
}
async def handle_get_user_kaleidx_scope_api_request(self, data: Dict) -> Dict:
kaleidxscope = await self.data.score.get_user_kaleidxscope_list(data["userId"])
if kaleidxscope is None:
return {"userId": data["userId"], "userKaleidxScopeList":[]}
kaleidxscope_list = []
for kaleidxscope_data in kaleidxscope:
tmp = kaleidxscope_data._asdict()
tmp.pop("user")
tmp.pop("id")
kaleidxscope_list.append(tmp)
return {
"userId": data["userId"],
"userKaleidxScopeList": kaleidxscope_list
}

View File

@ -1,3 +1,4 @@
from configparser import Interpolation
from typing import Dict, List, Optional from typing import Dict, List, Optional
from sqlalchemy import Column, Table, UniqueConstraint, and_ from sqlalchemy import Column, Table, UniqueConstraint, and_
@ -147,6 +148,7 @@ playlog = Table(
Column("extNum2", Integer), Column("extNum2", Integer),
Column("extNum4", Integer), Column("extNum4", Integer),
Column("extBool1", Boolean), # new with buddies Column("extBool1", Boolean), # new with buddies
Column("extBool2", Boolean), # new with prism
Column("trialPlayAchievement", Integer), Column("trialPlayAchievement", Integer),
mysql_charset="utf8mb4", mysql_charset="utf8mb4",
) )
@ -173,6 +175,34 @@ playlog_2p = Table(
mysql_charset="utf8mb4", mysql_charset="utf8mb4",
) )
kaleidxscope = Table(
"mai2_score_kaleidxscope",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("gateId", Integer),
Column("isGateFound", Boolean),
Column("isKeyFound", Boolean),
Column("isClear", Boolean),
Column("totalRestLife", Integer),
Column("totalAchievement", Integer),
Column("totalDeluxscore", Integer),
Column("bestAchievement", Integer),
Column("bestDeluxscore", Integer),
Column("bestAchievementDate", String(25)),
Column("bestDeluxscoreDate", String(25)),
Column("playCount", Integer),
Column("clearDate", String(25)),
Column("lastPlayDate", String(25)),
Column("isInfoWatched", Boolean),
UniqueConstraint("user", "gateId", name="mai2_score_best_uk"),
mysql_charset="utf8mb4"
)
course = Table( course = Table(
"mai2_score_course", "mai2_score_course",
metadata, metadata,
@ -450,3 +480,22 @@ class Mai2ScoreData(BaseData):
self.logger.warning(f"aime_id {aime_id} has no playlog ") self.logger.warning(f"aime_id {aime_id} has no playlog ")
return None return None
return result.scalar() return result.scalar()
async def get_user_kaleidxscope_list(self, user_id: int) -> Optional[List[Row]]:
sql = kaleidxscope.select(kaleidxscope.c.user == user_id)
result = await self.execute(sql)
if result is None:
return None
return result.fetchall()
async def put_user_kaleidxscope(self, user_id: int, user_kaleidxscope_data: Dict) -> Optional[int]:
user_kaleidxscope_data["user"] = user_id
sql = insert(kaleidxscope).values(**user_kaleidxscope_data)
conflict = sql.on_duplicate_key_update(**user_kaleidxscope_data)
result = await self.execute(conflict)
if result is None:
self.logger.error(f"put_user_kaleidxscope: Failed to insert! user_id {user_id}")
return None
return result.lastrowid