update client functions for eventual pc v2 semantics

This commit is contained in:
Martin Michelsen
2025-02-16 22:38:59 -08:00
parent 984d8f0f31
commit 44ea82771b
20 changed files with 253 additions and 143 deletions
+3 -3
View File
@@ -1373,7 +1373,7 @@ ChatCommandDefinition cc_loadchar(
// TODO: Support extended player info on other versions
auto s = a.c->require_server_state();
if (!a.c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL) ||
a.c->config.check_flag(Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY)) {
!a.c->config.check_flag(Client::Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE)) {
throw precondition_failed_printf("Can\'t load character\ndata on this game\nversion");
}
@@ -1588,7 +1588,7 @@ ChatCommandDefinition cc_patch(
send_function_call(c, fn, pca.label_writes);
c->function_call_response_queue.emplace_back(empty_function_call_response_handler);
} catch (const out_of_range&) {
throw precondition_failed("Invalid patch name");
send_text_message(c, "$C6Invalid patch name");
}
});
},
@@ -1607,7 +1607,7 @@ ChatCommandDefinition cc_patch(
// Don't forward the patch response to the server
ses->function_call_return_handler_queue.emplace_back(empty_function_call_response_handler);
} catch (const out_of_range&) {
throw precondition_failed("Invalid patch name");
send_text_message(ses->client_channel, "$C6Invalid patch name");
}
};
+12 -2
View File
@@ -51,20 +51,23 @@ void Client::Config::set_flags_for_version(Version version, int64_t sub_version)
case Version::DC_V2:
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
case Version::PC_NTE:
case Version::PC_V2:
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY);
// SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE not set here
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
case Version::GC_NTE:
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE);
break;
case Version::GC_EP3_NTE:
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE);
this->set_flag(Flag::ENCRYPTED_SEND_FUNCTION_CALL);
break;
case Version::GC_V3:
@@ -76,6 +79,7 @@ void Client::Config::set_flags_for_version(Version version, int64_t sub_version)
// TODO: Do all versions of XB need this flag? US does, at least.
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
default:
@@ -95,27 +99,32 @@ void Client::Config::set_flags_for_version(Version version, int64_t sub_version)
case 0x28: // DCv2 EU 60Hz (presumably)
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
case 0x29: // PC
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY);
// SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE not set here
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
this->set_flag(Flag::CAN_RECEIVE_ENABLE_B2_QUEST);
break;
case 0x30: // GC Ep1&2 GameJam demo, GC Ep1&2 Trial Edition, GC Ep1&2 JP v1.2, XB JP
case 0x31: // GC Ep1&2 US v1.0, GC US v1.1, XB US
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE);
break;
case 0x32: // GC Ep1&2 EU 50Hz
case 0x33: // GC Ep1&2 EU 60Hz
case 0x34: // GC Ep1&2 JP v1.3
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE);
break;
case 0x35: // GC Ep1&2 JP v1.4 (Plus)
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE);
this->set_flag(Flag::ENCRYPTED_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
@@ -130,6 +139,7 @@ void Client::Config::set_flags_for_version(Version version, int64_t sub_version)
case 0x40: // GC Ep3 JP and Trial Edition (and BB)
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE);
this->set_flag(Flag::ENCRYPTED_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
// sub_version can't be used to tell JP final and Trial Edition apart; we
+1 -1
View File
@@ -46,7 +46,7 @@ public:
// Flags describing the behavior for send_function_call
HAS_SEND_FUNCTION_CALL = 0x0000000000001000,
ENCRYPTED_SEND_FUNCTION_CALL = 0x0000000000002000,
SEND_FUNCTION_CALL_CHECKSUM_ONLY = 0x0000000000004000,
SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE = 0x0000000000004000,
SEND_FUNCTION_CALL_NO_CACHE_PATCH = 0x0000000000008000,
CAN_RECEIVE_ENABLE_B2_QUEST = 0x0000000000020000,
AWAITING_ENABLE_B2_QUEST = 0x0000000000040000, // Server-side only
+6 -4
View File
@@ -311,7 +311,9 @@ FunctionCodeIndex::FunctionCodeIndex(const string& directory) {
arch = CompiledFunctionCode::Architecture::SH4;
} else if (specific_version_is_gc(specific_version)) {
arch = CompiledFunctionCode::Architecture::POWERPC;
} else if (specific_version_is_xb(specific_version) || specific_version_is_bb(specific_version)) {
} else if (specific_version_is_pc_v2(specific_version) ||
specific_version_is_xb(specific_version) ||
specific_version_is_bb(specific_version)) {
arch = CompiledFunctionCode::Architecture::X86;
} else {
throw runtime_error("unable to determine architecture from specific_version");
@@ -370,7 +372,7 @@ shared_ptr<const Menu> FunctionCodeIndex::patch_menu(uint32_t specific_version)
fn->menu_item_id,
fn->long_name.empty() ? fn->short_name : fn->long_name,
fn->description,
MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL);
MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL_RUNS_CODE);
}
return ret;
}
@@ -389,7 +391,7 @@ shared_ptr<const Menu> FunctionCodeIndex::patch_switches_menu(
string name;
name.push_back(auto_patches_enabled.count(fn->short_name) ? '*' : '-');
name += fn->long_name.empty() ? fn->short_name : fn->long_name;
ret->items.emplace_back(fn->menu_item_id, name, fn->description, MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL);
ret->items.emplace_back(fn->menu_item_id, name, fn->description, MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL_RUNS_CODE);
}
return ret;
}
@@ -479,7 +481,7 @@ DOLFileIndex::DOLFileIndex(const string& directory) {
this->name_to_file.emplace(dol->name, dol);
this->item_id_to_file.emplace_back(dol);
menu->items.emplace_back(dol->menu_item_id, dol->name, description, MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL);
menu->items.emplace_back(dol->menu_item_id, dol->name, description, MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL_RUNS_CODE);
} catch (const exception& e) {
function_compiler_log.warning("Failed to load DOL file %s: %s", filename.c_str(), e.what());
+1 -1
View File
@@ -125,7 +125,7 @@ struct MenuItem {
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 = 0x100,
REQUIRES_SEND_FUNCTION_CALL = 0x200,
REQUIRES_SEND_FUNCTION_CALL_RUNS_CODE = 0x200,
REQUIRES_SAVE_DISABLED = 0x400,
INVISIBLE_IN_INFO_MENU = 0x800,
};
+101 -17
View File
@@ -374,13 +374,13 @@ static void send_main_menu(shared_ptr<Client> c) {
if (!s->is_replay) {
if (!s->function_code_index->patch_menu_empty(c->config.specific_version)) {
main_menu->items.emplace_back(MainMenuItemID::PATCHES, "Patches",
"Change game\nbehaviors", MenuItem::Flag::INVISIBLE_ON_PC | MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL);
"Change game\nbehaviors", MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL_RUNS_CODE);
main_menu->items.emplace_back(MainMenuItemID::PATCH_SWITCHES, "Patch switches",
"Automatically\napply patches every\ntime you connect", MenuItem::Flag::INVISIBLE_ON_PC | MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL);
"Automatically\napply patches every\ntime you connect", MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL_RUNS_CODE);
}
if (!s->dol_file_index->empty()) {
main_menu->items.emplace_back(MainMenuItemID::PROGRAMS, "Programs",
"Run GameCube\nprograms", MenuItem::Flag::GC_ONLY | MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL | MenuItem::Flag::REQUIRES_SAVE_DISABLED);
"Run GameCube\nprograms", MenuItem::Flag::GC_ONLY | MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL_RUNS_CODE | MenuItem::Flag::REQUIRES_SAVE_DISABLED);
}
}
main_menu->items.emplace_back(MainMenuItemID::DISCONNECT, "Disconnect",
@@ -427,30 +427,44 @@ void on_login_complete(shared_ptr<Client> c) {
auto s = c->require_server_state();
if (c->config.check_flag(Client::Flag::CAN_RECEIVE_ENABLE_B2_QUEST) &&
!c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL)) {
(!c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL) ||
!c->config.check_flag(Client::Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE))) {
shared_ptr<const Quest> q;
try {
int64_t quest_num = s->enable_send_function_call_quest_numbers.at(c->config.specific_version);
q = s->default_quest_index->get(quest_num);
} catch (const out_of_range&) {
}
if (q) {
if (!q) {
c->log.info("There is no quest to enable server function calls for specific version %08" PRIX32, c->config.specific_version);
} else if (q) {
auto vq = q->version(is_ep3(c->version()) ? Version::GC_V3 : c->version(), 1);
if (vq) {
c->config.set_flag(Client::Flag::HAS_SEND_FUNCTION_CALL);
c->config.set_flag(Client::Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE);
c->config.set_flag(Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
c->config.set_flag(Client::Flag::AWAITING_ENABLE_B2_QUEST);
send_update_client_config(c, false);
c->log.info("Sending %c version of quest \"%s\"", char_for_language_code(vq->language), vq->name.c_str());
string bin_filename = vq->bin_filename();
string dat_filename = vq->dat_filename();
string xb_filename = vq->xb_filename();
send_open_quest_file(c, bin_filename, bin_filename, xb_filename, vq->quest_number, QuestFileType::ONLINE, vq->bin_contents);
send_open_quest_file(c, dat_filename, dat_filename, xb_filename, vq->quest_number, QuestFileType::ONLINE, vq->dat_contents);
// PCv2 will crash if it receives an online quest file while it's
// not in a game, so we have to put it into a fake game first
if (c->version() == Version::PC_V2) {
S_JoinGame_PC_64 cmd;
auto& lobby_data = cmd.lobby_data[0];
lobby_data.player_tag = 0x00010000;
lobby_data.guild_card_number = c->login->account->account_id;
send_command_t(c, 0x64, 0x01, cmd);
} else {
c->log.info("Sending %c version of quest \"%s\"", char_for_language_code(vq->language), vq->name.c_str());
string bin_filename = vq->bin_filename();
string dat_filename = vq->dat_filename();
string xb_filename = vq->xb_filename();
send_open_quest_file(c, bin_filename, bin_filename, xb_filename, vq->quest_number, QuestFileType::ONLINE, vq->bin_contents);
send_open_quest_file(c, dat_filename, dat_filename, xb_filename, vq->quest_number, QuestFileType::ONLINE, vq->dat_contents);
if (!is_v1_or_v2(c->version())) {
send_command(c, 0xAC, 0x00);
if (!is_v1_or_v2(c->version())) {
send_command(c, 0xAC, 0x00);
}
}
}
}
@@ -1066,7 +1080,43 @@ static void on_9D_9E(shared_ptr<Client> c, uint16_t command, uint32_t, string& d
c->should_disconnect = true;
} else {
send_update_client_config(c, true);
on_login_complete(c);
// On PCv2, we send a B2 command to get the client's specific_version and
// check whether it has patch support or not; we'll call on_login_complete
// once we receive the B3 response
if (c->version() == Version::PC_V2) {
try {
auto code = s->function_code_index->name_to_function.at("ReturnTokenX86");
send_function_call(c, code, {{"token", c->login->account->account_id}}, nullptr, 0, 0x00400000, 0x0000E000, 0, true);
c->function_call_response_queue.emplace_back([wc = weak_ptr<Client>(c)](uint32_t return_value, uint32_t checksum) -> void {
auto c = wc.lock();
if (!c) {
return;
}
if (!c->login) {
throw logic_error("received PC_V2 version detect response with no login");
}
if (return_value == c->login->account->account_id) {
// Client already has the patch that enables patches
c->config.set_flag(Client::Flag::HAS_SEND_FUNCTION_CALL);
c->config.set_flag(Client::Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE);
c->config.set_flag(Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
}
if (checksum == 0x3677024C) {
c->config.specific_version = SPECIFIC_VERSION_PC_V2_DEFAULT;
c->log.info("Version detected as %08" PRIX32 " from PE header checksum %08" PRIX32,
c->config.specific_version, checksum);
} else {
c->config.specific_version = SPECIFIC_VERSION_PC_V2_INDETERMINATE;
c->log.info("Version cannot be determined from PE header checksum %08" PRIX32, checksum);
}
on_login_complete(c);
});
} catch (const out_of_range&) {
on_login_complete(c);
}
} else {
on_login_complete(c);
}
}
}
@@ -4714,15 +4764,49 @@ static void on_8A(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
} else {
check_size_v(data.size(), 0);
auto l = c->require_lobby();
send_lobby_name(c, l->name.c_str());
auto l = c->lobby.lock();
if (!l) {
if (c->config.check_flag(Client::Flag::AWAITING_ENABLE_B2_QUEST)) {
send_lobby_name(c, "");
} else {
throw std::runtime_error("received 8A command from client not in any lobby");
}
} else {
send_lobby_name(c, l->name.c_str());
}
}
}
static void on_6F(shared_ptr<Client> c, uint16_t command, uint32_t, string& data) {
check_size_v(data.size(), 0);
auto l = c->require_lobby();
auto l = c->lobby.lock();
if (!l) {
if (!c->config.check_flag(Client::Flag::AWAITING_ENABLE_B2_QUEST)) {
throw runtime_error("client is not in any lobby and is not awaiting a patch enabler quest");
}
auto s = c->require_server_state();
shared_ptr<const Quest> q;
try {
int64_t quest_num = s->enable_send_function_call_quest_numbers.at(c->config.specific_version);
q = s->default_quest_index->get(quest_num);
} catch (const out_of_range&) {
throw std::logic_error("cannot find patch enable quest after it was previously found during login");
}
auto vq = q->version(is_ep3(c->version()) ? Version::GC_V3 : c->version(), 1);
if (!vq) {
throw std::logic_error("cannot find patch enable quest version after it was previously found during login");
}
c->log.info("Sending %c version of quest \"%s\"", char_for_language_code(vq->language), vq->name.c_str());
string bin_filename = vq->bin_filename();
string dat_filename = vq->dat_filename();
string xb_filename = vq->xb_filename();
send_open_quest_file(c, bin_filename, bin_filename, xb_filename, vq->quest_number, QuestFileType::ONLINE, vq->bin_contents);
send_open_quest_file(c, dat_filename, dat_filename, xb_filename, vq->quest_number, QuestFileType::ONLINE, vq->dat_contents);
return;
}
// Now l is not null
if (!l->is_game()) {
throw runtime_error("client sent ready command outside of game");
}
+14 -8
View File
@@ -335,6 +335,7 @@ void prepare_client_for_patches(shared_ptr<Client> c, function<void()> on_comple
if (!c) {
return;
}
const char* version_detect_name = nullptr;
if (c->version() == Version::DC_V2) {
version_detect_name = "VersionDetectDC";
@@ -447,7 +448,8 @@ void send_function_call(
size_t suffix_size,
uint32_t checksum_addr,
uint32_t checksum_size,
uint32_t override_relocations_offset) {
uint32_t override_relocations_offset,
bool ignore_actually_runs_code_flag) {
return send_function_call(
c->channel,
c->config,
@@ -457,7 +459,8 @@ void send_function_call(
suffix_size,
checksum_addr,
checksum_size,
override_relocations_offset);
override_relocations_offset,
ignore_actually_runs_code_flag);
}
void send_function_call(
@@ -469,11 +472,14 @@ void send_function_call(
size_t suffix_size,
uint32_t checksum_addr,
uint32_t checksum_size,
uint32_t override_relocations_offset) {
uint32_t override_relocations_offset,
bool ignore_actually_runs_code_flag) {
if (!client_config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL)) {
throw logic_error("client does not support function calls");
}
if (code.get() && client_config.check_flag(Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY)) {
if (!ignore_actually_runs_code_flag &&
code.get() &&
!client_config.check_flag(Client::Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE)) {
throw logic_error("client only supports checksums in send_function_call");
}
@@ -508,7 +514,7 @@ bool send_protected_command(std::shared_ptr<Client> c, const void* data, size_t
auto s = c->require_server_state();
if (!s->enable_v3_v4_protected_subcommands ||
!c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL) ||
c->config.check_flag(Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY)) {
!c->config.check_flag(Client::Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE)) {
return false;
}
@@ -1426,8 +1432,8 @@ void send_menu_t(shared_ptr<Client> c, shared_ptr<const Menu> menu, bool is_info
if (item.flags & MenuItem::Flag::REQUIRES_MESSAGE_BOXES) {
is_visible &= !c->config.check_flag(Client::Flag::NO_D6);
}
if (item.flags & MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL) {
is_visible &= c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL);
if (item.flags & MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL_RUNS_CODE) {
is_visible &= (c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL) && c->config.check_flag(Client::Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE));
}
if (item.flags & MenuItem::Flag::REQUIRES_SAVE_DISABLED) {
is_visible &= !c->config.check_flag(Client::Flag::SAVE_ENABLED);
@@ -2402,7 +2408,7 @@ void send_get_player_info(shared_ptr<Client> c, bool request_extended) {
if (request_extended &&
c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL) &&
!c->config.check_flag(Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY)) {
c->config.check_flag(Client::Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE)) {
auto s = c->require_server_state();
prepare_client_for_patches(c, [wc = weak_ptr<Client>(c)]() {
auto c = wc.lock();
+4 -2
View File
@@ -169,7 +169,8 @@ void send_function_call(
size_t suffix_size = 0,
uint32_t checksum_addr = 0,
uint32_t checksum_size = 0,
uint32_t override_relocations_offset = 0);
uint32_t override_relocations_offset = 0,
bool ignore_actually_runs_code_flag = false);
void send_function_call(
std::shared_ptr<Client> c,
std::shared_ptr<const CompiledFunctionCode> code,
@@ -178,7 +179,8 @@ void send_function_call(
size_t suffix_size = 0,
uint32_t checksum_addr = 0,
uint32_t checksum_size = 0,
uint32_t override_relocations_offset = 0);
uint32_t override_relocations_offset = 0,
bool ignore_actually_runs_code_flag = false);
bool send_protected_command(std::shared_ptr<Client> c, const void* data, size_t size, bool echo_to_lobby);
void send_reconnect(std::shared_ptr<Client> c, uint32_t address, uint16_t port);
+6 -2
View File
@@ -253,7 +253,7 @@ uint32_t default_specific_version_for_version(Version version, int64_t sub_versi
case Version::DC_V2:
return SPECIFIC_VERSION_DC_V2_INDETERMINATE; // 2___; need to send VersionDetectDC
case Version::PC_V2:
return SPECIFIC_VERSION_PC_V2; // 2OJW
return SPECIFIC_VERSION_PC_V2_INDETERMINATE; // 2OJ_; need to send checksum command
case Version::GC_NTE:
return SPECIFIC_VERSION_GC_NTE; // 3OJT
case Version::GC_V3:
@@ -310,7 +310,11 @@ bool specific_version_is_dc(uint32_t specific_version) {
if (major_version < 0x31 || major_version > 0x32) {
return false;
}
return (specific_version != 0x324F4A57);
return (specific_version != SPECIFIC_VERSION_PC_V2_DEFAULT);
}
bool specific_version_is_pc_v2(uint32_t specific_version) {
return (specific_version == SPECIFIC_VERSION_PC_V2_DEFAULT);
}
bool specific_version_is_gc(uint32_t specific_version) {
+3 -1
View File
@@ -191,7 +191,8 @@ constexpr uint32_t SPECIFIC_VERSION_DC_V1_US = 0x314F4546; // 1OEF
constexpr uint32_t SPECIFIC_VERSION_DC_V1_EU_INDETERMINATE = 0x314F5000; // 1OP_
constexpr uint32_t SPECIFIC_VERSION_DC_V1_INDETERMINATE = 0x31000000; // 1___
constexpr uint32_t SPECIFIC_VERSION_DC_V2_INDETERMINATE = 0x32000000; // 2___
constexpr uint32_t SPECIFIC_VERSION_PC_V2 = 0x324F4A57; // 2OJW
constexpr uint32_t SPECIFIC_VERSION_PC_V2_INDETERMINATE = 0x324F4A00; // 2OJW
constexpr uint32_t SPECIFIC_VERSION_PC_V2_DEFAULT = 0x324F4A57; // 2OJW
constexpr uint32_t SPECIFIC_VERSION_GC_NTE = 0x334F4A54; // 3OJT
constexpr uint32_t SPECIFIC_VERSION_GC_V3_EU = 0x334F5030; // 3OP0
constexpr uint32_t SPECIFIC_VERSION_GC_V3_US_12 = 0x334F4532; // 3OE2
@@ -212,6 +213,7 @@ uint32_t default_sub_version_for_version(Version version);
uint32_t default_specific_version_for_version(Version version, int64_t sub_version);
bool specific_version_is_indeterminate(uint32_t specific_version);
bool specific_version_is_dc(uint32_t specific_version);
bool specific_version_is_pc_v2(uint32_t specific_version);
bool specific_version_is_gc(uint32_t specific_version);
bool specific_version_is_xb(uint32_t specific_version);
bool specific_version_is_bb(uint32_t specific_version);