diff --git a/Minecraft.Client/ChatScreen.cpp b/Minecraft.Client/ChatScreen.cpp index 53c90722..d1a6784b 100644 --- a/Minecraft.Client/ChatScreen.cpp +++ b/Minecraft.Client/ChatScreen.cpp @@ -14,7 +14,12 @@ wstring ChatScreen::s_historyDraft; bool ChatScreen::isAllowedChatChar(wchar_t c) { - return c >= 0x20 && (c == L'\u00A7' || allowedChars.empty() || allowedChars.find(c) != wstring::npos); + if (c < 0x20) return false; + // Block Unicode bidirectional override characters that can be used to + // spoof chat messages or impersonate players. + if (c >= 0x202A && c <= 0x202E) return false; // LRE, RLE, PDF, LRO, RLO + if (c >= 0x2066 && c <= 0x2069) return false; // LRI, RLI, FSI, PDI + return true; } ChatScreen::ChatScreen() @@ -93,6 +98,9 @@ void ChatScreen::keyPressed(wchar_t ch, int eventKey) if (eventKey == Keyboard::KEY_RETURN) { wstring trim = trimString(message); + { char buf[64]; sprintf_s(buf, "[CHAT] Sending (%d chars): ", (int)trim.length()); OutputDebugStringA(buf); } + OutputDebugStringW(trim.c_str()); + OutputDebugStringA("\n"); if (trim.length() > 0) { if (!minecraft->handleClientSideCommand(trim)) @@ -135,6 +143,7 @@ void ChatScreen::keyPressed(wchar_t ch, int eventKey) { message.insert(cursorIndex, 1, ch); cursorIndex++; + { char buf[64]; sprintf_s(buf, "[CHAT] Char U+%04X accepted (%d chars)\n", (unsigned)ch, (int)message.length()); OutputDebugStringA(buf); } } } diff --git a/Minecraft.Client/ClientConnection.cpp b/Minecraft.Client/ClientConnection.cpp index 5d9559ae..29a6c903 100644 --- a/Minecraft.Client/ClientConnection.cpp +++ b/Minecraft.Client/ClientConnection.cpp @@ -3315,7 +3315,9 @@ void ClientConnection::handleTileEditorOpen(shared_ptr pac void ClientConnection::handleSignUpdate(shared_ptr packet) { - app.DebugPrintf("ClientConnection::handleSignUpdate - "); + app.DebugPrintf("[SIGN] handleSignUpdate at (%d, %d, %d):\n", packet->x, packet->y, packet->z); + for (int i = 0; i < MAX_SIGN_LINES; i++) + app.DebugPrintf("[SIGN] Line%d: \"%ls\"\n", i+1, packet->lines[i].c_str()); if (minecraft->level->hasChunkAt(packet->x, packet->y, packet->z)) { shared_ptr te = minecraft->level->getTileEntity(packet->x, packet->y, packet->z); @@ -3329,7 +3331,7 @@ void ClientConnection::handleSignUpdate(shared_ptr packet) ste->SetMessage(i,packet->lines[i]); } - app.DebugPrintf("verified = %d\tCensored = %d\n",packet->m_bVerified,packet->m_bCensored); + app.DebugPrintf("[SIGN] verified=%d censored=%d\n", packet->m_bVerified, packet->m_bCensored); ste->SetVerified(packet->m_bVerified); ste->SetCensored(packet->m_bCensored); @@ -3337,12 +3339,12 @@ void ClientConnection::handleSignUpdate(shared_ptr packet) } else { - app.DebugPrintf("dynamic_pointer_cast(te) == nullptr\n"); + app.DebugPrintf("[SIGN] ERROR: tile entity is not a SignTileEntity\n"); } } else { - app.DebugPrintf("hasChunkAt failed\n"); + app.DebugPrintf("[SIGN] ERROR: chunk not loaded at position\n"); } } diff --git a/Minecraft.Client/Common/UI/UIBitmapFont.cpp b/Minecraft.Client/Common/UI/UIBitmapFont.cpp index 2b1518c4..52b97b9d 100644 --- a/Minecraft.Client/Common/UI/UIBitmapFont.cpp +++ b/Minecraft.Client/Common/UI/UIBitmapFont.cpp @@ -141,6 +141,9 @@ S32 UIBitmapFont::GetCodepointGlyph(U32 codepoint) // 4J-JEV: Change "right single quotation marks" to apostrophies. if (codepoint == 0x2019) codepoint = 0x27; + if (!m_cFontData->hasGlyph(codepoint)) + return IGGY_GLYPH_INVALID; + return m_cFontData->getGlyphId(codepoint); } @@ -253,19 +256,6 @@ rrbool UIBitmapFont::GetGlyphBitmap(S32 glyph,F32 pixel_scale,IggyBitmapCharacte while ( (0.5f + glyphScale) * truePixelScale < pixel_scale) glyphScale++; - // Debug: log each unique (font, pixel_scale) pair - { - static std::unordered_set s_loggedScaleKeys; - // Encode font pointer + quantized scale into a key to log each combo once - int scaleKey = (int)(pixel_scale * 100.0f) ^ (int)(uintptr_t)m_cFontData; - if (s_loggedScaleKeys.find(scaleKey) == s_loggedScaleKeys.end() && s_loggedScaleKeys.size() < 50) { - s_loggedScaleKeys.insert(scaleKey); - float tps = truePixelScale; - app.DebugPrintf("[FONT-DBG] GetGlyphBitmap: font=%s glyph=%d pixel_scale=%.3f truePixelScale=%.1f glyphScale=%.0f\n", - m_cFontData->getFontName().c_str(), glyph, pixel_scale, tps, glyphScale); - } - } - // 4J-JEV: Debug code to check which font sizes are being used. #if (!defined _CONTENT_PACKAGE) && (VERBOSE_FONT_OUTPUT > 0) diff --git a/Minecraft.Client/Common/UI/UIController.cpp b/Minecraft.Client/Common/UI/UIController.cpp index b12ea5e7..ecaaffe7 100644 --- a/Minecraft.Client/Common/UI/UIController.cpp +++ b/Minecraft.Client/Common/UI/UIController.cpp @@ -13,6 +13,7 @@ #include "..\..\EnderDragonRenderer.h" #include "..\..\MultiPlayerLocalPlayer.h" #include "UIFontData.h" +#include "UIUnicodeBitmapFont.h" #include "UISplitScreenHelpers.h" #ifdef _WINDOWS64 #include "..\..\Windows64\KeyboardMouseInput.h" @@ -193,6 +194,7 @@ UIController::UIController() m_mcTTFFont = nullptr; m_moj7 = nullptr; m_moj11 = nullptr; + m_unicodeBitmapFont = nullptr; // 4J-JEV: It's important that these remain the same, unless updateCurrentLanguage is going to be called. m_eCurrentFont = m_eTargetFont = eFont_NotLoaded; @@ -307,6 +309,14 @@ void UIController::postInit() IggySetAS3ExternalFunctionCallbackUTF16 ( &UIController::ExternalFunctionCallback, this ); IggySetTextureSubstitutionCallbacks ( &UIController::TextureSubstitutionCreateCallback , &UIController::TextureSubstitutionDestroyCallback, this ); + // Load a unicode bitmap font as Iggy's global fallback for characters not + // covered by the Mojangles bitmap font (CJK, Thai, Arabic, Korean, etc.). + // Uses the same glyph page PNGs as the legacy Font class, with matching + // Mojangles metrics for correct vertical alignment. + m_unicodeBitmapFont = new UIUnicodeBitmapFont("Mojangles_Unicode_Bitmap", SFontData::Mojangles_7); + m_unicodeBitmapFont->registerFont(); + IggyFontSetFallbackFontUTF8("Mojangles_Unicode_Bitmap", -1, IGGY_FONTFLAG_none); + SetupFont(); // loadSkins(); diff --git a/Minecraft.Client/Common/UI/UIController.h b/Minecraft.Client/Common/UI/UIController.h index 08a5ba08..4a189d4d 100644 --- a/Minecraft.Client/Common/UI/UIController.h +++ b/Minecraft.Client/Common/UI/UIController.h @@ -7,6 +7,7 @@ using namespace std; class UIAbstractBitmapFont; class UIBitmapFont; +class UIUnicodeBitmapFont; class UITTFFont; class UIComponent_DebugUIConsole; class UIComponent_DebugUIMarketingGuide; @@ -63,6 +64,7 @@ private: UIAbstractBitmapFont *m_mcBitmapFont; UITTFFont *m_mcTTFFont; UIBitmapFont *m_moj7, *m_moj11; + UIUnicodeBitmapFont *m_unicodeBitmapFont; std::mt19937 m_randomGenerator; std::uniform_real_distribution m_randomDistribution; diff --git a/Minecraft.Client/Common/UI/UIFontData.cpp b/Minecraft.Client/Common/UI/UIFontData.cpp index 715d08dd..dc9f70cd 100644 --- a/Minecraft.Client/Common/UI/UIFontData.cpp +++ b/Minecraft.Client/Common/UI/UIFontData.cpp @@ -335,6 +335,11 @@ bool CFontData::unicodeIsWhitespace(unsigned int unicode) return false; } +bool CFontData::hasGlyph(unsigned int unicodepoint) +{ + return m_unicodeMap.find(unicodepoint) != m_unicodeMap.end(); +} + void CFontData::moveCursor(unsigned char *&cursor, unsigned int dx, unsigned int dy) { cursor += (dy * m_sFontData->m_uiGlyphMapX) + dx; diff --git a/Minecraft.Client/Common/UI/UIFontData.h b/Minecraft.Client/Common/UI/UIFontData.h index b7e38ffa..7c4ac861 100644 --- a/Minecraft.Client/Common/UI/UIFontData.h +++ b/Minecraft.Client/Common/UI/UIFontData.h @@ -125,6 +125,9 @@ public: // Returns true if this unicodepoint is whitespace bool unicodeIsWhitespace(unsigned int unicodepoint); + // Returns true if this unicodepoint exists in the font's glyph map. + bool hasGlyph(unsigned int unicodepoint); + private: // Move a pointer in an image dx pixels right and dy pixels down, wrap around in either dimension leads to unknown behaviour. diff --git a/Minecraft.Client/Common/UI/UITTFFont.cpp b/Minecraft.Client/Common/UI/UITTFFont.cpp index 359ac76b..17765b08 100644 --- a/Minecraft.Client/Common/UI/UITTFFont.cpp +++ b/Minecraft.Client/Common/UI/UITTFFont.cpp @@ -4,7 +4,7 @@ #include "..\..\..\Minecraft.World\File.h" #include "UITTFFont.h" -UITTFFont::UITTFFont(const string &name, const string &path, S32 fallbackCharacter) +UITTFFont::UITTFFont(const string &name, const string &path, S32 fallbackCharacter, bool registerAsDefaultFonts) : m_strFontName(name) { app.DebugPrintf("UITTFFont opening %s\n",path.c_str()); @@ -41,9 +41,12 @@ UITTFFont::UITTFFont(const string &name, const string &path, S32 fallbackCharact IggyFontInstallTruetypeFallbackCodepointUTF8( m_strFontName.c_str(), -1, IGGY_FONTFLAG_none, fallbackCharacter ); - // 4J Stu - These are so we can use the default flash controls - IggyFontInstallTruetypeUTF8 ( (void *)pbData, IGGY_TTC_INDEX_none, "Times New Roman", -1, IGGY_FONTFLAG_none ); - IggyFontInstallTruetypeUTF8 ( (void *)pbData, IGGY_TTC_INDEX_none, "Arial", -1, IGGY_FONTFLAG_none ); + if (registerAsDefaultFonts) + { + // 4J Stu - These are so we can use the default flash controls + IggyFontInstallTruetypeUTF8 ( (void *)pbData, IGGY_TTC_INDEX_none, "Times New Roman", -1, IGGY_FONTFLAG_none ); + IggyFontInstallTruetypeUTF8 ( (void *)pbData, IGGY_TTC_INDEX_none, "Arial", -1, IGGY_FONTFLAG_none ); + } } } diff --git a/Minecraft.Client/Common/UI/UITTFFont.h b/Minecraft.Client/Common/UI/UITTFFont.h index 023bd51b..f33ef846 100644 --- a/Minecraft.Client/Common/UI/UITTFFont.h +++ b/Minecraft.Client/Common/UI/UITTFFont.h @@ -9,7 +9,7 @@ private: //DWORD dwDataSize; public: - UITTFFont(const string &name, const string &path, S32 fallbackCharacter); + UITTFFont(const string &name, const string &path, S32 fallbackCharacter, bool registerAsDefaultFonts = true); ~UITTFFont(); string getFontName(); diff --git a/Minecraft.Client/Common/UI/UIUnicodeBitmapFont.cpp b/Minecraft.Client/Common/UI/UIUnicodeBitmapFont.cpp new file mode 100644 index 00000000..6900558b --- /dev/null +++ b/Minecraft.Client/Common/UI/UIUnicodeBitmapFont.cpp @@ -0,0 +1,164 @@ +#include "stdafx.h" +#include "BufferedImage.h" +#include "UIFontData.h" +#include "UIUnicodeBitmapFont.h" + +UIUnicodeBitmapFont::UIUnicodeBitmapFont(const string &fontname, SFontData &referenceFontData) + : UIAbstractBitmapFont(fontname) +{ + m_numGlyphs = 65536; + m_referenceFontData = &referenceFontData; + memset(m_glyphPages, 0, sizeof(m_glyphPages)); + memset(m_unicodeWidth, 0, sizeof(m_unicodeWidth)); + + FILE *f = nullptr; + fopen_s(&f, "Common/res/1_2_2/font/glyph_sizes.bin", "rb"); + if (f) + { + fread(m_unicodeWidth, 1, 65536, f); + fclose(f); + } +} + +UIUnicodeBitmapFont::~UIUnicodeBitmapFont() +{ + for (int i = 0; i < 256; i++) + delete[] m_glyphPages[i]; +} + +void UIUnicodeBitmapFont::loadGlyphPage(int page) +{ + wchar_t fileName[64]; + swprintf(fileName, 64, L"/1_2_2/font/glyph_%02X.png", page); + BufferedImage bimg(fileName); + int *rawData = bimg.getData(); + if (!rawData) return; + + int size = 256 * 256; + m_glyphPages[page] = new unsigned char[size]; + for (int i = 0; i < size; i++) + m_glyphPages[page][i] = (rawData[i] & 0xFF000000) >> 24; +} + +IggyFontMetrics *UIUnicodeBitmapFont::GetFontMetrics(IggyFontMetrics *metrics) +{ + metrics->ascent = m_referenceFontData->m_fAscent; + metrics->descent = m_referenceFontData->m_fDescent; + metrics->average_glyph_width_for_tab_stops = 8.0f; + metrics->largest_glyph_bbox_y1 = metrics->descent; + return metrics; +} + +S32 UIUnicodeBitmapFont::GetCodepointGlyph(U32 codepoint) +{ + if (codepoint < 65536 && m_unicodeWidth[codepoint] != 0) + return (S32)codepoint; + return IGGY_GLYPH_INVALID; +} + +IggyGlyphMetrics *UIUnicodeBitmapFont::GetGlyphMetrics(S32 glyph, IggyGlyphMetrics *metrics) +{ + if (glyph < 0 || glyph >= 65536) { metrics->x0 = metrics->x1 = metrics->advance = metrics->y0 = metrics->y1 = 0; return metrics; } + int left = m_unicodeWidth[glyph] >> 4; + int right = (m_unicodeWidth[glyph] & 0xF) + 1; + float pixelWidth = (right - left) / 2.0f + 1.0f; + float advance = pixelWidth * m_referenceFontData->m_fAdvPerPixel; + + metrics->x0 = 0.0f; + metrics->x1 = advance; + metrics->advance = advance; + metrics->y0 = 0.0f; + metrics->y1 = 1.0f; + return metrics; +} + +rrbool UIUnicodeBitmapFont::IsGlyphEmpty(S32 glyph) +{ + if (glyph < 0 || glyph >= 65536) return true; + return m_unicodeWidth[glyph] == 0; +} + +F32 UIUnicodeBitmapFont::GetKerningForGlyphPair(S32 first_glyph, S32 second_glyph) +{ + return 0.0f; +} + +rrbool UIUnicodeBitmapFont::CanProvideBitmap(S32 glyph, F32 pixel_scale) +{ + return glyph >= 0 && glyph < 65536; +} + +rrbool UIUnicodeBitmapFont::GetGlyphBitmap(S32 glyph, F32 pixel_scale, IggyBitmapCharacter *bitmap) +{ + if (glyph < 0 || glyph >= 65536) return false; + int page = glyph / 256; + if (!m_glyphPages[page]) + { + loadGlyphPage(page); + if (!m_glyphPages[page]) return false; + } + + int cx = (glyph % 16) * 16; + int cy = ((glyph & 0xFF) / 16) * 16; + + bitmap->pixels_one_per_byte = m_glyphPages[page] + (cy * 256) + cx; + bitmap->width_in_pixels = 16; + bitmap->height_in_pixels = 16; + bitmap->stride_in_bytes = 256; + + bitmap->top_left_x = 0; + bitmap->top_left_y = -static_cast(16) * m_referenceFontData->m_fAscent; + + bitmap->oversample = 0; + + // Scale parameters: match UIBitmapFont's approach. + // truePixelScale = the pixel_scale at which 1 glyph pixel = 1 screen pixel. + // For 16px glyphs displayed at the same visual size as Mojangles_7 (8px glyphs with advPerPixel 1/10): + // The reference truePixelScale for Mojangles_7 is 1.0f/m_fAdvPerPixel = 10.0f + // Since our glyphs are 16px (2x the Mojangles 8px), our truePixelScale is 20.0f + float truePixelScale = 2.0f / m_referenceFontData->m_fAdvPerPixel; + +#ifdef _WINDOWS64 + bitmap->pixel_scale_correct = truePixelScale; + if (pixel_scale < truePixelScale) + { + bitmap->pixel_scale_min = 0.0f; + bitmap->pixel_scale_max = truePixelScale; + bitmap->point_sample = false; + } + else + { + bitmap->pixel_scale_min = truePixelScale; + bitmap->pixel_scale_max = 99.0f; + bitmap->point_sample = true; + } +#else + float glyphScale = 1.0f; + while ((0.5f + glyphScale) * truePixelScale < pixel_scale) + glyphScale++; + + if (glyphScale <= 1 && pixel_scale < truePixelScale) + { + bitmap->pixel_scale_correct = truePixelScale; + bitmap->pixel_scale_min = 0.0f; + bitmap->pixel_scale_max = truePixelScale * 1.001f; + bitmap->point_sample = false; + } + else + { + float actualScale = pixel_scale / glyphScale; + bitmap->pixel_scale_correct = actualScale; + bitmap->pixel_scale_min = truePixelScale; + bitmap->pixel_scale_max = 99.0f; + bitmap->point_sample = true; + } +#endif + + bitmap->user_context_for_free = nullptr; + return true; +} + +void UIUnicodeBitmapFont::FreeGlyphBitmap(S32 glyph, F32 pixel_scale, IggyBitmapCharacter *bitmap) +{ + // Pixel data lives in m_glyphPages -- nothing to free. +} diff --git a/Minecraft.Client/Common/UI/UIUnicodeBitmapFont.h b/Minecraft.Client/Common/UI/UIUnicodeBitmapFont.h new file mode 100644 index 00000000..53649b69 --- /dev/null +++ b/Minecraft.Client/Common/UI/UIUnicodeBitmapFont.h @@ -0,0 +1,27 @@ +#pragma once +#include "UIBitmapFont.h" + +struct SFontData; + +class UIUnicodeBitmapFont : public UIAbstractBitmapFont +{ +private: + unsigned char m_unicodeWidth[65536]; + unsigned char* m_glyphPages[256]; + SFontData* m_referenceFontData; + + void loadGlyphPage(int page); + +public: + UIUnicodeBitmapFont(const string &fontname, SFontData &referenceFontData); + ~UIUnicodeBitmapFont(); + + virtual IggyFontMetrics *GetFontMetrics(IggyFontMetrics *metrics); + virtual S32 GetCodepointGlyph(U32 codepoint); + virtual IggyGlyphMetrics *GetGlyphMetrics(S32 glyph, IggyGlyphMetrics *metrics); + virtual rrbool IsGlyphEmpty(S32 glyph); + virtual F32 GetKerningForGlyphPair(S32 first_glyph, S32 second_glyph); + virtual rrbool CanProvideBitmap(S32 glyph, F32 pixel_scale); + virtual rrbool GetGlyphBitmap(S32 glyph, F32 pixel_scale, IggyBitmapCharacter *bitmap); + virtual void FreeGlyphBitmap(S32 glyph, F32 pixel_scale, IggyBitmapCharacter *bitmap); +}; diff --git a/Minecraft.Client/Font.cpp b/Minecraft.Client/Font.cpp index db2a18c0..b49328e3 100644 --- a/Minecraft.Client/Font.cpp +++ b/Minecraft.Client/Font.cpp @@ -16,7 +16,7 @@ Font::Font(Options *options, const wstring& name, Textures* textures, bool enfor charWidths = new int[charC]; // 4J - added initialisers - memset(charWidths, 0, charC); + memset(charWidths, 0, charC * sizeof(int)); enforceUnicodeSheet = false; bidirectional = false; @@ -26,6 +26,19 @@ Font::Font(Options *options, const wstring& name, Textures* textures, bool enfor m_underline = false; m_strikethrough = false; + memset(unicodeTexID, 0, sizeof(unicodeTexID)); + memset(unicodeWidth, 0, sizeof(unicodeWidth)); + lastBoundTexture = 0; + + // Load unicode glyph sizes + FILE *glyphFile = nullptr; + fopen_s(&glyphFile, "Common/res/1_2_2/font/glyph_sizes.bin", "rb"); + if (glyphFile) + { + fread(unicodeWidth, 1, 65536, glyphFile); + fclose(glyphFile); + } + // Set up member variables m_cols = cols; m_rows = rows; @@ -261,7 +274,19 @@ void Font::drawLiteral(const wstring& str, int x, int y, int color) yPos = static_cast(y); wstring cleanStr = sanitize(str); for (size_t i = 0; i < cleanStr.length(); ++i) - renderCharacter(cleanStr.at(i)); + { + wchar_t c = cleanStr.at(i); + if (isUnicodeGlyphChar(c)) + { + renderUnicodeCharacter(c); + textures->bindTexture(m_textureLocation); + lastBoundTexture = fontTexture; + } + else + { + renderCharacter(c); + } + } } void Font::drawShadowWordWrap(const wstring &str, int x, int y, int w, int color, int h) @@ -345,7 +370,7 @@ void Font::draw(const wstring &str, bool dropShadow) } // "noise" for crazy splash screen message - if (noise) + if (noise && !isUnicodeGlyphChar(c)) { int newc; do @@ -355,7 +380,18 @@ void Font::draw(const wstring &str, bool dropShadow) c = newc; } - addCharacterQuad(c); + if (isUnicodeGlyphChar(c)) + { + t->end(); + renderUnicodeCharacter(c); + textures->bindTexture(m_textureLocation); + lastBoundTexture = fontTexture; + t->begin(); + } + else + { + addCharacterQuad(c); + } } t->end(); @@ -396,11 +432,22 @@ int Font::width(const wstring& str) ++i; else { - len += charWidths[167]; + if (isUnicodeGlyphChar(167)) + len += (int)unicodeCharWidth(167); + else + len += charWidths[167]; if (i + 1 < cleanStr.length()) - len += charWidths[static_cast(cleanStr[++i])]; + { + wchar_t nextC = cleanStr[++i]; + if (isUnicodeGlyphChar(nextC)) + len += (int)unicodeCharWidth(nextC); + else if (static_cast(nextC) < static_cast(m_cols * m_rows)) + len += charWidths[static_cast(nextC)]; + } } } + else if (isUnicodeGlyphChar(c)) + len += (int)unicodeCharWidth(c); else len += charWidths[c]; } @@ -414,7 +461,13 @@ int Font::widthLiteral(const wstring& str) if (cleanStr == L"") return 0; int len = 0; for (size_t i = 0; i < cleanStr.length(); ++i) - len += charWidths[static_cast(cleanStr.at(i))]; + { + wchar_t wc = cleanStr.at(i); + if (isUnicodeGlyphChar(wc)) + len += (int)unicodeCharWidth(wc); + else + len += charWidths[static_cast(wc)]; + } return len; } @@ -428,6 +481,10 @@ wstring Font::sanitize(const wstring& str) { sb[i] = MapCharacter(sb[i]); } + else if (unicodeWidth[sb[i]] != 0) + { + // Leave as-is: raw codepoint for glyph page rendering + } else { // If this character isn't supported, just show the first character (empty square box character) @@ -671,33 +728,22 @@ void Font::renderFakeCB(IntBuffer *ib) } } } +*/ void Font::loadUnicodePage(int page) { - wchar_t fileName[25]; - //String fileName = String.format("/1_2_2/font/glyph_%02X.png", page); - swprintf(fileName,25,L"/1_2_2/font/glyph_%02X.png",page); + wchar_t fileName[40]; + swprintf(fileName, 40, L"/1_2_2/font/glyph_%02X.png", page); BufferedImage *image = new BufferedImage(fileName); - //try - //{ - // image = ImageIO.read(Textures.class.getResourceAsStream(fileName.toString())); - //} - //catch (IOException e) - //{ - // throw new RuntimeException(e); - //} - unicodeTexID[page] = textures->getTexture(image); lastBoundTexture = unicodeTexID[page]; + delete image; } void Font::renderUnicodeCharacter(wchar_t c) { if (unicodeWidth[c] == 0) - { - // System.out.println("no-width char " + c); return; - } int page = c / 256; @@ -709,19 +755,17 @@ void Font::renderUnicodeCharacter(wchar_t c) lastBoundTexture = unicodeTexID[page]; } - // first column with non-trans pixels int firstLeft = unicodeWidth[c] >> 4; - // last column with non-trans pixels int firstRight = unicodeWidth[c] & 0xF; - float left = firstLeft; - float right = firstRight + 1; + float left = (float)firstLeft; + float right = (float)(firstRight + 1); - float xOff = c % 16 * 16 + left; - float yOff = (c & 0xFF) / 16 * 16; + float xOff = (c % 16) * 16 + left; + float yOff = ((c & 0xFF) / 16) * 16; float width = right - left - .02f; - Tesselator *t = Tesselator::getInstance(); + Tesselator *t = Tesselator::getInstance(); t->begin(GL_TRIANGLE_STRIP); t->tex(xOff / 256.0F, yOff / 256.0F); t->vertex(xPos, yPos, 0.0f); @@ -735,5 +779,17 @@ void Font::renderUnicodeCharacter(wchar_t c) xPos += (right - left) / 2 + 1; } -*/ + +float Font::unicodeCharWidth(wchar_t c) +{ + if (unicodeWidth[c] == 0) return 0; + int firstLeft = unicodeWidth[c] >> 4; + int firstRight = unicodeWidth[c] & 0xF; + return (firstRight + 1 - firstLeft) / 2.0f + 1; +} + +bool Font::isUnicodeGlyphChar(wchar_t c) +{ + return c >= m_cols * m_rows && unicodeWidth[c] != 0; +} diff --git a/Minecraft.Client/Font.h b/Minecraft.Client/Font.h index c78ea678..d711e570 100644 --- a/Minecraft.Client/Font.h +++ b/Minecraft.Client/Font.h @@ -18,6 +18,10 @@ private: Textures *textures; + int unicodeTexID[256]; + unsigned char unicodeWidth[65536]; + int lastBoundTexture; + float xPos; float yPos; @@ -70,6 +74,10 @@ private: void drawLiteral(const wstring& str, int x, int y, int color); // no ยง parsing int MapCharacter(wchar_t c); // 4J added bool CharacterExists(wchar_t c); // 4J added + void loadUnicodePage(int page); + void renderUnicodeCharacter(wchar_t c); + float unicodeCharWidth(wchar_t c); + bool isUnicodeGlyphChar(wchar_t c); public: int width(const wstring& str); diff --git a/Minecraft.Client/Gui.cpp b/Minecraft.Client/Gui.cpp index 43c17923..e79e98a1 100644 --- a/Minecraft.Client/Gui.cpp +++ b/Minecraft.Client/Gui.cpp @@ -1386,6 +1386,9 @@ void Gui::clearMessages(int iPad) void Gui::addMessage(const wstring& _string,int iPad,bool bIsDeathMessage) { + { char buf[32]; sprintf_s(buf, "[CHAT] Display (pad=%d): ", iPad); OutputDebugStringA(buf); } + OutputDebugStringW(_string.c_str()); + OutputDebugStringA("\n"); wstring string = _string; // 4J - Take copy of input as it is const //int iScale=1; diff --git a/Minecraft.Client/Minecraft.Client.vcxproj b/Minecraft.Client/Minecraft.Client.vcxproj index d97cbc38..4771e18f 100644 --- a/Minecraft.Client/Minecraft.Client.vcxproj +++ b/Minecraft.Client/Minecraft.Client.vcxproj @@ -6894,6 +6894,22 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true true @@ -29623,6 +29639,22 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false + + true + true + true + true + true + true + true + false + false + false + false + false + false + false + true true diff --git a/Minecraft.Client/Minecraft.Client.vcxproj.filters b/Minecraft.Client/Minecraft.Client.vcxproj.filters index 23b754fa..95483815 100644 --- a/Minecraft.Client/Minecraft.Client.vcxproj.filters +++ b/Minecraft.Client/Minecraft.Client.vcxproj.filters @@ -2953,6 +2953,9 @@ Common\Source Files\UI + + Common\Source Files\UI + Common\Source Files\UI @@ -5199,6 +5202,9 @@ Common\Source Files\UI + + Common\Source Files\UI + Common\Source Files\UI diff --git a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp index 81430ffc..e7c7a398 100644 --- a/Minecraft.Client/Windows64/Windows64_Minecraft.cpp +++ b/Minecraft.Client/Windows64/Windows64_Minecraft.cpp @@ -506,7 +506,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) break; default: - return DefWindowProc(hWnd, message, wParam, lParam); + return DefWindowProcW(hWnd, message, wParam, lParam); } break; case WM_PAINT: @@ -563,7 +563,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) else if (vk == VK_MENU) vk = (lParam & (1 << 24)) ? VK_RMENU : VK_LMENU; g_KBMInput.OnKeyDown(vk); - return DefWindowProc(hWnd, message, wParam, lParam); + return DefWindowProcW(hWnd, message, wParam, lParam); } case WM_KEYUP: case WM_SYSKEYUP: @@ -661,7 +661,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } break; default: - return DefWindowProc(hWnd, message, wParam, lParam); + return DefWindowProcW(hWnd, message, wParam, lParam); } return 0; } @@ -673,23 +673,23 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) // ATOM MyRegisterClass(HINSTANCE hInstance) { - WNDCLASSEX wcex; + WNDCLASSEXW wcex; - wcex.cbSize = sizeof(WNDCLASSEX); + wcex.cbSize = sizeof(WNDCLASSEXW); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; - wcex.hIcon = LoadIcon(hInstance, "Minecraft"); + wcex.hIcon = LoadIconW(hInstance, L"Minecraft"); wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); - wcex.lpszMenuName = "Minecraft"; - wcex.lpszClassName = "MinecraftClass"; + wcex.lpszMenuName = L"Minecraft"; + wcex.lpszClassName = L"MinecraftClass"; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_MINECRAFTWINDOWS)); - return RegisterClassEx(&wcex); + return RegisterClassExW(&wcex); } // @@ -709,8 +709,8 @@ BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) RECT wr = {0, 0, g_rScreenWidth, g_rScreenHeight}; // set the size, but not the position AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE); // adjust the size - g_hWnd = CreateWindow( "MinecraftClass", - "Minecraft", + g_hWnd = CreateWindowW( L"MinecraftClass", + L"Minecraft", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, diff --git a/Minecraft.Server/Minecraft.Server.vcxproj b/Minecraft.Server/Minecraft.Server.vcxproj index be2eb80c..7eaa6ef0 100644 --- a/Minecraft.Server/Minecraft.Server.vcxproj +++ b/Minecraft.Server/Minecraft.Server.vcxproj @@ -260,6 +260,7 @@ + diff --git a/Minecraft.World/SignTileEntity.cpp b/Minecraft.World/SignTileEntity.cpp index 598621eb..b311c669 100644 --- a/Minecraft.World/SignTileEntity.cpp +++ b/Minecraft.World/SignTileEntity.cpp @@ -50,11 +50,10 @@ void SignTileEntity::save(CompoundTag *tag) tag->putString(L"Text3", m_wsmessages[2] ); tag->putString(L"Text4", m_wsmessages[3] ); #ifndef _CONTENT_PACKAGE - OutputDebugStringW(L"### - Saving a sign with text - \n"); + app.DebugPrintf("[SIGN] Saving sign at (%d, %d, %d):\n", x, y, z); for(int i=0;i<4;i++) { - OutputDebugStringW(m_wsmessages[i].c_str()); - OutputDebugStringW(L"\n"); + app.DebugPrintf("[SIGN] Line%d: \"%ls\"\n", i+1, m_wsmessages[i].c_str()); } #endif } @@ -65,8 +64,8 @@ void SignTileEntity::load(CompoundTag *tag) TileEntity::load(tag); for (int i = 0; i < MAX_SIGN_LINES; i++) { - wchar_t *buf = new wchar_t[256]; - swprintf(buf, 256, L"Text%d", (i+1) ); + wchar_t buf[16]; + swprintf(buf, 16, L"Text%d", (i+1) ); m_wsmessages[i] = tag->getString( buf ); if (m_wsmessages[i].length() > MAX_LINE_LENGTH) m_wsmessages[i] = m_wsmessages[i].substr(0, MAX_LINE_LENGTH); }