forked from Hay1tsme/artemis
Merge pull request 'Chunithm Sun Plus, Int support' (#78) from EmmyHeart/artemis-sp:develop into develop
Reviewed-on: Hay1tsme/artemis#78
This commit is contained in:
commit
7b8611cee3
12
core/data/schema/versions/SDBT_4_rollback.sql
Normal file
12
core/data/schema/versions/SDBT_4_rollback.sql
Normal file
@ -0,0 +1,12 @@
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
ALTER TABLE chuni_score_playlog
|
||||
CHANGE COLUMN isClear isClear TINYINT(1) NULL DEFAULT NULL;
|
||||
|
||||
ALTER TABLE aime.chuni_score_best
|
||||
CHANGE COLUMN isSuccess isSuccess TINYINT(1) NULL DEFAULT NULL ;
|
||||
|
||||
ALTER TABLE chuni_score_playlog
|
||||
DROP COLUMN ticketId;
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
12
core/data/schema/versions/SDBT_5_upgrade.sql
Normal file
12
core/data/schema/versions/SDBT_5_upgrade.sql
Normal file
@ -0,0 +1,12 @@
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
ALTER TABLE chuni_score_playlog
|
||||
CHANGE COLUMN isClear isClear TINYINT(6) NULL DEFAULT NULL;
|
||||
|
||||
ALTER TABLE aime.chuni_score_best
|
||||
CHANGE COLUMN isSuccess isSuccess INT(11) NULL DEFAULT NULL ;
|
||||
|
||||
ALTER TABLE chuni_score_playlog
|
||||
ADD COLUMN ticketId INT(11) NULL AFTER machineType;
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
@ -54,6 +54,7 @@ Games listed below have been tested and confirmed working.
|
||||
| 11 | CHUNITHM NEW!! |
|
||||
| 12 | CHUNITHM NEW PLUS!! |
|
||||
| 13 | CHUNITHM SUN |
|
||||
| 14 | CHUNITHM SUN PLUS |
|
||||
|
||||
|
||||
### Importer
|
||||
@ -74,7 +75,6 @@ Config file is located in `config/chuni.yaml`.
|
||||
|------------------|----------------------------------------------------------------------------------------------------------------|
|
||||
| `news_msg` | If this is set, the news at the top of the main screen will be displayed (up to Chunithm Paradise Lost) |
|
||||
| `name` | If this is set, all players that are not on a team will use this one by default. |
|
||||
| `rank_scale` | Scales the in-game ranking based on the number of teams within the database |
|
||||
| `use_login_bonus`| This is used to enable the login bonuses |
|
||||
| `crypto` | This option is used to enable the TLS Encryption |
|
||||
|
||||
@ -132,12 +132,6 @@ INSERT INTO aime.chuni_profile_team (teamName) VALUES (<teamName>);
|
||||
```
|
||||
Team names can be regular ASCII, and they will be displayed ingame.
|
||||
|
||||
On smaller installations, you may also wish to enable scaled team rankings. By default, Chunithm determines team ranking within the first 100 teams. This can be configured in the YAML:
|
||||
```yaml
|
||||
team:
|
||||
rank_scale: True # Scales the in-game ranking based on the number of teams within the database, rather than the default scale of ~100 that the game normally uses.
|
||||
```
|
||||
|
||||
### Favorite songs
|
||||
You can set the songs that will be in a user's Favorite Songs category using the following SQL entries:
|
||||
```sql
|
||||
|
@ -19,6 +19,9 @@ version:
|
||||
13:
|
||||
rom: 2.10.00
|
||||
data: 2.10.00
|
||||
14:
|
||||
rom: 2.15.00
|
||||
data: 2.15.00
|
||||
|
||||
crypto:
|
||||
encrypted_only: False
|
@ -5,7 +5,7 @@ A network service emulator for games running SEGA'S ALL.NET service, and similar
|
||||
Games listed below have been tested and confirmed working. Only game versions older then the version currently active in arcades, or games versions that have not recieved a major update in over one year, are supported.
|
||||
|
||||
+ CHUNITHM
|
||||
+ All versions up to SUN
|
||||
+ All versions up to SUN PLUS
|
||||
|
||||
+ crossbeats REV.
|
||||
+ All versions + omnimix
|
||||
|
@ -6,5 +6,5 @@ from titles.chuni.read import ChuniReader
|
||||
index = ChuniServlet
|
||||
database = ChuniData
|
||||
reader = ChuniReader
|
||||
game_codes = [ChuniConstants.GAME_CODE, ChuniConstants.GAME_CODE_NEW]
|
||||
current_schema_version = 4
|
||||
game_codes = [ChuniConstants.GAME_CODE, ChuniConstants.GAME_CODE_NEW, ChuniConstants.GAME_CODE_INT]
|
||||
current_schema_version = 5
|
@ -240,7 +240,6 @@ class ChuniBase:
|
||||
"isDumpUpload": "false",
|
||||
"isAou": "false",
|
||||
}
|
||||
|
||||
def handle_get_user_activity_api_request(self, data: Dict) -> Dict:
|
||||
user_activity_list = self.data.profile.get_profile_activity(
|
||||
data["userId"], data["kind"]
|
||||
@ -420,13 +419,13 @@ class ChuniBase:
|
||||
all_entries = self.data.score.get_rival_music(rival_id)
|
||||
|
||||
# Process the entries based on max_count and nextIndex
|
||||
for music in all_entries[next_index:]:
|
||||
for music in all_entries:
|
||||
music_id = music["musicId"]
|
||||
level = music["level"]
|
||||
score = music["scoreMax"]
|
||||
rank = music["scoreRank"]
|
||||
|
||||
# Create a music entry for the current music_id
|
||||
# Create a music entry for the current music_id if it's unique
|
||||
music_entry = next((entry for entry in user_rival_music_list if entry["musicId"] == music_id), None)
|
||||
if music_entry is None:
|
||||
music_entry = {
|
||||
@ -436,15 +435,20 @@ class ChuniBase:
|
||||
}
|
||||
user_rival_music_list.append(music_entry)
|
||||
|
||||
# Create a level entry for the current level
|
||||
# Create a level entry for the current level if it's unique or has a higher score
|
||||
level_entry = next((entry for entry in music_entry["userRivalMusicDetailList"] if entry["level"] == level), None)
|
||||
if level_entry is None:
|
||||
level_entry = {
|
||||
"level": level,
|
||||
"scoreMax": score,
|
||||
"scoreRank": rank
|
||||
}
|
||||
music_entry["userRivalMusicDetailList"].append(level_entry)
|
||||
elif score > level_entry["scoreMax"]:
|
||||
level_entry["scoreMax"] = score
|
||||
level_entry["scoreRank"] = rank
|
||||
|
||||
# Calculate the length for each "musicId" by counting the levels
|
||||
# Calculate the length for each "musicId" by counting the unique levels
|
||||
for music_entry in user_rival_music_list:
|
||||
music_entry["length"] = len(music_entry["userRivalMusicDetailList"])
|
||||
|
||||
@ -452,12 +456,12 @@ class ChuniBase:
|
||||
result = {
|
||||
"userId": data["userId"],
|
||||
"rivalId": data["rivalId"],
|
||||
"nextIndex": str(next_index + len(all_entries) if len(all_entries) <= len(user_rival_music_list) else -1),
|
||||
"userRivalMusicList": user_rival_music_list[:max_count]
|
||||
"nextIndex": str(next_index + len(user_rival_music_list[next_index: next_index + max_count]) if max_count <= len(user_rival_music_list[next_index: next_index + max_count]) else -1),
|
||||
"userRivalMusicList": user_rival_music_list[next_index: next_index + max_count]
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def handle_get_user_favorite_item_api_request(self, data: Dict) -> Dict:
|
||||
user_fav_item_list = []
|
||||
|
||||
@ -873,8 +877,11 @@ class ChuniBase:
|
||||
if "userPlaylogList" in upsert:
|
||||
for playlog in upsert["userPlaylogList"]:
|
||||
# convert the player names to utf-8
|
||||
if playlog["playedUserName1"] is not None:
|
||||
playlog["playedUserName1"] = self.read_wtf8(playlog["playedUserName1"])
|
||||
if playlog["playedUserName2"] is not None:
|
||||
playlog["playedUserName2"] = self.read_wtf8(playlog["playedUserName2"])
|
||||
if playlog["playedUserName3"] is not None:
|
||||
playlog["playedUserName3"] = self.read_wtf8(playlog["playedUserName3"])
|
||||
self.data.score.put_playlog(user_id, playlog, self.version)
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
class ChuniConstants:
|
||||
GAME_CODE = "SDBT"
|
||||
GAME_CODE_NEW = "SDHD"
|
||||
GAME_CODE_INT = "SDGS"
|
||||
|
||||
CONFIG_NAME = "chuni.yaml"
|
||||
|
||||
@ -18,7 +19,7 @@ class ChuniConstants:
|
||||
VER_CHUNITHM_NEW = 11
|
||||
VER_CHUNITHM_NEW_PLUS = 12
|
||||
VER_CHUNITHM_SUN = 13
|
||||
|
||||
VER_CHUNITHM_SUN_PLUS = 14
|
||||
VERSION_NAMES = [
|
||||
"CHUNITHM",
|
||||
"CHUNITHM PLUS",
|
||||
@ -33,7 +34,8 @@ class ChuniConstants:
|
||||
"CHUNITHM PARADISE",
|
||||
"CHUNITHM NEW!!",
|
||||
"CHUNITHM NEW PLUS!!",
|
||||
"CHUNITHM SUN"
|
||||
"CHUNITHM SUN",
|
||||
"CHUNITHM SUN PLUS"
|
||||
]
|
||||
|
||||
@classmethod
|
||||
|
@ -31,6 +31,7 @@ from .paradise import ChuniParadise
|
||||
from .new import ChuniNew
|
||||
from .newplus import ChuniNewPlus
|
||||
from .sun import ChuniSun
|
||||
from .sunplus import ChuniSunPlus
|
||||
|
||||
|
||||
class ChuniServlet(BaseServlet):
|
||||
@ -58,6 +59,7 @@ class ChuniServlet(BaseServlet):
|
||||
ChuniNew,
|
||||
ChuniNewPlus,
|
||||
ChuniSun,
|
||||
ChuniSunPlus,
|
||||
]
|
||||
|
||||
self.logger = logging.getLogger("chuni")
|
||||
@ -120,8 +122,8 @@ class ChuniServlet(BaseServlet):
|
||||
return (
|
||||
[],
|
||||
[
|
||||
("render_POST", "/{version}/ChuniServlet/{endpoint}", {}),
|
||||
("render_POST", "/{version}/ChuniServlet/MatchingServer/{endpoint}", {})
|
||||
("render_POST", "/{game}/{version}/ChuniServlet/{endpoint}", {}),
|
||||
("render_POST", "/{game}/{version}/ChuniServlet/MatchingServer/{endpoint}", {})
|
||||
]
|
||||
)
|
||||
|
||||
@ -142,22 +144,25 @@ class ChuniServlet(BaseServlet):
|
||||
|
||||
def get_allnet_info(self, game_code: str, game_ver: int, keychip: str) -> Tuple[str, str]:
|
||||
if not self.core_cfg.server.is_using_proxy and Utils.get_title_port(self.core_cfg) != 80:
|
||||
return (f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}/{game_ver}/", self.core_cfg.title.hostname)
|
||||
return (f"http://{self.core_cfg.title.hostname}:{Utils.get_title_port(self.core_cfg)}/{game_code}/{game_ver}/", self.core_cfg.title.hostname)
|
||||
|
||||
return (f"http://{self.core_cfg.title.hostname}/{game_ver}/", self.core_cfg.title.hostname)
|
||||
return (f"http://{self.core_cfg.title.hostname}/{game_code}/{game_ver}/", self.core_cfg.title.hostname)
|
||||
|
||||
def render_POST(self, request: Request, game_code: str, matchers: Dict) -> bytes:
|
||||
endpoint = matchers['endpoint']
|
||||
version = int(matchers['version'])
|
||||
game_code = matchers['game']
|
||||
|
||||
if endpoint.lower() == "ping":
|
||||
return zlib.compress(b'{"returnCode": "1"}')
|
||||
|
||||
req_raw = request.content.getvalue()
|
||||
|
||||
encrtped = False
|
||||
internal_ver = 0
|
||||
client_ip = Utils.get_ip_addr(request)
|
||||
|
||||
if game_code == "SDHD" or game_code == "SDBT": # JP
|
||||
if version < 105: # 1.0
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM
|
||||
elif version >= 105 and version < 110: # PLUS
|
||||
@ -184,8 +189,21 @@ class ChuniServlet(BaseServlet):
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_NEW
|
||||
elif version >= 205 and version < 210: # NEW PLUS!!
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_NEW_PLUS
|
||||
elif version >= 210: # SUN
|
||||
elif version >= 210 and version < 215: # SUN
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_SUN
|
||||
elif version >= 215: # SUN
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_SUN_PLUS
|
||||
elif game_code == "SDGS": # Int
|
||||
if version < 110: # SUPERSTAR
|
||||
internal_ver = ChuniConstants.PARADISE
|
||||
elif version >= 110 and version < 115: # NEW
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_NEW
|
||||
elif version >= 115 and version < 120: # NEW PLUS!!
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_NEW_PLUS
|
||||
elif version >= 120 and version < 125: # SUN
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_SUN
|
||||
elif version >= 125: # SUN PLUS
|
||||
internal_ver = ChuniConstants.VER_CHUNITHM_SUN_PLUS
|
||||
|
||||
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
|
||||
@ -250,6 +268,7 @@ class ChuniServlet(BaseServlet):
|
||||
self.logger.info(f"v{version} {endpoint} request from {client_ip}")
|
||||
self.logger.debug(req_data)
|
||||
|
||||
endpoint = endpoint.replace("C3Exp", "") if game_code == "SDGS" else endpoint
|
||||
func_to_find = "handle_" + inflection.underscore(endpoint) + "_request"
|
||||
handler_cls = self.versions[internal_ver](self.core_cfg, self.game_cfg)
|
||||
|
||||
|
@ -71,11 +71,11 @@ class ChuniNew(ChuniBase):
|
||||
"matchErrorLimit": 9999,
|
||||
"romVersion": self.game_cfg.version.version(self.version)["rom"],
|
||||
"dataVersion": self.game_cfg.version.version(self.version)["data"],
|
||||
"matchingUri": f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/200/ChuniServlet/",
|
||||
"matchingUriX": f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/200/ChuniServlet/",
|
||||
"matchingUri": f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/200/ChuniServlet/",
|
||||
"matchingUriX": f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/200/ChuniServlet/",
|
||||
# might be really important for online battle to connect the cabs via UDP port 50201
|
||||
"udpHolePunchUri": f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/200/ChuniServlet/",
|
||||
"reflectorUri": f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/200/ChuniServlet/",
|
||||
"udpHolePunchUri": f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/200/ChuniServlet/",
|
||||
"reflectorUri": f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/200/ChuniServlet/",
|
||||
},
|
||||
"isDumpUpload": False,
|
||||
"isAou": False,
|
||||
|
@ -21,16 +21,16 @@ class ChuniNewPlus(ChuniNew):
|
||||
]
|
||||
ret["gameSetting"][
|
||||
"matchingUri"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/205/ChuniServlet/"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/205/ChuniServlet/"
|
||||
ret["gameSetting"][
|
||||
"matchingUriX"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/205/ChuniServlet/"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/205/ChuniServlet/"
|
||||
ret["gameSetting"][
|
||||
"udpHolePunchUri"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/205/ChuniServlet/"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/205/ChuniServlet/"
|
||||
ret["gameSetting"][
|
||||
"reflectorUri"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/205/ChuniServlet/"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/205/ChuniServlet/"
|
||||
return ret
|
||||
|
||||
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||
|
@ -24,7 +24,7 @@ course = Table(
|
||||
Column("scoreMax", Integer),
|
||||
Column("isFullCombo", Boolean),
|
||||
Column("isAllJustice", Boolean),
|
||||
Column("isSuccess", Boolean),
|
||||
Column("isSuccess", Integer),
|
||||
Column("scoreRank", Integer),
|
||||
Column("eventId", Integer),
|
||||
Column("lastPlayDate", String(25)),
|
||||
@ -32,7 +32,7 @@ course = Table(
|
||||
Column("param2", Integer),
|
||||
Column("param3", Integer),
|
||||
Column("param4", Integer),
|
||||
Column("isClear", Boolean),
|
||||
Column("isClear", Integer),
|
||||
Column("theoryCount", Integer),
|
||||
Column("orderId", Integer),
|
||||
Column("playerRating", Integer),
|
||||
@ -60,7 +60,7 @@ best_score = Table(
|
||||
Column("maxComboCount", Integer),
|
||||
Column("isFullCombo", Boolean),
|
||||
Column("isAllJustice", Boolean),
|
||||
Column("isSuccess", Boolean),
|
||||
Column("isSuccess", Integer),
|
||||
Column("fullChain", Integer),
|
||||
Column("maxChain", Integer),
|
||||
Column("scoreRank", Integer),
|
||||
@ -125,7 +125,7 @@ playlog = Table(
|
||||
Column("characterId", Integer),
|
||||
Column("skillId", Integer),
|
||||
Column("playKind", Integer),
|
||||
Column("isClear", Boolean),
|
||||
Column("isClear", Integer),
|
||||
Column("skillLevel", Integer),
|
||||
Column("skillEffect", Integer),
|
||||
Column("placeName", String(255)),
|
||||
@ -136,6 +136,7 @@ playlog = Table(
|
||||
Column("judgeHeaven", Integer),
|
||||
Column("regionId", Integer),
|
||||
Column("machineType", Integer),
|
||||
Column("ticketId", Integer),
|
||||
mysql_charset="utf8mb4"
|
||||
)
|
||||
|
||||
@ -255,4 +256,3 @@ class ChuniScoreData(BaseData):
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
|
@ -17,16 +17,16 @@ class ChuniSun(ChuniNewPlus):
|
||||
ret["gameSetting"]["dataVersion"] = self.game_cfg.version.version(self.version)["data"]
|
||||
ret["gameSetting"][
|
||||
"matchingUri"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/210/ChuniServlet/"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/210/ChuniServlet/"
|
||||
ret["gameSetting"][
|
||||
"matchingUriX"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/210/ChuniServlet/"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/210/ChuniServlet/"
|
||||
ret["gameSetting"][
|
||||
"udpHolePunchUri"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/210/ChuniServlet/"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/210/ChuniServlet/"
|
||||
ret["gameSetting"][
|
||||
"reflectorUri"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/210/ChuniServlet/"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/210/ChuniServlet/"
|
||||
return ret
|
||||
|
||||
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||
|
37
titles/chuni/sunplus.py
Normal file
37
titles/chuni/sunplus.py
Normal file
@ -0,0 +1,37 @@
|
||||
from typing import Dict, Any
|
||||
|
||||
from core.config import CoreConfig
|
||||
from titles.chuni.sun import ChuniSun
|
||||
from titles.chuni.const import ChuniConstants
|
||||
from titles.chuni.config import ChuniConfig
|
||||
|
||||
|
||||
class ChuniSunPlus(ChuniSun):
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: ChuniConfig) -> None:
|
||||
super().__init__(core_cfg, game_cfg)
|
||||
self.version = ChuniConstants.VER_CHUNITHM_SUN_PLUS
|
||||
|
||||
def handle_get_game_setting_api_request(self, data: Dict) -> Dict:
|
||||
ret = super().handle_get_game_setting_api_request(data)
|
||||
ret["gameSetting"]["romVersion"] = self.game_cfg.version.version(self.version)["rom"]
|
||||
ret["gameSetting"]["dataVersion"] = self.game_cfg.version.version(self.version)["data"]
|
||||
ret["gameSetting"][
|
||||
"matchingUri"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/215/ChuniServlet/"
|
||||
ret["gameSetting"][
|
||||
"matchingUriX"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/215/ChuniServlet/"
|
||||
ret["gameSetting"][
|
||||
"udpHolePunchUri"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/215/ChuniServlet/"
|
||||
ret["gameSetting"][
|
||||
"reflectorUri"
|
||||
] = f"http://{self.core_cfg.title.hostname}:{self.core_cfg.title.port}/SDHD/215/ChuniServlet/"
|
||||
return ret
|
||||
|
||||
def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict:
|
||||
user_data = super().handle_cm_get_user_preview_api_request(data)
|
||||
|
||||
# I don't know if lastDataVersion is going to matter, I don't think CardMaker 1.35 works this far up
|
||||
user_data["lastDataVersion"] = "2.15.00"
|
||||
return user_data
|
@ -45,7 +45,7 @@ class CardMakerBase:
|
||||
{
|
||||
"modelKind": 0,
|
||||
"type": 1,
|
||||
"titleUri": f"{uri}/{self._parse_int_ver(games_ver['chuni'])}/ChuniServlet/",
|
||||
"titleUri": f"{uri}/SDHD/{self._parse_int_ver(games_ver['chuni'])}/ChuniServlet/",
|
||||
},
|
||||
# maimai DX
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user