346 lines
11 KiB
C++
346 lines
11 KiB
C++
#include "Version.hh"
|
|
|
|
#include <strings.h>
|
|
|
|
#include <stdexcept>
|
|
|
|
#include "Client.hh"
|
|
|
|
using namespace std;
|
|
|
|
const char* login_port_name_for_version(Version v) {
|
|
switch (v) {
|
|
case Version::PC_PATCH:
|
|
return "pc-patch";
|
|
case Version::BB_PATCH:
|
|
return "bb-patch";
|
|
case Version::DC_NTE:
|
|
case Version::DC_V1_11_2000_PROTOTYPE:
|
|
case Version::DC_V1:
|
|
case Version::DC_V2:
|
|
case Version::GC_NTE:
|
|
case Version::GC_V3:
|
|
case Version::GC_EP3_NTE:
|
|
case Version::GC_EP3:
|
|
return "console-login";
|
|
case Version::PC_NTE:
|
|
case Version::PC_V2:
|
|
return "pc-login";
|
|
case Version::XB_V3:
|
|
return "xb-login";
|
|
case Version::BB_V4:
|
|
return "bb-init";
|
|
default:
|
|
throw runtime_error("unknown version");
|
|
}
|
|
}
|
|
|
|
const char* lobby_port_name_for_version(Version v) {
|
|
switch (v) {
|
|
case Version::PC_PATCH:
|
|
return "pc-patch";
|
|
case Version::BB_PATCH:
|
|
return "bb-patch";
|
|
case Version::DC_NTE:
|
|
case Version::DC_V1_11_2000_PROTOTYPE:
|
|
case Version::DC_V1:
|
|
case Version::DC_V2:
|
|
case Version::GC_NTE:
|
|
case Version::GC_V3:
|
|
case Version::GC_EP3_NTE:
|
|
case Version::GC_EP3:
|
|
return "console-lobby";
|
|
case Version::PC_NTE:
|
|
case Version::PC_V2:
|
|
return "pc-lobby";
|
|
case Version::XB_V3:
|
|
return "xb-lobby";
|
|
case Version::BB_V4:
|
|
return "bb-lobby";
|
|
default:
|
|
throw runtime_error("unknown version");
|
|
}
|
|
}
|
|
|
|
const char* proxy_port_name_for_version(Version v) {
|
|
switch (v) {
|
|
case Version::PC_PATCH:
|
|
return "pc-patch";
|
|
case Version::BB_PATCH:
|
|
return "bb-patch";
|
|
case Version::DC_NTE:
|
|
case Version::DC_V1_11_2000_PROTOTYPE:
|
|
case Version::DC_V1:
|
|
case Version::DC_V2:
|
|
case Version::GC_NTE:
|
|
return "dc-proxy";
|
|
case Version::GC_V3:
|
|
case Version::GC_EP3_NTE:
|
|
case Version::GC_EP3:
|
|
return "gc-proxy";
|
|
case Version::PC_NTE:
|
|
case Version::PC_V2:
|
|
return "pc-proxy";
|
|
case Version::XB_V3:
|
|
return "xb-proxy";
|
|
case Version::BB_V4:
|
|
return "bb-proxy";
|
|
default:
|
|
throw runtime_error("unknown version");
|
|
}
|
|
}
|
|
|
|
template <>
|
|
const char* name_for_enum<Version>(Version v) {
|
|
switch (v) {
|
|
case Version::PC_PATCH:
|
|
return "PC_PATCH";
|
|
case Version::BB_PATCH:
|
|
return "BB_PATCH";
|
|
case Version::DC_NTE:
|
|
return "DC_NTE";
|
|
case Version::DC_V1_11_2000_PROTOTYPE:
|
|
return "DC_V1_11_2000_PROTOTYPE";
|
|
case Version::DC_V1:
|
|
return "DC_V1";
|
|
case Version::DC_V2:
|
|
return "DC_V2";
|
|
case Version::PC_NTE:
|
|
return "PC_NTE";
|
|
case Version::PC_V2:
|
|
return "PC_V2";
|
|
case Version::GC_NTE:
|
|
return "GC_NTE";
|
|
case Version::GC_V3:
|
|
return "GC_V3";
|
|
case Version::GC_EP3_NTE:
|
|
return "GC_EP3_NTE";
|
|
case Version::GC_EP3:
|
|
return "GC_EP3";
|
|
case Version::XB_V3:
|
|
return "XB_V3";
|
|
case Version::BB_V4:
|
|
return "BB_V4";
|
|
default:
|
|
throw runtime_error("unknown version");
|
|
}
|
|
}
|
|
|
|
template <>
|
|
Version enum_for_name<Version>(const char* name) {
|
|
if (!strcmp(name, "PC_PATCH") || !strcasecmp(name, "patch")) {
|
|
return Version::PC_PATCH;
|
|
} else if (!strcmp(name, "BB_PATCH")) {
|
|
return Version::BB_PATCH;
|
|
} else if (!strcmp(name, "DC_NTE")) {
|
|
return Version::DC_NTE;
|
|
} else if (!strcmp(name, "DC_V1_11_2000_PROTOTYPE")) {
|
|
return Version::DC_V1_11_2000_PROTOTYPE;
|
|
} else if (!strcmp(name, "DC_V1")) {
|
|
return Version::DC_V1;
|
|
} else if (!strcmp(name, "DC_V2") || !strcasecmp(name, "dc")) {
|
|
return Version::DC_V2;
|
|
} else if (!strcmp(name, "PC_NTE")) {
|
|
return Version::PC_NTE;
|
|
} else if (!strcmp(name, "PC_V2") || !strcasecmp(name, "pc")) {
|
|
return Version::PC_V2;
|
|
} else if (!strcmp(name, "GC_NTE")) {
|
|
return Version::GC_NTE;
|
|
} else if (!strcmp(name, "GC_V3") || !strcasecmp(name, "gc")) {
|
|
return Version::GC_V3;
|
|
} else if (!strcmp(name, "GC_EP3_NTE")) {
|
|
return Version::GC_EP3_NTE;
|
|
} else if (!strcmp(name, "GC_EP3")) {
|
|
return Version::GC_EP3;
|
|
} else if (!strcmp(name, "XB_V3") || !strcasecmp(name, "xb")) {
|
|
return Version::XB_V3;
|
|
} else if (!strcmp(name, "BB_V4") || !strcasecmp(name, "bb")) {
|
|
return Version::BB_V4;
|
|
} else {
|
|
throw invalid_argument("incorrect version name");
|
|
}
|
|
}
|
|
|
|
template <>
|
|
const char* name_for_enum<ServerBehavior>(ServerBehavior behavior) {
|
|
switch (behavior) {
|
|
case ServerBehavior::PC_CONSOLE_DETECT:
|
|
return "pc_console_detect";
|
|
case ServerBehavior::LOGIN_SERVER:
|
|
return "login_server";
|
|
case ServerBehavior::LOBBY_SERVER:
|
|
return "lobby_server";
|
|
case ServerBehavior::PATCH_SERVER_PC:
|
|
return "patch_server_pc";
|
|
case ServerBehavior::PATCH_SERVER_BB:
|
|
return "patch_server_bb";
|
|
case ServerBehavior::PROXY_SERVER:
|
|
return "proxy_server";
|
|
}
|
|
throw logic_error("invalid server behavior");
|
|
}
|
|
|
|
template <>
|
|
ServerBehavior enum_for_name<ServerBehavior>(const char* name) {
|
|
if (!strcasecmp(name, "pc_console_detect")) {
|
|
return ServerBehavior::PC_CONSOLE_DETECT;
|
|
} else if (!strcasecmp(name, "login_server") || !strcasecmp(name, "login") || !strcasecmp(name, "data_server_bb")) {
|
|
return ServerBehavior::LOGIN_SERVER;
|
|
} else if (!strcasecmp(name, "lobby_server") || !strcasecmp(name, "lobby")) {
|
|
return ServerBehavior::LOBBY_SERVER;
|
|
} else if (!strcasecmp(name, "patch_server_pc") || !strcasecmp(name, "patch_pc")) {
|
|
return ServerBehavior::PATCH_SERVER_PC;
|
|
} else if (!strcasecmp(name, "patch_server_bb") || !strcasecmp(name, "patch_bb")) {
|
|
return ServerBehavior::PATCH_SERVER_BB;
|
|
} else if (!strcasecmp(name, "proxy_server") || !strcasecmp(name, "proxy")) {
|
|
return ServerBehavior::PROXY_SERVER;
|
|
} else {
|
|
throw invalid_argument("incorrect server behavior name");
|
|
}
|
|
}
|
|
|
|
uint32_t default_specific_version_for_version(Version version, int64_t sub_version) {
|
|
// For versions that don't support send_function_call by default, we need
|
|
// to set the specific_version based on sub_version. Fortunately, all
|
|
// versions that share sub_version values also support send_function_call,
|
|
// so for those versions we get the specific_version later by sending the
|
|
// VersionDetectDC, VersionDetectGC, or VersionDetectXB call.
|
|
switch (version) {
|
|
case Version::DC_NTE:
|
|
return SPECIFIC_VERSION_DC_NTE;
|
|
case Version::DC_V1_11_2000_PROTOTYPE:
|
|
return SPECIFIC_VERSION_DC_11_2000_PROTOTYPE;
|
|
case Version::DC_V1:
|
|
return SPECIFIC_VERSION_DC_V1_INDETERMINATE; // Need to send VersionDetectDC (but can't on V1; rip)
|
|
case Version::DC_V2:
|
|
return SPECIFIC_VERSION_DC_V2_INDETERMINATE; // Need to send VersionDetectDC
|
|
case Version::PC_V2:
|
|
return SPECIFIC_VERSION_PC_V2;
|
|
case Version::GC_NTE:
|
|
return SPECIFIC_VERSION_GC_NTE; // 3OJT
|
|
case Version::GC_V3:
|
|
switch (sub_version) {
|
|
case 0x32: // GC Ep1&2 EU 50Hz
|
|
case 0x33: // GC Ep1&2 EU 60Hz
|
|
return SPECIFIC_VERSION_GC_V3_EU; // 3OP0
|
|
case 0x36: // GC Ep1&2 US v1.2 (Plus)
|
|
return SPECIFIC_VERSION_GC_V3_US_12; // 3OE2
|
|
case 0x34: // GC Ep1&2 JP v1.3
|
|
return SPECIFIC_VERSION_GC_V3_JP_13; // 3OJ3
|
|
case 0x35: // GC Ep1&2 JP v1.4 (Plus)
|
|
return SPECIFIC_VERSION_GC_V3_JP_14; // 3OJ4
|
|
case 0x39: // GC Ep1&2 JP v1.5 (Plus)
|
|
return SPECIFIC_VERSION_GC_V3_JP_15; // 3OJ5
|
|
case -1: // Initial check (before sub_version recognition)
|
|
case 0x30: // GC Ep1&2 GameJam demo, GC Ep1&2 Trial Edition, GC Ep1&2 JP v1.2, at least one version of PSO XB
|
|
case 0x31: // GC Ep1&2 US v1.0, GC US v1.1, XB US
|
|
default:
|
|
return SPECIFIC_VERSION_GC_V3_INDETERMINATE; // Need to send VersionDetectGC
|
|
}
|
|
throw logic_error("this should be impossible");
|
|
case Version::GC_EP3_NTE:
|
|
return SPECIFIC_VERSION_GC_EP3_NTE; // 3SJT
|
|
case Version::GC_EP3:
|
|
switch (sub_version) {
|
|
case 0x41: // GC Ep3 US
|
|
return SPECIFIC_VERSION_GC_EP3_US; // 3SE0
|
|
case 0x42: // GC Ep3 EU 50Hz
|
|
case 0x43: // GC Ep3 EU 60Hz
|
|
return SPECIFIC_VERSION_GC_EP3_EU; // 3SP0
|
|
case -1: // Initial check (before sub_version recognition)
|
|
case 0x40: // GC Ep3 trial and GC Ep3 JP
|
|
default:
|
|
return SPECIFIC_VERSION_GC_EP3_INDETERMINATE; // Need to send VersionDetectGC
|
|
}
|
|
case Version::XB_V3:
|
|
return SPECIFIC_VERSION_XB_V3_INDETERMINATE;
|
|
case Version::BB_V4:
|
|
return SPECIFIC_VERSION_BB_V4_INDETERMINATE;
|
|
default:
|
|
return SPECIFIC_VERSION_INDETERMINATE;
|
|
}
|
|
}
|
|
|
|
bool specific_version_is_indeterminate(uint32_t specific_version) {
|
|
return ((specific_version & 0x000000FF) == 0);
|
|
}
|
|
|
|
bool specific_version_is_dc(uint32_t specific_version) {
|
|
// All v1 and v2 specific_versions are DC except 324F4A57 (2OJW), which is PC
|
|
uint8_t major_version = specific_version >> 24;
|
|
if (major_version < 0x31 || major_version > 0x32) {
|
|
return false;
|
|
}
|
|
return (specific_version != 0x324F4A57);
|
|
}
|
|
|
|
bool specific_version_is_gc(uint32_t specific_version) {
|
|
// GC specific_versions are 3GRV, where G is [OE], R is [JEP], V is [0-9T]
|
|
if ((specific_version & 0xFF000000) != 0x33000000) {
|
|
return false;
|
|
}
|
|
char game = specific_version >> 16;
|
|
if ((game != 'O') && (game != 'S')) {
|
|
return false;
|
|
}
|
|
char region = specific_version >> 8;
|
|
if ((region != 'J') && (region != 'E') && (region != 'P')) {
|
|
return false;
|
|
}
|
|
char revision = specific_version;
|
|
return (isdigit(revision) || (revision == 'T'));
|
|
}
|
|
|
|
bool specific_version_is_xb(uint32_t specific_version) {
|
|
// XB specific_versions are 4ORV, where R is [JEP], V is [BDU]
|
|
if ((specific_version & 0xFFFF0000) != 0x344F0000) {
|
|
return false;
|
|
}
|
|
char region = specific_version >> 8;
|
|
if ((region != 'J') && (region != 'E') && (region != 'P')) {
|
|
return false;
|
|
}
|
|
char revision = specific_version;
|
|
return ((revision == 'B') || (revision == 'D') || (revision == 'U'));
|
|
}
|
|
|
|
bool specific_version_is_bb(uint32_t specific_version) {
|
|
// BB specific_versions are 5XXX, where X is an encoding of the revision number
|
|
return (specific_version & 0xFF000000) == 0x35000000;
|
|
}
|
|
|
|
const char* file_path_token_for_version(Version version) {
|
|
switch (version) {
|
|
case Version::PC_PATCH:
|
|
return "pc-patch";
|
|
case Version::BB_PATCH:
|
|
return "bb-patch";
|
|
case Version::DC_NTE:
|
|
return "dc-nte";
|
|
case Version::DC_V1_11_2000_PROTOTYPE:
|
|
return "dc-11-2000";
|
|
case Version::DC_V1:
|
|
return "dc-v1";
|
|
case Version::DC_V2:
|
|
return "dc-v2";
|
|
case Version::PC_NTE:
|
|
return "pc-nte";
|
|
case Version::PC_V2:
|
|
return "pc-v2";
|
|
case Version::GC_NTE:
|
|
return "gc-nte";
|
|
case Version::GC_V3:
|
|
return "gc-v3";
|
|
case Version::GC_EP3_NTE:
|
|
return "gc-ep3-nte";
|
|
case Version::GC_EP3:
|
|
return "gc-ep3";
|
|
case Version::XB_V3:
|
|
return "xb-v3";
|
|
case Version::BB_V4:
|
|
return "bb-v4";
|
|
default:
|
|
throw runtime_error("invalid game version");
|
|
}
|
|
}
|