supporting changes for proxy server rewrite

This commit is contained in:
Martin Michelsen
2022-03-27 18:14:34 -07:00
parent 20410e7a94
commit d977cf0608
9 changed files with 360 additions and 269 deletions
+1 -1
View File
@@ -35,12 +35,12 @@ add_executable(newserv
src/FileContentsCache.cc
src/Menu.cc
src/PSOProtocol.cc
src/PSOEncryption.cc
src/Client.cc
src/Lobby.cc
src/ServerState.cc
src/Server.cc
src/License.cc
src/PSOEncryption.cc
src/Player.cc
src/SendCommands.cc
src/ChatCommands.cc
-1
View File
@@ -51,7 +51,6 @@ struct Client {
ServerBehavior server_behavior;
bool is_virtual_connection;
bool should_disconnect;
std::string recv_buffer;
// timing & menus
uint64_t play_time_begin; // time of connection (used for incrementing play time on BB)
+227 -176
View File
@@ -16,9 +16,9 @@ using namespace std;
// most ciphers used by pso are symmetric; alias decrypt to encrypt by default
void PSOEncryption::decrypt(void* data, size_t size) {
this->encrypt(data, size);
// Most ciphers used by PSO are symmetric; alias decrypt to encrypt by default
void PSOEncryption::decrypt(void* data, size_t size, bool advance) {
this->encrypt(data, size, advance);
}
@@ -71,26 +71,50 @@ PSOPCEncryption::PSOPCEncryption(uint32_t seed) : offset(1) {
}
}
uint32_t PSOPCEncryption::next() {
uint32_t PSOPCEncryption::next(bool advance) {
if (this->offset == PC_STREAM_LENGTH) {
this->update_stream();
this->offset = 1;
}
return this->stream[this->offset++];
uint32_t ret = this->stream[this->offset];
if (advance) {
this->offset++;
}
return ret;
}
void PSOPCEncryption::encrypt(void* vdata, size_t size) {
void PSOPCEncryption::encrypt(void* vdata, size_t size, bool advance) {
if (size & 3) {
throw invalid_argument("size must be a multiple of 4");
}
if (!advance && (size != 4)) {
throw logic_error("cannot peek-encrypt/decrypt with size > 4");
}
size >>= 2;
uint32_t* data = reinterpret_cast<uint32_t*>(vdata);
for (size_t x = 0; x < size; x++) {
data[x] ^= this->next();
data[x] ^= this->next(advance);
}
}
void PSOPCEncryption::skip(size_t size) {
if (size & 3) {
throw invalid_argument("size must be a multiple of 4");
}
size >>= 2;
// TODO: Do something smarter than just calling next() in a loop here
size_t new_offset = this->offset + size;
while (new_offset > PC_STREAM_LENGTH) {
this->update_stream();
// The PC encryption apparently always skips the first key in the stream
new_offset -= (PC_STREAM_LENGTH - 1);
}
this->offset = new_offset;
}
void PSOGCEncryption::update_stream() {
@@ -110,12 +134,15 @@ void PSOGCEncryption::update_stream() {
this->offset = 0;
}
uint32_t PSOGCEncryption::next() {
this->offset++;
uint32_t PSOGCEncryption::next(bool advance) {
if (this->offset == GC_STREAM_LENGTH) {
this->update_stream();
}
return this->stream[this->offset];
uint32_t ret = this->stream[this->offset];
if (advance) {
this->offset++;
}
return ret;
}
PSOGCEncryption::PSOGCEncryption(uint32_t seed) : offset(0) {
@@ -145,27 +172,43 @@ PSOGCEncryption::PSOGCEncryption(uint32_t seed) : offset(0) {
this->stream[this->offset++] = (this->stream[source3++] ^ (((this->stream[source1++] << 23) & 0xFF800000) ^ ((this->stream[source2++] >> 9) & 0x007FFFFF)));
}
for (size_t x = 0; x < 3; x++) {
for (size_t x = 0; x < 4; x++) {
this->update_stream();
}
this->offset = GC_STREAM_LENGTH - 1;
}
void PSOGCEncryption::encrypt(void* vdata, size_t size) {
void PSOGCEncryption::encrypt(void* vdata, size_t size, bool advance) {
if (size & 3) {
throw invalid_argument("size must be a multiple of 4");
}
if (!advance && (size != 4)) {
throw logic_error("cannot peek-encrypt/decrypt with size > 4");
}
size >>= 2;
uint32_t* data = reinterpret_cast<uint32_t*>(vdata);
for (size_t x = 0; x < size; x++) {
data[x] ^= this->next();
data[x] ^= this->next(advance);
}
}
void PSOGCEncryption::skip(size_t size) {
if (size & 3) {
throw invalid_argument("size must be a multiple of 4");
}
size >>= 2;
size_t new_offset = this->offset + size;
while (new_offset > GC_STREAM_LENGTH) {
this->update_stream();
new_offset -= GC_STREAM_LENGTH;
}
this->offset = new_offset;
}
void PSOBBEncryption::decrypt(void* vdata, size_t size) {
void PSOBBEncryption::decrypt(void* vdata, size_t size, bool) {
if (size & 7) {
throw invalid_argument("size must be a multiple of 8");
}
@@ -178,19 +221,19 @@ void PSOBBEncryption::decrypt(void* vdata, size_t size) {
while (edx < size) {
ebx = data[edx];
ebx = ebx ^ this->stream[5];
ebp = ((this->stream[(ebx >> 0x18) + 0x12]+this->stream[((ebx >> 0x10)& 0xff) + 0x112])
^ this->stream[((ebx >> 0x8)& 0xff) + 0x212]) + this->stream[(ebx & 0xff) + 0x312];
ebp = ((this->stream[(ebx >> 0x18) + 0x12] + this->stream[((ebx >> 0x10) & 0xFF) + 0x112])
^ this->stream[((ebx >> 0x8) & 0xFF) + 0x212]) + this->stream[(ebx & 0xFF) + 0x312];
ebp = ebp ^ this->stream[4];
ebp ^= data[edx + 1];
edi = ((this->stream[(ebp >> 0x18) + 0x12]+this->stream[((ebp >> 0x10)& 0xff) + 0x112])
^ this->stream[((ebp >> 0x8)& 0xff) + 0x212]) + this->stream[(ebp & 0xff) + 0x312];
edi = ((this->stream[(ebp >> 0x18) + 0x12] + this->stream[((ebp >> 0x10) & 0xFF) + 0x112])
^ this->stream[((ebp >> 0x8) & 0xFF) + 0x212]) + this->stream[(ebp & 0xFF) + 0x312];
edi = edi ^ this->stream[3];
ebx = ebx ^ edi;
esi = ((this->stream[(ebx >> 0x18) + 0x12]+this->stream[((ebx >> 0x10)& 0xff) + 0x112])
^ this->stream[((ebx >> 0x8)& 0xff) + 0x212]) + this->stream[(ebx & 0xff) + 0x312];
esi = ((this->stream[(ebx >> 0x18) + 0x12] + this->stream[((ebx >> 0x10) & 0xFF) + 0x112])
^ this->stream[((ebx >> 0x8) & 0xFF) + 0x212]) + this->stream[(ebx & 0xFF) + 0x312];
ebp = ebp ^ esi ^ this->stream[2];
edi = ((this->stream[(ebp >> 0x18) + 0x12]+this->stream[((ebp >> 0x10)& 0xff) + 0x112])
^ this->stream[((ebp >> 0x8)& 0xff) + 0x212]) + this->stream[(ebp & 0xff) + 0x312];
edi = ((this->stream[(ebp >> 0x18) + 0x12] + this->stream[((ebp >> 0x10) & 0xFF) + 0x112])
^ this->stream[((ebp >> 0x8) & 0xFF) + 0x212]) + this->stream[(ebp & 0xFF) + 0x312];
edi = edi ^ this->stream[1];
ebp = ebp ^ this->stream[0];
ebx = ebx ^ edi;
@@ -200,7 +243,7 @@ void PSOBBEncryption::decrypt(void* vdata, size_t size) {
}
}
void PSOBBEncryption::encrypt(void* vdata, size_t size) {
void PSOBBEncryption::encrypt(void* vdata, size_t size, bool) {
if (size & 7) {
throw invalid_argument("size must be a multiple of 8");
}
@@ -213,19 +256,19 @@ void PSOBBEncryption::encrypt(void* vdata, size_t size) {
while (edx < size) {
ebx = data[edx];
ebx = ebx ^ this->stream[0];
ebp = ((this->stream[(ebx >> 0x18) + 0x12]+this->stream[((ebx >> 0x10)& 0xff) + 0x112])
^ this->stream[((ebx >> 0x8)& 0xff) + 0x212]) + this->stream[(ebx & 0xff) + 0x312];
ebp = ((this->stream[(ebx >> 0x18) + 0x12] + this->stream[((ebx >> 0x10) & 0xFF) + 0x112])
^ this->stream[((ebx >> 0x8) & 0xFF) + 0x212]) + this->stream[(ebx & 0xFF) + 0x312];
ebp = ebp ^ this->stream[1];
ebp ^= data[edx + 1];
edi = ((this->stream[(ebp >> 0x18) + 0x12]+this->stream[((ebp >> 0x10)& 0xff) + 0x112])
^ this->stream[((ebp >> 0x8)& 0xff) + 0x212]) + this->stream[(ebp & 0xff) + 0x312];
edi = ((this->stream[(ebp >> 0x18) + 0x12] + this->stream[((ebp >> 0x10) & 0xFF) + 0x112])
^ this->stream[((ebp >> 0x8) & 0xFF) + 0x212]) + this->stream[(ebp & 0xFF) + 0x312];
edi = edi ^ this->stream[2];
ebx = ebx ^ edi;
esi = ((this->stream[(ebx >> 0x18) + 0x12]+this->stream[((ebx >> 0x10)& 0xff) + 0x112])
^ this->stream[((ebx >> 0x8)& 0xff) + 0x212]) + this->stream[(ebx & 0xff) + 0x312];
esi = ((this->stream[(ebx >> 0x18) + 0x12] + this->stream[((ebx >> 0x10) & 0xFF) + 0x112])
^ this->stream[((ebx >> 0x8) & 0xFF) + 0x212]) + this->stream[(ebx & 0xFF) + 0x312];
ebp = ebp ^ esi ^ this->stream[3];
edi = ((this->stream[(ebp >> 0x18) + 0x12]+this->stream[((ebp >> 0x10)& 0xff) + 0x112])
^ this->stream[((ebp >> 0x8)& 0xff) + 0x212]) + this->stream[(ebp & 0xff) + 0x312];
edi = ((this->stream[(ebp >> 0x18) + 0x12] + this->stream[((ebp >> 0x10) & 0xFF) + 0x112])
^ this->stream[((ebp >> 0x8) & 0xFF) + 0x212]) + this->stream[(ebp & 0xFF) + 0x312];
edi = edi ^ this->stream[4];
ebp = ebp ^ this->stream[5];
ebx = ebx ^ edi;
@@ -235,8 +278,12 @@ void PSOBBEncryption::encrypt(void* vdata, size_t size) {
}
}
PSOBBEncryption::PSOBBEncryption(const KeyFile& key, const void* original_seed,
size_t seed_size) : offset(0) {
PSOBBEncryption::PSOBBEncryption(
const KeyFile& key, const void* original_seed, size_t seed_size)
: stream(this->generate_stream(key, original_seed, seed_size)) { }
vector<uint32_t> PSOBBEncryption::generate_stream(
const KeyFile& key, const void* original_seed, size_t seed_size) {
if (seed_size % 3) {
throw invalid_argument("seed size must be divisible by 3");
}
@@ -250,194 +297,198 @@ PSOBBEncryption::PSOBBEncryption(const KeyFile& key, const void* original_seed,
seed.push_back(original_seed_data[x + 2] ^ 0x18);
}
memcpy(this->stream, &key, sizeof(key));
vector<uint32_t> stream(BB_STREAM_LENGTH, 0);
memcpy(stream.data(), &key, sizeof(key));
this->postprocess_initial_stream(seed);
}
// This block was formerly postprocess_initial_stream
{
uint32_t eax, ecx, edx, ebx, ebp, esi, edi, ou, x;
void PSOBBEncryption::postprocess_initial_stream(const string& seed) {
uint32_t eax, ecx, edx, ebx, ebp, esi, edi, ou, x;
ecx = 0;
ebx = 0;
ecx = 0;
ebx = 0;
while (ebx < (seed.size() / 4)) {
ebp = static_cast<uint32_t>(seed[ecx]) << 0x18;
eax = ecx + 1;
edx = eax % seed.size();
eax = (static_cast<uint32_t>(seed[edx]) << 0x10) & 0xFF0000;
ebp = (ebp | eax) & 0xffff00ff;
eax = ecx + 2;
edx = eax % seed.size();
eax = (static_cast<uint32_t>(seed[edx]) << 0x08) & 0xFF00;
ebp = (ebp | eax) & 0xffffff00;
eax = ecx + 3;
ecx = ecx + 4;
edx = eax % seed.size();
eax = static_cast<uint32_t>(seed[edx]);
ebp = ebp | eax;
eax = ecx;
edx = eax % seed.size();
this->stream[ebx] ^= ebp;
ecx = edx;
ebx++;
}
ebp = 0;
esi = 0;
ecx = 0;
edi = 0;
ebx = 0;
edx = 0x48;
while (edi < edx) {
esi = esi ^ this->stream[0];
eax = esi >> 0x18;
ebx = (esi >> 0x10) & 0xff;
eax = this->stream[eax + 0x12] + this->stream[ebx + 0x112];
ebx = (esi >> 8) & 0xFF;
eax = eax ^ this->stream[ebx + 0x212];
ebx = esi & 0xFF;
eax = eax + this->stream[ebx + 0x312];
eax = eax ^ this->stream[1];
ecx = ecx ^ eax;
ebx = ecx >> 0x18;
eax = (ecx >> 0x10) & 0xFF;
ebx = this->stream[ebx + 0x12] + this->stream[eax + 0x112];
eax = (ecx >> 8) & 0xFF;
ebx = ebx ^ this->stream[eax + 0x212];
eax = ecx & 0xFF;
ebx = ebx + this->stream[eax + 0x312];
for (x = 0; x <= 5; x++) {
ebx = ebx ^ this->stream[(x * 2) + 2];
esi = esi ^ ebx;
ebx = esi >> 0x18;
eax = (esi >> 0x10) & 0xFF;
ebx = this->stream[ebx + 0x12] + this->stream[eax + 0x112];
eax = (esi >> 8) & 0xFF;
ebx = ebx ^ this->stream[eax + 0x212];
eax = esi & 0xFF;
ebx = ebx + this->stream[eax + 0x312];
ebx = ebx ^ this->stream[(x * 2) + 3];
ecx = ecx ^ ebx;
ebx = ecx >> 0x18;
eax = (ecx >> 0x10) & 0xFF;
ebx = this->stream[ebx + 0x12] + this->stream[eax + 0x112];
eax = (ecx >> 8) & 0xFF;
ebx = ebx ^ this->stream[eax + 0x212];
eax = ecx & 0xff;
ebx = ebx + this->stream[eax + 0x312];
while (ebx < (seed.size() / 4)) {
ebp = static_cast<uint32_t>(seed[ecx]) << 0x18;
eax = ecx + 1;
edx = eax % seed.size();
eax = (static_cast<uint32_t>(seed[edx]) << 0x10) & 0xFF0000;
ebp = (ebp | eax) & 0xFFFF00FF;
eax = ecx + 2;
edx = eax % seed.size();
eax = (static_cast<uint32_t>(seed[edx]) << 0x08) & 0xFF00;
ebp = (ebp | eax) & 0xFFFFFF00;
eax = ecx + 3;
ecx = ecx + 4;
edx = eax % seed.size();
eax = static_cast<uint32_t>(seed[edx]);
ebp = ebp | eax;
eax = ecx;
edx = eax % seed.size();
stream[ebx] ^= ebp;
ecx = edx;
ebx++;
}
ebx = ebx ^ this->stream[14];
esi = esi ^ ebx;
eax = esi >> 0x18;
ebx = (esi >> 0x10) & 0xFF;
eax = this->stream[eax + 0x12] + this->stream[ebx + 0x112];
ebx = (esi >> 8) & 0xff;
eax = eax ^ this->stream[ebx + 0x212];
ebx = esi & 0xff;
eax = eax + this->stream[ebx + 0x312];
eax = eax ^ this->stream[15];
eax = ecx ^ eax;
ecx = eax >> 0x18;
ebx = (eax >> 0x10) & 0xFF;
ecx = this->stream[ecx + 0x12] + this->stream[ebx + 0x112];
ebx = (eax >> 8) & 0xFF;
ecx = ecx ^ this->stream[ebx + 0x212];
ebx = eax & 0xFF;
ecx = ecx + this->stream[ebx + 0x312];
ecx = ecx ^ this->stream[16];
ecx = ecx ^ esi;
esi = this->stream[17];
esi = esi ^ eax;
this->stream[(edi / 4)] = esi;
this->stream[(edi / 4)+1] = ecx;
edi = edi + 8;
}
eax = 0;
edx = 0;
ou = 0;
while (ou < 0x1000) {
edi = 0x48;
edx = 0x448;
ebp = 0;
esi = 0;
ecx = 0;
edi = 0;
ebx = 0;
edx = 0x48;
while (edi < edx) {
esi = esi ^ this->stream[0];
esi = esi ^ stream[0];
eax = esi >> 0x18;
ebx = (esi >> 0x10) & 0xff;
eax = this->stream[eax + 0x12] + this->stream[ebx + 0x112];
ebx = (esi >> 0x10) & 0xFF;
eax = stream[eax + 0x12] + stream[ebx + 0x112];
ebx = (esi >> 8) & 0xFF;
eax = eax ^ this->stream[ebx + 0x212];
eax = eax ^ stream[ebx + 0x212];
ebx = esi & 0xFF;
eax = eax + this->stream[ebx + 0x312];
eax = eax + stream[ebx + 0x312];
eax = eax ^ this->stream[1];
eax = eax ^ stream[1];
ecx = ecx ^ eax;
ebx = ecx >> 0x18;
eax = (ecx >> 0x10) & 0xFF;
ebx = this->stream[ebx + 0x12] + this->stream[eax + 0x112];
ebx = stream[ebx + 0x12] + stream[eax + 0x112];
eax = (ecx >> 8) & 0xFF;
ebx = ebx ^ this->stream[eax + 0x212];
ebx = ebx ^ stream[eax + 0x212];
eax = ecx & 0xFF;
ebx = ebx + this->stream[eax + 0x312];
ebx = ebx + stream[eax + 0x312];
for (x = 0; x <= 5; x++) {
ebx = ebx ^ this->stream[(x * 2) + 2];
ebx = ebx ^ stream[(x * 2) + 2];
esi = esi ^ ebx;
ebx = esi >> 0x18;
eax = (esi >> 0x10) & 0xFF;
ebx = this->stream[ebx + 0x12] + this->stream[eax + 0x112];
ebx = stream[ebx + 0x12] + stream[eax + 0x112];
eax = (esi >> 8) & 0xFF;
ebx = ebx ^ this->stream[eax + 0x212];
ebx = ebx ^ stream[eax + 0x212];
eax = esi & 0xFF;
ebx = ebx + this->stream[eax + 0x312];
ebx = ebx + stream[eax + 0x312];
ebx = ebx ^ this->stream[(x * 2) + 3];
ebx = ebx ^ stream[(x * 2) + 3];
ecx = ecx ^ ebx;
ebx = ecx >> 0x18;
eax = (ecx >> 0x10) & 0xFF;
ebx = this->stream[ebx + 0x12] + this->stream[eax + 0x112];
ebx = stream[ebx + 0x12] + stream[eax + 0x112];
eax = (ecx >> 8) & 0xFF;
ebx = ebx ^ this->stream[eax + 0x212];
ebx = ebx ^ stream[eax + 0x212];
eax = ecx & 0xFF;
ebx = ebx + this->stream[eax + 0x312];
ebx = ebx + stream[eax + 0x312];
}
ebx = ebx ^ this->stream[14];
ebx = ebx ^ stream[14];
esi = esi ^ ebx;
eax = esi >> 0x18;
ebx = (esi >> 0x10) & 0xFF;
eax = this->stream[eax + 0x12] + this->stream[ebx + 0x112];
eax = stream[eax + 0x12] + stream[ebx + 0x112];
ebx = (esi >> 8) & 0xFF;
eax = eax ^ this->stream[ebx + 0x212];
eax = eax ^ stream[ebx + 0x212];
ebx = esi & 0xFF;
eax = eax + this->stream[ebx + 0x312];
eax = eax + stream[ebx + 0x312];
eax = eax ^ this->stream[15];
eax = eax ^ stream[15];
eax = ecx ^ eax;
ecx = eax >> 0x18;
ebx = (eax >> 0x10) & 0xFF;
ecx = this->stream[ecx + 0x12] + this->stream[ebx + 0x112];
ecx = stream[ecx + 0x12] + stream[ebx + 0x112];
ebx = (eax >> 8) & 0xFF;
ecx = ecx ^ this->stream[ebx + 0x212];
ecx = ecx ^ stream[ebx + 0x212];
ebx = eax & 0xFF;
ecx = ecx + this->stream[ebx + 0x312];
ecx = ecx + stream[ebx + 0x312];
ecx = ecx ^ this->stream[16];
ecx = ecx ^ stream[16];
ecx = ecx ^ esi;
esi = this->stream[17];
esi = stream[17];
esi = esi ^ eax;
this->stream[(ou / 4) + (edi / 4)] = esi;
this->stream[(ou / 4) + (edi / 4) + 1] = ecx;
stream[(edi / 4)] = esi;
stream[(edi / 4)+1] = ecx;
edi = edi + 8;
}
ou = ou + 0x400;
eax = 0;
edx = 0;
ou = 0;
while (ou < 0x1000) {
edi = 0x48;
edx = 0x448;
while (edi < edx) {
esi = esi ^ stream[0];
eax = esi >> 0x18;
ebx = (esi >> 0x10) & 0xFF;
eax = stream[eax + 0x12] + stream[ebx + 0x112];
ebx = (esi >> 8) & 0xFF;
eax = eax ^ stream[ebx + 0x212];
ebx = esi & 0xFF;
eax = eax + stream[ebx + 0x312];
eax = eax ^ stream[1];
ecx = ecx ^ eax;
ebx = ecx >> 0x18;
eax = (ecx >> 0x10) & 0xFF;
ebx = stream[ebx + 0x12] + stream[eax + 0x112];
eax = (ecx >> 8) & 0xFF;
ebx = ebx ^ stream[eax + 0x212];
eax = ecx & 0xFF;
ebx = ebx + stream[eax + 0x312];
for (x = 0; x <= 5; x++) {
ebx = ebx ^ stream[(x * 2) + 2];
esi = esi ^ ebx;
ebx = esi >> 0x18;
eax = (esi >> 0x10) & 0xFF;
ebx = stream[ebx + 0x12] + stream[eax + 0x112];
eax = (esi >> 8) & 0xFF;
ebx = ebx ^ stream[eax + 0x212];
eax = esi & 0xFF;
ebx = ebx + stream[eax + 0x312];
ebx = ebx ^ stream[(x * 2) + 3];
ecx = ecx ^ ebx;
ebx = ecx >> 0x18;
eax = (ecx >> 0x10) & 0xFF;
ebx = stream[ebx + 0x12] + stream[eax + 0x112];
eax = (ecx >> 8) & 0xFF;
ebx = ebx ^ stream[eax + 0x212];
eax = ecx & 0xFF;
ebx = ebx + stream[eax + 0x312];
}
ebx = ebx ^ stream[14];
esi = esi ^ ebx;
eax = esi >> 0x18;
ebx = (esi >> 0x10) & 0xFF;
eax = stream[eax + 0x12] + stream[ebx + 0x112];
ebx = (esi >> 8) & 0xFF;
eax = eax ^ stream[ebx + 0x212];
ebx = esi & 0xFF;
eax = eax + stream[ebx + 0x312];
eax = eax ^ stream[15];
eax = ecx ^ eax;
ecx = eax >> 0x18;
ebx = (eax >> 0x10) & 0xFF;
ecx = stream[ecx + 0x12] + stream[ebx + 0x112];
ebx = (eax >> 8) & 0xFF;
ecx = ecx ^ stream[ebx + 0x212];
ebx = eax & 0xFF;
ecx = ecx + stream[ebx + 0x312];
ecx = ecx ^ stream[16];
ecx = ecx ^ esi;
esi = stream[17];
esi = esi ^ eax;
stream[(ou / 4) + (edi / 4)] = esi;
stream[(ou / 4) + (edi / 4) + 1] = ecx;
edi = edi + 8;
}
ou = ou + 0x400;
}
}
return stream;
}
void PSOBBEncryption::skip(size_t) { }
+16 -15
View File
@@ -4,6 +4,7 @@
#include <stddef.h>
#include <string>
#include <vector>
@@ -15,8 +16,9 @@ class PSOEncryption {
public:
virtual ~PSOEncryption() = default;
virtual void encrypt(void* data, size_t size) = 0;
virtual void decrypt(void* data, size_t size);
virtual void encrypt(void* data, size_t size, bool advance = true) = 0;
virtual void decrypt(void* data, size_t size, bool advance = true);
virtual void skip(size_t size) = 0;
protected:
PSOEncryption() = default;
@@ -26,11 +28,12 @@ class PSOPCEncryption : public PSOEncryption {
public:
explicit PSOPCEncryption(uint32_t seed);
virtual void encrypt(void* data, size_t size);
virtual void encrypt(void* data, size_t size, bool advance = true);
virtual void skip(size_t size);
protected:
void update_stream();
uint32_t next();
uint32_t next(bool advance = true);
uint32_t stream[PC_STREAM_LENGTH];
uint16_t offset;
@@ -40,11 +43,12 @@ class PSOGCEncryption : public PSOEncryption {
public:
explicit PSOGCEncryption(uint32_t key);
virtual void encrypt(void* data, size_t size);
virtual void encrypt(void* data, size_t size, bool advance = true);
virtual void skip(size_t size);
protected:
void update_stream();
uint32_t next();
uint32_t next(bool advance = true);
uint32_t stream[GC_STREAM_LENGTH];
uint16_t offset;
@@ -59,16 +63,13 @@ public:
PSOBBEncryption(const KeyFile& key, const void* seed, size_t seed_size);
virtual void encrypt(void* data, size_t size);
virtual void decrypt(void* data, size_t size);
virtual void encrypt(void* data, size_t size, bool advance = true);
virtual void decrypt(void* data, size_t size, bool advance = true);
virtual void skip(size_t size);
protected:
PSOBBEncryption() = default;
static std::vector<uint32_t> generate_stream(
const KeyFile& key, const void* seed, size_t seed_size);
void postprocess_initial_stream(const std::string& seed);
void update_stream();
uint32_t stream[BB_STREAM_LENGTH];
uint16_t offset;
const std::vector<uint32_t> stream;
};
+61
View File
@@ -1,11 +1,19 @@
#include "PSOProtocol.hh"
#include <event2/buffer.h>
#include <stdexcept>
using namespace std;
PSOCommandHeader::PSOCommandHeader() {
this->bb.size = 0;
this->bb.command = 0;
this->bb.flag = 0;
}
uint16_t PSOCommandHeader::command(GameVersion version) const {
switch (version) {
case GameVersion::DC:
@@ -48,3 +56,56 @@ uint32_t PSOCommandHeader::flag(GameVersion version) const {
throw logic_error("unknown game version");
}
void for_each_received_command(
struct bufferevent* bev,
GameVersion version,
PSOEncryption* crypt,
function<void(uint16_t, uint16_t, const string&)> fn) {
struct evbuffer* buf = bufferevent_get_input(bev);
size_t header_size = version == GameVersion::BB ? 8 : 4;
for (;;) {
PSOCommandHeader header;
if (evbuffer_copyout(buf, &header, header_size)
< static_cast<ssize_t>(header_size)) {
break;
}
if (crypt) {
crypt->decrypt(&header, header_size, false);
}
size_t command_logical_size = header.size(version);
// BB pads commands to 8-byte boundaries, and this is not reflected in the
// size field
size_t command_physical_size = (version == GameVersion::BB)
? (command_logical_size + header_size - 1) & ~(header_size - 1)
: command_logical_size;
if (evbuffer_get_length(buf) < command_physical_size) {
break;
}
// If we get here, then there is a full command in the buffer
evbuffer_drain(buf, header_size);
string command_data(command_logical_size - header_size, '\0');
if (evbuffer_remove(buf, command_data.data(), command_data.size())
< static_cast<ssize_t>(command_data.size())) {
throw logic_error("enough bytes available, but could not remove them");
}
if (command_logical_size != command_physical_size) {
evbuffer_drain(buf, command_physical_size - command_logical_size);
}
if (crypt) {
crypt->skip(header_size);
crypt->decrypt(command_data.data(), command_data.size());
}
fn(header.command(version), header.flag(version), command_data);
}
}
+17 -5
View File
@@ -1,26 +1,30 @@
#pragma once
#include <inttypes.h>
#include <event2/bufferevent.h>
#include <functional>
#include "Version.hh"
#include "PSOEncryption.hh"
struct PSOCommandHeaderPC {
uint16_t size;
uint8_t command;
uint8_t flag;
};
} __attribute__((packed));
struct PSOCommandHeaderDCGC {
uint8_t command;
uint8_t flag;
uint16_t size;
};
} __attribute__((packed));
struct PSOCommandHeaderBB {
uint16_t size;
uint16_t command;
uint32_t flag;
};
} __attribute__((packed));
union PSOCommandHeader {
PSOCommandHeaderDCGC dc;
@@ -31,10 +35,18 @@ union PSOCommandHeader {
uint16_t command(GameVersion version) const;
uint16_t size(GameVersion version) const;
uint32_t flag(GameVersion version) const;
};
PSOCommandHeader();
} __attribute__((packed));
union PSOSubcommand {
uint8_t byte[4];
uint16_t word[2];
uint32_t dword;
};
} __attribute__((packed));
void for_each_received_command(
struct bufferevent* bev,
GameVersion version,
PSOEncryption* crypt,
std::function<void(uint16_t, uint16_t, const std::string&)> fn);
+26 -21
View File
@@ -128,24 +128,34 @@ static const char* dc_lobby_server_copyright = "DreamCast Lobby Server. Copyrigh
static const char* bb_game_server_copyright = "Phantasy Star Online Blue Burst Game Server. Copyright 1999-2004 SONICTEAM.";
static const char* patch_server_copyright = "Patch Server. Copyright SonicTeam, LTD. 2001";
static void send_server_init_dc_pc_gc(shared_ptr<Client> c, const char* copyright_text,
uint8_t command) {
struct {
string prepare_server_init_contents_dc_pc_gc(
bool initial_connection,
uint32_t server_key,
uint32_t client_key) {
struct Command {
char copyright[0x40];
uint32_t server_key;
uint32_t client_key;
char after_message[200];
} cmd;
};
string ret(sizeof(Command), '\0');
auto* cmd = reinterpret_cast<Command*>(ret.data());
strcpy(cmd->copyright, initial_connection ? dc_port_map_copyright : dc_lobby_server_copyright);
cmd->server_key = server_key;
cmd->client_key = client_key;
strcpy(cmd->after_message, anti_copyright);
return ret;
}
static void send_server_init_dc_pc_gc(shared_ptr<Client> c,
bool initial_connection, uint8_t command) {
uint32_t server_key = random_object<uint32_t>();
uint32_t client_key = random_object<uint32_t>();
memset(&cmd, 0, sizeof(cmd));
strcpy(cmd.copyright, copyright_text);
cmd.server_key = server_key;
cmd.client_key = client_key;
strcpy(cmd.after_message, anti_copyright);
send_command(c, command, 0x00, cmd);
string data = prepare_server_init_contents_dc_pc_gc(
initial_connection, server_key, client_key);
send_command(c, command, 0x00, data);
switch (c->version) {
case GameVersion::DC:
@@ -163,19 +173,14 @@ static void send_server_init_dc_pc_gc(shared_ptr<Client> c, const char* copyrigh
}
static void send_server_init_pc(shared_ptr<Client> c, bool initial_connection) {
send_server_init_dc_pc_gc(c,
initial_connection ? dc_port_map_copyright : dc_lobby_server_copyright,
0x17);
send_server_init_dc_pc_gc(c, initial_connection, 0x17);
}
static void send_server_init_gc(shared_ptr<Client> c, bool initial_connection) {
send_server_init_dc_pc_gc(c,
initial_connection ? dc_port_map_copyright : dc_lobby_server_copyright,
initial_connection ? 0x17 : 0x02);
send_server_init_dc_pc_gc(c, initial_connection, initial_connection ? 0x17 : 0x02);
}
static void send_server_init_bb(shared_ptr<ServerState> s, shared_ptr<Client> c,
bool) {
static void send_server_init_bb(shared_ptr<ServerState> s, shared_ptr<Client> c) {
struct {
char copyright[0x60];
uint8_t server_key[0x30];
@@ -196,7 +201,7 @@ static void send_server_init_bb(shared_ptr<ServerState> s, shared_ptr<Client> c,
sizeof(cmd.client_key)));
}
static void send_server_init_patch(shared_ptr<Client> c, bool) {
static void send_server_init_patch(shared_ptr<Client> c) {
struct {
char copyright[0x40];
uint32_t server_key;
@@ -223,11 +228,11 @@ void send_server_init(shared_ptr<ServerState> s, shared_ptr<Client> c,
if (c->version == GameVersion::PC) {
send_server_init_pc(c, initial_connection);
} else if (c->version == GameVersion::PATCH) {
send_server_init_patch(c, initial_connection);
send_server_init_patch(c);
} else if (c->version == GameVersion::GC) {
send_server_init_gc(c, initial_connection);
} else if (c->version == GameVersion::BB) {
send_server_init_bb(s, c, initial_connection);
send_server_init_bb(s, c);
} else {
throw logic_error("unimplemented versioned command");
}
+2 -1
View File
@@ -74,7 +74,8 @@ void send_command(std::shared_ptr<TARGET> c, uint16_t command, uint32_t flag,
std::string prepare_server_init_contents_dc_pc_gc(
bool initial_connection, uint32_t server_key, uint32_t client_key);
void send_server_init(std::shared_ptr<ServerState> s, std::shared_ptr<Client> c,
bool initial_connection);
void send_update_client_config(std::shared_ptr<Client> c);
+10 -49
View File
@@ -208,55 +208,16 @@ void Server::on_disconnecting_client_error(struct bufferevent* bev,
}
void Server::receive_and_process_commands(shared_ptr<Client> c) {
struct evbuffer* buf = bufferevent_get_input(c->bev);
size_t header_size = (c->version == GameVersion::BB) ? 8 : 4;
// read as much data into recv_buffer as we can and decrypt it
size_t existing_bytes = c->recv_buffer.size();
size_t new_bytes = evbuffer_get_length(buf);
new_bytes &= ~(header_size - 1); // only read in multiples of header_size
c->recv_buffer.resize(existing_bytes + new_bytes);
void* recv_ptr = c->recv_buffer.data() + existing_bytes;
if (evbuffer_remove(buf, recv_ptr, new_bytes) != static_cast<ssize_t>(new_bytes)) {
throw runtime_error("some bytes could not be read from the receive buffer");
}
// decrypt the received data if encryption is enabled
if (c->crypt_in.get()) {
c->crypt_in->decrypt(recv_ptr, new_bytes);
}
// process as many commands as possible
size_t offset = 0;
while (offset < c->recv_buffer.size()) {
const PSOCommandHeader* header = reinterpret_cast<const PSOCommandHeader*>(
c->recv_buffer.data() + offset);
size_t size = header->size(c->version);
if (offset + size > c->recv_buffer.size()) {
break; // don't have a complete command; we're done for now
}
// if we get here, then we have a complete, decrypted command waiting to be
// processed. we copy it out and append zeroes on the end so that it's safe
// to call string functions on the buffer in command handlers
string data = c->recv_buffer.substr(offset + header_size, size - header_size);
data.append(4, '\0');
try {
process_command(this->state, c, header->command(c->version),
header->flag(c->version), size - header_size, data.data());
} catch (const exception& e) {
log(INFO, "[Server] Error in client stream: %s", e.what());
c->should_disconnect = true;
return;
}
// BB pads commands to 8-byte boundaries, so if we see a shorter command,
// skip over the padding
offset += (size + header_size - 1) & ~(header_size - 1);
}
// remove the processed commands from the receive buffer
c->recv_buffer = c->recv_buffer.substr(offset);
for_each_received_command(c->bev, c->version, c->crypt_in.get(),
[this, c](uint16_t command, uint16_t flag, const std::string& data) {
try {
process_command(this->state, c, command, flag, data.size(), data.data());
} catch (const exception& e) {
log(INFO, "[Server] Error in client stream: %s", e.what());
c->should_disconnect = true;
return;
}
});
}
Server::Server(shared_ptr<struct event_base> base,