add findings from psox disassembly
This commit is contained in:
+10
-9
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+5
-2
@@ -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
@@ -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
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user