reformat with black in preperation for merge to master

This commit is contained in:
Kevin Trocolli
2023-04-23 21:04:52 -04:00
parent 9d23d59e43
commit 238d437519
70 changed files with 920 additions and 603 deletions

View File

@@ -46,15 +46,21 @@ class IDZPortsConfig:
@property
def userdb(self) -> int:
return CoreConfig.get_config_field(self.__config, "idz", "ports", "userdb", default=10000)
return CoreConfig.get_config_field(
self.__config, "idz", "ports", "userdb", default=10000
)
@property
def match(self) -> int:
return CoreConfig.get_config_field(self.__config, "idz", "ports", "match", default=10010)
return CoreConfig.get_config_field(
self.__config, "idz", "ports", "match", default=10010
)
@property
def echo(self) -> int:
return CoreConfig.get_config_field(self.__config, "idz", "ports", "echo", default=10020)
return CoreConfig.get_config_field(
self.__config, "idz", "ports", "echo", default=10020
)
class IDZConfig(dict):
@@ -64,4 +70,4 @@ class IDZConfig(dict):
@property
def rsa_keys(self) -> List[Dict]:
return CoreConfig.get_config_field(self, "idz", "rsa_keys", default=[])
return CoreConfig.get_config_field(self, "idz", "rsa_keys", default=[])

View File

@@ -1,5 +1,6 @@
from enum import Enum
class IDZConstants:
GAME_CODE = "SDDF"
@@ -22,14 +23,26 @@ class IDZConstants:
LOCKED = 0
UNLOCKED = 1
OLD = 2
HASH_LUT = [
# No clue
0x9C82E674, 0x5A4738D9, 0x8B8D7AE0, 0x29EC9D81,
0x9C82E674,
0x5A4738D9,
0x8B8D7AE0,
0x29EC9D81,
# These three are from AES TE0
0x1209091B, 0x1D83839E, 0x582C2C74, 0x341A1A2E,
0x361B1B2D, 0xDC6E6EB2, 0xB45A5AEE, 0x5BA0A0FB,
0xA45252F6, 0x763B3B4D, 0xB7D6D661, 0x7DB3B3CE,
0x1209091B,
0x1D83839E,
0x582C2C74,
0x341A1A2E,
0x361B1B2D,
0xDC6E6EB2,
0xB45A5AEE,
0x5BA0A0FB,
0xA45252F6,
0x763B3B4D,
0xB7D6D661,
0x7DB3B3CE,
]
HASH_NUM = 0
HASH_MUL = [5, 7, 11, 12][HASH_NUM]

View File

@@ -1,6 +1,7 @@
from core.data import Data
from core.config import CoreConfig
class IDZData(Data):
def __init__(self, cfg: CoreConfig) -> None:
super().__init__(cfg)

View File

@@ -4,6 +4,7 @@ import logging
from core.config import CoreConfig
from .config import IDZConfig
class IDZEcho(DatagramProtocol):
def __init__(self, cfg: CoreConfig, game_cfg: IDZConfig) -> None:
super().__init__()
@@ -12,5 +13,7 @@ class IDZEcho(DatagramProtocol):
self.logger = logging.getLogger("idz")
def datagramReceived(self, data, addr):
self.logger.debug(f"Echo from from {addr[0]}:{addr[1]} -> {self.transport.getHost().port} - {data.hex()}")
self.transport.write(data, addr)
self.logger.debug(
f"Echo from from {addr[0]}:{addr[1]} -> {self.transport.getHost().port} - {data.hex()}"
)
self.transport.write(data, addr)

View File

@@ -5,7 +5,8 @@ from core.config import CoreConfig
from ..config import IDZConfig
from ..const import IDZConstants
class IDZHandlerBase():
class IDZHandlerBase:
name = "generic"
cmd_codes = [0x0000] * IDZConstants.NUM_VERS
rsp_codes = [0x0001] * IDZConstants.NUM_VERS
@@ -18,8 +19,8 @@ class IDZHandlerBase():
self.game = IDZConstants.GAME_CODE
self.version = version
self.size = 0x30
def handle(self, data: bytes) -> bytearray:
ret = bytearray([0] * self.size)
struct.pack_into("<H", ret, 0x0, self.rsp_codes[self.version])
return ret
return ret

View File

@@ -4,15 +4,16 @@ from .base import IDZHandlerBase
from core.config import CoreConfig
from ..config import IDZConfig
class IDZHandlerCheckTeamName(IDZHandlerBase):
cmd_codes = [0x00a2, 0x00a2, 0x0097, 0x0097]
rsp_codes = [0x00a3, 0x00a3, 0x0098, 0x0098]
cmd_codes = [0x00A2, 0x00A2, 0x0097, 0x0097]
rsp_codes = [0x00A3, 0x00A3, 0x0098, 0x0098]
name = "check_team_name"
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
self.size = 0x0010
def handle(self, data: bytes) -> bytearray:
ret = super().handle(data)
struct.pack_into("<I", ret, 0x4, 0x1)

View File

