forked from electronicarts/CnC_Generals_Zero_Hour
-
Notifications
You must be signed in to change notification settings - Fork 250
chore(lib): Implement Usp10Loader class #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
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,163 @@ | ||
| /* | ||
| ** 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() | ||
| { | ||
| 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) | ||
| { | ||
| CriticalSectionClass::LockClass lock(CriticalSection); | ||
| return load() ? ScriptIsComplexPtr(text, text_length, flags) : E_FAIL; | ||
|
xezon marked this conversation as resolved.
|
||
| } | ||
|
|
||
|
|
||
| HRESULT Usp10Loader::ScriptItemize(const WCHAR *text, int text_length, int item_capacity, | ||
| const ScriptControl *control, const ScriptState *state, ScriptItem *items, int *item_count) | ||
| { | ||
| CriticalSectionClass::LockClass lock(CriticalSection); | ||
| 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) | ||
| { | ||
| CriticalSectionClass::LockClass lock(CriticalSection); | ||
| 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) | ||
| { | ||
| CriticalSectionClass::LockClass lock(CriticalSection); | ||
| 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) | ||
| { | ||
| CriticalSectionClass::LockClass lock(CriticalSection); | ||
| 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) | ||
| { | ||
| CriticalSectionClass::LockClass lock(CriticalSection); | ||
| return load() ? ScriptStringFreePtr(analysis) : E_FAIL; | ||
| } | ||
|
|
||
|
|
||
| const SIZE *Usp10Loader::ScriptString_pSize(ScriptStringAnalysis analysis) | ||
| { | ||
| CriticalSectionClass::LockClass lock(CriticalSection); | ||
| 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) | ||
| { | ||
| CriticalSectionClass::LockClass lock(CriticalSection); | ||
| return load() ? ScriptStringOutPtr(analysis, x, y, options, rect, minimum_selection, maximum_selection, disabled) : E_FAIL; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | ||
| }; | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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