supporting changes for proxy server rewrite
This commit is contained in:
+1
-1
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user