@@ -8,37 +8,51 @@ from core.config import CoreConfig
from ..config import IDZConfig
AUTO_TEAM_NAMES = ["スピードスターズ", "レッドサンズ", "ナイトキッズ"]
FULL_WIDTH_NUMS = ["\uff10", "\uff11", "\uff12", "\uff13", "\uff14", "\uff15", "\uff16", "\uff17", "\uff18", "\uff19"]
FULL_WIDTH_NUMS = [
"\uff10",
"\uff11",
"\uff12",
"\uff13",
"\uff14",
"\uff15",
"\uff16",
"\uff17",
"\uff18",
"\uff19",
]
class IDZHandlerCreateAutoTeam(IDZHandlerBase):
cmd_codes = [0x007b, 0x007b, 0x0077, 0x0077]
rsp_codes = [0x007c, 0x007c, 0x0078, 0x0078]
cmd_codes = [0x007B, 0x007B, 0x0077, 0x0077]
rsp_codes = [0x007C, 0x007C, 0x0078, 0x0078]
name = "create_auto_team"
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
self.size = 0x0ca0
self.size = 0x0CA0
def handle(self, data: bytes) -> bytearray:
ret = super().handle(data)
aime_id = struct.unpack_from("<I", data, 0x04)[0]
name = choice(AUTO_TEAM_NAMES)
bg = indexOf(AUTO_TEAM_NAMES, name)
number = choice(FULL_WIDTH_NUMS) + choice(FULL_WIDTH_NUMS) + choice(FULL_WIDTH_NUMS)
number = (
choice(FULL_WIDTH_NUMS) + choice(FULL_WIDTH_NUMS) + choice(FULL_WIDTH_NUMS)
)
tdata = {
"id": aime_id,
"bg": bg,
"fx": 0,
}
tdata = {
"id": aime_id,
"name": (name + number),
"bg": bg,
"fx": 0,
}
tname = tdata['name'].encode("shift-jis")
tname = tdata["name"].encode("shift-jis")
struct.pack_into("<I", ret, 0x0C, tdata["id"])
struct.pack_into(f"{len(tname)}s", ret, 0x24, tname)

View File

