add findings from psox disassembly

This commit is contained in:
Martin Michelsen
2022-07-25 22:06:53 -07:00
parent d61c65cf16
commit ec205062ad
26 changed files with 1124 additions and 642 deletions
+10 -9
View File
@@ -75,17 +75,18 @@ void CatSession::on_channel_input(
uint16_t command, uint32_t flag, std::string& data) {
if (this->channel.version != GameVersion::BB) {
if (command == 0x02 || command == 0x17 || command == 0x91 || command == 0x9B) {
const auto& cmd = check_size_t<S_ServerInit_DC_PC_GC_02_17_91_9B>(data,
offsetof(S_ServerInit_DC_PC_GC_02_17_91_9B, after_message), 0xFFFF);
if (this->channel.version == GameVersion::GC) {
this->channel.crypt_in.reset(new PSOGCEncryption(cmd.server_key));
this->channel.crypt_out.reset(new PSOGCEncryption(cmd.client_key));
this->log.info("Enabled GC encryption (server key %08" PRIX32 ", client key %08" PRIX32 ")",
const auto& cmd = check_size_t<S_ServerInit_DC_PC_V3_02_17_91_9B>(data,
offsetof(S_ServerInit_DC_PC_V3_02_17_91_9B, after_message), 0xFFFF);
if ((this->channel.version == GameVersion::GC) ||
(this->channel.version == GameVersion::XB)) {
this->channel.crypt_in.reset(new PSOV3Encryption(cmd.server_key));
this->channel.crypt_out.reset(new PSOV3Encryption(cmd.client_key));
this->log.info("Enabled V3 encryption (server key %08" PRIX32 ", client key %08" PRIX32 ")",
cmd.server_key.load(), cmd.client_key.load());
} else { // PC, DC, or patch server
this->channel.crypt_in.reset(new PSOPCEncryption(cmd.server_key));
this->channel.crypt_out.reset(new PSOPCEncryption(cmd.client_key));
this->log.info("Enabled PC encryption (server key %08" PRIX32 ", client key %08" PRIX32 ")",
this->channel.crypt_in.reset(new PSOV2Encryption(cmd.server_key));
this->channel.crypt_out.reset(new PSOV2Encryption(cmd.client_key));
this->log.info("Enabled V2 encryption (server key %08" PRIX32 ", client key %08" PRIX32 ")",
cmd.server_key.load(), cmd.client_key.load());
}
}
+4 -3
View File
@@ -253,9 +253,10 @@ void Channel::send(uint16_t cmd, uint32_t flag, const void* data, size_t size,
size_t logical_size;
size_t send_data_size = 0;
switch (this->version) {
case GameVersion::DC:
case GameVersion::GC:
case GameVersion::DC: {
PSOCommandHeaderDCGC header;
case GameVersion::XB: {
PSOCommandHeaderDCV3 header;
if (this->crypt_out.get()) {
send_data_size = (sizeof(header) + size + 3) & ~3;
} else {
@@ -311,7 +312,7 @@ void Channel::send(uint16_t cmd, uint32_t flag, const void* data, size_t size,
throw logic_error("unimplemented game version in send_command");
}
// All versions of PSO I've seen (PC, GC, BB) have a receive buffer 0x7C00
// All versions of PSO I've seen (so far) have a receive buffer 0x7C00
// bytes in size
if (send_data_size > 0x7C00) {
throw runtime_error("outbound command too large");
+3 -1
View File
@@ -246,7 +246,9 @@ static void proxy_command_lobby_event(shared_ptr<ServerState>,
send_text_message(session.client_channel, u"$C6No such lobby event.");
} else {
session.override_lobby_event = new_event;
if (session.version == GameVersion::GC || session.version == GameVersion::BB) {
if ((session.version == GameVersion::GC) ||
(session.version == GameVersion::XB) ||
(session.version == GameVersion::BB)) {
session.client_channel.send(0xDA, session.override_lobby_event);
}
}
+1 -1
View File
@@ -31,7 +31,7 @@ Client::Client(
log("", client_log.min_level),
version(version),
bb_game_state(0),
flags(flags_for_version(this->version, 0)),
flags(flags_for_version(this->version, -1)),
channel(bev, this->version, nullptr, nullptr, this, string_printf("C-%" PRIX64, this->id), TerminalFormat::FG_YELLOW, TerminalFormat::FG_GREEN),
server_behavior(server_behavior),
should_disconnect(false),
+311 -196
View File
File diff suppressed because it is too large Load Diff
+5 -2
View File
@@ -322,6 +322,8 @@ int main(int argc, char** argv) {
cli_version = GameVersion::PC;
} else if (!strcmp(argv[x], "--gc")) {
cli_version = GameVersion::GC;
} else if (!strcmp(argv[x], "--xb")) {
cli_version = GameVersion::XB;
} else if (!strcmp(argv[x], "--bb")) {
cli_version = GameVersion::BB;
} else if (!strncmp(argv[x], "--seed=", 7)) {
@@ -350,10 +352,11 @@ int main(int argc, char** argv) {
case GameVersion::PATCH:
case GameVersion::DC:
case GameVersion::PC:
crypt.reset(new PSOPCEncryption(stoul(seed, nullptr, 16)));
crypt.reset(new PSOV2Encryption(stoul(seed, nullptr, 16)));
break;
case GameVersion::GC:
crypt.reset(new PSOGCEncryption(stoul(seed, nullptr, 16)));
case GameVersion::XB:
crypt.reset(new PSOV3Encryption(stoul(seed, nullptr, 16)));
break;
case GameVersion::BB: {
seed = parse_data_string(seed);
+10 -8
View File
@@ -58,14 +58,16 @@ struct MenuItem {
INVISIBLE_ON_DC = 0x01,
INVISIBLE_ON_PC = 0x02,
INVISIBLE_ON_GC = 0x04,
INVISIBLE_ON_BB = 0x08,
DC_ONLY = INVISIBLE_ON_PC | INVISIBLE_ON_GC | INVISIBLE_ON_BB,
PC_ONLY = INVISIBLE_ON_DC | INVISIBLE_ON_GC | INVISIBLE_ON_BB,
GC_ONLY = INVISIBLE_ON_DC | INVISIBLE_ON_PC | INVISIBLE_ON_BB,
BB_ONLY = INVISIBLE_ON_DC | INVISIBLE_ON_PC | INVISIBLE_ON_GC,
REQUIRES_MESSAGE_BOXES = 0x10,
REQUIRES_SEND_FUNCTION_CALL = 0x20,
REQUIRES_SAVE_DISABLED = 0x40,
INVISIBLE_ON_XB = 0x08,
INVISIBLE_ON_BB = 0x10,
DC_ONLY = INVISIBLE_ON_PC | INVISIBLE_ON_GC | INVISIBLE_ON_XB | INVISIBLE_ON_BB,
PC_ONLY = INVISIBLE_ON_DC | INVISIBLE_ON_GC | INVISIBLE_ON_XB | INVISIBLE_ON_BB,
GC_ONLY = INVISIBLE_ON_DC | INVISIBLE_ON_PC | INVISIBLE_ON_XB | INVISIBLE_ON_BB,
XB_ONLY = INVISIBLE_ON_DC | INVISIBLE_ON_PC | INVISIBLE_ON_GC | INVISIBLE_ON_BB,
BB_ONLY = INVISIBLE_ON_DC | INVISIBLE_ON_PC | INVISIBLE_ON_GC | INVISIBLE_ON_XB,
REQUIRES_MESSAGE_BOXES = 0x20,
REQUIRES_SEND_FUNCTION_CALL = 0x40,
REQUIRES_SAVE_DISABLED = 0x80,
};
uint32_t item_id;
+13 -13
View File
@@ -23,7 +23,7 @@ void PSOEncryption::decrypt(void* data, size_t size, bool advance) {
void PSOPCEncryption::update_stream() {
void PSOV2Encryption::update_stream() {
uint32_t esi, edi, eax, ebp, edx;
edi = 1;
edx = 0x18;
@@ -47,7 +47,7 @@ void PSOPCEncryption::update_stream() {
}
}
PSOPCEncryption::PSOPCEncryption(uint32_t seed) : offset(1) {
PSOV2Encryption::PSOV2Encryption(uint32_t seed) : offset(1) {
uint32_t esi, ebx, edi, eax, edx, var1;
esi = 1;
ebx = seed;
@@ -69,8 +69,8 @@ PSOPCEncryption::PSOPCEncryption(uint32_t seed) : offset(1) {
}
}
uint32_t PSOPCEncryption::next(bool advance) {
if (this->offset == PC_STREAM_LENGTH) {
uint32_t PSOV2Encryption::next(bool advance) {
if (this->offset == V2_STREAM_LENGTH) {
this->update_stream();
this->offset = 1;
}
@@ -81,7 +81,7 @@ uint32_t PSOPCEncryption::next(bool advance) {
return ret;
}
void PSOPCEncryption::encrypt(void* vdata, size_t size, bool advance) {
void PSOV2Encryption::encrypt(void* vdata, size_t size, bool advance) {
if (size & 3) {
throw invalid_argument("size must be a multiple of 4");
}
@@ -98,25 +98,25 @@ void PSOPCEncryption::encrypt(void* vdata, size_t size, bool advance) {
void PSOGCEncryption::update_stream() {
void PSOV3Encryption::update_stream() {
uint32_t r5, r6, r7;
r5 = 0;
r6 = 489;
r7 = 0;
while (r6 != GC_STREAM_LENGTH) {
while (r6 != V3_STREAM_LENGTH) {
this->stream[r5++] ^= this->stream[r6++];
}
while (r5 != GC_STREAM_LENGTH) {
while (r5 != V3_STREAM_LENGTH) {
this->stream[r5++] ^= this->stream[r7++];
}
this->offset = 0;
}
uint32_t PSOGCEncryption::next(bool advance) {
if (this->offset == GC_STREAM_LENGTH) {
uint32_t PSOV3Encryption::next(bool advance) {
if (this->offset == V3_STREAM_LENGTH) {
this->update_stream();
}
uint32_t ret = this->stream[this->offset];
@@ -126,7 +126,7 @@ uint32_t PSOGCEncryption::next(bool advance) {
return ret;
}
PSOGCEncryption::PSOGCEncryption(uint32_t seed) : offset(0) {
PSOV3Encryption::PSOV3Encryption(uint32_t seed) : offset(0) {
uint32_t x, y, basekey, source1, source2, source3;
basekey = 0;
@@ -149,7 +149,7 @@ PSOGCEncryption::PSOGCEncryption(uint32_t seed) : offset(0) {
source1 = 0;
source2 = 1;
source3 = this->offset - 1;
while (this->offset != GC_STREAM_LENGTH) {
while (this->offset != V3_STREAM_LENGTH) {
this->stream[this->offset++] = (this->stream[source3++] ^ (((this->stream[source1++] << 23) & 0xFF800000) ^ ((this->stream[source2++] >> 9) & 0x007FFFFF)));
}
@@ -158,7 +158,7 @@ PSOGCEncryption::PSOGCEncryption(uint32_t seed) : offset(0) {
}
}
void PSOGCEncryption::encrypt(void* vdata, size_t size, bool advance) {
void PSOV3Encryption::encrypt(void* vdata, size_t size, bool advance) {
if (size & 3) {
throw invalid_argument("size must be a multiple of 4");
}
+8 -8
View File
@@ -12,8 +12,8 @@
#define PC_STREAM_LENGTH 56
#define GC_STREAM_LENGTH 521
#define V2_STREAM_LENGTH 56
#define V3_STREAM_LENGTH 521
#define BB_STREAM_LENGTH 1042
class PSOEncryption {
@@ -34,9 +34,9 @@ protected:
PSOEncryption() = default;
};
class PSOPCEncryption : public PSOEncryption {
class PSOV2Encryption : public PSOEncryption {
public:
explicit PSOPCEncryption(uint32_t seed);
explicit PSOV2Encryption(uint32_t seed);
virtual void encrypt(void* data, size_t size, bool advance = true);
@@ -45,13 +45,13 @@ public:
protected:
void update_stream();
uint32_t stream[PC_STREAM_LENGTH + 1];
uint32_t stream[V2_STREAM_LENGTH + 1];
uint8_t offset;
};
class PSOGCEncryption : public PSOEncryption {
class PSOV3Encryption : public PSOEncryption {
public:
explicit PSOGCEncryption(uint32_t key);
explicit PSOV3Encryption(uint32_t key);
virtual void encrypt(void* data, size_t size, bool advance = true);
@@ -60,7 +60,7 @@ public:
protected:
void update_stream();
uint32_t stream[GC_STREAM_LENGTH];
uint32_t stream[V3_STREAM_LENGTH];
uint16_t offset;
};
+18 -2
View File
@@ -27,6 +27,8 @@ uint16_t PSOCommandHeader::command(GameVersion version) const {
return this->dc.command;
case GameVersion::GC:
return this->gc.command;
case GameVersion::XB:
return this->xb.command;
case GameVersion::PC:
case GameVersion::PATCH:
return this->pc.command;
@@ -45,6 +47,9 @@ void PSOCommandHeader::set_command(GameVersion version, uint16_t command) {
case GameVersion::GC:
this->gc.command = command;
break;
case GameVersion::XB:
this->xb.command = command;
break;
case GameVersion::PC:
case GameVersion::PATCH:
this->pc.command = command;
@@ -63,6 +68,8 @@ uint16_t PSOCommandHeader::size(GameVersion version) const {
return this->dc.size;
case GameVersion::GC:
return this->gc.size;
case GameVersion::XB:
return this->xb.size;
case GameVersion::PC:
case GameVersion::PATCH:
return this->pc.size;
@@ -81,6 +88,9 @@ void PSOCommandHeader::set_size(GameVersion version, uint32_t size) {
case GameVersion::GC:
this->gc.size = size;
break;
case GameVersion::XB:
this->xb.size = size;
break;
case GameVersion::PC:
case GameVersion::PATCH:
this->pc.size = size;
@@ -99,6 +109,8 @@ uint32_t PSOCommandHeader::flag(GameVersion version) const {
return this->dc.flag;
case GameVersion::GC:
return this->gc.flag;
case GameVersion::XB:
return this->xb.flag;
case GameVersion::PC:
case GameVersion::PATCH:
return this->pc.flag;
@@ -117,6 +129,9 @@ void PSOCommandHeader::set_flag(GameVersion version, uint32_t flag) {
case GameVersion::GC:
this->gc.flag = flag;
break;
case GameVersion::XB:
this->xb.flag = flag;
break;
case GameVersion::PC:
case GameVersion::PATCH:
this->pc.flag = flag;
@@ -157,9 +172,10 @@ std::string prepend_command_header(
const std::string& data) {
StringWriter ret;
switch (version) {
case GameVersion::DC:
case GameVersion::GC:
case GameVersion::DC: {
PSOCommandHeaderDCGC header;
case GameVersion::XB: {
PSOCommandHeaderDCV3 header;
if (encryption_enabled) {
header.size = (sizeof(header) + data.size() + 3) & ~3;
} else {
+4 -3
View File
@@ -15,7 +15,7 @@ struct PSOCommandHeaderPC {
uint8_t flag;
} __attribute__((packed));
struct PSOCommandHeaderDCGC {
struct PSOCommandHeaderDCV3 {
uint8_t command;
uint8_t flag;
le_uint16_t size;
@@ -28,9 +28,10 @@ struct PSOCommandHeaderBB {
} __attribute__((packed));
union PSOCommandHeader {
PSOCommandHeaderDCGC dc;
PSOCommandHeaderDCV3 dc;
PSOCommandHeaderPC pc;
PSOCommandHeaderDCGC gc;
PSOCommandHeaderDCV3 gc;
PSOCommandHeaderDCV3 xb;
PSOCommandHeaderBB bb;
uint16_t command(GameVersion version) const;
+118 -47
View File
@@ -34,7 +34,7 @@ static FileContentsCache player_files_cache(300 * 1000 * 1000);
PlayerStats::PlayerStats() noexcept
: atp(0), mst(0), evp(0), hp(0), dfp(0), ata(0), lck(0) { }
PlayerDispDataPCGC::PlayerDispDataPCGC() noexcept
PlayerDispDataPCV3::PlayerDispDataPCV3() noexcept
: level(0),
experience(0),
meseta(0),
@@ -58,7 +58,7 @@ PlayerDispDataPCGC::PlayerDispDataPCGC() noexcept
proportion_x(0),
proportion_y(0) { }
void PlayerDispDataPCGC::enforce_pc_limits() {
void PlayerDispDataPCV3::enforce_pc_limits() {
// PC has fewer classes, so we'll substitute some here
if (this->char_class == 11) {
this->char_class = 0; // FOmar -> HUmar
@@ -77,7 +77,7 @@ void PlayerDispDataPCGC::enforce_pc_limits() {
this->version = 2;
}
PlayerDispDataBB PlayerDispDataPCGC::to_bb() const {
PlayerDispDataBB PlayerDispDataPCV3::to_bb() const {
PlayerDispDataBB bb;
bb.stats.atp = this->stats.atp;
bb.stats.mst = this->stats.mst;
@@ -143,43 +143,43 @@ PlayerDispDataBB::PlayerDispDataBB() noexcept
proportion_x(0),
proportion_y(0) { }
PlayerDispDataPCGC PlayerDispDataBB::to_pcgc() const {
PlayerDispDataPCGC pcgc;
pcgc.stats.atp = this->stats.atp;
pcgc.stats.mst = this->stats.mst;
pcgc.stats.evp = this->stats.evp;
pcgc.stats.hp = this->stats.hp;
pcgc.stats.dfp = this->stats.dfp;
pcgc.stats.ata = this->stats.ata;
pcgc.stats.lck = this->stats.lck;
pcgc.unknown_a1 = this->unknown_a1;
pcgc.level = this->level;
pcgc.experience = this->experience;
pcgc.meseta = this->meseta;
pcgc.unknown_a2 = this->unknown_a2;
pcgc.name_color = this->name_color;
pcgc.extra_model = this->extra_model;
pcgc.unused = this->unused;
pcgc.name_color_checksum = this->name_color_checksum;
pcgc.section_id = this->section_id;
pcgc.char_class = this->char_class;
pcgc.v2_flags = this->v2_flags;
pcgc.version = this->version;
pcgc.v1_flags = this->v1_flags;
pcgc.costume = this->costume;
pcgc.skin = this->skin;
pcgc.face = this->face;
pcgc.head = this->head;
pcgc.hair = this->hair;
pcgc.hair_r = this->hair_r;
pcgc.hair_g = this->hair_g;
pcgc.hair_b = this->hair_b;
pcgc.proportion_x = this->proportion_x;
pcgc.proportion_y = this->proportion_y;
pcgc.name = remove_language_marker(this->name);
pcgc.config = this->config;
pcgc.technique_levels = this->technique_levels;
return pcgc;
PlayerDispDataPCV3 PlayerDispDataBB::to_pcv3() const {
PlayerDispDataPCV3 ret;
ret.stats.atp = this->stats.atp;
ret.stats.mst = this->stats.mst;
ret.stats.evp = this->stats.evp;
ret.stats.hp = this->stats.hp;
ret.stats.dfp = this->stats.dfp;
ret.stats.ata = this->stats.ata;
ret.stats.lck = this->stats.lck;
ret.unknown_a1 = this->unknown_a1;
ret.level = this->level;
ret.experience = this->experience;
ret.meseta = this->meseta;
ret.unknown_a2 = this->unknown_a2;
ret.name_color = this->name_color;
ret.extra_model = this->extra_model;
ret.unused = this->unused;
ret.name_color_checksum = this->name_color_checksum;
ret.section_id = this->section_id;
ret.char_class = this->char_class;
ret.v2_flags = this->v2_flags;
ret.version = this->version;
ret.v1_flags = this->v1_flags;
ret.costume = this->costume;
ret.skin = this->skin;
ret.face = this->face;
ret.head = this->head;
ret.hair = this->hair;
ret.hair_r = this->hair_r;
ret.hair_g = this->hair_g;
ret.hair_b = this->hair_b;
ret.proportion_x = this->proportion_x;
ret.proportion_y = this->proportion_y;
ret.name = remove_language_marker(this->name);
ret.config = this->config;
ret.technique_levels = this->technique_levels;
return ret;
}
PlayerDispDataBBPreview PlayerDispDataBB::to_preview() const {
@@ -267,11 +267,20 @@ PlayerDispDataBBPreview::PlayerDispDataBBPreview() noexcept
GuildCardGC::GuildCardGC() noexcept
: player_tag(0), serial_number(0), reserved1(1), reserved2(1), section_id(0), char_class(0) { }
GuildCardV3::GuildCardV3() noexcept
: player_tag(0),
serial_number(0),
reserved1(1),
reserved2(1),
section_id(0),
char_class(0) { }
GuildCardBB::GuildCardBB() noexcept
: serial_number(0), reserved1(1), reserved2(1), section_id(0), char_class(0) { }
: serial_number(0),
reserved1(1),
reserved2(1),
section_id(0),
char_class(0) { }
@@ -455,7 +464,7 @@ void ClientGameData::import_player(const PSOPlayerDataPC& pc) {
// auto_reply = pc.auto_reply;
}
void ClientGameData::import_player(const PSOPlayerDataGC& gc) {
void ClientGameData::import_player(const PSOPlayerDataV3& gc) {
auto account = this->account();
auto player = this->player();
player->inventory = gc.inventory;
@@ -518,15 +527,77 @@ PlayerBB ClientGameData::export_player_bb() {
XBNetworkLocation::XBNetworkLocation() noexcept
: internal_ipv4_address(0x0A0A0A0A),
external_ipv4_address(0x23232323),
port(9100),
account_id(0xFFFFFFFFFFFFFFFF) {
this->unknown_a1[0] = 0xCCCCCCCC;
this->unknown_a1[1] = 0xDDDDDDDD;
this->mac_address.clear(0x77);
}
// There's a strange behavior (bug? "feature"?) in Episode 3 where the start
// button does nothing in the lobby (hence you can't "quit game") if the
// client's IP address is zero. So, we fill it in with a fake nonzero value to
// avoid this behavior, and to be consistent, we make IP addresses fake and
// nonzero on all other versions too.
PlayerLobbyDataPC::PlayerLobbyDataPC() noexcept
: player_tag(0), guild_card(0), ip_address(0), client_id(0) { }
: player_tag(0), guild_card(0), ip_address(0x7F000001), client_id(0) { }
PlayerLobbyDataGC::PlayerLobbyDataGC() noexcept
: player_tag(0), guild_card(0), ip_address(0), client_id(0) { }
: player_tag(0), guild_card(0), ip_address(0x7F000001), client_id(0) { }
PlayerLobbyDataXB::PlayerLobbyDataXB() noexcept
: player_tag(0), guild_card(0), client_id(0) { }
PlayerLobbyDataBB::PlayerLobbyDataBB() noexcept
: player_tag(0), guild_card(0), ip_address(0), client_id(0), unknown2(0) { }
: player_tag(0), guild_card(0), ip_address(0x7F000001), client_id(0), unknown_a2(0) { }
void PlayerLobbyDataPC::clear() {
this->player_tag = 0;
this->guild_card = 0;
this->ip_address = 0;
this->client_id = 0;
ptext<char16_t, 0x10> name;
}
void PlayerLobbyDataGC::clear() {
this->player_tag = 0;
this->guild_card = 0;
this->ip_address = 0;
this->client_id = 0;
ptext<char, 0x10> name;
}
void XBNetworkLocation::clear() {
this->internal_ipv4_address = 0;
this->external_ipv4_address = 0;
this->port = 0;
this->mac_address.clear(0);
this->unknown_a1.clear(0);
this->account_id = 0;
this->unknown_a2.clear(0);
}
void PlayerLobbyDataXB::clear() {
this->player_tag = 0;
this->guild_card = 0;
this->netloc.clear();
this->client_id = 0;
this->name.clear(0);
}
void PlayerLobbyDataBB::clear() {
this->player_tag = 0;
this->guild_card = 0;
this->ip_address = 0;
this->unknown_a1.clear(0);
this->client_id = 0;
this->name.clear(0);
this->unknown_a2 = 0;
}
////////////////////////////////////////////////////////////////////////////////
+58 -26
View File
@@ -93,8 +93,8 @@ struct PendingItemTrade {
struct PlayerDispDataBB;
// PC/GC player appearance and stats data
struct PlayerDispDataPCGC { // 0xD0 bytes
// PC/V3 player appearance and stats data
struct PlayerDispDataPCV3 { // 0xD0 bytes
PlayerStats stats;
parray<uint8_t, 0x0A> unknown_a1;
le_uint32_t level;
@@ -128,7 +128,7 @@ struct PlayerDispDataPCGC { // 0xD0 bytes
// that has a fixed-size array. If we didn't define this constructor, the
// trivial fields in that array's members would be uninitialized, and we could
// send uninitialized memory to the client.
PlayerDispDataPCGC() noexcept;
PlayerDispDataPCV3() noexcept;
void enforce_pc_limits();
PlayerDispDataBB to_bb() const;
@@ -200,14 +200,16 @@ struct PlayerDispDataBB {
PlayerDispDataBB() noexcept;
inline void enforce_pc_limits() { }
PlayerDispDataPCGC to_pcgc() const;
PlayerDispDataPCV3 to_pcv3() const;
PlayerDispDataBBPreview to_preview() const;
void apply_preview(const PlayerDispDataBBPreview&);
} __attribute__((packed));
struct GuildCardGC {
// TODO: Is this the same for XB as it is for GC? (This struct is based on the
// GC format)
struct GuildCardV3 {
le_uint32_t player_tag;
le_uint32_t serial_number;
ptext<char, 0x18> name;
@@ -217,7 +219,7 @@ struct GuildCardGC {
uint8_t section_id;
uint8_t char_class;
GuildCardGC() noexcept;
GuildCardV3() noexcept;
} __attribute__((packed));
// BB guild card format
@@ -271,6 +273,7 @@ struct PlayerLobbyDataPC {
ptext<char16_t, 0x10> name;
PlayerLobbyDataPC() noexcept;
void clear();
} __attribute__((packed));
struct PlayerLobbyDataGC {
@@ -281,6 +284,31 @@ struct PlayerLobbyDataGC {
ptext<char, 0x10> name;
PlayerLobbyDataGC() noexcept;
void clear();
} __attribute__((packed));
struct XBNetworkLocation {
le_uint32_t internal_ipv4_address;
le_uint32_t external_ipv4_address;
le_uint16_t port;
parray<uint8_t, 6> mac_address;
parray<le_uint32_t, 2> unknown_a1;
le_uint64_t account_id;
parray<le_uint32_t, 4> unknown_a2;
XBNetworkLocation() noexcept;
void clear();
} __attribute__((packed));
struct PlayerLobbyDataXB {
le_uint32_t player_tag;
le_uint32_t guild_card;
XBNetworkLocation netloc;
le_uint32_t client_id;
ptext<char, 0x10> name;
PlayerLobbyDataXB() noexcept;
void clear();
} __attribute__((packed));
struct PlayerLobbyDataBB {
@@ -290,14 +318,15 @@ struct PlayerLobbyDataBB {
parray<uint8_t, 0x10> unknown_a1;
le_uint32_t client_id;
ptext<char16_t, 0x10> name;
le_uint32_t unknown2;
le_uint32_t unknown_a2;
PlayerLobbyDataBB() noexcept;
void clear();
} __attribute__((packed));
struct PlayerChallengeDataGC {
struct PlayerChallengeDataV3 {
le_uint32_t client_id;
struct {
le_uint16_t unknown_a1;
@@ -309,21 +338,21 @@ struct PlayerChallengeDataGC {
parray<uint8_t, 2> unknown_a3;
parray<le_uint32_t, 5> unknown_a4;
parray<uint8_t, 0x34> unknown_a5;
} __attribute__((packed)) unknown_a4;
} __attribute__((packed)) unknown_a4; // 0x50 bytes
struct {
parray<uint8_t, 4> unknown_a1;
parray<le_uint32_t, 3> unknown_a2;
} __attribute__((packed)) unknown_a5;
} __attribute__((packed)) unknown_a5; // 0x10 bytes
struct UnknownPair {
le_uint32_t unknown_a1;
le_uint32_t unknown_a2;
} __attribute__((packed));
parray<UnknownPair, 3> unknown_a6;
parray<UnknownPair, 3> unknown_a6; // 0x18 bytes
parray<uint8_t, 0x28> unknown_a7;
} __attribute__((packed)) unknown_a1;
} __attribute__((packed)) unknown_a1; // 0x100 bytes
parray<le_uint16_t, 8> unknown_a2;
parray<le_uint32_t, 2> unknown_a3;
} __attribute__((packed));
} __attribute__((packed)); // 0x11C bytes
struct PlayerChallengeDataBB {
le_uint32_t client_id;
@@ -334,24 +363,27 @@ struct PlayerChallengeDataBB {
struct PSOPlayerDataPC { // For command 61
PlayerInventory inventory;
PlayerDispDataPCGC disp;
PlayerDispDataPCV3 disp;
} __attribute__((packed));
struct PSOPlayerDataGC { // For command 61
struct PSOPlayerDataV3 { // For command 61
PlayerInventory inventory;
PlayerDispDataPCGC disp;
PlayerChallengeDataGC challenge_data;
PlayerDispDataPCV3 disp;
PlayerChallengeDataV3 challenge_data;
parray<uint8_t, 0x18> unknown;
ptext<char, 0xAC> info_board;
parray<le_uint32_t, 0x1E> blocked_senders;
le_uint32_t auto_reply_enabled;
// The auto-reply message can be up to 0x200 bytes. If it's shorter than that,
// the client truncates the command after the first zero byte (rounded up to
// the next 4-byte boundary).
char auto_reply[0];
} __attribute__((packed));
struct PSOPlayerDataGCEp3 { // For command 61
PlayerInventory inventory;
PlayerDispDataPCGC disp;
PlayerChallengeDataGC challenge_data;
PlayerDispDataPCV3 disp;
PlayerChallengeDataV3 challenge_data;
parray<uint8_t, 0x18> unknown;
ptext<char, 0xAC> info_board;
parray<le_uint32_t, 0x1E> blocked_senders;
@@ -480,7 +512,7 @@ public:
void save_player_data() const;
void import_player(const PSOPlayerDataPC& pd);
void import_player(const PSOPlayerDataGC& pd);
void import_player(const PSOPlayerDataV3& pd);
void import_player(const PSOPlayerDataBB& pd);
// Note: this function is not const because it can cause player and account
// data to be loaded
@@ -500,20 +532,20 @@ DestT convert_player_disp_data(const SrcT&) {
}
template <>
inline PlayerDispDataPCGC convert_player_disp_data<PlayerDispDataPCGC>(
const PlayerDispDataPCGC& src) {
inline PlayerDispDataPCV3 convert_player_disp_data<PlayerDispDataPCV3>(
const PlayerDispDataPCV3& src) {
return src;
}
template <>
inline PlayerDispDataPCGC convert_player_disp_data<PlayerDispDataPCGC, PlayerDispDataBB>(
inline PlayerDispDataPCV3 convert_player_disp_data<PlayerDispDataPCV3, PlayerDispDataBB>(
const PlayerDispDataBB& src) {
return src.to_pcgc();
return src.to_pcv3();
}
template <>
inline PlayerDispDataBB convert_player_disp_data<PlayerDispDataBB, PlayerDispDataPCGC>(
const PlayerDispDataPCGC& src) {
inline PlayerDispDataBB convert_player_disp_data<PlayerDispDataBB, PlayerDispDataPCV3>(
const PlayerDispDataPCV3& src) {
return src.to_bb();
}
+128 -70
View File
@@ -68,7 +68,8 @@ static void send_text_message_to_client(
const std::string& message) {
StringWriter w;
w.put<SC_TextHeader_01_06_11_B0_EE>({0, 0});
if (session.version == GameVersion::PC) {
if ((session.version == GameVersion::PC) ||
(session.version == GameVersion::BB)) {
auto decoded = decode_sjis(message);
w.write(decoded.data(), decoded.size() * sizeof(decoded[0]));
w.put_u16l(0);
@@ -158,7 +159,7 @@ static HandlerResult process_server_gc_9A(shared_ptr<ServerState>,
return HandlerResult::Type::SUPPRESS;
}
static HandlerResult process_server_pc_gc_patch_02_17(shared_ptr<ServerState> s,
static HandlerResult process_server_pc_v3_patch_02_17(shared_ptr<ServerState> s,
ProxyServer::LinkedSession& session, uint16_t command, uint32_t flag, string& data) {
if (session.version == GameVersion::PATCH && command == 0x17) {
throw invalid_argument("patch server sent 17 server init");
@@ -166,8 +167,8 @@ static HandlerResult process_server_pc_gc_patch_02_17(shared_ptr<ServerState> s,
// Most servers don't include after_message or have a shorter
// after_message than newserv does, so don't require it
const auto& cmd = check_size_t<S_ServerInit_DC_PC_GC_02_17_91_9B>(data,
offsetof(S_ServerInit_DC_PC_GC_02_17_91_9B, after_message), 0xFFFF);
const auto& cmd = check_size_t<S_ServerInit_DC_PC_V3_02_17_91_9B>(data,
offsetof(S_ServerInit_DC_PC_V3_02_17_91_9B, after_message), 0xFFFF);
if (!session.license) {
session.log.info("No license in linked session");
@@ -176,16 +177,17 @@ static HandlerResult process_server_pc_gc_patch_02_17(shared_ptr<ServerState> s,
// client will be able to understand it.
forward_command(session, false, command, flag, data);
if (session.version == GameVersion::GC) {
session.server_channel.crypt_in.reset(new PSOGCEncryption(cmd.server_key));
session.server_channel.crypt_out.reset(new PSOGCEncryption(cmd.client_key));
session.client_channel.crypt_in.reset(new PSOGCEncryption(cmd.client_key));
session.client_channel.crypt_out.reset(new PSOGCEncryption(cmd.server_key));
if ((session.version == GameVersion::GC) ||
(session.version == GameVersion::XB)) {
session.server_channel.crypt_in.reset(new PSOV3Encryption(cmd.server_key));
session.server_channel.crypt_out.reset(new PSOV3Encryption(cmd.client_key));
session.client_channel.crypt_in.reset(new PSOV3Encryption(cmd.client_key));
session.client_channel.crypt_out.reset(new PSOV3Encryption(cmd.server_key));
} else { // PC or patch server (they both use PC encryption)
session.server_channel.crypt_in.reset(new PSOPCEncryption(cmd.server_key));
session.server_channel.crypt_out.reset(new PSOPCEncryption(cmd.client_key));
session.client_channel.crypt_in.reset(new PSOPCEncryption(cmd.client_key));
session.client_channel.crypt_out.reset(new PSOPCEncryption(cmd.server_key));
session.server_channel.crypt_in.reset(new PSOV2Encryption(cmd.server_key));
session.server_channel.crypt_out.reset(new PSOV2Encryption(cmd.client_key));
session.client_channel.crypt_in.reset(new PSOV2Encryption(cmd.client_key));
session.client_channel.crypt_out.reset(new PSOV2Encryption(cmd.server_key));
}
return HandlerResult::Type::SUPPRESS;
@@ -194,12 +196,14 @@ static HandlerResult process_server_pc_gc_patch_02_17(shared_ptr<ServerState> s,
session.log.info("Existing license in linked session");
// This isn't forwarded to the client, so don't recreate the client's crypts
if ((session.version == GameVersion::PATCH) || (session.version == GameVersion::PC)) {
session.server_channel.crypt_in.reset(new PSOPCEncryption(cmd.server_key));
session.server_channel.crypt_out.reset(new PSOPCEncryption(cmd.client_key));
} else if (session.version == GameVersion::GC) {
session.server_channel.crypt_in.reset(new PSOGCEncryption(cmd.server_key));
session.server_channel.crypt_out.reset(new PSOGCEncryption(cmd.client_key));
if ((session.version == GameVersion::PATCH) ||
(session.version == GameVersion::PC)) {
session.server_channel.crypt_in.reset(new PSOV2Encryption(cmd.server_key));
session.server_channel.crypt_out.reset(new PSOV2Encryption(cmd.client_key));
} else if ((session.version == GameVersion::GC) ||
(session.version == GameVersion::XB)) {
session.server_channel.crypt_in.reset(new PSOV3Encryption(cmd.server_key));
session.server_channel.crypt_out.reset(new PSOV3Encryption(cmd.client_key));
} else {
throw invalid_argument("unsupported version");
}
@@ -236,7 +240,7 @@ static HandlerResult process_server_pc_gc_patch_02_17(shared_ptr<ServerState> s,
} else if (session.version == GameVersion::GC) {
if (command == 0x17) {
C_VerifyLicense_GC_DB cmd;
C_VerifyLicense_V3_DB cmd;
cmd.serial_number = string_printf("%08" PRIX32 "",
session.license->serial_number);
cmd.access_key = session.license->access_key;
@@ -252,6 +256,9 @@ static HandlerResult process_server_pc_gc_patch_02_17(shared_ptr<ServerState> s,
return process_server_gc_9A(s, session, command, flag, data);
}
} else if (session.version == GameVersion::XB) {
throw runtime_error("xbox licenses are not implemented");
} else {
throw logic_error("invalid game version in server init handler");
}
@@ -314,13 +321,13 @@ static HandlerResult process_server_bb_03(shared_ptr<ServerState> s,
}
}
static HandlerResult process_server_dc_pc_gc_04(shared_ptr<ServerState>,
static HandlerResult process_server_dc_pc_v3_04(shared_ptr<ServerState>,
ProxyServer::LinkedSession& session, uint16_t, uint32_t, string& data) {
// Some servers send a short 04 command if they don't use all of the 0x20
// bytes available. We should be prepared to handle that.
auto& cmd = check_size_t<S_UpdateClientConfig_DC_PC_GC_04>(data,
offsetof(S_UpdateClientConfig_DC_PC_GC_04, cfg),
sizeof(S_UpdateClientConfig_DC_PC_GC_04));
auto& cmd = check_size_t<S_UpdateClientConfig_DC_PC_V3_04>(data,
offsetof(S_UpdateClientConfig_DC_PC_V3_04, cfg),
sizeof(S_UpdateClientConfig_DC_PC_V3_04));
// If this is a licensed session, hide the guild card number assigned by the
// remote server so the client doesn't see it change. If this is an unlicensed
@@ -351,7 +358,7 @@ static HandlerResult process_server_dc_pc_gc_04(shared_ptr<ServerState>,
: "t Port Map. Copyright SEGA Enter",
session.remote_client_config_data.bytes());
memcpy(session.remote_client_config_data.data(), &cmd.cfg,
min<size_t>(data.size() - sizeof(S_UpdateClientConfig_DC_PC_GC_04),
min<size_t>(data.size() - sizeof(S_UpdateClientConfig_DC_PC_V3_04),
session.remote_client_config_data.bytes()));
// If the guild card number was not set, pretend (to the server) that this is
@@ -369,7 +376,7 @@ static HandlerResult process_server_dc_pc_gc_04(shared_ptr<ServerState>,
return session.license ? HandlerResult::Type::MODIFIED : HandlerResult::Type::FORWARD;
}
static HandlerResult process_server_dc_pc_gc_06(shared_ptr<ServerState>,
static HandlerResult process_server_dc_pc_v3_06(shared_ptr<ServerState>,
ProxyServer::LinkedSession& session, uint16_t, uint32_t, string& data) {
if (session.license) {
auto& cmd = check_size_t<SC_TextHeader_01_06_11_B0_EE>(data,
@@ -538,7 +545,7 @@ static HandlerResult process_server_C4(shared_ptr<ServerState>,
static HandlerResult process_server_gc_E4(shared_ptr<ServerState>,
ProxyServer::LinkedSession& session, uint16_t, uint32_t, string& data) {
auto& cmd = check_size_t<S_CardLobbyGame_GC_E4>(data);
auto& cmd = check_size_t<S_CardLobbyGame_GC_Ep3_E4>(data);
bool modified = false;
for (size_t x = 0; x < 4; x++) {
if (cmd.entries[x].guild_card_number == session.remote_guild_card_number) {
@@ -640,12 +647,12 @@ static HandlerResult process_server_game_19_patch_14(shared_ptr<ServerState>,
}
}
static HandlerResult process_server_gc_1A_D5(shared_ptr<ServerState>,
static HandlerResult process_server_v3_1A_D5(shared_ptr<ServerState>,
ProxyServer::LinkedSession& session, uint16_t, uint32_t, string&) {
// If the client is a version that sends close confirmations and the client
// has the no-close-confirmation flag set in its newserv client config, send a
// fake confirmation to the remote server immediately.
if ((session.version == GameVersion::GC) &&
if (((session.version == GameVersion::GC) || (session.version == GameVersion::XB)) &&
(session.newserv_client_config.cfg.flags & Client::Flag::NO_MESSAGE_BOX_CLOSE_CONFIRMATION)) {
session.server_channel.send(0xD6);
}
@@ -676,7 +683,7 @@ template <typename T>
static HandlerResult process_server_44_A6(shared_ptr<ServerState>,
ProxyServer::LinkedSession& session, uint16_t command, uint32_t, string& data) {
if (session.save_files) {
const auto& cmd = check_size_t<S_OpenFile_PC_GC_44_A6>(data);
const auto& cmd = check_size_t<S_OpenFile_PC_V3_44_A6>(data);
bool is_download_quest = (command == 0xA6);
string filename = cmd.filename;
@@ -821,21 +828,26 @@ static HandlerResult process_server_64(shared_ptr<ServerState>,
session.lobby_players.resize(4);
session.log.info("Cleared lobby players");
const size_t expected_size = session.sub_version >= 0x40
? sizeof(CmdT)
: offsetof(CmdT, players_ep3);
auto& cmd = check_size_t<CmdT>(data, expected_size, expected_size);
CmdT* cmd;
S_JoinGame_GC_Ep3_64* cmd_ep3 = nullptr;
if (session.sub_version >= 0x40) {
cmd = &check_size_t<CmdT>(data, sizeof(S_JoinGame_GC_Ep3_64), sizeof(S_JoinGame_GC_Ep3_64));
cmd_ep3 = &check_size_t<S_JoinGame_GC_Ep3_64>(data);
} else {
cmd = &check_size_t<CmdT>(data);
}
bool modified = false;
session.lobby_client_id = cmd.client_id;
session.lobby_client_id = cmd->client_id;
for (size_t x = 0; x < flag; x++) {
if (cmd.lobby_data[x].guild_card == session.remote_guild_card_number) {
cmd.lobby_data[x].guild_card = session.license->serial_number;
if (cmd->lobby_data[x].guild_card == session.remote_guild_card_number) {
cmd->lobby_data[x].guild_card = session.license->serial_number;
modified = true;
}
session.lobby_players[x].guild_card_number = cmd.lobby_data[x].guild_card;
if (data.size() == sizeof(CmdT)) {
ptext<char, 0x10> name = cmd.players_ep3[x].disp.name;
session.lobby_players[x].guild_card_number = cmd->lobby_data[x].guild_card;
if (cmd_ep3) {
ptext<char, 0x10> name = cmd_ep3->players_ep3[x].disp.name;
session.lobby_players[x].name = name;
} else {
session.lobby_players[x].name.clear();
@@ -847,15 +859,15 @@ static HandlerResult process_server_64(shared_ptr<ServerState>,
}
if (session.override_section_id >= 0) {
cmd.section_id = session.override_section_id;
cmd->section_id = session.override_section_id;
modified = true;
}
if (session.override_lobby_event >= 0) {
cmd.event = session.override_lobby_event;
cmd->event = session.override_lobby_event;
modified = true;
}
if (session.override_random_seed >= 0) {
cmd.rare_seed = session.override_random_seed;
cmd->rare_seed = session.override_random_seed;
modified = true;
}
@@ -885,7 +897,7 @@ static HandlerResult process_client_06(shared_ptr<ServerState> s,
text = u16string(cmd.text.pcbb, (data.size() - sizeof(C_Chat_06)) / sizeof(char16_t));
} else {
const auto& cmd = check_size_t<C_Chat_06>(data, sizeof(C_Chat_06), 0xFFFF);
text = decode_sjis(cmd.text.dcgc, data.size() - sizeof(C_Chat_06));
text = decode_sjis(cmd.text.dcv3, data.size() - sizeof(C_Chat_06));
}
strip_trailing_zeroes(text);
@@ -939,7 +951,7 @@ static HandlerResult process_client_40(shared_ptr<ServerState>,
template <typename CmdT>
static HandlerResult process_client_81(shared_ptr<ServerState>,
ProxyServer::LinkedSession& session, uint16_t, uint32_t, string& data) {
auto& cmd = check_size_t<SC_SimpleMail_GC_81>(data);
auto& cmd = check_size_t<SC_SimpleMail_V3_81>(data);
if (session.license) {
if (cmd.from_guild_card_number == session.license->serial_number) {
cmd.from_guild_card_number = session.remote_guild_card_number;
@@ -1016,7 +1028,7 @@ HandlerResult process_client_60_62_6C_6D_C9_CB<void>(shared_ptr<ServerState>,
return HandlerResult::Type::FORWARD;
}
static HandlerResult process_client_dc_pc_gc_A0_A1(shared_ptr<ServerState> s,
static HandlerResult process_client_dc_pc_v3_A0_A1(shared_ptr<ServerState> s,
ProxyServer::LinkedSession& session, uint16_t, uint32_t, string&) {
if (!session.license) {
return HandlerResult::Type::FORWARD;
@@ -1041,14 +1053,14 @@ static HandlerResult process_client_dc_pc_gc_A0_A1(shared_ptr<ServerState> s,
"You\'ve returned to\n\tC6%s", encoded_name.c_str()));
// Restore newserv_client_config, so the login server gets the client flags
S_UpdateClientConfig_DC_PC_GC_04 update_client_config_cmd;
S_UpdateClientConfig_DC_PC_V3_04 update_client_config_cmd;
update_client_config_cmd.player_tag = 0x00010000;
update_client_config_cmd.guild_card_number = session.license->serial_number;
update_client_config_cmd.cfg = session.newserv_client_config.cfg;
session.client_channel.send(0x04, 0x00, &update_client_config_cmd, sizeof(update_client_config_cmd));
static const vector<string> version_to_port_name({
"dc-login", "pc-login", "bb-patch", "gc-us3", "bb-login"});
"dc-login", "pc-login", "bb-patch", "gc-us3", "xb-login", "bb-login"});
const auto& port_name = version_to_port_name.at(static_cast<size_t>(
session.version));
@@ -1094,11 +1106,11 @@ typedef HandlerResult (*process_command_t)(
auto defh = process_default;
static process_command_t dc_server_handlers[0x100] = {
/* 00 */ defh, defh, defh, defh, process_server_dc_pc_gc_04, defh, process_server_dc_pc_gc_06, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 00 */ defh, defh, defh, defh, process_server_dc_pc_v3_04, defh, process_server_dc_pc_v3_06, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 10 */ defh, defh, defh, process_server_13_A7, defh, defh, defh, defh, defh, process_server_game_19_patch_14, defh, defh, defh, defh, defh, defh,
/* 20 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 30 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 40 */ defh, process_server_41<S_GuildCardSearchResult_DC_GC_41>, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 40 */ defh, process_server_41<S_GuildCardSearchResult_DC_V3_41>, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 50 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 60 */ process_server_60_62_6C_6D_C9_CB, defh, process_server_60_62_6C_6D_C9_CB, defh, defh, defh, process_server_66_69, defh, defh, process_server_66_69, defh, defh, process_server_60_62_6C_6D_C9_CB, process_server_60_62_6C_6D_C9_CB, defh, defh,
/* 70 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
@@ -1112,17 +1124,17 @@ static process_command_t dc_server_handlers[0x100] = {
/* F0 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
};
static process_command_t pc_server_handlers[0x100] = {
/* 00 */ defh, defh, process_server_pc_gc_patch_02_17, defh, process_server_dc_pc_gc_04, defh, process_server_dc_pc_gc_06, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 10 */ defh, defh, defh, process_server_13_A7, defh, defh, defh, process_server_pc_gc_patch_02_17, defh, process_server_game_19_patch_14, defh, defh, defh, defh, defh, defh,
/* 00 */ defh, defh, process_server_pc_v3_patch_02_17, defh, process_server_dc_pc_v3_04, defh, process_server_dc_pc_v3_06, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 10 */ defh, defh, defh, process_server_13_A7, defh, defh, defh, process_server_pc_v3_patch_02_17, defh, process_server_game_19_patch_14, defh, defh, defh, defh, defh, defh,
/* 20 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 30 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 40 */ defh, process_server_41<S_GuildCardSearchResult_PC_41>, defh, defh, process_server_44_A6<S_OpenFile_PC_GC_44_A6>, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 40 */ defh, process_server_41<S_GuildCardSearchResult_PC_41>, defh, defh, process_server_44_A6<S_OpenFile_PC_V3_44_A6>, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 50 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 60 */ process_server_60_62_6C_6D_C9_CB, defh, process_server_60_62_6C_6D_C9_CB, defh, process_server_64<S_JoinGame_PC_64>, process_server_65_67_68<S_JoinLobby_PC_65_67_68>, process_server_66_69, process_server_65_67_68<S_JoinLobby_PC_65_67_68>, process_server_65_67_68<S_JoinLobby_PC_65_67_68>, process_server_66_69, defh, defh, process_server_60_62_6C_6D_C9_CB, process_server_60_62_6C_6D_C9_CB, defh, defh,
/* 70 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 80 */ defh, defh, defh, defh, defh, defh, defh, defh, process_server_88, defh, defh, defh, defh, defh, defh, defh,
/* 90 */ defh, defh, defh, defh, defh, defh, defh, process_server_97, defh, defh, defh, defh, defh, defh, defh, defh,
/* A0 */ defh, defh, defh, defh, defh, defh, process_server_44_A6<S_OpenFile_PC_GC_44_A6>, process_server_13_A7, defh, defh, defh, defh, defh, defh, defh, defh,
/* A0 */ defh, defh, defh, defh, defh, defh, process_server_44_A6<S_OpenFile_PC_V3_44_A6>, process_server_13_A7, defh, defh, defh, defh, defh, defh, defh, defh,
/* B0 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* C0 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* D0 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
@@ -1130,23 +1142,41 @@ static process_command_t pc_server_handlers[0x100] = {
/* F0 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
};
static process_command_t gc_server_handlers[0x100] = {
/* 00 */ defh, defh, process_server_pc_gc_patch_02_17, defh, process_server_dc_pc_gc_04, defh, process_server_dc_pc_gc_06, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 10 */ defh, defh, defh, process_server_13_A7, defh, defh, defh, process_server_pc_gc_patch_02_17, defh, process_server_game_19_patch_14, process_server_gc_1A_D5, defh, defh, defh, defh, defh,
/* 00 */ defh, defh, process_server_pc_v3_patch_02_17, defh, process_server_dc_pc_v3_04, defh, process_server_dc_pc_v3_06, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 10 */ defh, defh, defh, process_server_13_A7, defh, defh, defh, process_server_pc_v3_patch_02_17, defh, process_server_game_19_patch_14, process_server_v3_1A_D5, defh, defh, defh, defh, defh,
/* 20 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 30 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 40 */ defh, process_server_41<S_GuildCardSearchResult_DC_GC_41>, defh, defh, process_server_44_A6<S_OpenFile_PC_GC_44_A6>, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 40 */ defh, process_server_41<S_GuildCardSearchResult_DC_V3_41>, defh, defh, process_server_44_A6<S_OpenFile_PC_V3_44_A6>, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 50 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 60 */ process_server_60_62_6C_6D_C9_CB, defh, process_server_60_62_6C_6D_C9_CB, defh, process_server_64<S_JoinGame_GC_64>, process_server_65_67_68<S_JoinLobby_GC_65_67_68>, process_server_66_69, process_server_65_67_68<S_JoinLobby_GC_65_67_68>, process_server_65_67_68<S_JoinLobby_GC_65_67_68>, process_server_66_69, defh, defh, process_server_60_62_6C_6D_C9_CB, process_server_60_62_6C_6D_C9_CB, defh, defh,
/* 70 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 80 */ defh, process_server_81<SC_SimpleMail_GC_81>, defh, defh, defh, defh, defh, defh, process_server_88, defh, defh, defh, defh, defh, defh, defh,
/* 80 */ defh, process_server_81<SC_SimpleMail_V3_81>, defh, defh, defh, defh, defh, defh, process_server_88, defh, defh, defh, defh, defh, defh, defh,
/* 90 */ defh, defh, defh, defh, defh, defh, defh, process_server_97, defh, defh, process_server_gc_9A, defh, defh, defh, defh, defh,
/* A0 */ defh, defh, defh, defh, defh, defh, process_server_44_A6<S_OpenFile_PC_GC_44_A6>, process_server_13_A7, defh, defh, defh, defh, defh, defh, defh, defh,
/* A0 */ defh, defh, defh, defh, defh, defh, process_server_44_A6<S_OpenFile_PC_V3_44_A6>, process_server_13_A7, defh, defh, defh, defh, defh, defh, defh, defh,
/* B0 */ defh, defh, process_server_B2, defh, defh, defh, defh, defh, process_server_gc_B8, defh, defh, defh, defh, defh, defh, defh,
/* C0 */ defh, defh, defh, defh, process_server_C4<S_ChoiceSearchResultEntry_GC_C4>, defh, defh, defh, defh, process_server_60_62_6C_6D_C9_CB, defh, process_server_60_62_6C_6D_C9_CB, defh, defh, defh, defh,
/* D0 */ defh, defh, defh, defh, defh, process_server_gc_1A_D5, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* C0 */ defh, defh, defh, defh, process_server_C4<S_ChoiceSearchResultEntry_V3_C4>, defh, defh, defh, defh, process_server_60_62_6C_6D_C9_CB, defh, process_server_60_62_6C_6D_C9_CB, defh, defh, defh, defh,
/* D0 */ defh, defh, defh, defh, defh, process_server_v3_1A_D5, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* E0 */ defh, defh, defh, defh, process_server_gc_E4, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* F0 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
};
static process_command_t xb_server_handlers[0x100] = {
/* 00 */ defh, defh, process_server_pc_v3_patch_02_17, defh, process_server_dc_pc_v3_04, defh, process_server_dc_pc_v3_06, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 10 */ defh, defh, defh, process_server_13_A7, defh, defh, defh, process_server_pc_v3_patch_02_17, defh, process_server_game_19_patch_14, process_server_v3_1A_D5, defh, defh, defh, defh, defh,
/* 20 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 30 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 40 */ defh, process_server_41<S_GuildCardSearchResult_DC_V3_41>, defh, defh, process_server_44_A6<S_OpenFile_PC_V3_44_A6>, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 50 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 60 */ process_server_60_62_6C_6D_C9_CB, defh, process_server_60_62_6C_6D_C9_CB, defh, process_server_64<S_JoinGame_GC_64>, process_server_65_67_68<S_JoinLobby_GC_65_67_68>, process_server_66_69, process_server_65_67_68<S_JoinLobby_GC_65_67_68>, process_server_65_67_68<S_JoinLobby_GC_65_67_68>, process_server_66_69, defh, defh, process_server_60_62_6C_6D_C9_CB, process_server_60_62_6C_6D_C9_CB, defh, defh,
/* 70 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 80 */ defh, process_server_81<SC_SimpleMail_V3_81>, defh, defh, defh, defh, defh, defh, process_server_88, defh, defh, defh, defh, defh, defh, defh,
/* 90 */ defh, defh, defh, defh, defh, defh, defh, process_server_97, defh, defh, defh, defh, defh, defh, defh, defh,
/* A0 */ defh, defh, defh, defh, defh, defh, process_server_44_A6<S_OpenFile_PC_V3_44_A6>, process_server_13_A7, defh, defh, defh, defh, defh, defh, defh, defh,
/* B0 */ defh, defh, process_server_B2, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* C0 */ defh, defh, defh, defh, process_server_C4<S_ChoiceSearchResultEntry_V3_C4>, defh, defh, defh, defh, process_server_60_62_6C_6D_C9_CB, defh, process_server_60_62_6C_6D_C9_CB, defh, defh, defh, defh,
/* D0 */ defh, defh, defh, defh, defh, process_server_v3_1A_D5, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* E0 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* F0 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
};
static process_command_t bb_server_handlers[0x100] = {
/* 00 */ defh, defh, defh, process_server_bb_03, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 10 */ defh, defh, defh, process_server_13_A7, defh, defh, defh, defh, defh, process_server_game_19_patch_14, defh, defh, defh, defh, defh, defh,
@@ -1166,7 +1196,7 @@ static process_command_t bb_server_handlers[0x100] = {
/* F0 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
};
static process_command_t patch_server_handlers[0x100] = {
/* 00 */ defh, defh, process_server_pc_gc_patch_02_17, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 00 */ defh, defh, process_server_pc_v3_patch_02_17, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 10 */ defh, defh, defh, defh, process_server_game_19_patch_14, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 20 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 30 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
@@ -1197,7 +1227,7 @@ static process_command_t dc_client_handlers[0x100] = {
/* 70 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 80 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 90 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* A0 */ process_client_dc_pc_gc_A0_A1, process_client_dc_pc_gc_A0_A1, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* A0 */ process_client_dc_pc_v3_A0_A1, process_client_dc_pc_v3_A0_A1, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* B0 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* C0 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* D0 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
@@ -1215,7 +1245,7 @@ static process_command_t pc_client_handlers[0x100] = {
/* 70 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 80 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 90 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* A0 */ process_client_dc_pc_gc_A0_A1, process_client_dc_pc_gc_A0_A1, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* A0 */ process_client_dc_pc_v3_A0_A1, process_client_dc_pc_v3_A0_A1, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* B0 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* C0 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* D0 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
@@ -1229,11 +1259,29 @@ static process_command_t gc_client_handlers[0x100] = {
/* 30 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 40 */ process_client_40, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 50 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 60 */ process_client_60_62_6C_6D_C9_CB<G_SendGuildCard_GC_6x06>, defh, process_client_60_62_6C_6D_C9_CB<G_SendGuildCard_GC_6x06>, defh, defh, defh, defh, defh, defh, defh, defh, defh, process_client_60_62_6C_6D_C9_CB<G_SendGuildCard_GC_6x06>, process_client_60_62_6C_6D_C9_CB<G_SendGuildCard_GC_6x06>, defh, defh,
/* 60 */ process_client_60_62_6C_6D_C9_CB<G_SendGuildCard_V3_6x06>, defh, process_client_60_62_6C_6D_C9_CB<G_SendGuildCard_V3_6x06>, defh, defh, defh, defh, defh, defh, defh, defh, defh, process_client_60_62_6C_6D_C9_CB<G_SendGuildCard_V3_6x06>, process_client_60_62_6C_6D_C9_CB<G_SendGuildCard_V3_6x06>, defh, defh,
/* 70 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 80 */ defh, process_client_81<SC_SimpleMail_GC_81>, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 80 */ defh, process_client_81<SC_SimpleMail_V3_81>, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 90 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* A0 */ process_client_dc_pc_gc_A0_A1, process_client_dc_pc_gc_A0_A1, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* A0 */ process_client_dc_pc_v3_A0_A1, process_client_dc_pc_v3_A0_A1, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* B0 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* C0 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* D0 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* E0 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* F0 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
};
static process_command_t xb_client_handlers[0x100] = {
/* 00 */ defh, defh, defh, defh, defh, defh, process_client_06, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 10 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 20 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 30 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 40 */ process_client_40, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 50 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 60 */ process_client_60_62_6C_6D_C9_CB<G_SendGuildCard_V3_6x06>, defh, process_client_60_62_6C_6D_C9_CB<G_SendGuildCard_V3_6x06>, defh, defh, defh, defh, defh, defh, defh, defh, defh, process_client_60_62_6C_6D_C9_CB<G_SendGuildCard_V3_6x06>, process_client_60_62_6C_6D_C9_CB<G_SendGuildCard_V3_6x06>, defh, defh,
/* 70 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 80 */ defh, process_client_81<SC_SimpleMail_V3_81>, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* 90 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* A0 */ process_client_dc_pc_v3_A0_A1, process_client_dc_pc_v3_A0_A1, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* B0 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* C0 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
/* D0 */ defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh, defh,
@@ -1280,13 +1328,23 @@ static process_command_t patch_client_handlers[0x100] = {
static process_command_t* server_handlers[] = {
dc_server_handlers, pc_server_handlers, patch_server_handlers, gc_server_handlers, bb_server_handlers};
dc_server_handlers,
pc_server_handlers,
patch_server_handlers,
gc_server_handlers,
xb_server_handlers,
bb_server_handlers};
static process_command_t* client_handlers[] = {
dc_client_handlers, pc_client_handlers, patch_client_handlers, gc_client_handlers, bb_client_handlers};
dc_client_handlers,
pc_client_handlers,
patch_client_handlers,
gc_client_handlers,
xb_client_handlers,
bb_client_handlers};
static process_command_t get_handler(GameVersion version, bool from_server, uint8_t command) {
size_t version_index = static_cast<size_t>(version);
if (version_index >= 5) {
if (version_index >= 6) {
throw logic_error("invalid game version on proxy server");
}
return (from_server ? server_handlers : client_handlers)[version_index][command];
+14 -7
View File
@@ -187,20 +187,21 @@ void ProxyServer::on_client_connect(
case GameVersion::PATCH:
throw logic_error("cannot create unlinked patch session");
case GameVersion::PC:
case GameVersion::GC: {
case GameVersion::GC:
case GameVersion::XB: {
uint32_t server_key = random_object<uint32_t>();
uint32_t client_key = random_object<uint32_t>();
auto cmd = prepare_server_init_contents_dc_pc_gc(
auto cmd = prepare_server_init_contents_dc_pc_v3(
false, server_key, client_key);
session->channel.send(0x02, 0x00, &cmd, sizeof(cmd));
// TODO: Is this actually needed?
// bufferevent_flush(session->channel.bev.get(), EV_READ | EV_WRITE, BEV_FLUSH);
if (version == GameVersion::PC) {
session->channel.crypt_out.reset(new PSOPCEncryption(server_key));
session->channel.crypt_in.reset(new PSOPCEncryption(client_key));
session->channel.crypt_out.reset(new PSOV2Encryption(server_key));
session->channel.crypt_in.reset(new PSOV2Encryption(client_key));
} else {
session->channel.crypt_out.reset(new PSOGCEncryption(server_key));
session->channel.crypt_in.reset(new PSOGCEncryption(client_key));
session->channel.crypt_out.reset(new PSOV3Encryption(server_key));
session->channel.crypt_in.reset(new PSOV3Encryption(client_key));
}
break;
}
@@ -230,7 +231,10 @@ void ProxyServer::on_client_connect(
ProxyServer::UnlinkedSession::UnlinkedSession(
ProxyServer* server, struct bufferevent* bev, uint16_t local_port, GameVersion version)
ProxyServer* server,
struct bufferevent* bev,
uint16_t local_port,
GameVersion version)
: server(server),
log(string_printf("[ProxyServer:UnlinkedSession:%p] ", bev), proxy_server_log.min_level),
channel(
@@ -288,6 +292,9 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
character_name = cmd.name;
client_config.cfg = cmd.client_config.cfg;
} else if (session->version == GameVersion::XB) {
throw runtime_error("xbox licenses are not implemented");
} else if (session->version == GameVersion::BB) {
// We should only get a 93 while the session is unlinked; if we get
// anything else, disconnect
+12 -8
View File
@@ -112,6 +112,8 @@ struct PSOQuestHeaderPC {
ptext<char16_t, 0x120> long_description;
} __attribute__((packed));
// TODO: Is the XB quest header format the same as on GC? If not, make a
// separate struct; if so, rename this struct to V3.
struct PSOQuestHeaderGC {
uint32_t start_offset;
uint32_t unknown_offset1;
@@ -248,6 +250,7 @@ Quest::Quest(const string& bin_filename)
{"pc", GameVersion::PC},
{"gc", GameVersion::GC},
{"gc3", GameVersion::GC},
{"xb", GameVersion::XB},
{"bb", GameVersion::BB},
});
this->version = name_to_version.at(tokens[1]);
@@ -290,6 +293,7 @@ Quest::Quest(const string& bin_filename)
break;
}
case GameVersion::XB:
case GameVersion::GC: {
if (this->category == QuestCategory::EPISODE_3) {
// these all appear to be the same size
@@ -467,12 +471,11 @@ string Quest::decode_dlq(const string& filename) {
data = read_all(f.get());
}
PSOPCEncryption encr(key);
// The compressed data size does not need to be a multiple of 4, but the PC
// The compressed data size does not need to be a multiple of 4, but the V2
// encryption (which is used for all download quests, even in V3) requires the
// data size to be a multiple of 4. We'll just temporarily stick a few bytes
// on the end, then throw them away later if needed.
PSOV2Encryption encr(key);
size_t original_size = data.size();
data.resize((data.size() + 3) & (~3));
encr.decrypt(data);
@@ -583,15 +586,15 @@ pair<string, string> Quest::decode_qst(const string& filename) {
// the first 4 bytes in the file:
// - BB: 58 00 44 00
// - PC: 3C ?? 44 00
// - DC/GC: 44 ?? 3C 00
// - DC/V3: 44 ?? 3C 00
uint32_t signature = freadx<be_uint32_t>(f.get());
fseek(f.get(), 0, SEEK_SET);
if (signature == 0x58004400) {
return decode_qst_t<PSOCommandHeaderBB, S_OpenFile_BB_44_A6>(f.get());
} else if ((signature & 0xFF00FFFF) == 0x3C004400) {
return decode_qst_t<PSOCommandHeaderPC, S_OpenFile_PC_GC_44_A6>(f.get());
return decode_qst_t<PSOCommandHeaderPC, S_OpenFile_PC_V3_44_A6>(f.get());
} else if ((signature & 0xFF00FFFF) == 0x44003C00) {
return decode_qst_t<PSOCommandHeaderDCGC, S_OpenFile_PC_GC_44_A6>(f.get());
return decode_qst_t<PSOCommandHeaderDCV3, S_OpenFile_PC_V3_44_A6>(f.get());
} else {
throw runtime_error("invalid qst file format");
}
@@ -681,11 +684,11 @@ static string create_download_quest_file(const string& compressed_data,
data += compressed_data;
// Add temporary extra bytes if necessary so encryption won't fail - the data
// size must be a multiple of 4 for PSO PC encryption.
// size must be a multiple of 4 for PSO V2 encryption.
size_t original_size = data.size();
data.resize((data.size() + 3) & (~3));
PSOPCEncryption encr(encryption_seed);
PSOV3Encryption encr(encryption_seed);
encr.encrypt(data.data() + sizeof(PSODownloadQuestHeader),
data.size() - sizeof(PSODownloadQuestHeader));
data.resize(original_size);
@@ -715,6 +718,7 @@ shared_ptr<Quest> Quest::create_download_quest() const {
}
reinterpret_cast<PSOQuestHeaderPC*>(data_ptr)->is_download = 0x01;
break;
case GameVersion::XB:
case GameVersion::GC:
if (decompressed_bin.size() < sizeof(PSOQuestHeaderGC)) {
throw runtime_error("bin file is too small for header");
+187 -69
View File
@@ -149,9 +149,9 @@ void process_disconnect(shared_ptr<ServerState> s, shared_ptr<Client> c) {
////////////////////////////////////////////////////////////////////////////////
void process_verify_license_gc(shared_ptr<ServerState> s, shared_ptr<Client> c,
void process_verify_license_v3(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t, uint32_t, const string& data) { // DB
const auto& cmd = check_size_t<C_VerifyLicense_GC_DB>(data);
const auto& cmd = check_size_t<C_VerifyLicense_V3_DB>(data);
uint32_t serial_number = stoul(cmd.serial_number, nullptr, 16);
try {
@@ -176,29 +176,37 @@ void process_verify_license_gc(shared_ptr<ServerState> s, shared_ptr<Client> c,
send_command(c, 0x9A, 0x02);
}
void process_login_a_dc_pc_gc(shared_ptr<ServerState> s, shared_ptr<Client> c,
void process_login_a_dc_pc_v3(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t, uint32_t, const string& data) { // 9A
const auto& cmd = check_size_t<C_Login_DC_PC_GC_9A>(data);
const auto& cmd = check_size_t<C_Login_DC_PC_V3_9A>(data);
c->flags |= flags_for_version(c->version, cmd.sub_version);
uint32_t serial_number = stoul(cmd.serial_number, nullptr, 16);
try {
shared_ptr<const License> l;
if (c->version == GameVersion::GC) {
l = s->license_manager->verify_gc(serial_number, cmd.access_key);
} else {
l = s->license_manager->verify_pc(serial_number, cmd.access_key);
switch (c->version) {
case GameVersion::PC:
l = s->license_manager->verify_pc(serial_number, cmd.access_key);
break;
case GameVersion::GC:
l = s->license_manager->verify_gc(serial_number, cmd.access_key);
break;
case GameVersion::XB:
throw runtime_error("xbox licenses are not implemented");
break;
default:
throw logic_error("unsupported versioned command");
}
c->set_license(l);
} catch (const exception& e) {
// On GC, the client should have sent a different command containing the
// On V3, the client should have sent a different command containing the
// password already, which should have created and added a temporary
// license. So, if no license exists at this point, disconnect the client
// even if unregistered clients are allowed.
shared_ptr<License> l;
if (c->version == GameVersion::GC) {
if ((c->version == GameVersion::GC) || (c->version == GameVersion::XB)) {
u16string message = u"Login failed: " + decode_sjis(e.what());
send_message_box(c, message.c_str());
c->should_disconnect = true;
@@ -215,20 +223,28 @@ void process_login_a_dc_pc_gc(shared_ptr<ServerState> s, shared_ptr<Client> c,
send_command(c, 0x9C, 0x01);
}
void process_login_c_dc_pc_gc(shared_ptr<ServerState> s, shared_ptr<Client> c,
void process_login_c_dc_pc_v3(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t, uint32_t, const string& data) { // 9C
const auto& cmd = check_size_t<C_Register_DC_PC_GC_9C>(data);
const auto& cmd = check_size_t<C_Register_DC_PC_V3_9C>(data);
c->flags |= flags_for_version(c->version, cmd.sub_version);
uint32_t serial_number = stoul(cmd.serial_number, nullptr, 16);
try {
shared_ptr<const License> l;
if (c->version == GameVersion::GC) {
l = s->license_manager->verify_gc(serial_number, cmd.access_key,
cmd.password);
} else {
l = s->license_manager->verify_pc(serial_number, cmd.access_key);
switch (c->version) {
case GameVersion::PC:
l = s->license_manager->verify_pc(serial_number, cmd.access_key);
break;
case GameVersion::GC:
l = s->license_manager->verify_gc(serial_number, cmd.access_key,
cmd.password);
break;
case GameVersion::XB:
throw runtime_error("xbox licenses are not implemented");
break;
default:
throw logic_error("unsupported versioned command");
}
c->set_license(l);
@@ -240,12 +256,20 @@ void process_login_c_dc_pc_gc(shared_ptr<ServerState> s, shared_ptr<Client> c,
return;
} else {
shared_ptr<License> l;
if (c->version == GameVersion::GC) {
l = LicenseManager::create_license_gc(serial_number, cmd.access_key,
cmd.password, true);
} else {
l = LicenseManager::create_license_pc(serial_number, cmd.access_key,
true);
switch (c->version) {
case GameVersion::PC:
l = LicenseManager::create_license_pc(serial_number, cmd.access_key,
true);
break;
case GameVersion::GC:
l = LicenseManager::create_license_gc(serial_number, cmd.access_key,
cmd.password, true);
break;
case GameVersion::XB:
throw runtime_error("xbox licenses are not implemented");
break;
default:
throw logic_error("unsupported versioned command");
}
s->license_manager->add(l);
c->set_license(l);
@@ -255,7 +279,7 @@ void process_login_c_dc_pc_gc(shared_ptr<ServerState> s, shared_ptr<Client> c,
send_command(c, 0x9C, 0x01);
}
void process_login_d_e_pc_gc(shared_ptr<ServerState> s, shared_ptr<Client> c,
void process_login_d_e_pc_v3(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t command, uint32_t, const string& data) { // 9D 9E
// The client sends extra unused data the first time it sends these commands,
@@ -272,8 +296,8 @@ void process_login_d_e_pc_gc(shared_ptr<ServerState> s, shared_ptr<Client> c,
base_cmd = &cmd;
if (cmd.is_extended) {
const auto& cmd = check_size_t<C_LoginExtended_GC_9E>(data);
if (cmd.menu_id == MenuID::LOBBY) {
c->preferred_lobby_id = cmd.preferred_lobby_id;
if (cmd.extension.menu_id == MenuID::LOBBY) {
c->preferred_lobby_id = cmd.extension.preferred_lobby_id;
}
}
@@ -296,12 +320,18 @@ void process_login_d_e_pc_gc(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint32_t serial_number = stoul(base_cmd->serial_number, nullptr, 16);
try {
shared_ptr<const License> l;
if (c->version == GameVersion::GC) {
l = s->license_manager->verify_gc(serial_number,
base_cmd->access_key);
} else {
l = s->license_manager->verify_pc(serial_number,
base_cmd->access_key);
switch (c->version) {
case GameVersion::PC:
l = s->license_manager->verify_pc(serial_number, base_cmd->access_key);
break;
case GameVersion::GC:
l = s->license_manager->verify_gc(serial_number, base_cmd->access_key);
break;
case GameVersion::XB:
throw runtime_error("xbox licenses are not implemented");
break;
default:
throw logic_error("unsupported versioned command");
}
c->set_license(l);
@@ -337,7 +367,7 @@ void process_login_bb(shared_ptr<ServerState> s, shared_ptr<Client> c,
throw runtime_error("invalid size for 93 command");
}
c->flags |= flags_for_version(c->version, 0);
c->flags |= flags_for_version(c->version, -1);
try {
auto l = s->license_manager->verify_bb(cmd.username, cmd.password);
@@ -411,7 +441,7 @@ void process_return_client_config(shared_ptr<ServerState>, shared_ptr<Client> c,
void process_client_checksum(shared_ptr<ServerState>, shared_ptr<Client> c,
uint16_t, uint32_t, const string& data) { // 96
check_size_t<C_CharSaveInfo_GC_BB_96>(data);
check_size_t<C_CharSaveInfo_V3_BB_96>(data);
send_server_time(c);
}
@@ -430,7 +460,7 @@ void process_server_time_request(shared_ptr<ServerState> s, shared_ptr<Client> c
// responds after saving.
if (c->should_send_to_lobby_server) {
static const vector<string> version_to_port_name({
"dc-lobby", "pc-lobby", "bb-lobby", "gc-lobby", "bb-lobby"});
"dc-lobby", "pc-lobby", "bb-lobby", "gc-lobby", "xb-lobby", "bb-lobby"});
const auto& port_name = version_to_port_name.at(static_cast<size_t>(c->version));
send_reconnect(c, s->connect_address_for_client(c),
s->name_to_port_config.at(port_name)->port);
@@ -756,7 +786,7 @@ void process_menu_selection(shared_ptr<ServerState> s, shared_ptr<Client> c,
item_id = cmd.item_id;
unknown_a1 = cmd.unknown_a1;
} else {
const auto& cmd = check_size_t<C_MenuSelection_DC_GC_10_Flag01>(data);
const auto& cmd = check_size_t<C_MenuSelection_DC_V3_10_Flag01>(data);
menu_id = cmd.menu_id;
item_id = cmd.item_id;
unknown_a1 = decode_sjis(cmd.unknown_a1);
@@ -768,7 +798,7 @@ void process_menu_selection(shared_ptr<ServerState> s, shared_ptr<Client> c,
item_id = cmd.item_id;
password = cmd.password;
} else {
const auto& cmd = check_size_t<C_MenuSelection_DC_GC_10_Flag02>(data);
const auto& cmd = check_size_t<C_MenuSelection_DC_V3_10_Flag02>(data);
menu_id = cmd.menu_id;
item_id = cmd.item_id;
password = decode_sjis(cmd.password);
@@ -781,7 +811,7 @@ void process_menu_selection(shared_ptr<ServerState> s, shared_ptr<Client> c,
unknown_a1 = cmd.unknown_a1;
password = cmd.password;
} else {
const auto& cmd = check_size_t<C_MenuSelection_DC_GC_10_Flag03>(data);
const auto& cmd = check_size_t<C_MenuSelection_DC_V3_10_Flag03>(data);
menu_id = cmd.menu_id;
item_id = cmd.item_id;
unknown_a1 = decode_sjis(cmd.unknown_a1);
@@ -802,7 +832,7 @@ void process_menu_selection(shared_ptr<ServerState> s, shared_ptr<Client> c,
send_update_client_config(c);
} else {
static const vector<string> version_to_port_name({
"dc-lobby", "pc-lobby", "bb-lobby", "gc-lobby", "bb-lobby"});
"dc-lobby", "pc-lobby", "bb-lobby", "gc-lobby", "xb-lobby", "bb-lobby"});
const auto& port_name = version_to_port_name.at(static_cast<size_t>(c->version));
send_reconnect(c, s->connect_address_for_client(c),
s->name_to_port_config.at(port_name)->port);
@@ -899,7 +929,7 @@ void process_menu_selection(shared_ptr<ServerState> s, shared_ptr<Client> c,
// license/char name/etc. for remote auth)
static const vector<string> version_to_port_name({
"dc-proxy", "pc-proxy", "", "gc-proxy", "bb-proxy"});
"dc-proxy", "pc-proxy", "", "gc-proxy", "xb-proxy", "bb-proxy"});
const auto& port_name = version_to_port_name.at(static_cast<size_t>(c->version));
uint16_t local_port = s->name_to_port_config.at(port_name)->port;
@@ -1038,10 +1068,11 @@ void process_menu_selection(shared_ptr<ServerState> s, shared_ptr<Client> c,
continue;
}
// TODO: It looks like blasting all the chunks to the client at once can
// cause GC clients to crash in rare cases. Find a way to slow this down
// (perhaps by only sending each new chunk when they acknowledge the
// previous chunk with a 44 [first chunk] or 13 [later chunks] command).
// TODO: It looks like blasting all the chunks to the client at once
// can cause GC clients to crash in rare cases. Find a way to slow
// this down (perhaps by only sending each new chunk when they
// acknowledge the previous chunk with a 44 [first chunk] or 13 [later
// chunks] command).
send_quest_file(l->clients[x], bin_basename + ".bin", bin_basename,
*bin_contents, QuestFileType::ONLINE);
send_quest_file(l->clients[x], dat_basename + ".dat", dat_basename,
@@ -1186,7 +1217,7 @@ void process_change_ship(shared_ptr<ServerState> s, shared_ptr<Client> c,
send_message_box(c, u"");
static const vector<string> version_to_port_name({
"dc-login", "pc-login", "bb-patch", "gc-us3", "bb-init"});
"dc-login", "pc-login", "bb-patch", "gc-us3", "xb-login", "bb-init"});
const auto& port_name = version_to_port_name.at(static_cast<size_t>(c->version));
send_reconnect(c, s->connect_address_for_client(c),
@@ -1377,18 +1408,19 @@ void process_player_data(shared_ptr<ServerState> s, shared_ptr<Client> c,
c->game_data.import_player(pd);
break;
}
case GameVersion::GC: {
const PSOPlayerDataGC* pd;
case GameVersion::GC:
case GameVersion::XB: {
const PSOPlayerDataV3* pd;
if (flag == 4) { // Episode 3
if (!(c->flags & Client::Flag::EPISODE_3)) {
throw runtime_error("non-Episode 3 client sent Episode 3 player data");
}
const auto* pd3 = &check_size_t<PSOPlayerDataGCEp3>(data);
c->game_data.ep3_config.reset(new Ep3Config(pd3->ep3_config));
pd = reinterpret_cast<const PSOPlayerDataGC*>(pd3);
pd = reinterpret_cast<const PSOPlayerDataV3*>(pd3);
} else {
pd = &check_size_t<PSOPlayerDataGC>(data, sizeof(PSOPlayerDataGC),
sizeof(PSOPlayerDataGC) + c->game_data.player()->auto_reply.bytes());
pd = &check_size_t<PSOPlayerDataV3>(data, sizeof(PSOPlayerDataV3),
sizeof(PSOPlayerDataV3) + c->game_data.player()->auto_reply.bytes());
}
c->game_data.import_player(*pd);
break;
@@ -1514,10 +1546,10 @@ void process_chat_pc_bb(shared_ptr<ServerState> s, shared_ptr<Client> c,
process_chat_generic(s, c, text);
}
void process_chat_dc_gc(shared_ptr<ServerState> s, shared_ptr<Client> c,
void process_chat_dc_v3(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t, uint32_t, const string& data) {
const auto& cmd = check_size_t<C_Chat_06>(data, sizeof(C_Chat_06), 0xFFFF);
u16string decoded_s = decode_sjis(cmd.text.dcgc, data.size() - sizeof(C_Chat_06));
u16string decoded_s = decode_sjis(cmd.text.dcv3, data.size() - sizeof(C_Chat_06));
process_chat_generic(s, c, decoded_s);
}
@@ -1722,13 +1754,13 @@ void process_choice_search(shared_ptr<ServerState>, shared_ptr<Client> c,
void process_simple_mail(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t, uint32_t, const string& data) { // 81
if (c->version != GameVersion::GC) {
// TODO: implement this for DC, PC, BB
if ((c->version != GameVersion::GC) && (c->version != GameVersion::XB)) {
// TODO: implement this for non-V3
send_text_message(c, u"$C6Simple Mail is not\nsupported yet on\nthis platform.");
return;
}
const auto& cmd = check_size_t<SC_SimpleMail_GC_81>(data);
const auto& cmd = check_size_t<SC_SimpleMail_V3_81>(data);
auto target = s->find_client(nullptr, cmd.to_guild_card_number);
@@ -1788,7 +1820,7 @@ void process_disable_auto_reply(shared_ptr<ServerState>, shared_ptr<Client> c,
void process_set_blocked_senders_list(shared_ptr<ServerState>, shared_ptr<Client> c,
uint16_t, uint32_t, const string& data) { // C6
const auto& cmd = check_size_t<C_SetBlockedSenders_GC_BB_C6>(data);
const auto& cmd = check_size_t<C_SetBlockedSenders_V3_BB_C6>(data);
c->game_data.account()->blocked_senders = cmd.blocked_senders;
}
@@ -1957,9 +1989,9 @@ void process_create_game_pc(shared_ptr<ServerState> s, shared_ptr<Client> c,
cmd.difficulty, cmd.battle_mode, cmd.challenge_mode, 0);
}
void process_create_game_dc_gc(shared_ptr<ServerState> s, shared_ptr<Client> c,
void process_create_game_dc_v3(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t command, uint32_t, const string& data) { // C1 EC (EC Ep3 only)
const auto& cmd = check_size_t<C_CreateGame_DC_GC_C1_EC>(data);
const auto& cmd = check_size_t<C_CreateGame_DC_V3_C1_Ep3_EC>(data);
// only allow EC from Ep3 clients
bool client_is_ep3 = c->flags & Client::Flag::EPISODE_3;
@@ -1968,7 +2000,7 @@ void process_create_game_dc_gc(shared_ptr<ServerState> s, shared_ptr<Client> c,
}
uint8_t episode = cmd.episode;
if (c->version == GameVersion::DC) {
if ((c->version == GameVersion::DC) || (c->version == GameVersion::PC)) {
episode = 1;
}
if (client_is_ep3) {
@@ -2212,7 +2244,7 @@ typedef void (*process_command_t)(shared_ptr<ServerState> s, shared_ptr<Client>
static process_command_t dc_handlers[0x100] = {
// 00
nullptr, nullptr, nullptr, nullptr,
nullptr, process_ignored_command, process_chat_dc_gc, nullptr,
nullptr, process_ignored_command, process_chat_dc_v3, nullptr,
process_game_list_request, process_menu_item_info_request, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr,
@@ -2273,7 +2305,7 @@ static process_command_t dc_handlers[0x100] = {
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
// C0
nullptr, process_create_game_dc_gc, nullptr, nullptr,
nullptr, process_create_game_dc_v3, nullptr, nullptr,
nullptr, nullptr, process_set_blocked_senders_list, process_set_auto_reply_t<char>,
process_disable_auto_reply, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr,
@@ -2341,8 +2373,8 @@ static process_command_t pc_handlers[0x100] = {
// 90
nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, process_client_checksum, nullptr,
process_player_data, process_ignored_command, process_login_a_dc_pc_gc, nullptr,
process_login_c_dc_pc_gc, process_login_d_e_pc_gc, process_login_d_e_pc_gc, nullptr,
process_player_data, process_ignored_command, process_login_a_dc_pc_v3, nullptr,
process_login_c_dc_pc_v3, process_login_d_e_pc_v3, process_login_d_e_pc_v3, nullptr,
// A0
process_change_ship, process_change_block, process_quest_list_request, nullptr,
@@ -2378,7 +2410,7 @@ static process_command_t pc_handlers[0x100] = {
static process_command_t gc_handlers[0x100] = {
// 00
nullptr, nullptr, nullptr, nullptr,
nullptr, process_ignored_command, process_chat_dc_gc, nullptr,
nullptr, process_ignored_command, process_chat_dc_v3, nullptr,
process_game_list_request, process_menu_item_info_request, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr,
@@ -2426,7 +2458,7 @@ static process_command_t gc_handlers[0x100] = {
nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, process_client_checksum, nullptr,
process_player_data, process_ignored_command, nullptr, nullptr,
process_login_c_dc_pc_gc, process_login_d_e_pc_gc, process_login_d_e_pc_gc, process_return_client_config,
process_login_c_dc_pc_v3, process_login_d_e_pc_v3, process_login_d_e_pc_v3, process_return_client_config,
// A0
process_change_ship, process_change_block, process_quest_list_request, nullptr,
@@ -2441,7 +2473,7 @@ static process_command_t gc_handlers[0x100] = {
nullptr, nullptr, nullptr, nullptr,
// C0
process_choice_search, process_create_game_dc_gc, nullptr, nullptr,
process_choice_search, process_create_game_dc_v3, nullptr, nullptr,
nullptr, nullptr, process_set_blocked_senders_list, process_set_auto_reply_t<char>,
process_disable_auto_reply, process_game_command, process_ep3_server_data_request, process_game_command,
nullptr, nullptr, nullptr, nullptr,
@@ -2449,14 +2481,100 @@ static process_command_t gc_handlers[0x100] = {
// D0
process_trade_start, nullptr, process_trade_execute, nullptr,
process_trade_error, nullptr, process_message_box_closed, process_gba_file_request,
process_info_board_request, process_write_info_board_t<char>, nullptr, process_verify_license_gc,
process_info_board_request, process_write_info_board_t<char>, nullptr, process_verify_license_v3,
process_ep3_menu_challenge, nullptr, nullptr, nullptr,
// E0
nullptr, nullptr, process_ep3_tournament_control, nullptr,
process_ignored_command, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr,
process_create_game_dc_gc, nullptr, nullptr, nullptr,
process_create_game_dc_v3, nullptr, nullptr, nullptr,
// F0
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
};
static process_command_t xb_handlers[0x100] = {
// 00
nullptr, nullptr, nullptr, nullptr,
nullptr, process_ignored_command, process_chat_dc_v3, nullptr,
process_game_list_request, process_menu_item_info_request, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr,
// 10
process_menu_selection, nullptr, nullptr, process_ignored_command,
nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr,
nullptr, process_ignored_command, nullptr, nullptr,
// 20
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
// 30
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
// 40
process_card_search, nullptr, nullptr, nullptr,
process_ignored_command, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr,
// 50
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
// 60
process_game_command, process_player_data, process_game_command, nullptr,
nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr,
process_game_command, process_game_command, nullptr, process_client_ready,
// 70
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
// 80
nullptr, process_simple_mail, nullptr, nullptr,
process_change_lobby, nullptr, nullptr, nullptr,
nullptr, process_change_arrow_color, process_lobby_name_request, nullptr,
nullptr, nullptr, nullptr, nullptr,
// 90
nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, process_client_checksum, nullptr,
process_player_data, process_ignored_command, nullptr, nullptr,
process_login_c_dc_pc_v3, process_login_d_e_pc_v3, process_login_d_e_pc_v3, process_return_client_config,
// A0
process_change_ship, process_change_block, process_quest_list_request, nullptr,
nullptr, nullptr, process_ignored_command, process_ignored_command,
nullptr, process_ignored_command, process_update_quest_statistics, nullptr,
process_quest_barrier, nullptr, nullptr, nullptr,
// B0
nullptr, process_server_time_request, nullptr, process_function_call_result,
nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr,
// C0
process_choice_search, process_create_game_dc_v3, nullptr, nullptr,
nullptr, nullptr, process_set_blocked_senders_list, process_set_auto_reply_t<char>,
process_disable_auto_reply, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr,
// D0
process_trade_start, nullptr, process_trade_execute, nullptr,
process_trade_error, nullptr, process_message_box_closed, process_gba_file_request,
process_info_board_request, process_write_info_board_t<char>, nullptr, process_verify_license_v3,
nullptr, nullptr, nullptr, nullptr,
// E0
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
// F0
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
@@ -2606,7 +2724,7 @@ static process_command_t patch_handlers[0x100] = {
};
static process_command_t* handlers[6] = {
dc_handlers, pc_handlers, patch_handlers, gc_handlers, bb_handlers};
dc_handlers, pc_handlers, patch_handlers, gc_handlers, xb_handlers, bb_handlers};
void process_command(shared_ptr<ServerState> s, shared_ptr<Client> c,
uint16_t command, uint32_t flag, const string& data) {
+2 -2
View File
@@ -117,8 +117,8 @@ static void process_subcommand_send_guild_card(shared_ptr<ServerState>,
if (c->version == GameVersion::PC) {
const auto* cmd = check_size_sc<G_SendGuildCard_PC_6x06>(data);
c->game_data.player()->guild_card_desc = cmd->desc;
} else if (c->version == GameVersion::GC) {
const auto* cmd = check_size_sc<G_SendGuildCard_GC_6x06>(data);
} else if ((c->version == GameVersion::GC) || (c->version == GameVersion::XB)) {
const auto* cmd = check_size_sc<G_SendGuildCard_V3_6x06>(data);
c->game_data.player()->guild_card_desc = cmd->desc;
} else if (c->version == GameVersion::BB) {
const auto* cmd = check_size_sc<G_SendGuildCard_BB_6x06>(data);
+22 -23
View File
@@ -62,22 +62,23 @@ void ReplaySession::apply_default_mask(shared_ptr<Event> ev) {
break;
}
case GameVersion::PC:
case GameVersion::GC: {
case GameVersion::GC:
case GameVersion::XB: {
uint8_t command;
if (version == GameVersion::PC) {
command = check_size_t<PSOCommandHeaderPC>(
ev->data, sizeof(PSOCommandHeaderPC), 0xFFFF).command;
} else {
command = check_size_t<PSOCommandHeaderDCGC>(
ev->data, sizeof(PSOCommandHeaderDCGC), 0xFFFF).command;
} else { // V3
command = check_size_t<PSOCommandHeaderDCV3>(
ev->data, sizeof(PSOCommandHeaderDCV3), 0xFFFF).command;
}
switch (command) {
case 0x02:
case 0x17:
case 0x91:
case 0x9B: {
auto& cmd_mask = check_size_t<S_ServerInit_DC_PC_GC_02_17_91_9B>(
cmd_data, cmd_size, sizeof(S_ServerInit_DC_PC_GC_02_17_91_9B), 0xFFFF);
auto& cmd_mask = check_size_t<S_ServerInit_DC_PC_V3_02_17_91_9B>(
cmd_data, cmd_size, sizeof(S_ServerInit_DC_PC_V3_02_17_91_9B), 0xFFFF);
cmd_mask.server_key = 0;
cmd_mask.client_key = 0;
break;
@@ -90,13 +91,12 @@ void ReplaySession::apply_default_mask(shared_ptr<Event> ev) {
}
case 0x64: {
if (version == GameVersion::PC) {
auto& cmd_mask = check_size_t<S_JoinGame_PC_64>(cmd_data, cmd_size,
offsetof(S_JoinGame_GC_64, players_ep3));
auto& cmd_mask = check_size_t<S_JoinGame_PC_64>(cmd_data, cmd_size);
cmd_mask.variations.clear(0);
cmd_mask.rare_seed = 0;
} else { // GC
} else { // V3
auto& cmd_mask = check_size_t<S_JoinGame_GC_64>(cmd_data, cmd_size,
offsetof(S_JoinGame_GC_64, players_ep3));
sizeof(S_JoinGame_GC_64), sizeof(S_JoinGame_GC_Ep3_64));
cmd_mask.variations.clear(0);
cmd_mask.rare_seed = 0;
}
@@ -128,9 +128,7 @@ void ReplaySession::apply_default_mask(shared_ptr<Event> ev) {
break;
}
case 0x0064: {
auto& cmd_mask = check_size_t<S_JoinGame_BB_64>(cmd_data, cmd_size,
offsetof(S_JoinGame_BB_64, players_ep3),
offsetof(S_JoinGame_BB_64, players_ep3));
auto& cmd_mask = check_size_t<S_JoinGame_BB_64>(cmd_data, cmd_size);
cmd_mask.variations.clear(0);
cmd_mask.rare_seed = 0;
break;
@@ -422,21 +420,22 @@ void ReplaySession::on_command_received(
case GameVersion::PATCH:
if (command == 0x02) {
auto& cmd = check_size_t<S_ServerInit_Patch_02>(data);
c->channel.crypt_in.reset(new PSOPCEncryption(cmd.server_key));
c->channel.crypt_out.reset(new PSOPCEncryption(cmd.client_key));
c->channel.crypt_in.reset(new PSOV2Encryption(cmd.server_key));
c->channel.crypt_out.reset(new PSOV2Encryption(cmd.client_key));
}
break;
case GameVersion::PC:
case GameVersion::GC:
case GameVersion::XB:
if (command == 0x02 || command == 0x17 || command == 0x91 || command == 0x9B) {
auto& cmd = check_size_t<S_ServerInit_DC_PC_GC_02_17_91_9B>(data,
offsetof(S_ServerInit_DC_PC_GC_02_17_91_9B, after_message), 0xFFFF);
if (c->version == GameVersion::GC) {
c->channel.crypt_in.reset(new PSOGCEncryption(cmd.server_key));
c->channel.crypt_out.reset(new PSOGCEncryption(cmd.client_key));
} else {
c->channel.crypt_in.reset(new PSOPCEncryption(cmd.server_key));
c->channel.crypt_out.reset(new PSOPCEncryption(cmd.client_key));
auto& cmd = check_size_t<S_ServerInit_DC_PC_V3_02_17_91_9B>(data,
offsetof(S_ServerInit_DC_PC_V3_02_17_91_9B, after_message), 0xFFFF);
if (c->version == GameVersion::PC) {
c->channel.crypt_in.reset(new PSOV2Encryption(cmd.server_key));
c->channel.crypt_out.reset(new PSOV2Encryption(cmd.client_key));
} else { // V3
c->channel.crypt_in.reset(new PSOV3Encryption(cmd.server_key));
c->channel.crypt_out.reset(new PSOV3Encryption(cmd.client_key));
}
}
break;
+124 -101
View File
@@ -61,9 +61,10 @@ void send_command_with_header_t(Channel& ch, const void* data, size_t size) {
void send_command_with_header(Channel& ch, const void* data, size_t size) {
switch (ch.version) {
case GameVersion::GC:
case GameVersion::DC:
send_command_with_header_t<PSOCommandHeaderDCGC>(ch, data, size);
case GameVersion::GC:
case GameVersion::XB:
send_command_with_header_t<PSOCommandHeaderDCV3>(ch, data, size);
break;
case GameVersion::PC:
case GameVersion::PATCH:
@@ -79,15 +80,6 @@ void send_command_with_header(Channel& ch, const void* data, size_t size) {
// specific command sending functions follow. in general, they're written in
// such a way that you don't need to think about anything, even the client's
// version, before calling them. for this reason, some of them are quite
// complex. many are split into several functions, one for each version of PSO,
// named with suffixes _GC, _BB, and the like. in these cases, the function
// without the suffix simply calls the appropriate function for the client's
// version. thus, if you change something in one of the version-specific
// functions, you may have to change it in all of them.
////////////////////////////////////////////////////////////////////////////////
// CommandServerInit: this function sends the command that initializes encryption
@@ -99,11 +91,11 @@ static const char* bb_game_server_copyright = "Phantasy Star Online Blue Burst G
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";
S_ServerInit_DC_PC_GC_02_17_91_9B prepare_server_init_contents_dc_pc_gc(
S_ServerInit_DC_PC_V3_02_17_91_9B prepare_server_init_contents_dc_pc_v3(
bool initial_connection,
uint32_t server_key,
uint32_t client_key) {
S_ServerInit_DC_PC_GC_02_17_91_9B cmd;
S_ServerInit_DC_PC_V3_02_17_91_9B cmd;
cmd.copyright = initial_connection
? dc_port_map_copyright : dc_lobby_server_copyright;
cmd.server_key = server_key;
@@ -112,25 +104,26 @@ S_ServerInit_DC_PC_GC_02_17_91_9B prepare_server_init_contents_dc_pc_gc(
return cmd;
}
void send_server_init_dc_pc_gc(shared_ptr<Client> c,
void send_server_init_dc_pc_v3(shared_ptr<Client> c,
bool initial_connection) {
uint8_t command = initial_connection ? 0x17 : 0x02;
uint32_t server_key = random_object<uint32_t>();
uint32_t client_key = random_object<uint32_t>();
auto cmd = prepare_server_init_contents_dc_pc_gc(
auto cmd = prepare_server_init_contents_dc_pc_v3(
initial_connection, server_key, client_key);
send_command_t(c, command, 0x00, cmd);
switch (c->version) {
case GameVersion::DC:
case GameVersion::PC:
c->channel.crypt_out.reset(new PSOPCEncryption(server_key));
c->channel.crypt_in.reset(new PSOPCEncryption(client_key));
c->channel.crypt_out.reset(new PSOV2Encryption(server_key));
c->channel.crypt_in.reset(new PSOV2Encryption(client_key));
break;
case GameVersion::GC:
c->channel.crypt_out.reset(new PSOGCEncryption(server_key));
c->channel.crypt_in.reset(new PSOGCEncryption(client_key));
case GameVersion::XB:
c->channel.crypt_out.reset(new PSOV3Encryption(server_key));
c->channel.crypt_in.reset(new PSOV3Encryption(client_key));
break;
default:
throw invalid_argument("incorrect client version");
@@ -180,8 +173,8 @@ void send_server_init_patch(shared_ptr<Client> c) {
cmd.client_key = client_key;
send_command_t(c, 0x02, 0x00, cmd);
c->channel.crypt_out.reset(new PSOPCEncryption(server_key));
c->channel.crypt_in.reset(new PSOPCEncryption(client_key));
c->channel.crypt_out.reset(new PSOV2Encryption(server_key));
c->channel.crypt_in.reset(new PSOV2Encryption(client_key));
}
void send_server_init(shared_ptr<ServerState> s, shared_ptr<Client> c,
@@ -190,7 +183,8 @@ void send_server_init(shared_ptr<ServerState> s, shared_ptr<Client> c,
case GameVersion::DC:
case GameVersion::PC:
case GameVersion::GC:
send_server_init_dc_pc_gc(c, initial_connection);
case GameVersion::XB:
send_server_init_dc_pc_v3(c, initial_connection);
break;
case GameVersion::PATCH:
send_server_init_patch(c);
@@ -207,7 +201,7 @@ void send_server_init(shared_ptr<ServerState> s, shared_ptr<Client> c,
// for non-BB clients, updates the client's guild card and security data
void send_update_client_config(shared_ptr<Client> c) {
S_UpdateClientConfig_DC_PC_GC_04 cmd;
S_UpdateClientConfig_DC_PC_V3_04 cmd;
cmd.player_tag = 0x00010000;
cmd.guild_card_number = c->license->serial_number;
cmd.cfg = c->export_config();
@@ -236,31 +230,33 @@ void send_function_call(
string data;
uint32_t index = 0;
if (code.get()) {
// TODO: If we end up supporting B2 on non-GC platforms, we have to rework
// generate_client_command to be able to generate little-endian footers.
data = code->generate_client_command(label_writes, suffix);
index = code->index;
}
if (c->flags & Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL) {
uint32_t key = random_object<uint32_t>();
if (c->flags & Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL) {
uint32_t key = random_object<uint32_t>();
StringWriter w;
w.put_u32b(data.size());
w.put_u32b(key);
StringWriter w;
w.put_u32b(data.size());
w.put_u32b(key);
// Round size up to a multiple of 4 for encryption
data.resize((data.size() + 3) & ~3);
// Round size up to a multiple of 4 for encryption
data.resize((data.size() + 3) & ~3);
// For this format, the code section is decrypted without byteswapping on
// the client (GameCube), which is big-endian, so we have to treat the data
// the same way here (hence we can't just use crypt.encrypt).
data = prs_compress(data);
StringReader compressed_r(data);
PSOPCEncryption crypt(key);
while (!compressed_r.eof()) {
w.put_u32b(compressed_r.get_u32b() ^ crypt.next());
// For this format, the code section is decrypted without byteswapping on
// the client (GameCube), which is big-endian, so we have to treat the data
// the same way here (hence we can't just use crypt.encrypt).
data = prs_compress(data);
StringReader compressed_r(data);
PSOV2Encryption crypt(key);
while (!compressed_r.eof()) {
w.put_u32b(compressed_r.get_u32b() ^ crypt.next());
}
data = move(w.str());
}
data = move(w.str());
}
S_ExecuteCode_B2 header = {data.size(), checksum_addr, checksum_size};
@@ -462,7 +458,9 @@ void send_enter_directory_patch(shared_ptr<Client> c, const string& dir) {
void send_text(Channel& ch, StringWriter& w, uint16_t command,
const u16string& text, bool should_add_color) {
if ((ch.version == GameVersion::DC) || (ch.version == GameVersion::GC)) {
if ((ch.version == GameVersion::DC) ||
(ch.version == GameVersion::GC) ||
(ch.version == GameVersion::XB)) {
string data = encode_sjis(text);
if (should_add_color) {
add_color(w, data.c_str(), data.size());
@@ -558,9 +556,9 @@ void send_chat_message(shared_ptr<Client> c, uint32_t from_guild_card_number,
send_header_text(c->channel, 0x06, from_guild_card_number, data, false);
}
void send_simple_mail_gc(shared_ptr<Client> c, uint32_t from_guild_card_number,
void send_simple_mail_v3(shared_ptr<Client> c, uint32_t from_guild_card_number,
const u16string& from_name, const u16string& text) {
SC_SimpleMail_GC_81 cmd;
SC_SimpleMail_V3_81 cmd;
cmd.player_tag = 0x00010000;
cmd.from_guild_card_number = from_guild_card_number;
cmd.from_name = from_name;
@@ -571,8 +569,8 @@ void send_simple_mail_gc(shared_ptr<Client> c, uint32_t from_guild_card_number,
void send_simple_mail(shared_ptr<Client> c, uint32_t from_guild_card_number,
const u16string& from_name, const u16string& text) {
if (c->version == GameVersion::GC) {
send_simple_mail_gc(c, from_guild_card_number, from_name, text);
if ((c->version == GameVersion::GC) || (c->version == GameVersion::XB)) {
send_simple_mail_v3(c, from_guild_card_number, from_name, text);
} else {
throw logic_error("unimplemented versioned command");
}
@@ -619,7 +617,7 @@ void send_card_search_result_t(
shared_ptr<Client> result,
shared_ptr<Lobby> result_lobby) {
static const vector<string> version_to_port_name({
"dc-lobby", "pc-lobby", "bb-lobby", "gc-lobby", "bb-lobby"});
"dc-lobby", "pc-lobby", "bb-lobby", "gc-lobby", "xb-lobby", "bb-lobby"});
const auto& port_name = version_to_port_name.at(static_cast<size_t>(c->version));
S_GuildCardSearchResult<CommandHeaderT, CharT> cmd;
@@ -659,8 +657,10 @@ void send_card_search_result(
shared_ptr<Client> c,
shared_ptr<Client> result,
shared_ptr<Lobby> result_lobby) {
if ((c->version == GameVersion::DC) || (c->version == GameVersion::GC)) {
send_card_search_result_t<PSOCommandHeaderDCGC, char>(
if ((c->version == GameVersion::DC) ||
(c->version == GameVersion::GC) ||
(c->version == GameVersion::XB)) {
send_card_search_result_t<PSOCommandHeaderDCV3, char>(
s, c, result, result_lobby);
} else if (c->version == GameVersion::PC) {
send_card_search_result_t<PSOCommandHeaderPC, char16_t>(
@@ -676,7 +676,7 @@ void send_card_search_result(
template <typename CmdT>
void send_guild_card_pc_gc(shared_ptr<Client> c, shared_ptr<Client> source) {
void send_guild_card_pc_v3(shared_ptr<Client> c, shared_ptr<Client> source) {
CmdT cmd;
cmd.subcommand = 0x06;
cmd.size = sizeof(CmdT) / 4;
@@ -711,9 +711,10 @@ void send_guild_card_bb(shared_ptr<Client> c, shared_ptr<Client> source) {
void send_guild_card(shared_ptr<Client> c, shared_ptr<Client> source) {
if (c->version == GameVersion::PC) {
send_guild_card_pc_gc<G_SendGuildCard_PC_6x06>(c, source);
} else if (c->version == GameVersion::GC) {
send_guild_card_pc_gc<G_SendGuildCard_GC_6x06>(c, source);
send_guild_card_pc_v3<G_SendGuildCard_PC_6x06>(c, source);
} else if ((c->version == GameVersion::GC) ||
(c->version == GameVersion::XB)) {
send_guild_card_pc_v3<G_SendGuildCard_V3_6x06>(c, source);
} else if (c->version == GameVersion::BB) {
send_guild_card_bb(c, source);
} else {
@@ -747,6 +748,7 @@ void send_menu_t(
if (((c->version == GameVersion::DC) && (item.flags & MenuItem::Flag::INVISIBLE_ON_DC)) ||
((c->version == GameVersion::PC) && (item.flags & MenuItem::Flag::INVISIBLE_ON_PC)) ||
((c->version == GameVersion::GC) && (item.flags & MenuItem::Flag::INVISIBLE_ON_GC)) ||
((c->version == GameVersion::XB) && (item.flags & MenuItem::Flag::INVISIBLE_ON_XB)) ||
((c->version == GameVersion::BB) && (item.flags & MenuItem::Flag::INVISIBLE_ON_BB)) ||
((item.flags & MenuItem::Flag::REQUIRES_MESSAGE_BOXES) && (c->flags & Client::Flag::NO_MESSAGE_BOX_CLOSE_CONFIRMATION)) ||
((item.flags & MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL) && (c->flags & Client::Flag::DOES_NOT_SUPPORT_SEND_FUNCTION_CALL)) ||
@@ -769,7 +771,7 @@ void send_menu(shared_ptr<Client> c, const u16string& menu_name,
c->version == GameVersion::BB) {
send_menu_t<S_MenuEntry_PC_BB_07_1F>(c, menu_name, menu_id, items, is_info_menu);
} else {
send_menu_t<S_MenuEntry_DC_GC_07_1F>(c, menu_name, menu_id, items, is_info_menu);
send_menu_t<S_MenuEntry_DC_V3_07_1F>(c, menu_name, menu_id, items, is_info_menu);
}
}
@@ -817,7 +819,9 @@ void send_game_menu_t(shared_ptr<Client> c, shared_ptr<ServerState> s) {
}
void send_game_menu(shared_ptr<Client> c, shared_ptr<ServerState> s) {
if ((c->version == GameVersion::DC) || (c->version == GameVersion::GC)) {
if ((c->version == GameVersion::DC) ||
(c->version == GameVersion::GC) ||
(c->version == GameVersion::XB)) {
send_game_menu_t<char>(c, s);
} else {
send_game_menu_t<char16_t>(c, s);
@@ -868,6 +872,8 @@ void send_quest_menu(shared_ptr<Client> c, uint32_t menu_id,
send_quest_menu_t<S_QuestMenuEntry_PC_A2_A4>(c, menu_id, quests, is_download_menu);
} else if (c->version == GameVersion::GC) {
send_quest_menu_t<S_QuestMenuEntry_GC_A2_A4>(c, menu_id, quests, is_download_menu);
} else if (c->version == GameVersion::XB) {
send_quest_menu_t<S_QuestMenuEntry_XB_A2_A4>(c, menu_id, quests, is_download_menu);
} else if (c->version == GameVersion::BB) {
send_quest_menu_t<S_QuestMenuEntry_BB_A2_A4>(c, menu_id, quests, is_download_menu);
} else {
@@ -881,6 +887,8 @@ void send_quest_menu(shared_ptr<Client> c, uint32_t menu_id,
send_quest_menu_t<S_QuestMenuEntry_PC_A2_A4>(c, menu_id, items, is_download_menu);
} else if (c->version == GameVersion::GC) {
send_quest_menu_t<S_QuestMenuEntry_GC_A2_A4>(c, menu_id, items, is_download_menu);
} else if (c->version == GameVersion::XB) {
send_quest_menu_t<S_QuestMenuEntry_XB_A2_A4>(c, menu_id, items, is_download_menu);
} else if (c->version == GameVersion::BB) {
send_quest_menu_t<S_QuestMenuEntry_BB_A2_A4>(c, menu_id, items, is_download_menu);
} else {
@@ -917,47 +925,55 @@ void send_lobby_list(shared_ptr<Client> c, shared_ptr<ServerState> s) {
template <typename LobbyDataT, typename DispDataT>
void send_join_game_t(shared_ptr<Client> c, shared_ptr<Lobby> l) {
S_JoinGame<LobbyDataT, DispDataT> cmd;
bool is_ep3 = (l->flags & Lobby::Flag::EPISODE_3_ONLY);
string data(is_ep3 ? sizeof(S_JoinGame_GC_Ep3_64) : sizeof(S_JoinGame<LobbyDataT, DispDataT>), '\0');
cmd.variations = l->variations;
// TODO: This is a terrible way to handle the different Ep3 format within the
// template. Find a way to make this cleaner.
auto* cmd = reinterpret_cast<S_JoinGame<LobbyDataT, DispDataT>*>(data.data());
S_JoinGame_GC_Ep3_64* cmd_ep3 = nullptr;
if (is_ep3) {
cmd_ep3 = reinterpret_cast<S_JoinGame_GC_Ep3_64*>(data.data());
new (cmd_ep3) S_JoinGame_GC_Ep3_64();
} else {
new (cmd) S_JoinGame<LobbyDataT, DispDataT>();
}
cmd->variations = l->variations;
size_t player_count = 0;
for (size_t x = 0; x < 4; x++) {
if (l->clients[x]) {
cmd.lobby_data[x].player_tag = 0x00010000;
cmd.lobby_data[x].guild_card = l->clients[x]->license->serial_number;
// See comment in send_join_lobby_t about Episode III behavior here
cmd.lobby_data[x].ip_address = 0x7F000001;
cmd.lobby_data[x].client_id = c->lobby_client_id;
cmd.lobby_data[x].name = l->clients[x]->game_data.player()->disp.name;
if (l->flags & Lobby::Flag::EPISODE_3_ONLY) {
cmd.players_ep3[x].inventory = l->clients[x]->game_data.player()->inventory;
cmd.players_ep3[x].disp = convert_player_disp_data<DispDataT>(
cmd->lobby_data[x].player_tag = 0x00010000;
cmd->lobby_data[x].guild_card = l->clients[x]->license->serial_number;
cmd->lobby_data[x].client_id = c->lobby_client_id;
cmd->lobby_data[x].name = l->clients[x]->game_data.player()->disp.name;
if (cmd_ep3) {
cmd_ep3->players_ep3[x].inventory = l->clients[x]->game_data.player()->inventory;
cmd_ep3->players_ep3[x].disp = convert_player_disp_data<PlayerDispDataPCV3>(
l->clients[x]->game_data.player()->disp);
}
player_count++;
} else {
cmd->lobby_data[x].clear();
}
}
cmd.client_id = c->lobby_client_id;
cmd.leader_id = l->leader_id;
cmd.disable_udp = 0x01; // TODO: This is unused on PC/BB. Is it OK to use 1 here anyway?
cmd.difficulty = l->difficulty;
cmd.battle_mode = (l->mode == 1) ? 1 : 0;
cmd.event = l->event;
cmd.section_id = l->section_id;
cmd.challenge_mode = (l->mode == 2) ? 1 : 0;
cmd.rare_seed = l->random_seed;
cmd.episode = l->episode;
cmd.unused2 = 0x01;
cmd.solo_mode = (l->mode == 3);
cmd.unused3 = 0x00;
cmd->client_id = c->lobby_client_id;
cmd->leader_id = l->leader_id;
cmd->disable_udp = 0x01; // Unused on PC/XB/BB
cmd->difficulty = l->difficulty;
cmd->battle_mode = (l->mode == 1) ? 1 : 0;
cmd->event = l->event;
cmd->section_id = l->section_id;
cmd->challenge_mode = (l->mode == 2) ? 1 : 0;
cmd->rare_seed = l->random_seed;
cmd->episode = l->episode;
cmd->unused2 = 0x01;
cmd->solo_mode = (l->mode == 3);
cmd->unused3 = 0x00;
// Player data is only sent in Episode III games; in other versions, the
// players send each other their data using 62/6D commands during loading
size_t data_size = (l->flags & Lobby::Flag::EPISODE_3_ONLY)
? sizeof(cmd) : (sizeof(cmd) - sizeof(cmd.players_ep3));
send_command(c, 0x64, player_count, &cmd, data_size);
send_command(c, 0x64, player_count, data);
}
template <typename LobbyDataT, typename DispDataT>
@@ -975,7 +991,8 @@ void send_join_lobby_t(shared_ptr<Client> c, shared_ptr<Lobby> l,
}
uint8_t lobby_type = (l->type > 14) ? (l->block - 1) : l->type;
// Allow non-canonical lobby types on GC
// Allow non-canonical lobby types on GC. They may work on other versions too,
// but I haven't verified which values don't crash on each version.
if (c->version == GameVersion::GC) {
if (c->flags & Client::Flag::EPISODE_3) {
if ((l->type > 0x14) && (l->type < 0xE9)) {
@@ -998,8 +1015,9 @@ void send_join_lobby_t(shared_ptr<Client> c, shared_ptr<Lobby> l,
cmd.disable_udp = 0x01;
cmd.lobby_number = lobby_type;
cmd.block_number = l->block;
cmd.unknown_a1 = 0;
cmd.event = l->event;
cmd.unused = 0x00000000;
cmd.unknown_a2 = 0;
vector<shared_ptr<Client>> lobby_clients;
if (joining_client) {
@@ -1017,11 +1035,6 @@ void send_join_lobby_t(shared_ptr<Client> c, shared_ptr<Lobby> l,
auto& e = cmd.entries[used_entries++];
e.lobby_data.player_tag = 0x00010000;
e.lobby_data.guild_card = lc->license->serial_number;
// There's a strange behavior (bug? "feature"?) in Episode 3 where the start
// button does nothing in the lobby (hence you can't "quit game") if the
// client's IP address is zero. So, we fill it in with a fake nonzero value
// to avoid this behavior.
e.lobby_data.ip_address = 0x7F000001;
e.lobby_data.client_id = lc->lobby_client_id;
e.lobby_data.name = lc->game_data.player()->disp.name;
e.inventory = lc->game_data.player()->inventory;
@@ -1037,9 +1050,11 @@ void send_join_lobby_t(shared_ptr<Client> c, shared_ptr<Lobby> l,
void send_join_lobby(shared_ptr<Client> c, shared_ptr<Lobby> l) {
if (l->is_game()) {
if (c->version == GameVersion::PC) {
send_join_game_t<PlayerLobbyDataPC, PlayerDispDataPCGC>(c, l);
send_join_game_t<PlayerLobbyDataPC, PlayerDispDataPCV3>(c, l);
} else if (c->version == GameVersion::GC) {
send_join_game_t<PlayerLobbyDataGC, PlayerDispDataPCGC>(c, l);
send_join_game_t<PlayerLobbyDataGC, PlayerDispDataPCV3>(c, l);
} else if (c->version == GameVersion::XB) {
send_join_game_t<PlayerLobbyDataXB, PlayerDispDataPCV3>(c, l);
} else if (c->version == GameVersion::BB) {
send_join_game_t<PlayerLobbyDataBB, PlayerDispDataBB>(c, l);
} else {
@@ -1047,9 +1062,11 @@ void send_join_lobby(shared_ptr<Client> c, shared_ptr<Lobby> l) {
}
} else {
if (c->version == GameVersion::PC) {
send_join_lobby_t<PlayerLobbyDataPC, PlayerDispDataPCGC>(c, l);
send_join_lobby_t<PlayerLobbyDataPC, PlayerDispDataPCV3>(c, l);
} else if (c->version == GameVersion::GC) {
send_join_lobby_t<PlayerLobbyDataGC, PlayerDispDataPCGC>(c, l);
send_join_lobby_t<PlayerLobbyDataGC, PlayerDispDataPCV3>(c, l);
} else if (c->version == GameVersion::XB) {
send_join_lobby_t<PlayerLobbyDataXB, PlayerDispDataPCV3>(c, l);
} else if (c->version == GameVersion::BB) {
send_join_lobby_t<PlayerLobbyDataBB, PlayerDispDataBB>(c, l);
} else {
@@ -1069,9 +1086,11 @@ void send_join_lobby(shared_ptr<Client> c, shared_ptr<Lobby> l) {
void send_player_join_notification(shared_ptr<Client> c,
shared_ptr<Lobby> l, shared_ptr<Client> joining_client) {
if (c->version == GameVersion::PC) {
send_join_lobby_t<PlayerLobbyDataPC, PlayerDispDataPCGC>(c, l, joining_client);
send_join_lobby_t<PlayerLobbyDataPC, PlayerDispDataPCV3>(c, l, joining_client);
} else if (c->version == GameVersion::GC) {
send_join_lobby_t<PlayerLobbyDataGC, PlayerDispDataPCGC>(c, l, joining_client);
send_join_lobby_t<PlayerLobbyDataGC, PlayerDispDataPCV3>(c, l, joining_client);
} else if (c->version == GameVersion::XB) {
send_join_lobby_t<PlayerLobbyDataXB, PlayerDispDataPCV3>(c, l, joining_client);
} else if (c->version == GameVersion::BB) {
send_join_lobby_t<PlayerLobbyDataBB, PlayerDispDataBB>(c, l, joining_client);
} else {
@@ -1470,8 +1489,10 @@ void send_quest_file_chunk(
void send_quest_file(shared_ptr<Client> c, const string& quest_name,
const string& basename, const string& contents, QuestFileType type) {
if (c->version == GameVersion::PC || c->version == GameVersion::GC) {
send_quest_open_file_t<S_OpenFile_PC_GC_44_A6>(
if ((c->version == GameVersion::PC) ||
(c->version == GameVersion::GC) ||
(c->version == GameVersion::XB)) {
send_quest_open_file_t<S_OpenFile_PC_V3_44_A6>(
c, quest_name, basename, contents.size(), type);
} else if (c->version == GameVersion::BB) {
send_quest_open_file_t<S_OpenFile_BB_44_A6>(
@@ -1509,8 +1530,10 @@ void send_server_time(shared_ptr<Client> c) {
}
void send_change_event(shared_ptr<Client> c, uint8_t new_event) {
// THis command isn't supported on versions before GC apparently
if ((c->version == GameVersion::GC) || (c->version == GameVersion::BB)) {
// THis command isn't supported on versions before V3
if ((c->version == GameVersion::GC) ||
(c->version == GameVersion::XB) ||
(c->version == GameVersion::BB)) {
send_command(c, 0xDA, new_event);
}
}
+1 -1
View File
@@ -92,7 +92,7 @@ void send_command_with_header(Channel& c, const void* data, size_t size);
S_ServerInit_DC_PC_GC_02_17_91_9B prepare_server_init_contents_dc_pc_gc(
S_ServerInit_DC_PC_V3_02_17_91_9B prepare_server_init_contents_dc_pc_v3(
bool initial_connection,
uint32_t server_key,
uint32_t client_key);
+38 -23
View File
@@ -218,8 +218,8 @@ uint32_t ServerState::connect_address_for_client(std::shared_ptr<Client> c) {
shared_ptr<const vector<MenuItem>> ServerState::information_menu_for_version(GameVersion version) {
if (version == GameVersion::PC) {
return this->information_menu_pc;
} else if (version == GameVersion::GC) {
return this->information_menu_gc;
} else if ((version == GameVersion::GC) || (version == GameVersion::XB)) {
return this->information_menu_v3;
}
throw out_of_range("no information menu exists for this version");
}
@@ -229,6 +229,8 @@ const vector<MenuItem>& ServerState::proxy_destinations_menu_for_version(GameVer
return this->proxy_destinations_menu_pc;
} else if (version == GameVersion::GC) {
return this->proxy_destinations_menu_gc;
} else if (version == GameVersion::XB) {
return this->proxy_destinations_menu_xb;
}
throw out_of_range("no proxy destinations menu exists for this version");
}
@@ -238,6 +240,8 @@ const vector<pair<string, uint16_t>>& ServerState::proxy_destinations_for_versio
return this->proxy_destinations_pc;
} else if (version == GameVersion::GC) {
return this->proxy_destinations_gc;
} else if (version == GameVersion::XB) {
return this->proxy_destinations_xb;
}
throw out_of_range("no proxy destinations menu exists for this version");
}
@@ -265,10 +269,10 @@ void ServerState::create_menus(shared_ptr<const JSONObject> config_json) {
const auto& d = config_json->as_dict();
shared_ptr<vector<MenuItem>> information_menu_pc(new vector<MenuItem>());
shared_ptr<vector<MenuItem>> information_menu_gc(new vector<MenuItem>());
shared_ptr<vector<MenuItem>> information_menu_v3(new vector<MenuItem>());
shared_ptr<vector<u16string>> information_contents(new vector<u16string>());
information_menu_gc->emplace_back(InformationMenuItemID::GO_BACK, u"Go back",
information_menu_v3->emplace_back(InformationMenuItemID::GO_BACK, u"Go back",
u"Return to the\nmain menu", 0);
{
uint32_t item_id = 0;
@@ -276,45 +280,52 @@ void ServerState::create_menus(shared_ptr<const JSONObject> config_json) {
auto& v = item->as_list();
information_menu_pc->emplace_back(item_id, decode_sjis(v.at(0)->as_string()),
decode_sjis(v.at(1)->as_string()), 0);
information_menu_gc->emplace_back(item_id, decode_sjis(v.at(0)->as_string()),
information_menu_v3->emplace_back(item_id, decode_sjis(v.at(0)->as_string()),
decode_sjis(v.at(1)->as_string()), MenuItem::Flag::REQUIRES_MESSAGE_BOXES);
information_contents->emplace_back(decode_sjis(v.at(2)->as_string()));
item_id++;
}
}
this->information_menu_pc = information_menu_pc;
this->information_menu_gc = information_menu_gc;
this->information_menu_v3 = information_menu_v3;
this->information_contents = information_contents;
auto generate_proxy_destinations_menu = +[](
auto generate_proxy_destinations_menu = [&](
vector<MenuItem>& ret_menu,
vector<pair<string, uint16_t>>& ret_pds,
const unordered_map<string, shared_ptr<JSONObject>>& d) {
ret_menu.clear();
ret_pds.clear();
const char* key) {
try {
const auto& items = d.at(key);
ret_menu.clear();
ret_pds.clear();
ret_menu.emplace_back(ProxyDestinationsMenuItemID::GO_BACK, u"Go back",
u"Return to the\nmain menu", 0);
ret_menu.emplace_back(ProxyDestinationsMenuItemID::GO_BACK, u"Go back",
u"Return to the\nmain menu", 0);
uint32_t item_id = 0;
for (const auto& item : d) {
const string& netloc_str = item.second->as_string();
const string& description = "$C7Remote server:\n$C6" + netloc_str;
ret_menu.emplace_back(item_id, decode_sjis(item.first),
decode_sjis(description), 0);
ret_pds.emplace_back(parse_netloc(netloc_str));
item_id++;
}
uint32_t item_id = 0;
for (const auto& item : items->as_dict()) {
const string& netloc_str = item.second->as_string();
const string& description = "$C7Remote server:\n$C6" + netloc_str;
ret_menu.emplace_back(item_id, decode_sjis(item.first),
decode_sjis(description), 0);
ret_pds.emplace_back(parse_netloc(netloc_str));
item_id++;
}
} catch (const out_of_range&) { }
};
generate_proxy_destinations_menu(
this->proxy_destinations_menu_pc,
this->proxy_destinations_pc,
d.at("ProxyDestinations-PC")->as_dict());
"ProxyDestinations-PC");
generate_proxy_destinations_menu(
this->proxy_destinations_menu_gc,
this->proxy_destinations_gc,
d.at("ProxyDestinations-GC")->as_dict());
"ProxyDestinations-GC");
generate_proxy_destinations_menu(
this->proxy_destinations_menu_xb,
this->proxy_destinations_xb,
"ProxyDestinations-XB");
try {
const string& netloc_str = d.at("ProxyDestination-Patch")->as_string();
@@ -355,6 +366,10 @@ void ServerState::create_menus(shared_ptr<const JSONObject> config_json) {
this->main_menu.emplace_back(MainMenuItemID::PROXY_DESTINATIONS, u"Proxy server",
u"Connect to another\nserver", MenuItem::Flag::GC_ONLY);
}
if (!this->proxy_destinations_xb.empty()) {
this->main_menu.emplace_back(MainMenuItemID::PROXY_DESTINATIONS, u"Proxy server",
u"Connect to another\nserver", MenuItem::Flag::XB_ONLY);
}
this->main_menu.emplace_back(MainMenuItemID::DOWNLOAD_QUESTS, u"Download quests",
u"Download quests", MenuItem::Flag::INVISIBLE_ON_BB);
if (!this->function_code_index->patch_menu_empty()) {
+3 -1
View File
@@ -62,12 +62,14 @@ struct ServerState {
std::vector<MenuItem> main_menu;
std::shared_ptr<std::vector<MenuItem>> information_menu_pc;
std::shared_ptr<std::vector<MenuItem>> information_menu_gc;
std::shared_ptr<std::vector<MenuItem>> information_menu_v3;
std::shared_ptr<std::vector<std::u16string>> information_contents;
std::vector<MenuItem> proxy_destinations_menu_pc;
std::vector<MenuItem> proxy_destinations_menu_gc;
std::vector<MenuItem> proxy_destinations_menu_xb;
std::vector<std::pair<std::string, uint16_t>> proxy_destinations_pc;
std::vector<std::pair<std::string, uint16_t>> proxy_destinations_gc;
std::vector<std::pair<std::string, uint16_t>> proxy_destinations_xb;
std::pair<std::string, uint16_t> proxy_destination_patch;
std::pair<std::string, uint16_t> proxy_destination_bb;
std::u16string welcome_message;
+21 -16
View File
@@ -10,13 +10,14 @@ using namespace std;
uint16_t flags_for_version(GameVersion version, uint8_t sub_version) {
uint16_t flags_for_version(GameVersion version, int64_t sub_version) {
switch (sub_version) {
case 0x00: // initial check (before 9E recognition)
case -1: // initial check (before 9E recognition)
switch (version) {
case GameVersion::DC:
return Client::Flag::DEFAULT_V2_DC;
case GameVersion::GC:
case GameVersion::XB:
return Client::Flag::DEFAULT_V3_GC;
case GameVersion::PC:
return Client::Flag::DEFAULT_V2_PC;
@@ -26,33 +27,35 @@ uint16_t flags_for_version(GameVersion version, uint8_t sub_version) {
return Client::Flag::DEFAULT_V4_BB;
}
break;
case 0x29: // PSO PC
case 0x29: // PC
return Client::Flag::DEFAULT_V2_PC;
case 0x30: // PSO Ep1&2 JP v1.02
case 0x31: // PSO Ep1&2 US v1.00, US v1.01, EU v1.00, JP v1.00
case 0x32: // PSO Ep1&2 EU 50Hz
case 0x33: // PSO Ep1&2 EU 60Hz
case 0x34: // PSO Ep1&2 JP v1.03
case 0x30: // GC Ep1&2 JP v1.02, at least one version of PSO XB
case 0x31: // GC Ep1&2 US v1.00, GC US v1.01, GC EU v1.00, GC JP v1.00
case 0x32: // GC Ep1&2 EU 50Hz
case 0x33: // GC Ep1&2 EU 60Hz
case 0x34: // GC Ep1&2 JP v1.03
return Client::Flag::DEFAULT_V3_GC;
case 0x35: // PSO Ep1&2 JP v1.04 (Plus)
case 0x35: // GC Ep1&2 JP v1.04 (Plus)
return Client::Flag::DEFAULT_V3_GC_PLUS;
case 0x36: // PSO Ep1&2 US v1.02 (Plus)
case 0x39: // PSO Ep1&2 JP v1.05 (Plus)
case 0x36: // GC Ep1&2 US v1.02 (Plus)
case 0x39: // GC Ep1&2 JP v1.05 (Plus)
return Client::Flag::DEFAULT_V3_GC_PLUS_NO_SFC;
case 0x42: // PSO Ep3 JP
case 0x42: // GC Ep3 JP
return Client::Flag::DEFAULT_V3_GC_EP3;
case 0x40: // PSO Ep3 trial (TODO: Does this support send_function_call?)
case 0x41: // PSO Ep3 US
case 0x43: // PSO Ep3 EU
case 0x40: // GC Ep3 trial (TODO: Does this support send_function_call?)
case 0x41: // GC Ep3 US
case 0x43: // GC Ep3 EU
return Client::Flag::DEFAULT_V3_GC_EP3_NO_SFC;
}
return 0;
throw runtime_error("unknown sub_version");
}
const char* name_for_version(GameVersion version) {
switch (version) {
case GameVersion::GC:
return "GC";
case GameVersion::XB:
return "XB";
case GameVersion::PC:
return "PC";
case GameVersion::BB:
@@ -73,6 +76,8 @@ GameVersion version_for_name(const char* name) {
return GameVersion::PC;
} else if (!strcasecmp(name, "GC") || !strcasecmp(name, "GameCube")) {
return GameVersion::GC;
} else if (!strcasecmp(name, "XB") || !strcasecmp(name, "Xbox")) {
return GameVersion::XB;
} else if (!strcasecmp(name, "BB") || !strcasecmp(name, "BlueBurst") ||
!strcasecmp(name, "Blue Burst")) {
return GameVersion::BB;
+2 -1
View File
@@ -9,6 +9,7 @@ enum class GameVersion {
PC,
PATCH,
GC,
XB,
BB,
};
@@ -21,7 +22,7 @@ enum class ServerBehavior {
PROXY_SERVER,
};
uint16_t flags_for_version(GameVersion version, uint8_t sub_version);
uint16_t flags_for_version(GameVersion version, int64_t sub_version);
const char* name_for_version(GameVersion version);
GameVersion version_for_name(const char* name);
+7 -1
View File
@@ -36,6 +36,9 @@
"bb-patch": [11000, "patch", "patch_server"],
"bb-init": [12000, "bb", "data_server_bb"],
// TODO: If Xbox support ever gets built, add this port to the above config.
// "xb-login": [????, "xb", "login_server"],
// Schthack PSOBB uses these ports.
// "bb-patch2": [10500, "patch", "patch_server"],
// "bb-init2": [13000, "bb", "data_server_bb"],
@@ -55,9 +58,11 @@
"pc-lobby": [9420, "pc", "lobby_server"],
"gc-lobby": [9421, "gc", "lobby_server"],
"bb-lobby": [9422, "bb", "lobby_server"],
"xb-lobby": [9423, "xb", "lobby_server"],
"pc-proxy": [9520, "pc", "proxy_server"],
"gc-proxy": [9521, "gc", "proxy_server"],
"bb-proxy": [9522, "bb", "proxy_server"],
"xb-proxy": [9523, "xb", "proxy_server"],
"bb-data1": [12004, "bb", "data_server_bb"],
"bb-data2": [12005, "bb", "data_server_bb"],
},
@@ -75,8 +80,9 @@
// Other servers to support proxying to. If either of these is empty, the
// proxy server is disabled for that game version. Entries are like
// "name": "address:port"; the names are used in the proxy server menu.
"ProxyDestinations-GC": {},
"ProxyDestinations-PC": {},
"ProxyDestinations-GC": {},
"ProxyDestinations-XB": {},
// Proxy destination for patch server clients. If this is given, the internal
// patch server (for PC and BB) is bypassed, and any client that connects to
// the patch server is instead proxied to this destination.