From 3fff023cad4ded7424c291dd1c5a81c3ba595126 Mon Sep 17 00:00:00 2001 From: GabsPuNs Date: Sun, 14 Jun 2026 17:26:03 -0400 Subject: [PATCH] Fixes and implement WideToUtf8 and Utf8ToWide from Minecraft.Server * MessageBox now uses WideToUtf8. This fixes RUS and ES languages. * Fixed OnButtonClicked. --- Minecraft.Client/Common/Minecraft_Utils.cpp | 45 ++++++++++ Minecraft.Client/Common/Minecraft_Utils.h | 3 + .../Common/UI/UIScene_MessageBox.cpp | 88 ++++++++++--------- Minecraft.Client/Extrax64Stubs.cpp | 4 +- .../Windows64/RmlUi_SystemInterface_Win64.cpp | 12 +-- 5 files changed, 100 insertions(+), 52 deletions(-) diff --git a/Minecraft.Client/Common/Minecraft_Utils.cpp b/Minecraft.Client/Common/Minecraft_Utils.cpp index b3886f87..7f13602f 100644 --- a/Minecraft.Client/Common/Minecraft_Utils.cpp +++ b/Minecraft.Client/Common/Minecraft_Utils.cpp @@ -11,3 +11,48 @@ fs::path CMinecraftUtils::GetExeDir() GetModuleFileNameW(nullptr, szExePath, MAX_PATH); return fs::path(szExePath).parent_path(); } + +std::string CMinecraftUtils::WideToUtf8(const std::wstring &value) +{ + if (value.empty()) + return std::string(); + + int charCount = WideCharToMultiByte(CP_UTF8, 0, value.c_str(), (int)value.length(), nullptr, 0, nullptr, nullptr); + if (charCount <= 0) + return std::string(); + + std::string utf8; + utf8.resize(charCount); + WideCharToMultiByte(CP_UTF8, 0, value.c_str(), (int)value.length(), &utf8[0], charCount, nullptr, nullptr); + return utf8; +} + +std::wstring CMinecraftUtils::Utf8ToWide(const char *value) +{ + if (value == nullptr || value[0] == 0) + return std::wstring(); + + int wideCount = MultiByteToWideChar(CP_UTF8, 0, value, -1, nullptr, 0); + if (wideCount <= 0) + { + // Fall back to the current ANSI code page so legacy non-UTF-8 inputs remain readable. + wideCount = MultiByteToWideChar(CP_ACP, 0, value, -1, nullptr, 0); + if (wideCount <= 0) + return std::wstring(); + + std::wstring wide; + wide.resize(wideCount - 1); + MultiByteToWideChar(CP_ACP, 0, value, -1, &wide[0], wideCount); + return wide; + } + + std::wstring wide; + wide.resize(wideCount - 1); + MultiByteToWideChar(CP_UTF8, 0, value, -1, &wide[0], wideCount); + return wide; +} + +std::wstring CMinecraftUtils::Utf8ToWide(const std::string &value) +{ + return Utf8ToWide(value.c_str()); +} \ No newline at end of file diff --git a/Minecraft.Client/Common/Minecraft_Utils.h b/Minecraft.Client/Common/Minecraft_Utils.h index d6873a38..5c0b12ea 100644 --- a/Minecraft.Client/Common/Minecraft_Utils.h +++ b/Minecraft.Client/Common/Minecraft_Utils.h @@ -6,6 +6,9 @@ public: CMinecraftUtils(); fs::path GetExeDir(); + std::string WideToUtf8(const std::wstring &value); + std::wstring Utf8ToWide(const char *value); + std::wstring Utf8ToWide(const std::string &value); }; extern CMinecraftUtils utils; diff --git a/Minecraft.Client/Common/UI/UIScene_MessageBox.cpp b/Minecraft.Client/Common/UI/UIScene_MessageBox.cpp index 3b1b0e5b..77a0b907 100644 --- a/Minecraft.Client/Common/UI/UIScene_MessageBox.cpp +++ b/Minecraft.Client/Common/UI/UIScene_MessageBox.cpp @@ -48,22 +48,16 @@ UIScene_MessageBox::UIScene_MessageBox(int iPad, void *initData, UILayer *parent app.DebugPrintf("[RmlMsgBox] Document loaded OK\n"); - m_document->GetElementById("button0")->AddEventListener(Rml::EventId::Click, this); - m_document->GetElementById("button1")->AddEventListener(Rml::EventId::Click, this); - m_document->GetElementById("button2")->AddEventListener(Rml::EventId::Click, this); - m_document->GetElementById("button3")->AddEventListener(Rml::EventId::Click, this); - wstring titleText = app.GetString(param->uiTitle); wstring descText = app.GetString(param->uiText); - auto* titleEl = m_document->GetElementById("title"); - auto* descEl = m_document->GetElementById("description"); + Rml::String titleUtf8 = utils.WideToUtf8(titleText); + Rml::String descUtf8 = utils.WideToUtf8(descText); - Rml::String titleUtf8(titleText.begin(), titleText.end()); - Rml::String descUtf8(descText.begin(), descText.end()); - - if (titleEl) titleEl->SetInnerRML(titleUtf8); - if (descEl) descEl->SetInnerRML(descUtf8); + if (auto* titleEl = m_document->GetElementById("title")) + titleEl->SetInnerRML(titleUtf8); + if (auto* descEl = m_document->GetElementById("description")) + descEl->SetInnerRML(descUtf8); int buttonIndex = 0; Rml::Element* buttons[4] = { @@ -76,35 +70,27 @@ UIScene_MessageBox::UIScene_MessageBox(int iPad, void *initData, UILayer *parent for (int i = 0; i < 4; i++) { if (buttons[i]) + { + buttons[i]->AddEventListener(Rml::EventId::Click, this); buttons[i]->SetProperty("display", "none"); + } } - if (m_buttonCount > 3 && buttons[0]) + int startIndex = 4 - m_buttonCount; + if (startIndex < 0) + startIndex = 0; + + int labelIndex = 0; + for (int i = startIndex; i < 4; i++) { - wstring label = app.GetString(param->uiOptionA[buttonIndex]); - buttons[0]->SetInnerRML(Rml::String(label.begin(), label.end())); - buttons[0]->SetProperty("display", "block"); - ++buttonIndex; - } - if (m_buttonCount > 2 && buttons[1]) - { - wstring label = app.GetString(param->uiOptionA[buttonIndex]); - buttons[1]->SetInnerRML(Rml::String(label.begin(), label.end())); - buttons[1]->SetProperty("display", "block"); - ++buttonIndex; - } - if (m_buttonCount > 1 && buttons[2]) - { - wstring label = app.GetString(param->uiOptionA[buttonIndex]); - buttons[2]->SetInnerRML(Rml::String(label.begin(), label.end())); - buttons[2]->SetProperty("display", "block"); - ++buttonIndex; - } - if (buttons[3]) - { - wstring label = app.GetString(param->uiOptionA[buttonIndex]); - buttons[3]->SetInnerRML(Rml::String(label.begin(), label.end())); - buttons[3]->SetProperty("display", "block"); + if (buttons[i]) + { + wstring label = app.GetString(param->uiOptionA[labelIndex]); + Rml::String labelUtf8 = utils.WideToUtf8(label); + buttons[i]->SetInnerRML(labelUtf8); + buttons[i]->SetProperty("display", "block"); + labelIndex++; + } } int focusButton = param->dwFocusButton; @@ -157,6 +143,10 @@ void UIScene_MessageBox::reloadMovie(bool force) { } +//GabsPuN: When Iggy is completely replaced, some functions should be moved to UIScene.cpp. +//This way, the tick() function won't be repeated in every UIScene_ cpp file. +//Also, since iggy no longer exists, we can take liberties with optimization or code +//that is easier to work with. void UIScene_MessageBox::tick() { m_hasTickedOnce = true; @@ -223,7 +213,10 @@ void UIScene_MessageBox::handleInput(int iPad, int key, bool repeat, bool presse int tmpPad = m_iPad; navigateBack(); - if (tmpFunc) tmpFunc(tmpParam, tmpPad, result); + + if (tmpFunc) + tmpFunc(tmpParam, tmpPad, result); + handled = true; break; } @@ -234,11 +227,11 @@ void UIScene_MessageBox::handleInput(int iPad, int key, bool repeat, bool presse bool UIScene_MessageBox::hasFocus(int iPad) { - if (m_iPad == 255) + if (m_iPad == 255) // Message box is for everyone return bHasFocus; - else if (ProfileManager.IsSignedIn(m_iPad)) + else if (ProfileManager.IsSignedIn(m_iPad)) // Owner is still present return bHasFocus && (iPad == m_iPad); - else + else // Original owner has left so let everyone interact return bHasFocus; } @@ -257,8 +250,16 @@ void UIScene_MessageBox::ProcessEvent(Rml::Event& event) void UIScene_MessageBox::OnButtonClicked(int buttonIndex) { + //GabsPuN: Something is wrong with the IDs. This fixed it but + //I don't know if this is the right way to do it + int startIndex = 4 - m_buttonCount; + if (startIndex < 0) + startIndex = 0; + + int RButtonIndex = buttonIndex - startIndex; + C4JStorage::EMessageResult result = C4JStorage::EMessage_Cancelled; - switch (buttonIndex) + switch (RButtonIndex) { case 0: result = C4JStorage::EMessage_ResultAccept; break; case 1: result = C4JStorage::EMessage_ResultDecline; break; @@ -273,7 +274,8 @@ void UIScene_MessageBox::OnButtonClicked(int buttonIndex) int tmpPad = m_iPad; navigateBack(); - if (tmpFunc) tmpFunc(tmpParam, tmpPad, result); + if (tmpFunc) + tmpFunc(tmpParam, tmpPad, result); } void UIScene_MessageBox::navigateBack() diff --git a/Minecraft.Client/Extrax64Stubs.cpp b/Minecraft.Client/Extrax64Stubs.cpp index d240579c..9ec024e4 100644 --- a/Minecraft.Client/Extrax64Stubs.cpp +++ b/Minecraft.Client/Extrax64Stubs.cpp @@ -641,7 +641,9 @@ char* C_4JProfile::GetGamertag(int iPad) { !IQNet::m_player[iPad].m_isRemote) { static char s_padGamertag[XUSER_MAX_COUNT][17]; - WideCharToMultiByte(CP_ACP, 0, IQNet::m_player[iPad].m_gamertag, -1, s_padGamertag[iPad], 17, nullptr, nullptr); + std::string utf8Tag = utils.WideToUtf8(IQNet::m_player[iPad].m_gamertag); + strncpy(s_padGamertag[iPad], utf8Tag.c_str(), 16); + s_padGamertag[iPad][16] = '\0'; return s_padGamertag[iPad]; } return g_Win64Username; diff --git a/Minecraft.Client/Windows64/RmlUi_SystemInterface_Win64.cpp b/Minecraft.Client/Windows64/RmlUi_SystemInterface_Win64.cpp index f618825b..bb4db3c7 100644 --- a/Minecraft.Client/Windows64/RmlUi_SystemInterface_Win64.cpp +++ b/Minecraft.Client/Windows64/RmlUi_SystemInterface_Win64.cpp @@ -38,19 +38,15 @@ int SystemInterface_Win64::TranslateString(Rml::String& translated, const Rml::S if (end != Rml::String::npos) { Rml::String key = input.substr(i + 1, end - i - 1); - int wlen = MultiByteToWideChar(CP_UTF8, 0, key.c_str(), (int)key.size(), nullptr, 0); - if (wlen > 0) + std::wstring wkey = utils.Utf8ToWide(key); + if (!wkey.empty()) { - std::wstring wkey(wlen, L'\0'); - MultiByteToWideChar(CP_UTF8, 0, key.c_str(), (int)key.size(), &wkey[0], wlen); LPCWSTR wstr = app.GetString(wkey); if (wstr && wcslen(wstr) > 0) { - int utf8len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, nullptr, 0, nullptr, nullptr); - if (utf8len > 0) + std::string utf8 = utils.WideToUtf8(wstr); + if (!utf8.empty()) { - std::string utf8(utf8len - 1, '\0'); - WideCharToMultiByte(CP_UTF8, 0, wstr, -1, &utf8[0], utf8len, nullptr, nullptr); result += utf8; count++; }