@@ -6,6 +6,7 @@ from .base import IDZHandlerBase
from core.config import CoreConfig
from ..config import IDZConfig
class IDZHandlerCreateProfile(IDZHandlerBase):
cmd_codes = [0x0066, 0x0066, 0x0064, 0x0064]
rsp_codes = [0x0067, 0x0065, 0x0065, 0x0065]
@@ -14,34 +15,26 @@ class IDZHandlerCreateProfile(IDZHandlerBase):
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
self.size = 0x0020
def handle(self, data: bytes) -> bytearray:
ret = super().handle(data)
aime_id = struct.unpack_from("<L", data, 0x04)[0]
name = data[0x1E:0x0034].decode("shift-jis").replace("\x00", "")
car = data[0x40:0xa0].hex()
chara = data[0xa8:0xbc].hex()
car = data[0x40:0xA0].hex()
chara = data[0xA8:0xBC].hex()
self.logger.info(f"Create profile for {name} (aime id {aime_id})")
auto_team = None
if not auto_team:
team = {
"bg": 0,
"id": 0,
"shop": ""
}
team = {"bg": 0, "id": 0, "shop": ""}
else:
tdata = json.loads(auto_team["data"])
team = {
"bg": tdata["bg"],
"id": tdata["fx"],
"shop": ""
}
profile_data={
team = {"bg": tdata["bg"], "id": tdata["fx"], "shop": ""}
profile_data = {
"profile": {
"xp": 0,
"lv": 1,
@@ -50,30 +43,19 @@ class IDZHandlerCreateProfile(IDZHandlerBase):
"milage": 0,
"playstamps": 0,
"last_login": int(datetime.now().timestamp()),
"car_str": car, # These should probably be chaged to dicts
"chara_str": chara, # But this works for now...
"car_str": car, # These should probably be chaged to dicts
"chara_str": chara, # But this works for now...
},
"options": {
"music": 0,
"pack": 13640,
"aura": 0,
"paper_cup": 0,
"gauges": 5,
"driving_style": 0
"driving_style": 0,
},
"missions": {
"team": [],
"solo": []
},
"story": {
"x": 0,
"y": 0,
"rows": {}
},
"missions": {"team": [], "solo": []},
"story": {"x": 0, "y": 0, "rows": {}},
"unlocks": {
"auras": 1,
"cup": 0,
@@ -81,9 +63,9 @@ class IDZHandlerCreateProfile(IDZHandlerBase):
"music": 0,
"last_mileage_reward": 0,
},
"team": team
"team": team,
}
if self.version > 2:
struct.pack_into("<L", ret, 0x04, aime_id)
else:

View File

@@ -5,15 +5,16 @@ from .base import IDZHandlerBase
from core.config import CoreConfig
from ..config import IDZConfig
class IDZHandlerDiscoverProfile(IDZHandlerBase):
cmd_codes = [0x006b, 0x0067]
rsp_codes = [0x006c, 0x0068, 0x0068,0x0068]
cmd_codes = [0x006B, 0x0067]
rsp_codes = [0x006C, 0x0068, 0x0068, 0x0068]
name = "discover_profile"
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
self.size = 0x0010
def handle(self, data: bytes) -> bytearray:
ret = super().handle(data)
user_id = struct.unpack_from("<I", data, 0x04)[0]

View File

@@ -5,21 +5,23 @@ from core.config import CoreConfig
from ..config import IDZConfig
from ..const import IDZConstants
class IDZHandlerLoad2on2A(IDZHandlerBase):
cmd_codes = [0x00b0, 0x00b0, 0x00a3, 0x00a3]
rsp_codes = [0x00b1, 0x00b1, 0x00a4, 0x00a4]
cmd_codes = [0x00B0, 0x00B0, 0x00A3, 0x00A3]
rsp_codes = [0x00B1, 0x00B1, 0x00A4, 0x00A4]
name = "load_2on2A"
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
self.size = 0x04c0
self.size = 0x04C0
if version >= IDZConstants.VER_IDZ_210:
self.size = 0x1290
def handle(self, data: bytes) -> bytearray:
return super().handle(data)
class IDZHandlerLoad2on2B(IDZHandlerBase):
cmd_codes = [0x0132] * 4
rsp_codes = [0x0133] * 4
@@ -27,10 +29,10 @@ class IDZHandlerLoad2on2B(IDZHandlerBase):
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
self.size = 0x04c0
self.size = 0x04C0
if version >= IDZConstants.VER_IDZ_210:
self.size = 0x0540
def handle(self, data: bytes) -> bytearray:
return super().handle(data)

View File

@@ -5,6 +5,7 @@ from core.config import CoreConfig
from ..config import IDZConfig
from ..const import IDZConstants
class IDZHandlerLoadConfigA(IDZHandlerBase):
cmd_codes = [0x0004] * IDZConstants.NUM_VERS
rsp_codes = [0x0005] * IDZConstants.NUM_VERS
@@ -12,29 +13,30 @@ class IDZHandlerLoadConfigA(IDZHandlerBase):
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
self.size = 0x01a0
self.size = 0x01A0
if self.version > 1:
self.size = 0x05e0
self.size = 0x05E0
def handle(self, data: bytes) -> bytearray:
ret = super().handle(data)
struct.pack_into("<H", ret, 0x02, 1)
struct.pack_into("<I", ret, 0x16, 230)
return ret
class IDZHandlerLoadConfigB(IDZHandlerBase):
cmd_codes = [0x00ab, 0x00ab, 0x00a0, 0x00a0]
rsp_codes = [0x00ac, 0x00ac, 0x00a1, 0x00a1]
cmd_codes = [0x00AB, 0x00AB, 0x00A0, 0x00A0]
rsp_codes = [0x00AC, 0x00AC, 0x00A1, 0x00A1]
name = "load_config_b"
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
self.size = 0x0230
if self.version > 1:
self.size = 0x0240
def handle(self, data: bytes) -> bytearray:
ret = super().handle(data)
struct.pack_into("<H", ret, 0x02, 1)

View File

@@ -4,15 +4,16 @@ from .base import IDZHandlerBase
from core.config import CoreConfig
from ..config import IDZConfig
class IDZHandlerLoadGhost(IDZHandlerBase):
cmd_codes = [0x00a0, 0x00a0, 0x0095, 0x0095]
rsp_codes = [0x00a1, 0x00a1, 0x0096, 0x0096]
cmd_codes = [0x00A0, 0x00A0, 0x0095, 0x0095]
rsp_codes = [0x00A1, 0x00A1, 0x0096, 0x0096]
name = "load_ghost"
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
self.size = 0x0070
def handle(self, data: bytes) -> bytearray:
ret = super().handle(data)
struct.pack_into("<I", ret, 0x02, 0x5)

View File

@@ -5,23 +5,24 @@ from core.config import CoreConfig
from ..config import IDZConfig
from ..const import IDZConstants
class IDZHandlerLoadProfile(IDZHandlerBase):
cmd_codes = [0x0067, 0x012f, 0x012f, 0x0142]
rsp_codes = [0x0065, 0x012e, 0x012e, 0x0141]
cmd_codes = [0x0067, 0x012F, 0x012F, 0x0142]
rsp_codes = [0x0065, 0x012E, 0x012E, 0x0141]
name = "load_profile"
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
if self.version == IDZConstants.VER_IDZ_110:
self.size = 0x0d30
self.size = 0x0D30
elif self.version == IDZConstants.VER_IDZ_130:
self.size = 0x0ea0
self.size = 0x0EA0
elif self.version == IDZConstants.VER_IDZ_210:
self.size = 0x1360
elif self.version == IDZConstants.VER_IDZ_230:
self.size = 0x1640
def handle(self, data: bytes) -> bytearray:
ret = super().handle(data)
aime_id = struct.unpack_from("<L", data, 0x04)[0]

View File

@@ -4,6 +4,7 @@ from .base import IDZHandlerBase
from core.config import CoreConfig
from ..config import IDZConfig
class IDZHandlerLoadRewardTable(IDZHandlerBase):
cmd_codes = [0x0086, 0x0086, 0x007F, 0x007F]
rsp_codes = [0x0087, 0x0087, 0x0080, 0x0080]
@@ -11,7 +12,7 @@ class IDZHandlerLoadRewardTable(IDZHandlerBase):
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
self.size = 0x01c0
self.size = 0x01C0
def handle(self, data: bytes) -> bytearray:
return super().handle(data)

View File

@@ -5,6 +5,7 @@ from core.config import CoreConfig
from ..config import IDZConfig
from ..const import IDZConstants
class IDZHandlerLoadServerInfo(IDZHandlerBase):
cmd_codes = [0x0006] * IDZConstants.NUM_VERS
rsp_codes = [0x0007] * IDZConstants.NUM_VERS
@@ -12,14 +13,14 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase):
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
self.size = 0x04b0
self.size = 0x04B0
def handle(self, data: bytes) -> bytearray:
ret = super().handle(data)
offset = 0
if self.version >= IDZConstants.VER_IDZ_210:
if self.version >= IDZConstants.VER_IDZ_210:
offset = 2
news_str = f"http://{self.core_config.title.hostname}:{self.core_config.title.port}/SDDF/230/news/news80**.txt"
err_str = f"http://{self.core_config.title.hostname}:{self.core_config.title.port}/SDDF/230/error"
@@ -27,34 +28,69 @@ class IDZHandlerLoadServerInfo(IDZHandlerBase):
len_news = len(news_str)
len_error = len(err_str)
struct.pack_into("<I", ret, 0x2 + offset, 1) # Status
struct.pack_into(f"{len_hostname}s", ret, 0x4 + offset, self.core_config.title.hostname.encode())
struct.pack_into("<I", ret, 0x2 + offset, 1) # Status
struct.pack_into(
f"{len_hostname}s",
ret,
0x4 + offset,
self.core_config.title.hostname.encode(),
)
struct.pack_into("<I", ret, 0x84 + offset, self.game_cfg.ports.userdb)
struct.pack_into("<I", ret, 0x86 + offset, self.game_cfg.ports.userdb + 1)
struct.pack_into(f"{len_hostname}s", ret, 0x88 + offset, self.core_config.title.hostname.encode())
struct.pack_into(
f"{len_hostname}s",
ret,
0x88 + offset,
self.core_config.title.hostname.encode(),
)
struct.pack_into("<I", ret, 0x108 + offset, self.game_cfg.ports.match - 1)
struct.pack_into("<I", ret, 0x10a + offset, self.game_cfg.ports.match - 3)
struct.pack_into("<I", ret, 0x10c + offset, self.game_cfg.ports.match - 2)
struct.pack_into("<I", ret, 0x10A + offset, self.game_cfg.ports.match - 3)
struct.pack_into("<I", ret, 0x10C + offset, self.game_cfg.ports.match - 2)
struct.pack_into("<I", ret, 0x10e + offset, self.game_cfg.ports.match + 2)
struct.pack_into("<I", ret, 0x10E + offset, self.game_cfg.ports.match + 2)
struct.pack_into("<I", ret, 0x110 + offset, self.game_cfg.ports.match + 3)
struct.pack_into("<I", ret, 0x112 + offset, self.game_cfg.ports.match + 1)
struct.pack_into(f"{len_hostname}s", ret, 0x114 + offset, self.core_config.title.hostname.encode())
struct.pack_into(
f"{len_hostname}s",
ret,
0x114 + offset,
self.core_config.title.hostname.encode(),
)
struct.pack_into("<I", ret, 0x194 + offset, self.game_cfg.ports.echo + 2)
struct.pack_into(f"{len_hostname}s", ret, 0x0199 + offset, self.core_config.title.hostname.encode())
struct.pack_into(
f"{len_hostname}s",
ret,
0x0199 + offset,
self.core_config.title.hostname.encode(),
)
struct.pack_into("<I", ret, 0x0219 + offset, self.game_cfg.ports.echo + 3)
struct.pack_into(f"{len_hostname}s", ret, 0x021c + offset, self.core_config.title.hostname.encode())
struct.pack_into(f"{len_hostname}s", ret, 0x029c + offset, self.core_config.title.hostname.encode())
struct.pack_into(f"{len_hostname}s", ret, 0x031c + offset, self.core_config.title.hostname.encode())
struct.pack_into(
f"{len_hostname}s",
ret,
0x021C + offset,
self.core_config.title.hostname.encode(),
)
struct.pack_into(
f"{len_hostname}s",
ret,
0x029C + offset,
self.core_config.title.hostname.encode(),
)
struct.pack_into(
f"{len_hostname}s",
ret,
0x031C + offset,
self.core_config.title.hostname.encode(),
)
struct.pack_into("<I", ret, 0x39c + offset, self.game_cfg.ports.echo)
struct.pack_into("<I", ret, 0x39e + offset, self.game_cfg.ports.echo + 1)
struct.pack_into("<I", ret, 0x39C + offset, self.game_cfg.ports.echo)
struct.pack_into("<I", ret, 0x39E + offset, self.game_cfg.ports.echo + 1)
struct.pack_into(f"{len_news}s", ret, 0x03a0 + offset, news_str.encode())
struct.pack_into(f"{len_news}s", ret, 0x03A0 + offset, news_str.encode())
struct.pack_into(f"{len_error}s", ret, 0x0424 + offset, err_str.encode())
return ret

