implement some parts of DC NTE (but not all of it)
This commit is contained in:
+4
-4
@@ -77,8 +77,8 @@ void CatSession::on_channel_input(
|
|||||||
uint16_t command, uint32_t flag, std::string& data) {
|
uint16_t command, uint32_t flag, std::string& data) {
|
||||||
if (this->channel.version != GameVersion::BB) {
|
if (this->channel.version != GameVersion::BB) {
|
||||||
if (command == 0x02 || command == 0x17 || command == 0x91 || command == 0x9B) {
|
if (command == 0x02 || command == 0x17 || command == 0x91 || command == 0x9B) {
|
||||||
const auto& cmd = check_size_t<S_ServerInit_DC_PC_V3_02_17_91_9B>(data,
|
const auto& cmd = check_size_t<S_ServerInitDefault_DC_PC_V3_02_17_91_9B>(data,
|
||||||
offsetof(S_ServerInit_DC_PC_V3_02_17_91_9B, after_message), 0xFFFF);
|
sizeof(S_ServerInitDefault_DC_PC_V3_02_17_91_9B), 0xFFFF);
|
||||||
if ((this->channel.version == GameVersion::GC) ||
|
if ((this->channel.version == GameVersion::GC) ||
|
||||||
(this->channel.version == GameVersion::XB)) {
|
(this->channel.version == GameVersion::XB)) {
|
||||||
this->channel.crypt_in.reset(new PSOV3Encryption(cmd.server_key));
|
this->channel.crypt_in.reset(new PSOV3Encryption(cmd.server_key));
|
||||||
@@ -97,8 +97,8 @@ void CatSession::on_channel_input(
|
|||||||
if (!this->bb_key_file) {
|
if (!this->bb_key_file) {
|
||||||
throw runtime_error("BB encryption requires a key file");
|
throw runtime_error("BB encryption requires a key file");
|
||||||
}
|
}
|
||||||
const auto& cmd = check_size_t<S_ServerInit_BB_03_9B>(data,
|
const auto& cmd = check_size_t<S_ServerInitDefault_BB_03_9B>(data,
|
||||||
offsetof(S_ServerInit_DC_PC_V3_02_17_91_9B, after_message), 0xFFFF);
|
sizeof(S_ServerInitDefault_BB_03_9B), 0xFFFF);
|
||||||
this->channel.crypt_in.reset(new PSOBBEncryption(*this->bb_key_file, &cmd.server_key[0], sizeof(cmd.server_key)));
|
this->channel.crypt_in.reset(new PSOBBEncryption(*this->bb_key_file, &cmd.server_key[0], sizeof(cmd.server_key)));
|
||||||
this->channel.crypt_out.reset(new PSOBBEncryption(*this->bb_key_file, &cmd.client_key[0], sizeof(cmd.client_key)));
|
this->channel.crypt_out.reset(new PSOBBEncryption(*this->bb_key_file, &cmd.client_key[0], sizeof(cmd.client_key)));
|
||||||
this->log.info("Enabled BB encryption");
|
this->log.info("Enabled BB encryption");
|
||||||
|
|||||||
+7
-8
@@ -204,16 +204,15 @@ Channel::Message Channel::recv(bool print_contents) {
|
|||||||
throw logic_error("enough bytes available, but could not remove them");
|
throw logic_error("enough bytes available, but could not remove them");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some versions of PSO DC can send commands whose sizes are not a multiple of
|
|
||||||
// 4, but the server is expected to always use a multiple of 4 bytes when
|
|
||||||
// decrypting (the extra cipher bytes are lost). To emulate this behavior, we
|
|
||||||
// have to round up the size for DC commands here.
|
|
||||||
if (version == GameVersion::DC) {
|
|
||||||
command_data.resize((command_data.size() + 3) & (~3));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->crypt_in.get()) {
|
if (this->crypt_in.get()) {
|
||||||
|
// Some versions of PSO DC can send commands whose sizes are not a multiple
|
||||||
|
// of 4, but the server is expected to always use a multiple of 4 bytes when
|
||||||
|
// decrypting (the extra cipher bytes are lost). To emulate this behavior,
|
||||||
|
// we have to round up the size for DC commands here.
|
||||||
|
size_t orig_size = command_data.size();
|
||||||
|
command_data.resize((orig_size + 3) & (~3), 0);
|
||||||
this->crypt_in->decrypt(command_data.data(), command_data.size());
|
this->crypt_in->decrypt(command_data.data(), command_data.size());
|
||||||
|
command_data.resize(orig_size);
|
||||||
}
|
}
|
||||||
command_data.resize(command_logical_size - header_size);
|
command_data.resize(command_logical_size - header_size);
|
||||||
|
|
||||||
|
|||||||
+61
-7
@@ -312,13 +312,17 @@ struct SC_TextHeader_01_06_11_B0_EE {
|
|||||||
// (The above text is required on all versions that use this command, including
|
// (The above text is required on all versions that use this command, including
|
||||||
// those versions that don't run on the DreamCast.)
|
// those versions that don't run on the DreamCast.)
|
||||||
|
|
||||||
struct S_ServerInit_DC_PC_V3_02_17_91_9B {
|
struct S_ServerInitDefault_DC_PC_V3_02_17_91_9B {
|
||||||
ptext<char, 0x40> copyright;
|
ptext<char, 0x40> copyright;
|
||||||
le_uint32_t server_key; // Key for data sent by server
|
le_uint32_t server_key; // Key for data sent by server
|
||||||
le_uint32_t client_key; // Key for data sent by client
|
le_uint32_t client_key; // Key for data sent by client
|
||||||
|
};
|
||||||
|
|
||||||
|
template <size_t AfterBytes>
|
||||||
|
struct S_ServerInitWithAfterMessage_DC_PC_V3_02_17_91_9B : S_ServerInitDefault_DC_PC_V3_02_17_91_9B {
|
||||||
// This field is not part of SEGA's implementation; the client ignores it.
|
// This field is not part of SEGA's implementation; the client ignores it.
|
||||||
// newserv sends a message here disavowing the preceding copyright notice.
|
// newserv sends a message here disavowing the preceding copyright notice.
|
||||||
ptext<char, 0xC0> after_message;
|
ptext<char, AfterBytes> after_message;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 03 (C->S): Legacy login (non-BB)
|
// 03 (C->S): Legacy login (non-BB)
|
||||||
@@ -352,10 +356,13 @@ struct C_LegacyLogin_PC_V3_03 {
|
|||||||
// The copyright field in the below structure must contain the following text:
|
// The copyright field in the below structure must contain the following text:
|
||||||
// "Phantasy Star Online Blue Burst Game Server. Copyright 1999-2004 SONICTEAM."
|
// "Phantasy Star Online Blue Burst Game Server. Copyright 1999-2004 SONICTEAM."
|
||||||
|
|
||||||
struct S_ServerInit_BB_03_9B {
|
struct S_ServerInitDefault_BB_03_9B {
|
||||||
ptext<char, 0x60> copyright;
|
ptext<char, 0x60> copyright;
|
||||||
parray<uint8_t, 0x30> server_key;
|
parray<uint8_t, 0x30> server_key;
|
||||||
parray<uint8_t, 0x30> client_key;
|
parray<uint8_t, 0x30> client_key;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct S_ServerInitWithAfterMessage_BB_03_9B : S_ServerInitDefault_BB_03_9B {
|
||||||
// As in 02, this field is not part of SEGA's implementation.
|
// As in 02, this field is not part of SEGA's implementation.
|
||||||
ptext<char, 0xC0> after_message;
|
ptext<char, 0xC0> after_message;
|
||||||
};
|
};
|
||||||
@@ -1144,6 +1151,19 @@ struct C_LobbySelection_84 {
|
|||||||
// 86: Invalid command
|
// 86: Invalid command
|
||||||
// 87: Invalid command
|
// 87: Invalid command
|
||||||
|
|
||||||
|
// 88 (C->S): License check (DC NTE only)
|
||||||
|
// The server should respond with an 88 command.
|
||||||
|
|
||||||
|
struct C_Login_DCNTE_88 {
|
||||||
|
ptext<char, 0x11> serial_number;
|
||||||
|
ptext<char, 0x11> access_key;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 88 (S->C): License check result (DC NTE only)
|
||||||
|
// No arguemnts except header.flag.
|
||||||
|
// If header.flag is zero, client will respond with an 8A command. Otherwise, it
|
||||||
|
// will respond with an 8B command.
|
||||||
|
|
||||||
// 88 (S->C): Update lobby arrows
|
// 88 (S->C): Update lobby arrows
|
||||||
// If this command is sent while a client is joining a lobby, the client may
|
// If this command is sent while a client is joining a lobby, the client may
|
||||||
// ignore it. For this reason, the server should wait a few seconds after a
|
// ignore it. For this reason, the server should wait a few seconds after a
|
||||||
@@ -1178,17 +1198,51 @@ struct S_ArrowUpdateEntry_88 {
|
|||||||
// header.flag = arrow color number (see above); no other arguments.
|
// header.flag = arrow color number (see above); no other arguments.
|
||||||
// Server should send an 88 command to all players in the lobby.
|
// Server should send an 88 command to all players in the lobby.
|
||||||
|
|
||||||
// 8A (C->S): Request lobby/game name
|
// 8A (C->S): Connection information (DC NTE only)
|
||||||
|
// The server should respond with an 8A command.
|
||||||
|
|
||||||
|
struct C_ConnectionInfo_DCNTE_8A {
|
||||||
|
ptext<char, 0x08> hardware_id;
|
||||||
|
le_uint32_t sub_version; // 0x20
|
||||||
|
le_uint32_t unknown_a1;
|
||||||
|
ptext<char, 0x30> username;
|
||||||
|
ptext<char, 0x30> password;
|
||||||
|
ptext<char, 0x30> email_address; // From Sylverant documentation
|
||||||
|
};
|
||||||
|
|
||||||
|
// 8A (S->C): Connection information result (DC NTE only)
|
||||||
|
// header.flag is a success flag. If 0 is sent, the client shows an error
|
||||||
|
// message and disconnects. Otherwise, the client responds with an 8B command.
|
||||||
|
|
||||||
|
// 8A (C->S): Request lobby/game name (except DC NTE)
|
||||||
// No arguments
|
// No arguments
|
||||||
|
|
||||||
// 8A (S->C): Lobby/game name
|
// 8A (S->C): Lobby/game name (except DC NTE)
|
||||||
// Contents is a string (char16_t on PC/BB, char on DC/V3) containing the lobby
|
// Contents is a string (char16_t on PC/BB, char on DC/V3) containing the lobby
|
||||||
// or game name. The client generally only sends this immediately after joining
|
// or game name. The client generally only sends this immediately after joining
|
||||||
// a game, but Sega's servers also replied to it if it was sent in a lobby. They
|
// a game, but Sega's servers also replied to it if it was sent in a lobby. They
|
||||||
// would return a string like "LOBBY01" even though this would never be used
|
// would return a string like "LOBBY01" even though this would never be used
|
||||||
// under normal circumstances.
|
// under normal circumstances.
|
||||||
|
|
||||||
// 8B: Invalid command
|
// 8B: Log in (DC NTE only)
|
||||||
|
|
||||||
|
struct C_Login_DCNTE_8B {
|
||||||
|
le_uint32_t player_tag;
|
||||||
|
le_uint32_t guild_card_number;
|
||||||
|
parray<uint8_t, 0x08> hardware_id;
|
||||||
|
le_uint32_t sub_version;
|
||||||
|
uint8_t is_extended;
|
||||||
|
uint8_t language;
|
||||||
|
parray<uint8_t, 2> unused1;
|
||||||
|
ptext<char, 0x11> serial_number;
|
||||||
|
ptext<char, 0x11> access_key;
|
||||||
|
ptext<char, 0x30> username;
|
||||||
|
ptext<char, 0x30> password;
|
||||||
|
ptext<char, 0x10> name;
|
||||||
|
parray<uint8_t, 2> unused;
|
||||||
|
SC_MeetUserExtension<char> extension;
|
||||||
|
};
|
||||||
|
|
||||||
// 8C: Invalid command
|
// 8C: Invalid command
|
||||||
// 8D: Invalid command
|
// 8D: Invalid command
|
||||||
// 8E: Invalid command
|
// 8E: Invalid command
|
||||||
@@ -1752,7 +1806,7 @@ struct S_RankUpdate_GC_Ep3_B7 {
|
|||||||
struct S_Unknown_GC_Ep3_B9 {
|
struct S_Unknown_GC_Ep3_B9 {
|
||||||
le_uint32_t unknown_a1; // Must be 1-4 (inclusive)
|
le_uint32_t unknown_a1; // Must be 1-4 (inclusive)
|
||||||
le_uint32_t unknown_a2;
|
le_uint32_t unknown_a2;
|
||||||
le_uint16_t unknown_a3;
|
le_uint16_t unknown_a3; // Looks like a size of some sort
|
||||||
le_uint16_t unused;
|
le_uint16_t unused;
|
||||||
parray<uint8_t, 0x3800> unknown_a5;
|
parray<uint8_t, 0x3800> unknown_a5;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -171,8 +171,8 @@ static HandlerResult on_server_dc_pc_v3_patch_02_17(
|
|||||||
|
|
||||||
// Most servers don't include after_message or have a shorter
|
// Most servers don't include after_message or have a shorter
|
||||||
// after_message than newserv does, so don't require it
|
// after_message than newserv does, so don't require it
|
||||||
const auto& cmd = check_size_t<S_ServerInit_DC_PC_V3_02_17_91_9B>(data,
|
const auto& cmd = check_size_t<S_ServerInitDefault_DC_PC_V3_02_17_91_9B>(data,
|
||||||
offsetof(S_ServerInit_DC_PC_V3_02_17_91_9B, after_message), 0xFFFF);
|
sizeof(S_ServerInitDefault_DC_PC_V3_02_17_91_9B), 0xFFFF);
|
||||||
|
|
||||||
if (!session.license) {
|
if (!session.license) {
|
||||||
session.log.info("No license in linked session");
|
session.log.info("No license in linked session");
|
||||||
@@ -335,8 +335,8 @@ static HandlerResult on_server_bb_03(shared_ptr<ServerState> s,
|
|||||||
ProxyServer::LinkedSession& session, uint16_t, uint32_t, string& data) {
|
ProxyServer::LinkedSession& session, uint16_t, uint32_t, string& data) {
|
||||||
// Most servers don't include after_message or have a shorter after_message
|
// Most servers don't include after_message or have a shorter after_message
|
||||||
// than newserv does, so don't require it
|
// than newserv does, so don't require it
|
||||||
const auto& cmd = check_size_t<S_ServerInit_BB_03_9B>(data,
|
const auto& cmd = check_size_t<S_ServerInitDefault_BB_03_9B>(data,
|
||||||
offsetof(S_ServerInit_BB_03_9B, after_message), 0xFFFF);
|
sizeof(S_ServerInitDefault_BB_03_9B), 0xFFFF);
|
||||||
|
|
||||||
// If the session has a detector crypt, then it was resumed from an unlinked
|
// If the session has a detector crypt, then it was resumed from an unlinked
|
||||||
// session, during which we already sent an 03 command.
|
// session, during which we already sent an 03 command.
|
||||||
|
|||||||
+80
-8
@@ -212,6 +212,70 @@ static void on_verify_license_v3(shared_ptr<ServerState> s, shared_ptr<Client> c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void on_login_8_dcnte(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||||
|
uint16_t, uint32_t, const string& data) { // 88
|
||||||
|
const auto& cmd = check_size_t<C_Login_DCNTE_88>(data);
|
||||||
|
c->channel.version = GameVersion::DC;
|
||||||
|
c->flags |= flags_for_version(c->version(), -1);
|
||||||
|
c->flags |= Client::Flag::IS_DC_V1 | Client::Flag::IS_TRIAL_EDITION;
|
||||||
|
|
||||||
|
uint32_t serial_number = stoul(cmd.serial_number, nullptr, 16);
|
||||||
|
try {
|
||||||
|
shared_ptr<const License> l = s->license_manager->verify_pc(
|
||||||
|
serial_number, cmd.access_key);
|
||||||
|
c->set_license(l);
|
||||||
|
send_command(c, 0x88, 0x00);
|
||||||
|
|
||||||
|
} catch (const incorrect_access_key& e) {
|
||||||
|
send_message_box(c, u"Incorrect access key");
|
||||||
|
c->should_disconnect = true;
|
||||||
|
|
||||||
|
} catch (const missing_license& e) {
|
||||||
|
if (!s->allow_unregistered_users) {
|
||||||
|
send_message_box(c, u"Incorrect serial number");
|
||||||
|
c->should_disconnect = true;
|
||||||
|
} else {
|
||||||
|
auto l = LicenseManager::create_license_pc(
|
||||||
|
serial_number, cmd.access_key, true);
|
||||||
|
s->license_manager->add(l);
|
||||||
|
c->set_license(l);
|
||||||
|
send_command(c, 0x88, 0x00);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_login_b_dcnte(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||||
|
uint16_t, uint32_t, const string& data) { // 8B
|
||||||
|
const auto& cmd = check_size_t<C_Login_DCNTE_8B>(data);
|
||||||
|
c->channel.version = GameVersion::DC;
|
||||||
|
c->flags |= flags_for_version(c->version(), -1);
|
||||||
|
c->flags |= Client::Flag::IS_DC_V1 | Client::Flag::IS_TRIAL_EDITION;
|
||||||
|
|
||||||
|
uint32_t serial_number = stoul(cmd.serial_number, nullptr, 16);
|
||||||
|
try {
|
||||||
|
shared_ptr<const License> l = s->license_manager->verify_pc(
|
||||||
|
serial_number, cmd.access_key);
|
||||||
|
c->set_license(l);
|
||||||
|
send_command(c, 0x8B, 0x01);
|
||||||
|
|
||||||
|
} catch (const incorrect_access_key& e) {
|
||||||
|
send_message_box(c, u"Incorrect access key");
|
||||||
|
c->should_disconnect = true;
|
||||||
|
|
||||||
|
} catch (const missing_license& e) {
|
||||||
|
if (!s->allow_unregistered_users) {
|
||||||
|
send_message_box(c, u"Incorrect serial number");
|
||||||
|
c->should_disconnect = true;
|
||||||
|
} else {
|
||||||
|
auto l = LicenseManager::create_license_pc(
|
||||||
|
serial_number, cmd.access_key, true);
|
||||||
|
s->license_manager->add(l);
|
||||||
|
c->set_license(l);
|
||||||
|
send_command(c, 0x8B, 0x01);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void on_login_0_dc_pc_v3(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
static void on_login_0_dc_pc_v3(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||||
uint16_t, uint32_t, const string& data) { // 90
|
uint16_t, uint32_t, const string& data) { // 90
|
||||||
const auto& cmd = check_size_t<C_LoginV1_DC_PC_V3_90>(data);
|
const auto& cmd = check_size_t<C_LoginV1_DC_PC_V3_90>(data);
|
||||||
@@ -2364,12 +2428,19 @@ static void on_create_game_bb(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
|||||||
|
|
||||||
static void on_lobby_name_request(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
static void on_lobby_name_request(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||||
uint16_t, uint32_t, const string& data) { // 8A
|
uint16_t, uint32_t, const string& data) { // 8A
|
||||||
check_size_v(data.size(), 0);
|
if ((c->version() == GameVersion::DC) && (c->flags & Client::Flag::IS_TRIAL_EDITION)) {
|
||||||
auto l = s->find_lobby(c->lobby_id);
|
const auto& cmd = check_size_t<C_ConnectionInfo_DCNTE_8A>(data);
|
||||||
if (!l) {
|
set_console_client_flags(c, cmd.sub_version);
|
||||||
throw invalid_argument("client not in any lobby");
|
send_command(c, 0x8A, 0x01);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
check_size_v(data.size(), 0);
|
||||||
|
auto l = s->find_lobby(c->lobby_id);
|
||||||
|
if (!l) {
|
||||||
|
throw invalid_argument("client not in any lobby");
|
||||||
|
}
|
||||||
|
send_lobby_name(c, l->name.c_str());
|
||||||
}
|
}
|
||||||
send_lobby_name(c, l->name.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void on_client_ready(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
static void on_client_ready(shared_ptr<ServerState> s, shared_ptr<Client> c,
|
||||||
@@ -2822,10 +2893,10 @@ static on_command_t handlers[0x100][6] = {
|
|||||||
/* 85 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, }, /* 85 */
|
/* 85 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, }, /* 85 */
|
||||||
/* 86 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, }, /* 86 */
|
/* 86 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, }, /* 86 */
|
||||||
/* 87 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, }, /* 87 */
|
/* 87 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, }, /* 87 */
|
||||||
/* 88 */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, }, /* 88 */
|
/* 88 */ {nullptr, on_login_8_dcnte, nullptr, on_login_8_dcnte, nullptr, nullptr, }, /* 88 */
|
||||||
/* 89 */ {nullptr, on_change_arrow_color, on_change_arrow_color, on_change_arrow_color, on_change_arrow_color, on_change_arrow_color, }, /* 89 */
|
/* 89 */ {nullptr, on_change_arrow_color, on_change_arrow_color, on_change_arrow_color, on_change_arrow_color, on_change_arrow_color, }, /* 89 */
|
||||||
/* 8A */ {nullptr, on_lobby_name_request, on_lobby_name_request, on_lobby_name_request, on_lobby_name_request, on_lobby_name_request, }, /* 8A */
|
/* 8A */ {nullptr, on_lobby_name_request, on_lobby_name_request, on_lobby_name_request, on_lobby_name_request, on_lobby_name_request, }, /* 8A */
|
||||||
/* 8B */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, }, /* 8B */
|
/* 8B */ {nullptr, on_login_b_dcnte, nullptr, nullptr, nullptr, nullptr, }, /* 8B */
|
||||||
/* 8C */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, }, /* 8C */
|
/* 8C */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, }, /* 8C */
|
||||||
/* 8D */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, }, /* 8D */
|
/* 8D */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, }, /* 8D */
|
||||||
/* 8E */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, }, /* 8E */
|
/* 8E */ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, }, /* 8E */
|
||||||
@@ -2963,7 +3034,8 @@ static void check_unlicensed_command(GameVersion version, uint8_t command) {
|
|||||||
case GameVersion::GC:
|
case GameVersion::GC:
|
||||||
case GameVersion::XB:
|
case GameVersion::XB:
|
||||||
// See comment in the DC case above for why DC commands are included here.
|
// See comment in the DC case above for why DC commands are included here.
|
||||||
if (command != 0x90 && // DC v1
|
if (command != 0x88 && // DC NTE
|
||||||
|
command != 0x90 && // DC v1
|
||||||
command != 0x93 && // DC v1
|
command != 0x93 && // DC v1
|
||||||
command != 0x9A && // DC v2
|
command != 0x9A && // DC v2
|
||||||
command != 0x9D && // DC v2, GC trial edition
|
command != 0x9D && // DC v2, GC trial edition
|
||||||
|
|||||||
@@ -218,8 +218,8 @@ void ReplaySession::apply_default_mask(shared_ptr<Event> ev) {
|
|||||||
case 0x17:
|
case 0x17:
|
||||||
case 0x91:
|
case 0x91:
|
||||||
case 0x9B: {
|
case 0x9B: {
|
||||||
auto& cmd_mask = check_size_t<S_ServerInit_DC_PC_V3_02_17_91_9B>(
|
auto& cmd_mask = check_size_t<S_ServerInitDefault_DC_PC_V3_02_17_91_9B>(
|
||||||
cmd_data, cmd_size, sizeof(S_ServerInit_DC_PC_V3_02_17_91_9B), 0xFFFF);
|
cmd_data, cmd_size, sizeof(S_ServerInitDefault_DC_PC_V3_02_17_91_9B), 0xFFFF);
|
||||||
cmd_mask.server_key = 0;
|
cmd_mask.server_key = 0;
|
||||||
cmd_mask.client_key = 0;
|
cmd_mask.client_key = 0;
|
||||||
break;
|
break;
|
||||||
@@ -275,8 +275,8 @@ void ReplaySession::apply_default_mask(shared_ptr<Event> ev) {
|
|||||||
ev->data, sizeof(PSOCommandHeaderBB), 0xFFFF).command;
|
ev->data, sizeof(PSOCommandHeaderBB), 0xFFFF).command;
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case 0x0003: {
|
case 0x0003: {
|
||||||
auto& cmd_mask = check_size_t<S_ServerInit_BB_03_9B>(
|
auto& cmd_mask = check_size_t<S_ServerInitDefault_BB_03_9B>(
|
||||||
cmd_data, cmd_size, sizeof(S_ServerInit_BB_03_9B), 0xFFFF);
|
cmd_data, cmd_size, sizeof(S_ServerInitDefault_BB_03_9B), 0xFFFF);
|
||||||
cmd_mask.server_key.clear(0);
|
cmd_mask.server_key.clear(0);
|
||||||
cmd_mask.client_key.clear(0);
|
cmd_mask.client_key.clear(0);
|
||||||
break;
|
break;
|
||||||
@@ -593,8 +593,8 @@ void ReplaySession::on_command_received(
|
|||||||
case GameVersion::GC:
|
case GameVersion::GC:
|
||||||
case GameVersion::XB:
|
case GameVersion::XB:
|
||||||
if (command == 0x02 || command == 0x17 || command == 0x91 || command == 0x9B) {
|
if (command == 0x02 || command == 0x17 || command == 0x91 || command == 0x9B) {
|
||||||
auto& cmd = check_size_t<S_ServerInit_DC_PC_V3_02_17_91_9B>(data,
|
auto& cmd = check_size_t<S_ServerInitDefault_DC_PC_V3_02_17_91_9B>(data,
|
||||||
offsetof(S_ServerInit_DC_PC_V3_02_17_91_9B, after_message), 0xFFFF);
|
sizeof(S_ServerInitDefault_DC_PC_V3_02_17_91_9B), 0xFFFF);
|
||||||
if ((c->version == GameVersion::DC) || (c->version == GameVersion::PC)) {
|
if ((c->version == GameVersion::DC) || (c->version == GameVersion::PC)) {
|
||||||
c->channel.crypt_in.reset(new PSOV2Encryption(cmd.server_key));
|
c->channel.crypt_in.reset(new PSOV2Encryption(cmd.server_key));
|
||||||
c->channel.crypt_out.reset(new PSOV2Encryption(cmd.client_key));
|
c->channel.crypt_out.reset(new PSOV2Encryption(cmd.client_key));
|
||||||
@@ -606,8 +606,8 @@ void ReplaySession::on_command_received(
|
|||||||
break;
|
break;
|
||||||
case GameVersion::BB:
|
case GameVersion::BB:
|
||||||
if (command == 0x03 || command == 0x9B) {
|
if (command == 0x03 || command == 0x9B) {
|
||||||
auto& cmd = check_size_t<S_ServerInit_BB_03_9B>(data,
|
auto& cmd = check_size_t<S_ServerInitDefault_BB_03_9B>(data,
|
||||||
sizeof(S_ServerInit_BB_03_9B), 0xFFFF);
|
sizeof(S_ServerInitDefault_BB_03_9B), 0xFFFF);
|
||||||
// TODO: At some point it may matter which BB private key file we use.
|
// TODO: At some point it may matter which BB private key file we use.
|
||||||
// Don't just blindly use the first one here.
|
// Don't just blindly use the first one here.
|
||||||
c->channel.crypt_in.reset(new PSOBBEncryption(
|
c->channel.crypt_in.reset(new PSOBBEncryption(
|
||||||
|
|||||||
+8
-6
@@ -27,6 +27,7 @@ extern FileContentsCache file_cache;
|
|||||||
|
|
||||||
|
|
||||||
const unordered_set<uint32_t> v2_crypt_initial_client_commands({
|
const unordered_set<uint32_t> v2_crypt_initial_client_commands({
|
||||||
|
0x00260088, // (17) DCNTE license check
|
||||||
0x00280090, // (17) DCv1 license check
|
0x00280090, // (17) DCv1 license check
|
||||||
0x00B00093, // (02) DCv1 login
|
0x00B00093, // (02) DCv1 login
|
||||||
0x01140093, // (02) DCv1 extended login
|
0x01140093, // (02) DCv1 extended login
|
||||||
@@ -111,17 +112,18 @@ void send_command_with_header(Channel& ch, const void* data, size_t size) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
static const char* anti_copyright = "This server is in no way affiliated, sponsored, or supported by SEGA Enterprises or SONICTEAM. The preceding message exists only in order to remain compatible with programs that expect it.";
|
static const char* anti_copyright = "This server is in no way affiliated, sponsored, or supported by SEGA Enterprises or SONICTEAM. The preceding message exists only to remain compatible with programs that expect it.";
|
||||||
static const char* dc_port_map_copyright = "DreamCast Port Map. Copyright SEGA Enterprises. 1999";
|
static const char* dc_port_map_copyright = "DreamCast Port Map. Copyright SEGA Enterprises. 1999";
|
||||||
static const char* dc_lobby_server_copyright = "DreamCast Lobby Server. Copyright SEGA Enterprises. 1999";
|
static const char* dc_lobby_server_copyright = "DreamCast Lobby Server. Copyright SEGA Enterprises. 1999";
|
||||||
static const char* bb_game_server_copyright = "Phantasy Star Online Blue Burst Game Server. Copyright 1999-2004 SONICTEAM.";
|
static const char* bb_game_server_copyright = "Phantasy Star Online Blue Burst Game Server. Copyright 1999-2004 SONICTEAM.";
|
||||||
static const char* bb_pm_server_copyright = "PSO NEW PM Server. Copyright 1999-2002 SONICTEAM.";
|
static const char* bb_pm_server_copyright = "PSO NEW PM Server. Copyright 1999-2002 SONICTEAM.";
|
||||||
static const char* patch_server_copyright = "Patch Server. Copyright SonicTeam, LTD. 2001";
|
static const char* patch_server_copyright = "Patch Server. Copyright SonicTeam, LTD. 2001";
|
||||||
|
|
||||||
S_ServerInit_DC_PC_V3_02_17_91_9B prepare_server_init_contents_console(
|
S_ServerInitWithAfterMessage_DC_PC_V3_02_17_91_9B<0xCC>
|
||||||
|
prepare_server_init_contents_console(
|
||||||
uint32_t server_key, uint32_t client_key, uint8_t flags) {
|
uint32_t server_key, uint32_t client_key, uint8_t flags) {
|
||||||
bool initial_connection = (flags & SendServerInitFlag::IS_INITIAL_CONNECTION);
|
bool initial_connection = (flags & SendServerInitFlag::IS_INITIAL_CONNECTION);
|
||||||
S_ServerInit_DC_PC_V3_02_17_91_9B cmd;
|
S_ServerInitWithAfterMessage_DC_PC_V3_02_17_91_9B<0xCC> cmd;
|
||||||
cmd.copyright = initial_connection
|
cmd.copyright = initial_connection
|
||||||
? dc_port_map_copyright : dc_lobby_server_copyright;
|
? dc_port_map_copyright : dc_lobby_server_copyright;
|
||||||
cmd.server_key = server_key;
|
cmd.server_key = server_key;
|
||||||
@@ -138,7 +140,7 @@ void send_server_init_dc_pc_v3(shared_ptr<Client> c, uint8_t flags) {
|
|||||||
|
|
||||||
auto cmd = prepare_server_init_contents_console(
|
auto cmd = prepare_server_init_contents_console(
|
||||||
server_key, client_key, initial_connection);
|
server_key, client_key, initial_connection);
|
||||||
send_command_t(c, command, 0x00, cmd);
|
send_command_t(c, command, 0x01, cmd);
|
||||||
|
|
||||||
switch (c->version()) {
|
switch (c->version()) {
|
||||||
case GameVersion::PC:
|
case GameVersion::PC:
|
||||||
@@ -159,12 +161,12 @@ void send_server_init_dc_pc_v3(shared_ptr<Client> c, uint8_t flags) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
S_ServerInit_BB_03_9B prepare_server_init_contents_bb(
|
S_ServerInitWithAfterMessage_BB_03_9B prepare_server_init_contents_bb(
|
||||||
const parray<uint8_t, 0x30>& server_key,
|
const parray<uint8_t, 0x30>& server_key,
|
||||||
const parray<uint8_t, 0x30>& client_key,
|
const parray<uint8_t, 0x30>& client_key,
|
||||||
uint8_t flags) {
|
uint8_t flags) {
|
||||||
bool use_secondary_message = (flags & SendServerInitFlag::USE_SECONDARY_MESSAGE);
|
bool use_secondary_message = (flags & SendServerInitFlag::USE_SECONDARY_MESSAGE);
|
||||||
S_ServerInit_BB_03_9B cmd;
|
S_ServerInitWithAfterMessage_BB_03_9B cmd;
|
||||||
cmd.copyright = use_secondary_message ? bb_pm_server_copyright : bb_game_server_copyright;
|
cmd.copyright = use_secondary_message ? bb_pm_server_copyright : bb_game_server_copyright;
|
||||||
cmd.server_key = server_key;
|
cmd.server_key = server_key;
|
||||||
cmd.client_key = client_key;
|
cmd.client_key = client_key;
|
||||||
|
|||||||
+3
-2
@@ -102,9 +102,10 @@ enum SendServerInitFlag {
|
|||||||
USE_SECONDARY_MESSAGE = 0x02,
|
USE_SECONDARY_MESSAGE = 0x02,
|
||||||
};
|
};
|
||||||
|
|
||||||
S_ServerInit_DC_PC_V3_02_17_91_9B prepare_server_init_contents_console(
|
S_ServerInitWithAfterMessage_DC_PC_V3_02_17_91_9B<0xCC>
|
||||||
|
prepare_server_init_contents_console(
|
||||||
uint32_t server_key, uint32_t client_key, uint8_t flags);
|
uint32_t server_key, uint32_t client_key, uint8_t flags);
|
||||||
S_ServerInit_BB_03_9B prepare_server_init_contents_bb(
|
S_ServerInitWithAfterMessage_BB_03_9B prepare_server_init_contents_bb(
|
||||||
const parray<uint8_t, 0x30>& server_key,
|
const parray<uint8_t, 0x30>& server_key,
|
||||||
const parray<uint8_t, 0x30>& client_key,
|
const parray<uint8_t, 0x30>& client_key,
|
||||||
uint8_t flags);
|
uint8_t flags);
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ uint16_t flags_for_version(GameVersion version, int64_t sub_version) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
// TODO: Which other sub_versions of DC v1 and v2 exist?
|
// TODO: Which other sub_versions of DC v1 and v2 exist?
|
||||||
|
case 0x20: // DCNTE
|
||||||
|
// In the case of DCNTE, the IS_TRIAL_EDITION flag is already set when we
|
||||||
|
// get here, so the remaining flags are the same as DCv1
|
||||||
case 0x21: // DCv1 US
|
case 0x21: // DCv1 US
|
||||||
return Client::Flag::IS_DC_V1 |
|
return Client::Flag::IS_DC_V1 |
|
||||||
Client::Flag::NO_D6 |
|
Client::Flag::NO_D6 |
|
||||||
|
|||||||
Reference in New Issue
Block a user