diff --git a/src/CommandFormats.hh b/src/CommandFormats.hh index 4bc8f6a6..ceff8afb 100644 --- a/src/CommandFormats.hh +++ b/src/CommandFormats.hh @@ -856,7 +856,8 @@ struct S_ReconnectSplit_19 { // the chat log window contents will appear in the message box, prepended to // the message text from the command. // The maximum length of the message is 0x400 bytes. This is the only -// difference between this command and the D5 command. +// difference between this command and the D5 command (except on BB - see the +// notes on D5 for more information). // 1B (S->C): Valid but ignored (all versions) // Internal name: RcvBattleData @@ -2765,7 +2766,10 @@ struct SC_TradeItems_D0_D3 { // D0 when sent by client, D3 when sent by server // See D0 description for usage information. // D5: Large message box (V3/BB) -// Same as 1A command, except the maximum length of the message is 0x1000 bytes. +// Same as 1A command, except the maximum length of the message is 0x1000 +// bytes. On BB, this command is not valid during the data server phase +// (whereas 1A is valid there). The BB client ignores all D5 commands after the +// first one sent in each connection; this logic does not apply to 1A. // D6 (C->S): Large message box closed (V3) // No arguments diff --git a/src/SendCommands.cc b/src/SendCommands.cc index 08b62cbe..d2f92ac0 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -870,32 +870,49 @@ static void send_header_text(std::shared_ptr ch, uint16_t command, uint } void send_message_box(shared_ptr c, const string& text) { - uint16_t command; - switch (c->version()) { - case Version::PC_PATCH: - case Version::BB_PATCH: - command = 0x13; - break; - case Version::DC_NTE: - case Version::DC_11_2000: - case Version::DC_V1: - case Version::DC_V2: - case Version::PC_NTE: - case Version::PC_V2: - command = 0x1A; - break; - case Version::GC_NTE: - case Version::GC_V3: - case Version::GC_EP3_NTE: - case Version::GC_EP3: - case Version::XB_V3: - case Version::BB_V4: - command = 0xD5; - break; - default: - throw logic_error("invalid game version"); + if (is_v4(c->version())) { + phosg::StringWriter w; + try { + w.write(tt_encode_marked_optional(add_color(text), c->language(), true)); + } catch (const runtime_error& e) { + phosg::log_warning_f("Failed to encode text for message box command: {}", e.what()); + return; + } + w.put_u16(0); + while (w.str().size() & 3) { + w.put_u8(0); + } + send_command(c, (w.size() <= 0x400) ? 0x1A : 0xD5, 0x00, w.str()); + + } else { + uint16_t command; + switch (c->version()) { + case Version::PC_PATCH: + case Version::BB_PATCH: + command = 0x13; + break; + case Version::DC_NTE: + case Version::DC_11_2000: + case Version::DC_V1: + case Version::DC_V2: + case Version::PC_NTE: + case Version::PC_V2: + command = 0x1A; + break; + case Version::GC_NTE: + case Version::GC_V3: + case Version::GC_EP3_NTE: + case Version::GC_EP3: + case Version::XB_V3: + command = 0xD5; + break; + case Version::BB_V4: + throw std::logic_error("BB not handled before version switch"); + default: + throw logic_error("invalid game version"); + } + send_text(c->channel, command, 0x00, text, ColorMode::ADD); } - send_text(c->channel, command, 0x00, text, ColorMode::ADD); } void send_ep3_timed_message_box(std::shared_ptr ch, uint32_t frames, const string& message) {