View File

@@ -4,26 +4,28 @@ from .base import IDZHandlerBase
from core.config import CoreConfig
from ..config import IDZConfig
class IDZHandlerLoadTeamRankingA(IDZHandlerBase):
cmd_codes = [0x00b9, 0x00b9, 0x00a7, 0x00a7]
rsp_codes = [0x00b1] * 4
cmd_codes = [0x00B9, 0x00B9, 0x00A7, 0x00A7]
rsp_codes = [0x00B1] * 4
name = "load_team_ranking_a"
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
self.size = 0x0ba0
self.size = 0x0BA0
def handle(self, data: bytes) -> bytearray:
return super().handle(data)
class IDZHandlerLoadTeamRankingB(IDZHandlerBase):
cmd_codes = [0x00bb, 0x00bb, 0x00a9, 0x00a9]
rsp_codes = [0x00a8] * 4
cmd_codes = [0x00BB, 0x00BB, 0x00A9, 0x00A9]
rsp_codes = [0x00A8] * 4
name = "load_team_ranking_b"
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
self.size = 0x0ba0
self.size = 0x0BA0
def handle(self, data: bytes) -> bytearray:
return super().handle(data)

View File

@@ -5,9 +5,10 @@ from .base import IDZHandlerBase
from core.config import CoreConfig
from ..config import IDZConfig
class IDZHandlerLoadTopTen(IDZHandlerBase):
cmd_codes = [0x012c] * 4
rsp_codes = [0x00ce] * 4
cmd_codes = [0x012C] * 4
rsp_codes = [0x00CE] * 4
name = "load_top_ten"
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
@@ -19,12 +20,16 @@ class IDZHandlerLoadTopTen(IDZHandlerBase):
tracks_dates: List[Tuple[int, int]] = []
for i in range(32):
tracks_dates.append(
(struct.unpack_from("<H", data, 0x04 + (2 * i))[0], "little",
struct.unpack_from("<I", data, 0x44 + (4 * i))[0], "little")
(
struct.unpack_from("<H", data, 0x04 + (2 * i))[0],
"little",
struct.unpack_from("<I", data, 0x44 + (4 * i))[0],
"little",
)
)
# TODO: Best scores
for i in range (3):
offset = 0x16c0 + 0x1c * i
struct.pack_into("<B", ret, offset + 0x02, 0xff)
for i in range(3):
offset = 0x16C0 + 0x1C * i
struct.pack_into("<B", ret, offset + 0x02, 0xFF)
return ret

