From 61fa2ccf199413cfb68f3836f809eac700c2719a Mon Sep 17 00:00:00 2001 From: Omar Aglan Date: Mon, 31 Aug 2026 22:26:32 +0300 Subject: [PATCH] feat(system): Load Uniscribe at runtime --- .../Source/WWVegas/WWLib/CMakeLists.txt | 2 + .../Source/WWVegas/WWLib/Usp10Loader.cpp | 157 ++++++++++++++++++ .../Source/WWVegas/WWLib/Usp10Loader.h | 121 ++++++++++++++ 3 files changed, 280 insertions(+) create mode 100644 Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.cpp create mode 100644 Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.h diff --git a/Core/Libraries/Source/WWVegas/WWLib/CMakeLists.txt b/Core/Libraries/Source/WWVegas/WWLib/CMakeLists.txt index 77721250c6c..a74cd8baf2c 100644 --- a/Core/Libraries/Source/WWVegas/WWLib/CMakeLists.txt +++ b/Core/Libraries/Source/WWVegas/WWLib/CMakeLists.txt @@ -162,6 +162,8 @@ if(WIN32) rcfile.h registry.cpp registry.h + Usp10Loader.cpp + Usp10Loader.h verchk.cpp verchk.h WWCOMUtil.cpp diff --git a/Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.cpp b/Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.cpp new file mode 100644 index 00000000000..7f577d244a2 --- /dev/null +++ b/Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.cpp @@ -0,0 +1,157 @@ +/* +** Command & Conquer Generals Zero Hour(tm) +** Copyright 2026 TheSuperHackers +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#include "Usp10Loader.h" + + +CriticalSectionClass Usp10Loader::CriticalSection; +HMODULE Usp10Loader::Module = HMODULE(nullptr); +bool Usp10Loader::LoadAttempted = false; +Usp10Loader::ScriptIsComplex_t Usp10Loader::ScriptIsComplexPtr = nullptr; +Usp10Loader::ScriptItemize_t Usp10Loader::ScriptItemizePtr = nullptr; +Usp10Loader::ScriptBreak_t Usp10Loader::ScriptBreakPtr = nullptr; +Usp10Loader::ScriptLayout_t Usp10Loader::ScriptLayoutPtr = nullptr; +Usp10Loader::ScriptStringAnalyse_t Usp10Loader::ScriptStringAnalysePtr = nullptr; +Usp10Loader::ScriptStringFree_t Usp10Loader::ScriptStringFreePtr = nullptr; +Usp10Loader::ScriptString_pSize_t Usp10Loader::ScriptString_pSizePtr = nullptr; +Usp10Loader::ScriptStringOut_t Usp10Loader::ScriptStringOutPtr = nullptr; + + +bool Usp10Loader::load() +{ + CriticalSectionClass::LockClass lock(CriticalSection); + + if (LoadAttempted) { + return Module != HMODULE(nullptr); + } + LoadAttempted = true; + + char dll_path[MAX_PATH]; + const char dll_name[] = "\\usp10.dll"; + const UINT path_length = ::GetSystemDirectoryA(dll_path, ARRAY_SIZE(dll_path)); + if (path_length == 0 || path_length + ARRAY_SIZE(dll_name) > ARRAY_SIZE(dll_path)) { + return false; + } + strcpy(dll_path + path_length, dll_name); + + Module = ::LoadLibraryA(dll_path); + if (Module == HMODULE(nullptr)) { + return false; + } + + ScriptIsComplexPtr = reinterpret_cast(::GetProcAddress(Module, "ScriptIsComplex")); + ScriptItemizePtr = reinterpret_cast(::GetProcAddress(Module, "ScriptItemize")); + ScriptBreakPtr = reinterpret_cast(::GetProcAddress(Module, "ScriptBreak")); + ScriptLayoutPtr = reinterpret_cast(::GetProcAddress(Module, "ScriptLayout")); + ScriptStringAnalysePtr = reinterpret_cast(::GetProcAddress(Module, "ScriptStringAnalyse")); + ScriptStringFreePtr = reinterpret_cast(::GetProcAddress(Module, "ScriptStringFree")); + ScriptString_pSizePtr = reinterpret_cast(::GetProcAddress(Module, "ScriptString_pSize")); + ScriptStringOutPtr = reinterpret_cast(::GetProcAddress(Module, "ScriptStringOut")); + + if (ScriptIsComplexPtr == nullptr || ScriptItemizePtr == nullptr || ScriptBreakPtr == nullptr || + ScriptLayoutPtr == nullptr || ScriptStringAnalysePtr == nullptr || ScriptStringFreePtr == nullptr || + ScriptString_pSizePtr == nullptr || ScriptStringOutPtr == nullptr) + { + freeResources(); + return false; + } + + return true; +} + + +void Usp10Loader::unload() +{ + CriticalSectionClass::LockClass lock(CriticalSection); + + freeResources(); + LoadAttempted = false; +} + + +void Usp10Loader::freeResources() +{ + if (Module != HMODULE(nullptr)) { + ::FreeLibrary(Module); + Module = HMODULE(nullptr); + } + + ScriptIsComplexPtr = nullptr; + ScriptItemizePtr = nullptr; + ScriptBreakPtr = nullptr; + ScriptLayoutPtr = nullptr; + ScriptStringAnalysePtr = nullptr; + ScriptStringFreePtr = nullptr; + ScriptString_pSizePtr = nullptr; + ScriptStringOutPtr = nullptr; +} + + +HRESULT Usp10Loader::ScriptIsComplex(const WCHAR *text, int text_length, DWORD flags) +{ + return load() ? ScriptIsComplexPtr(text, text_length, flags) : E_FAIL; +} + + +HRESULT Usp10Loader::ScriptItemize(const WCHAR *text, int text_length, int item_capacity, + const ScriptControl *control, const ScriptState *state, ScriptItem *items, int *item_count) +{ + return load() ? ScriptItemizePtr(text, text_length, item_capacity, control, state, items, item_count) : E_FAIL; +} + + +HRESULT Usp10Loader::ScriptBreak(const WCHAR *text, int text_length, const ScriptAnalysis *analysis, + ScriptLogAttr *attributes) +{ + return load() ? ScriptBreakPtr(text, text_length, analysis, attributes) : E_FAIL; +} + + +HRESULT Usp10Loader::ScriptLayout(int run_count, const BYTE *levels, int *visual_to_logical, + int *logical_to_visual) +{ + return load() ? ScriptLayoutPtr(run_count, levels, visual_to_logical, logical_to_visual) : E_FAIL; +} + + +HRESULT Usp10Loader::ScriptStringAnalyse(HDC dc, const void *text, int text_length, int glyph_count, + int charset, DWORD flags, int required_width, ScriptControl *control, ScriptState *state, + const int *spacing, ScriptTabDefinition *tabs, const BYTE *character_classes, ScriptStringAnalysis *analysis) +{ + return load() ? ScriptStringAnalysePtr(dc, text, text_length, glyph_count, charset, flags, required_width, + control, state, spacing, tabs, character_classes, analysis) : E_FAIL; +} + + +HRESULT Usp10Loader::ScriptStringFree(ScriptStringAnalysis *analysis) +{ + return load() ? ScriptStringFreePtr(analysis) : E_FAIL; +} + + +const SIZE *Usp10Loader::ScriptString_pSize(ScriptStringAnalysis analysis) +{ + return load() ? ScriptString_pSizePtr(analysis) : nullptr; +} + + +HRESULT Usp10Loader::ScriptStringOut(ScriptStringAnalysis analysis, int x, int y, UINT options, + const RECT *rect, int minimum_selection, int maximum_selection, BOOL disabled) +{ + return load() ? ScriptStringOutPtr(analysis, x, y, options, rect, minimum_selection, maximum_selection, disabled) : E_FAIL; +} diff --git a/Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.h b/Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.h new file mode 100644 index 00000000000..3b35b03b82b --- /dev/null +++ b/Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.h @@ -0,0 +1,121 @@ +/* +** Command & Conquer Generals Zero Hour(tm) +** Copyright 2026 TheSuperHackers +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#pragma once + +#include "mutex.h" +#include "win.h" + + +// This static class loads usp10.dll on first use and unloads it during engine shutdown. + +class Usp10Loader +{ +public: + + typedef void *ScriptStringAnalysis; + struct ScriptControl; + struct ScriptTabDefinition; + + struct ScriptState + { + WORD bidi_level : 5; + WORD reserved : 11; + }; + + struct ScriptAnalysis + { + WORD script : 10; + WORD right_to_left : 1; + WORD layout_right_to_left : 1; + WORD link_before : 1; + WORD link_after : 1; + WORD logical_order : 1; + WORD no_glyph_index : 1; + ScriptState state; + }; + + struct ScriptItem + { + int character_position; + ScriptAnalysis analysis; + }; + + struct ScriptLogAttr + { + BYTE soft_break : 1; + BYTE white_space : 1; + BYTE char_stop : 1; + BYTE word_stop : 1; + BYTE invalid : 1; + BYTE reserved : 3; + }; + + enum + { + SIC_COMPLEX = 0x00000001, + SSA_FALLBACK = 0x00000020, + SSA_GLYPHS = 0x00000080, + SSA_RTL = 0x00000100, + }; + + static HRESULT ScriptIsComplex(const WCHAR *text, int text_length, DWORD flags); + static HRESULT ScriptItemize(const WCHAR *text, int text_length, int item_capacity, + const ScriptControl *control, const ScriptState *state, ScriptItem *items, int *item_count); + static HRESULT ScriptBreak(const WCHAR *text, int text_length, const ScriptAnalysis *analysis, + ScriptLogAttr *attributes); + static HRESULT ScriptLayout(int run_count, const BYTE *levels, int *visual_to_logical, + int *logical_to_visual); + static HRESULT ScriptStringAnalyse(HDC dc, const void *text, int text_length, int glyph_count, + int charset, DWORD flags, int required_width, ScriptControl *control, ScriptState *state, + const int *spacing, ScriptTabDefinition *tabs, const BYTE *character_classes, + ScriptStringAnalysis *analysis); + static HRESULT ScriptStringFree(ScriptStringAnalysis *analysis); + static const SIZE *ScriptString_pSize(ScriptStringAnalysis analysis); + static HRESULT ScriptStringOut(ScriptStringAnalysis analysis, int x, int y, UINT options, + const RECT *rect, int minimum_selection, int maximum_selection, BOOL disabled); + static void unload(); + +private: + + static bool load(); + static void freeResources(); + + typedef HRESULT (WINAPI *ScriptIsComplex_t)(const WCHAR *, int, DWORD); + typedef HRESULT (WINAPI *ScriptItemize_t)(const WCHAR *, int, int, const ScriptControl *, + const ScriptState *, ScriptItem *, int *); + typedef HRESULT (WINAPI *ScriptBreak_t)(const WCHAR *, int, const ScriptAnalysis *, ScriptLogAttr *); + typedef HRESULT (WINAPI *ScriptLayout_t)(int, const BYTE *, int *, int *); + typedef HRESULT (WINAPI *ScriptStringAnalyse_t)(HDC, const void *, int, int, int, DWORD, int, + ScriptControl *, ScriptState *, const int *, ScriptTabDefinition *, const BYTE *, ScriptStringAnalysis *); + typedef HRESULT (WINAPI *ScriptStringFree_t)(ScriptStringAnalysis *); + typedef const SIZE *(WINAPI *ScriptString_pSize_t)(ScriptStringAnalysis); + typedef HRESULT (WINAPI *ScriptStringOut_t)(ScriptStringAnalysis, int, int, UINT, const RECT *, int, int, BOOL); + + static CriticalSectionClass CriticalSection; + static HMODULE Module; + static bool LoadAttempted; + static ScriptIsComplex_t ScriptIsComplexPtr; + static ScriptItemize_t ScriptItemizePtr; + static ScriptBreak_t ScriptBreakPtr; + static ScriptLayout_t ScriptLayoutPtr; + static ScriptStringAnalyse_t ScriptStringAnalysePtr; + static ScriptStringFree_t ScriptStringFreePtr; + static ScriptString_pSize_t ScriptString_pSizePtr; + static ScriptStringOut_t ScriptStringOutPtr; +};