From 68003b2e2fea70bfe5ab04608e3f47619a936d4e Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Wed, 8 Jan 2025 23:35:12 -0800 Subject: [PATCH] unify menu item format --- src/CommandFormats.hh | 62 ++++++++++++++++++--------------------- src/DownloadSession.cc | 6 ++-- src/HTTPServer.hh | 4 ++- src/PSOEncryption.cc | 8 ++--- src/ReceiveSubcommands.cc | 3 +- src/SendCommands.cc | 19 ++++++------ 6 files changed, 48 insertions(+), 54 deletions(-) diff --git a/src/CommandFormats.hh b/src/CommandFormats.hh index 68184ac1..14615047 100644 --- a/src/CommandFormats.hh +++ b/src/CommandFormats.hh @@ -515,50 +515,37 @@ struct S_UpdateClientConfig_BB_04 { // there was a separate command to send the block list, but it was scrapped. // Perhaps this was used for command A1, which is identical to 07 and A0 in all // versions of PSO (except DC NTE). -// The menu is titles "Ship Select" unless the first menu item begins with the +// The menu is titled "Ship Select" unless the first menu item begins with the // text "BLOCK" (all caps), in which case it is titled "Block Select". // Command is a list of these; header.flag is the entry count. The first entry -// is not included in the count and does not appear on the client. The text of -// the first entry becomes the ship name when the client joins a lobby. -template -struct S_MenuEntryT { +// is not included in the count and does not appear on the client. The first +// entry's text becomes the ship name displayed in the corner of the lobby HUD, +// unless it begins with "BLOCK", in which case it is ignored. +template +struct S_MenuItemT { le_uint32_t menu_id = 0; le_uint32_t item_id = 0; - le_uint16_t flags = 0x0F04; // Should be this value, apparently - pstring text; -} __packed__; -using S_MenuEntry_PC_BB_07_1F = S_MenuEntryT; -using S_MenuEntry_DC_V3_07_1F = S_MenuEntryT; -check_struct_size(S_MenuEntry_PC_BB_07_1F, 0x2C); -check_struct_size(S_MenuEntry_DC_V3_07_1F, 0x1C); -// 08 (C->S): Request game list -// Internal name: SndGameList -// No arguments - -// 08 (S->C): Game list -// Internal name: RcvGameList -// Client responds with 09 and 10 commands (or nothing if the player cancels). - -// Command is a list of these; header.flag is the entry count. The first entry -// is not included in the count and does not appear on the client. -template -struct S_GameMenuEntryT { - le_uint32_t menu_id = 0; - le_uint32_t game_id = 0; + // The following two fields are only used for the game menu; for Ship Select, + // Block Select, and Information, they are unused. // difficulty_tag is 0x0A on Episode 3; on all other versions, it's - // difficulty + 0x22 (so 0x25 means Ultimate, for example) + // difficulty + 0x22 (so 0x25 means Ultimate, for example). uint8_t difficulty_tag = 0; uint8_t num_players = 0; + pstring name; + + // The episode field is only used for the game menu; for Ship Select, Block + // Select, and Information, it is unused. // The episode field is used differently by different versions: // - On DCv1, PC, and GC Episode 3, the value is ignored. // - On DCv2, 1 means v1 players can't join the game, and 0 means they can. // - On GC Ep1&2, 0x40 means Episode 1, and 0x41 means Episode 2. // - On BB, 0x40/0x41 mean Episodes 1/2 as on GC, and 0x43 means Episode 4. uint8_t episode = 0; - // Flags: + // Flags (01 and 02 are used for all menus; the rest are only used for the + // game menu): // 01 = Send name? (client sends the name field in the 10 command if this // item is chosen, but it's blank) // 02 = Locked (lock icon appears in menu; player is prompted for password if @@ -567,16 +554,25 @@ struct S_GameMenuEntryT { // 04 = Disabled (BB; used for solo games) // 10 = Is battle mode // 20 = Is challenge mode - // 40 = Is v2 only (DCv2/PC); name renders in orange + // 40 = Is v2 only (DCv2/PC); game name renders in orange // 40 = Is Episode 1 (V3/BB) // 80 = Is Episode 2 (V3/BB) // C0 = Is Episode 4 (BB) uint8_t flags = 0; } __packed__; -using S_GameMenuEntry_PC_BB_08 = S_GameMenuEntryT; -using S_GameMenuEntry_DC_V3_08_Ep3_E6 = S_GameMenuEntryT; -check_struct_size(S_GameMenuEntry_PC_BB_08, 0x2C); -check_struct_size(S_GameMenuEntry_DC_V3_08_Ep3_E6, 0x1C); +using S_MenuItem_PC_BB_08 = S_MenuItemT; +using S_MenuItem_DC_V3_08_Ep3_E6 = S_MenuItemT; +check_struct_size(S_MenuItem_PC_BB_08, 0x2C); +check_struct_size(S_MenuItem_DC_V3_08_Ep3_E6, 0x1C); + +// 08 (C->S): Request game list +// Internal name: SndGameList +// No arguments + +// 08 (S->C): Game list +// Internal name: RcvGameList +// Client responds with 09 and 10 commands (or nothing if the player cancels). +// Command format is the same as 07. // 09 (C->S): Menu item info request // Internal name: SndInfo diff --git a/src/DownloadSession.cc b/src/DownloadSession.cc index 88d16e3b..ada5a035 100644 --- a/src/DownloadSession.cc +++ b/src/DownloadSession.cc @@ -484,7 +484,7 @@ void DownloadSession::on_channel_input(uint16_t command, uint32_t flag, std::str this->log.info("Ship Select menu:"); for (item_index = 1; item_index <= flag; item_index++) { const auto& item = items[item_index]; - auto text = strip_color(item.text.decode()); + auto text = strip_color(item.name.decode()); this->log.info("%zu: (%08" PRIX32 " %08" PRIX32 ") %s", item_index, item.menu_id.load(), item.item_id.load(), text.c_str()); if (this->ship_menu_selections.count(text)) { break; @@ -506,9 +506,9 @@ void DownloadSession::on_channel_input(uint16_t command, uint32_t flag, std::str }; if (uses_utf16(this->channel.version)) { - handle_command.operator()(); + handle_command.operator()(); } else { - handle_command.operator()(); + handle_command.operator()(); } this->channel.send(0x10, 0x00, ret); diff --git a/src/HTTPServer.hh b/src/HTTPServer.hh index a1080f37..382afdc4 100644 --- a/src/HTTPServer.hh +++ b/src/HTTPServer.hh @@ -13,8 +13,10 @@ class HTTPServer { public: - // shared_base should be null unless + // shared_base should be null unless the HTTP server should run on the main + // thread (on Windows). HTTPServer(std::shared_ptr state, std::shared_ptr shared_base); + HTTPServer(const HTTPServer&) = delete; HTTPServer(HTTPServer&&) = delete; HTTPServer& operator=(const HTTPServer&) = delete; diff --git a/src/PSOEncryption.cc b/src/PSOEncryption.cc index 5f807d55..c683173b 100644 --- a/src/PSOEncryption.cc +++ b/src/PSOEncryption.cc @@ -885,19 +885,15 @@ uint32_t encrypt_challenge_time(uint16_t value) { available_bits.erase(it); } - uint32_t ret = (mask << 16) | (value ^ mask); - fprintf(stderr, "encrypt_challenge_time %04hX => %08" PRIX32 "\n", value, ret); - return ret; + return (mask << 16) | (value ^ mask); } uint16_t decrypt_challenge_time(uint32_t value) { uint16_t mask = (value >> 0x10); uint8_t mask_one_bits = count_one_bits(mask); - uint16_t ret = ((mask_one_bits < 4) || (mask_one_bits > 12)) + return ((mask_one_bits < 4) || (mask_one_bits > 12)) ? 0xFFFF : ((mask ^ value) & 0xFFFF); - fprintf(stderr, "decrypt_challenge_time %08" PRIX32 " => %04hX\n", value, ret); - return ret; } string decrypt_v2_registry_value(const void* data, size_t size) { diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index 767c4853..ff83685e 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -391,7 +391,6 @@ void forward_subcommand_with_entity_id_transcode_t( lc->log.info("Subcommand cannot be translated to client\'s version"); } } else { - fprintf(stderr, "NOCOMMIT: same version\n"); send_command_t(lc, command, flag, cmd); } } @@ -2903,7 +2902,7 @@ static void on_entity_drop_item_request(shared_ptr c, uint8_t command, u return; case Lobby::DropMode::CLIENT: { // If the leader is BB, use SERVER_SHARED instead - // NOCOMMIT: We should also use server drops if any clients have incompatible object lists, since they might generate incorrect IDs for items and we can't override them + // TODO: We should also use server drops if any clients have incompatible object lists, since they might generate incorrect IDs for items and we can't override them auto leader = l->clients[l->leader_id]; if (leader && leader->version() == Version::BB_V4) { drop_mode = Lobby::DropMode::SERVER_SHARED; diff --git a/src/SendCommands.cc b/src/SendCommands.cc index dc4be521..a66cae74 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -1385,8 +1385,7 @@ void send_menu_t(shared_ptr c, shared_ptr menu, bool is_info auto& e = entries.emplace_back(); e.menu_id = menu->menu_id; e.item_id = 0xFFFFFFFF; - e.flags = 0x0004; - e.text.encode(menu->name, c->language()); + e.name.encode(menu->name, c->language()); } for (const auto& item : menu->items) { @@ -1440,8 +1439,10 @@ void send_menu_t(shared_ptr c, shared_ptr menu, bool is_info auto& e = entries.emplace_back(); e.menu_id = menu->menu_id; e.item_id = item.item_id; - e.flags = (c->version() == Version::BB_V4) ? 0x0004 : 0x0F04; - e.text.encode(item.name, c->language()); + e.name.encode(item.name, c->language()); + e.difficulty_tag = 0x04; + e.num_players = (c->version() == Version::BB_V4) ? 0x00 : 0x0F; + e.flags = 0xFF; } } @@ -1451,9 +1452,9 @@ void send_menu_t(shared_ptr c, shared_ptr menu, bool is_info void send_menu(shared_ptr c, shared_ptr menu, bool is_info_menu) { if (uses_utf16(c->version())) { - send_menu_t(c, menu, is_info_menu); + send_menu_t(c, menu, is_info_menu); } else { - send_menu_t(c, menu, is_info_menu); + send_menu_t(c, menu, is_info_menu); } } @@ -1464,11 +1465,11 @@ void send_game_menu_t( bool show_tournaments_only) { auto s = c->require_server_state(); - vector> entries; + vector> entries; { auto& e = entries.emplace_back(); e.menu_id = MenuID::GAME; - e.game_id = 0x00000000; + e.item_id = 0x00000000; e.difficulty_tag = 0x00; e.num_players = 0x00; e.name.encode(s->name, c->language()); @@ -1512,7 +1513,7 @@ void send_game_menu_t( auto& e = entries.emplace_back(); e.menu_id = MenuID::GAME; - e.game_id = l->lobby_id; + e.item_id = l->lobby_id; e.difficulty_tag = (is_ep3(c->version()) ? 0x0A : (l->difficulty + 0x22)); e.num_players = l->count_clients(); if (is_dc(c->version())) {