View File

@@ -7,20 +7,23 @@ from core.config import CoreConfig
from ..config import IDZConfig
from ..const import IDZConstants
class IDZHandlerLockProfile(IDZHandlerBase):
cmd_codes = [0x0069, 0x0069, 0x0065, 0x0065]
rsp_codes = [0x006a, 0x006a, 0x0066, 0x0066]
rsp_codes = [0x006A, 0x006A, 0x0066, 0x0066]
name = "lock_profile"
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
self.size = 0x0020
def handle(self, data: bytes) -> bytearray:
ret = super().handle(data)
profile_data = {
"status": IDZConstants.PROFILE_STATUS.UNLOCKED.value,
"expire_time": int((datetime.now() + timedelta(hours=1)).timestamp() / 1000)
"status": IDZConstants.PROFILE_STATUS.UNLOCKED.value,
"expire_time": int(
(datetime.now() + timedelta(hours=1)).timestamp() / 1000
),
}
user_id = struct.unpack_from("<I", data, 0x04)[0]
profile = None
@@ -29,11 +32,11 @@ class IDZHandlerLockProfile(IDZHandlerBase):
old_profile = None
if old_profile is not None:
profile_data["status"] = IDZConstants.PROFILE_STATUS.OLD.value
return self.handle_common(profile_data, ret)
def handle_common(cls, data: Dict, ret: bytearray) -> bytearray:
struct.pack_into("<B", ret, 0x18, data["status"])
struct.pack_into("<h", ret, 0x1a, -1)
struct.pack_into("<I", ret, 0x1c, data["expire_time"])
struct.pack_into("<h", ret, 0x1A, -1)
struct.pack_into("<I", ret, 0x1C, data["expire_time"])
return ret

View File

@@ -3,12 +3,13 @@ from core.config import CoreConfig
from ..config import IDZConfig
from ..const import IDZConstants
class IDZHandlerSaveExpedition(IDZHandlerBase):
cmd_codes = [0x008c, 0x013f]
cmd_codes = [0x008C, 0x013F]
name = "save_expedition"
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
def handle(self, data: bytes) -> bytearray:
return super().handle(data)

View File

@@ -4,12 +4,13 @@ from .base import IDZHandlerBase
from core.config import CoreConfig
from ..config import IDZConfig
class IDZHandlerSaveProfile(IDZHandlerBase):
cmd_codes = [0x0068, 0x0138, 0x0138, 0x0143]
name = "save_profile"
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
def handle(self, data: bytes) -> bytearray:
return super().handle(data)

View File

@@ -5,18 +5,19 @@ from core.config import CoreConfig
from ..config import IDZConfig
from ..const import IDZConstants
class IDZHandlerSaveTimeAttack(IDZHandlerBase):
cmd_codes = [0x00CD, 0x0136, 0x0136, 0x0136]
rsp_codes = [0x00ce, 0x00ce, 0x00cd, 0x00cd]
rsp_codes = [0x00CE, 0x00CE, 0x00CD, 0x00CD]
name = "save_time_attack"
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
self.size = 0x00b0
self.size = 0x00B0
if self.version > IDZConstants.VER_IDZ_130:
self.size = 0x00f0
self.size = 0x00F0
def handle(self, data: bytes) -> bytearray:
ret = super().handle(data)
return ret
return ret

View File

@@ -4,6 +4,7 @@ from .base import IDZHandlerBase
from core.config import CoreConfig
from ..config import IDZConfig
class IDZHandlerSaveTopic(IDZHandlerBase):
cmd_codes = [0x009A, 0x009A, 0x0091, 0x0091]
rsp_codes = [0x009B, 0x009B, 0x0092, 0x0092]
@@ -11,7 +12,7 @@ class IDZHandlerSaveTopic(IDZHandlerBase):
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
self.size = 0x05d0
self.size = 0x05D0
def handle(self, data: bytes) -> bytearray:
return super().handle(data)

View File

