-
Notifications
You must be signed in to change notification settings - Fork 248
feat(system): Load Uniscribe at runtime #3241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 <http://www.gnu.org/licenses/>. | ||
| */ | ||
|
|
||
| #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<ScriptIsComplex_t>(::GetProcAddress(Module, "ScriptIsComplex")); | ||
| ScriptItemizePtr = reinterpret_cast<ScriptItemize_t>(::GetProcAddress(Module, "ScriptItemize")); | ||
| ScriptBreakPtr = reinterpret_cast<ScriptBreak_t>(::GetProcAddress(Module, "ScriptBreak")); | ||
| ScriptLayoutPtr = reinterpret_cast<ScriptLayout_t>(::GetProcAddress(Module, "ScriptLayout")); | ||
| ScriptStringAnalysePtr = reinterpret_cast<ScriptStringAnalyse_t>(::GetProcAddress(Module, "ScriptStringAnalyse")); | ||
| ScriptStringFreePtr = reinterpret_cast<ScriptStringFree_t>(::GetProcAddress(Module, "ScriptStringFree")); | ||
| ScriptString_pSizePtr = reinterpret_cast<ScriptString_pSize_t>(::GetProcAddress(Module, "ScriptString_pSize")); | ||
| ScriptStringOutPtr = reinterpret_cast<ScriptStringOut_t>(::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; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Useful? React with 👍 / 👎. |
||
| } | ||
|
|
||
|
|
||
| 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; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 <http://www.gnu.org/licenses/>. | ||
| */ | ||
|
|
||
| #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(); | ||
|
OmarAglan marked this conversation as resolved.
|
||
| 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; | ||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Meaning of USP10
USP10 stands for Uniscribe Script Processor version 1.0. This name reflects its primary function within the Windows operating system.
Functionality
Versioning
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If microsoft bumps the version number... 😆 Looks like they did not in 28 years.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well, that's true, this is very old tech from win 98SE era, i suppose it now got replaced with direct write, this is adding support for Arabic or rtl language and bidi without rewriting the backend, see https://learn.microsoft.com/en-us/windows/win32/intl/uniscribe
also, it has serval minor versions, see https://scripts.sil.org/cms/scripts/page.php?id=uniscribeversions&site_id=nrsi