From 1dbdd3f1917976c0895f0702f0c04049b4713078 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Fri, 30 Dec 2022 23:05:50 -0800 Subject: [PATCH] add infinite ep3 meseta and ability to save media updates --- src/Client.cc | 1 + src/Client.hh | 1 + src/Menu.hh | 1 + src/ProxyCommands.cc | 61 +++++++++++++++++++++++++++++++++++++++--- src/ReceiveCommands.cc | 42 ++++++++++++++++------------- 5 files changed, 85 insertions(+), 21 deletions(-) diff --git a/src/Client.cc b/src/Client.cc index 450ed14b..296b1622 100644 --- a/src/Client.cc +++ b/src/Client.cc @@ -38,6 +38,7 @@ ClientOptions::ClientOptions() enable_chat_filter(true), suppress_remote_login(false), zero_remote_guild_card(false), + ep3_infinite_meseta(false), function_call_return_value(-1) { } diff --git a/src/Client.hh b/src/Client.hh index 977e42ef..268f0e49 100644 --- a/src/Client.hh +++ b/src/Client.hh @@ -40,6 +40,7 @@ struct ClientOptions { bool enable_chat_filter; bool suppress_remote_login; bool zero_remote_guild_card; + bool ep3_infinite_meseta; int64_t function_call_return_value; // -1 = don't block function calls ClientOptions(); diff --git a/src/Menu.hh b/src/Menu.hh index 4855a3f2..e5e48977 100644 --- a/src/Menu.hh +++ b/src/Menu.hh @@ -67,6 +67,7 @@ namespace ProxyOptionsMenuItemID { constexpr uint32_t SAVE_FILES = 0xAA6666AA; constexpr uint32_t SUPPRESS_LOGIN = 0xAA7777AA; constexpr uint32_t SKIP_CARD = 0xAA8888AA; + constexpr uint32_t EP3_INFINITE_MESETA = 0xAA9999AA; } diff --git a/src/ProxyCommands.cc b/src/ProxyCommands.cc index 9473ef61..0b848557 100644 --- a/src/ProxyCommands.cc +++ b/src/ProxyCommands.cc @@ -1101,8 +1101,15 @@ static HandlerResult S_13_A7(shared_ptr, } static HandlerResult S_G_B7(shared_ptr, - ProxyServer::LinkedSession& session, uint16_t, uint32_t, string&) { + ProxyServer::LinkedSession& session, uint16_t, uint32_t, string& data) { if (session.newserv_client_config.cfg.flags & Client::Flag::IS_EPISODE_3) { + if (session.options.ep3_infinite_meseta) { + auto& cmd = check_size_t(data); + if (cmd.meseta != 1000000) { + cmd.meseta = 1000000; + return HandlerResult::Type::MODIFIED; + } + } return HandlerResult::Type::FORWARD; } else { session.server_channel.send(0xB7, 0x00); @@ -1139,6 +1146,54 @@ static HandlerResult S_G_B8(shared_ptr, : HandlerResult::Type::SUPPRESS; } +static HandlerResult S_G_B9(shared_ptr, + ProxyServer::LinkedSession& session, uint16_t, uint32_t, string& data) { + + if (session.options.save_files) { + try { + const auto& header = check_size_t(data, + sizeof(S_UpdateMediaHeader_GC_Ep3_B9), 0xFFFF); + + if (data.size() - sizeof(header) < header.size) { + throw runtime_error("Media data size extends beyond end of command; not saving file"); + } + + string decompressed_data = prs_decompress( + data.data() + sizeof(header), data.size() - sizeof(header)); + + string output_filename = string_printf("media-update.%" PRIu64, now()); + if (header.type == 1) { + output_filename += ".gvm"; + } else if (header.type == 2 || header.type == 3) { + output_filename += ".bml"; + } else { + output_filename += ".bin"; + } + save_file(output_filename, decompressed_data); + session.log.info("Wrote %zu bytes to %s", + decompressed_data.size(), output_filename.c_str()); + } catch (const exception& e) { + session.log.warning("Failed to save file: %s", e.what()); + } + } + + return (session.newserv_client_config.cfg.flags & Client::Flag::IS_EPISODE_3) + ? HandlerResult::Type::FORWARD + : HandlerResult::Type::SUPPRESS; +} + +static HandlerResult S_G_BA(shared_ptr, + ProxyServer::LinkedSession& session, uint16_t, uint32_t, string& data) { + if (session.options.ep3_infinite_meseta) { + auto& cmd = check_size_t(data); + if (cmd.remaining_meseta != 1000000) { + cmd.remaining_meseta = 1000000; + return HandlerResult::Type::MODIFIED; + } + } + return HandlerResult::Type::FORWARD; +} + static void update_leader_id(ProxyServer::LinkedSession& session, uint8_t leader_id) { if (session.leader_client_id != leader_id) { session.leader_client_id = leader_id; @@ -1654,8 +1709,8 @@ static on_command_t handlers[0x100][6][2] = { /* B6 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}}, /* B7 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_G_B7, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}}, /* B8 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_G_B8, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}}, -/* B9 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}}, -/* BA */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}}, +/* B9 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_G_B9, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}}, +/* BA */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_G_BA, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}}, /* BB */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}}, /* BC */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}}, /* BD */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}}, diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index b8032060..1fee620c 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -76,6 +76,7 @@ static const unordered_map proxy_options_menu_descrip {ProxyOptionsMenuItemID::INFINITE_HP, u"Enable automatic HP\nrestoration when\nyou are hit by an\nenemy or trap\n\nCannot revive you\nfrom one-hit kills"}, {ProxyOptionsMenuItemID::INFINITE_TP, u"Enable automatic TP\nrestoration when\nyou cast any\ntechnique"}, {ProxyOptionsMenuItemID::SWITCH_ASSIST, u"Automatically try\nto unlock 2-player\ndoors when you step\non both switches\nsequentially"}, + {ProxyOptionsMenuItemID::EP3_INFINITE_MESETA, u"Fix Meseta value\nat 1,000,000"}, {ProxyOptionsMenuItemID::BLOCK_EVENTS, u"Disable seasonal\nevents in the lobby\nand in games"}, {ProxyOptionsMenuItemID::BLOCK_PATCHES, u"Disable patches sent\nby the remote server"}, {ProxyOptionsMenuItemID::SAVE_FILES, u"Save local copies of\nfiles from the remote\nserver (quests, etc.)"}, @@ -91,30 +92,32 @@ static vector proxy_options_menu_for_client( // way in which the menu abstraction is currently insufficient (there is a // TODO about this in README.md). ret.emplace_back(ProxyOptionsMenuItemID::GO_BACK, u"Go back", u"", 0); - ret.emplace_back(ProxyOptionsMenuItemID::CHAT_FILTER, - c->options.enable_chat_filter ? u"Chat filter ON" : u"Chat filter OFF", u"", 0); + + auto add_option = [&](uint32_t item_id, bool is_enabled, const char16_t* text) -> void { + u16string option = is_enabled ? u"* " : u"- "; + option += text; + ret.emplace_back(item_id, option, u"", 0); + }; + + add_option(ProxyOptionsMenuItemID::CHAT_FILTER, c->options.enable_chat_filter, u"Chat filter"); if (!(c->flags & Client::Flag::IS_EPISODE_3)) { - ret.emplace_back(ProxyOptionsMenuItemID::INFINITE_HP, - c->options.infinite_hp ? u"Infinite HP ON" : u"Infinite HP OFF", u"", 0); - ret.emplace_back(ProxyOptionsMenuItemID::INFINITE_TP, - c->options.infinite_tp ? u"Infinite TP ON" : u"Infinite TP OFF", u"", 0); - ret.emplace_back(ProxyOptionsMenuItemID::SWITCH_ASSIST, - c->options.switch_assist ? u"Switch assist ON" : u"Switch assist OFF", u"", 0); + add_option(ProxyOptionsMenuItemID::INFINITE_HP, c->options.infinite_hp, u"Infinite HP"); + add_option(ProxyOptionsMenuItemID::INFINITE_TP, c->options.infinite_tp, u"Infinite TP"); + add_option(ProxyOptionsMenuItemID::SWITCH_ASSIST, c->options.switch_assist, u"Switch assist"); + } else { + // Note: Thie option's text is the maximum possible length for any menu item + add_option(ProxyOptionsMenuItemID::EP3_INFINITE_MESETA, c->options.ep3_infinite_meseta, u"Infinite Meseta"); } - ret.emplace_back(ProxyOptionsMenuItemID::BLOCK_EVENTS, - (c->options.override_lobby_event >= 0) ? u"Block events ON" : u"Block events OFF", u"", 0); - ret.emplace_back(ProxyOptionsMenuItemID::BLOCK_PATCHES, - (c->options.function_call_return_value >= 0) ? u"Block patches ON" : u"Block patches OFF", u"", 0); + add_option(ProxyOptionsMenuItemID::BLOCK_EVENTS, (c->options.override_lobby_event >= 0), u"Block events"); + add_option(ProxyOptionsMenuItemID::BLOCK_PATCHES, (c->options.function_call_return_value >= 0), u"Block patches"); if (s->proxy_allow_save_files) { - ret.emplace_back(ProxyOptionsMenuItemID::SAVE_FILES, - c->options.save_files ? u"Save files ON" : u"Save files OFF", u"", 0); + add_option(ProxyOptionsMenuItemID::SAVE_FILES, c->options.save_files, u"Save files"); } if (s->proxy_enable_login_options) { - ret.emplace_back(ProxyOptionsMenuItemID::SUPPRESS_LOGIN, - c->options.suppress_remote_login ? u"Skip login ON" : u"Skip login OFF", u"", 0); - ret.emplace_back(ProxyOptionsMenuItemID::SKIP_CARD, - c->options.zero_remote_guild_card ? u"Skip card ON" : u"Skip card OFF", u"", 0); + add_option(ProxyOptionsMenuItemID::SUPPRESS_LOGIN, c->options.suppress_remote_login, u"Skip login"); + add_option(ProxyOptionsMenuItemID::SKIP_CARD, c->options.zero_remote_guild_card, u"Skip card"); } + return ret; } @@ -1801,6 +1804,9 @@ static void on_10(shared_ptr s, shared_ptr c, case ProxyOptionsMenuItemID::SWITCH_ASSIST: c->options.switch_assist = !c->options.switch_assist; goto resend_proxy_options_menu; + case ProxyOptionsMenuItemID::EP3_INFINITE_MESETA: + c->options.ep3_infinite_meseta = !c->options.ep3_infinite_meseta; + goto resend_proxy_options_menu; case ProxyOptionsMenuItemID::BLOCK_EVENTS: if (c->options.override_lobby_event >= 0) { c->options.override_lobby_event = -1;