@@ -4,8 +4,9 @@ from .base import IDZHandlerBase
from core.config import CoreConfig
from ..config import IDZConfig
class IDZHandlerUnknown(IDZHandlerBase):
cmd_codes = [0x00ad, 0x00ad, 0x00a2, 0x00a2]
cmd_codes = [0x00AD, 0x00AD, 0x00A2, 0x00A2]
name = "unknown"
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:

View File

@@ -5,16 +5,17 @@ from core.config import CoreConfig
from ..config import IDZConfig
from ..const import IDZConstants
class IDZHandlerUnlockProfile(IDZHandlerBase):
cmd_codes = [0x006f, 0x006f, 0x006b, 0x006b]
rsp_codes = [0x0070, 0x0070, 0x006c, 0x006c]
cmd_codes = [0x006F, 0x006F, 0x006B, 0x006B]
rsp_codes = [0x0070, 0x0070, 0x006C, 0x006C]
name = "unlock_profile"
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
self.size = 0x0010
def handle(self, data: bytes) -> bytearray:
ret = super().handle(data)
ret = super().handle(data)
struct.pack_into("<H", ret, 0x4, 1)
return ret

View File

@@ -4,6 +4,7 @@ from .base import IDZHandlerBase
from core.config import CoreConfig
from ..config import IDZConfig
class IDZHandlerUpdateProvisionalStoreRank(IDZHandlerBase):
cmd_codes = [0x0082, 0x0082, 0x007C, 0x007C]
rsp_codes = [0x0083, 0x0083, 0x007D, 0x007D]
@@ -11,10 +12,10 @@ class IDZHandlerUpdateProvisionalStoreRank(IDZHandlerBase):
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
self.size = 0x02b0
self.size = 0x02B0
def handle(self, data: bytes) -> bytearray:
return super().handle(data)
return super().handle(data)
def handle_common(cls, aime_id: int, ret: bytearray) -> bytearray:
pass

View File

@@ -5,22 +5,23 @@ from core.config import CoreConfig
from ..config import IDZConfig
from ..const import IDZConstants
class IDZHandlerUpdateStoryClearNum(IDZHandlerBase):
cmd_codes = [0x007f, 0x097f, 0x013d, 0x0144]
rsp_codes = [0x0080, 0x013e, 0x013e, 0x0145]
cmd_codes = [0x007F, 0x097F, 0x013D, 0x0144]
rsp_codes = [0x0080, 0x013E, 0x013E, 0x0145]
name = "update_story_clear_num"
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
if self.version == IDZConstants.VER_IDZ_110:
self.size = 0x0220
elif self.version == IDZConstants.VER_IDZ_130:
self.size = 0x04f0
self.size = 0x04F0
elif self.version == IDZConstants.VER_IDZ_210:
self.size = 0x0510
elif self.version == IDZConstants.VER_IDZ_230:
self.size = 0x0800
def handle(self, data: bytes) -> bytearray:
return super().handle(data)

View File

@@ -5,13 +5,14 @@ from core.config import CoreConfig
from ..config import IDZConfig
from ..const import IDZConstants
class IDZHandleUpdateTeamPoints(IDZHandlerBase):
cmd_codes = [0x0081, 0x0081, 0x007b, 0x007b]
cmd_codes = [0x0081, 0x0081, 0x007B, 0x007B]
name = "unlock_profile"
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
def handle(self, data: bytes) -> bytearray:
ret = super().handle(data)
ret = super().handle(data)
return ret

View File

@@ -5,13 +5,14 @@ from core.config import CoreConfig
from ..config import IDZConfig
from ..const import IDZConstants
class IDZHandleUpdateUIReport(IDZHandlerBase):
cmd_codes = [0x0084, 0x0084, 0x007e, 0x007e]
cmd_codes = [0x0084, 0x0084, 0x007E, 0x007E]
name = "update_ui_report"
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
def handle(self, data: bytes) -> bytearray:
ret = super().handle(data)
ret = super().handle(data)
return ret

View File

@@ -5,13 +5,14 @@ from core.config import CoreConfig
from ..config import IDZConfig
from ..const import IDZConstants
class IDZHandleUpdateUserLog(IDZHandlerBase):
cmd_codes = [0x00bd, 0x00bd, 0x00ab, 0x00b3]
cmd_codes = [0x00BD, 0x00BD, 0x00AB, 0x00B3]
name = "update_user_log"
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, version: int) -> None:
super().__init__(core_cfg, game_cfg, version)
def handle(self, data: bytes) -> bytearray:
ret = super().handle(data)
ret = super().handle(data)
return ret

View File

@@ -16,6 +16,7 @@ from .userdb import IDZUserDBFactory, IDZUserDBWeb, IDZKey
from .echo import IDZEcho
from .handlers import IDZHandlerLoadConfigB
class IDZServlet:
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
self.core_cfg = core_cfg
@@ -51,13 +52,16 @@ class IDZServlet:
level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str
)
self.logger.inited = True
@classmethod
def rsaHashKeyN(cls, data):
hash_ = 0
for i in data:
hash_ = hash_ * IDZConstants.HASH_MUL + (i ^ IDZConstants.HASH_XOR) ^ IDZConstants.HASH_LUT[i & 0xf]
hash_ &= 0xffffffff
hash_ = (
hash_ * IDZConstants.HASH_MUL + (i ^ IDZConstants.HASH_XOR)
^ IDZConstants.HASH_LUT[i & 0xF]
)
hash_ &= 0xFFFFFFFF
return hash_
@classmethod
@@ -77,14 +81,18 @@ class IDZServlet:
logging.getLogger("idz").error("IDZ: No RSA/AES keys! IDZ cannot start")
return (False, "", "")
hostname = core_cfg.title.hostname if not game_cfg.server.hostname else game_cfg.server.hostname
hostname = (
core_cfg.title.hostname
if not game_cfg.server.hostname
else game_cfg.server.hostname
)
return (
True,
f"",
f"{hostname}:{game_cfg.ports.userdb}",
)
def setup(self):
def setup(self):
for key in self.game_cfg.rsa_keys:
if "N" not in key or "d" not in key or "e" not in key:
self.logger.error(f"Invalid IDZ key {key}")
@@ -92,14 +100,14 @@ class IDZServlet:
hashN = self.rsaHashKeyN(str(key["N"]).encode())
self.rsa_keys.append(IDZKey(key["N"], key["d"], key["e"], hashN))
if len(self.rsa_keys) <= 0:
self.logger.error("No valid RSA keys provided! IDZ cannot start!")
return
handler_map = [{} for _ in range(IDZConstants.NUM_VERS)]
handler_mod = mod = importlib.import_module(f"titles.idz.handlers")
for cls_name in dir(handler_mod):
if cls_name.startswith("__"):
continue
@@ -109,7 +117,7 @@ class IDZServlet:
mod_cmds: List = getattr(mod, "cmd_codes")
while len(mod_cmds) < IDZConstants.NUM_VERS:
mod_cmds.append(None)
for i in range(len(mod_cmds)):
if mod_cmds[i] is None:
mod_cmds[i] = mod_cmds[i - 1]
@@ -119,27 +127,47 @@ class IDZServlet:
except AttributeError as e:
continue
endpoints.serverFromString(reactor, f"tcp:{self.game_cfg.ports.userdb}:interface={self.core_cfg.server.listen_address}")\
.listen(IDZUserDBFactory(self.core_cfg, self.game_cfg, self.rsa_keys, handler_map))
reactor.listenUDP(self.game_cfg.ports.echo, IDZEcho(self.core_cfg, self.game_cfg))
reactor.listenUDP(self.game_cfg.ports.echo + 1, IDZEcho(self.core_cfg, self.game_cfg))
reactor.listenUDP(self.game_cfg.ports.match, IDZEcho(self.core_cfg, self.game_cfg))
reactor.listenUDP(self.game_cfg.ports.userdb + 1, IDZEcho(self.core_cfg, self.game_cfg))
endpoints.serverFromString(
reactor,
f"tcp:{self.game_cfg.ports.userdb}:interface={self.core_cfg.server.listen_address}",
).listen(
IDZUserDBFactory(self.core_cfg, self.game_cfg, self.rsa_keys, handler_map)
)
reactor.listenUDP(
self.game_cfg.ports.echo, IDZEcho(self.core_cfg, self.game_cfg)
)
reactor.listenUDP(
self.game_cfg.ports.echo + 1, IDZEcho(self.core_cfg, self.game_cfg)
)
reactor.listenUDP(
self.game_cfg.ports.match, IDZEcho(self.core_cfg, self.game_cfg)
)
reactor.listenUDP(
self.game_cfg.ports.userdb + 1, IDZEcho(self.core_cfg, self.game_cfg)
)
self.logger.info(f"UserDB Listening on port {self.game_cfg.ports.userdb}")
def render_POST(self, request: Request, version: int, url_path: str) -> bytes:
req_raw = request.content.getvalue()
req_raw = request.content.getvalue()
self.logger.info(f"IDZ POST request: {url_path} - {req_raw}")
return b""
def render_GET(self, request: Request, version: int, url_path: str) -> bytes:
self.logger.info(f"IDZ GET request: {url_path}")
request.responseHeaders.setRawHeaders('Content-Type', [b"text/plain; charset=utf-8"])
request.responseHeaders.setRawHeaders("Last-Modified", [b"Sun, 23 Apr 2023 05:33:20 GMT"])
news = self.game_cfg.server.news if self.game_cfg.server.news else f"Welcome to Initial D Arcade Stage Zero on {self.core_cfg.server.name}!"
self.logger.info(f"IDZ GET request: {url_path}")
request.responseHeaders.setRawHeaders(
"Content-Type", [b"text/plain; charset=utf-8"]
)
request.responseHeaders.setRawHeaders(
"Last-Modified", [b"Sun, 23 Apr 2023 05:33:20 GMT"]
)
news = (
self.game_cfg.server.news
if self.game_cfg.server.news
else f"Welcome to Initial D Arcade Stage Zero on {self.core_cfg.server.name}!"
)
news += "\r\n"
news = "1979/01/01 00:00:00 2099/12/31 23:59:59 " + news

View File

@@ -19,6 +19,7 @@ from .handlers import IDZHandlerBase
HANDLER_MAP: List[Dict]
class IDZKey:
def __init__(self, n, d, e, hashN: int) -> None:
self.N = n
@@ -26,9 +27,16 @@ class IDZKey:
self.e = e
self.hashN = hashN
class IDZUserDBProtocol(Protocol):
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig, keys: List[IDZKey], handlers: List[Dict]) -> None:
self.logger = logging.getLogger('idz')
def __init__(
self,
core_cfg: CoreConfig,
game_cfg: IDZConfig,
keys: List[IDZKey],
handlers: List[Dict],
) -> None:
self.logger = logging.getLogger("idz")
self.core_config = core_cfg
self.game_config = game_cfg
self.rsa_keys = keys
@@ -37,14 +45,14 @@ class IDZUserDBProtocol(Protocol):
self.version = None
self.version_internal = None
self.skip_next = False
def append_padding(self, data: bytes):
"""Appends 0s to the end of the data until it's at the correct size"""
length = struct.unpack_from("<H", data, 6)
padding_size = length[0] - len(data)
data += bytes(padding_size)
return data
def connectionMade(self) -> None:
self.logger.debug(f"{self.transport.getPeer().host} Connected")
base = 0
@@ -57,13 +65,17 @@ class IDZUserDBProtocol(Protocol):
rsa_key = random.choice(self.rsa_keys)
key_enc: int = pow(base, rsa_key.e, rsa_key.N)
result = key_enc.to_bytes(0x40, "little") + struct.pack("<I", 0x01020304) + rsa_key.hashN.to_bytes(4, "little")
result = (
key_enc.to_bytes(0x40, "little")
+ struct.pack("<I", 0x01020304)
+ rsa_key.hashN.to_bytes(4, "little")
)
self.logger.debug(f"Send handshake {result.hex()}")
self.transport.write(result)
def connectionLost(self, reason) -> None:
def connectionLost(self, reason) -> None:
self.logger.debug(
f"{self.transport.getPeer().host} Disconnected - {reason.value}"
)
@@ -84,7 +96,7 @@ class IDZUserDBProtocol(Protocol):
self.transport.write(b"\x00")
return
elif magic == 0x01020304:
elif magic == 0x01020304:
self.version = int(data_dec[16:19].decode())
if self.version == 110:
@@ -99,10 +111,12 @@ class IDZUserDBProtocol(Protocol):
self.logger.warn(f"Bad version v{self.version}")
self.version = None
self.version_internal = None
self.logger.debug(f"Userdb v{self.version} handshake response from {self.transport.getPeer().host}")
self.logger.debug(
f"Userdb v{self.version} handshake response from {self.transport.getPeer().host}"
)
return
elif self.skip_next:
self.skip_next = False
self.transport.write(b"\x00")
@@ -110,19 +124,25 @@ class IDZUserDBProtocol(Protocol):
elif self.version is None:
# We didn't get a handshake before, and this isn't one now, so we're up the creek
self.logger.info(f"Bad UserDB request from from {self.transport.getPeer().host}")
self.logger.info(
f"Bad UserDB request from from {self.transport.getPeer().host}"
)
self.transport.write(b"\x00")
return
cmd = struct.unpack_from("<H", data_dec, 0)[0]
handler_cls: Optional[Type[IDZHandlerBase]] = self.handlers[self.version_internal].get(cmd, None)
handler_cls: Optional[Type[IDZHandlerBase]] = self.handlers[
self.version_internal
].get(cmd, None)
if handler_cls is None:
self.logger.warn(f"No handler for v{self.version} {hex(cmd)} cmd")
handler_cls = IDZHandlerBase
handler = handler_cls(self.core_config, self.game_config, self.version_internal)
self.logger.info(f"Userdb v{self.version} {handler.name} request from {self.transport.getPeer().host}")
handler = handler_cls(self.core_config, self.game_config, self.version_internal)
self.logger.info(
f"Userdb v{self.version} {handler.name} request from {self.transport.getPeer().host}"
)
response = handler.handle(data_dec)
self.logger.debug(f"Response: {response.hex()}")
@@ -136,14 +156,23 @@ class IDZUserDBProtocol(Protocol):
class IDZUserDBFactory(Factory):
protocol = IDZUserDBProtocol
def __init__(self, cfg: CoreConfig, game_cfg: IDZConfig, keys: List[IDZKey], handlers: List[Dict]) -> None:
def __init__(
self,
cfg: CoreConfig,
game_cfg: IDZConfig,
keys: List[IDZKey],
handlers: List[Dict],
) -> None:
self.core_config = cfg
self.game_config = game_cfg
self.keys = keys
self.handlers = handlers
def buildProtocol(self, addr):
return IDZUserDBProtocol(self.core_config, self.game_config, self.keys, self.handlers)
return IDZUserDBProtocol(
self.core_config, self.game_config, self.keys, self.handlers
)
class IDZUserDBWeb(resource.Resource):
def __init__(self, core_cfg: CoreConfig, game_cfg: IDZConfig):
@@ -151,12 +180,16 @@ class IDZUserDBWeb(resource.Resource):
self.isLeaf = True
self.core_config = core_cfg
self.game_config = game_cfg
self.logger = logging.getLogger('idz')
self.logger = logging.getLogger("idz")
def render_POST(self, request: Request) -> bytes:
self.logger.info(f"IDZUserDBWeb POST from {request.getClientAddress().host} to {request.uri} with data {request.content.getvalue()}")
self.logger.info(
f"IDZUserDBWeb POST from {request.getClientAddress().host} to {request.uri} with data {request.content.getvalue()}"
)
return b""
def render_GET(self, request: Request) -> bytes:
self.logger.info(f"IDZUserDBWeb GET from {request.getClientAddress().host} to {request.uri}")
return b""
self.logger.info(
f"IDZUserDBWeb GET from {request.getClientAddress().host} to {request.uri}"
)
return b""