Skip to content

Commit c7272cd

Browse files
committed
feat(system): Load Uniscribe at runtime
1 parent af10d57 commit c7272cd

3 files changed

Lines changed: 178 additions & 0 deletions

File tree

Core/Libraries/Source/WWVegas/WWLib/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ if(WIN32)
162162
rcfile.h
163163
registry.cpp
164164
registry.h
165+
Usp10Loader.cpp
166+
Usp10Loader.h
165167
verchk.cpp
166168
verchk.h
167169
WWCOMUtil.cpp
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
** Command & Conquer Generals Zero Hour(tm)
3+
** Copyright 2026 TheSuperHackers
4+
**
5+
** This program is free software: you can redistribute it and/or modify
6+
** it under the terms of the GNU General Public License as published by
7+
** the Free Software Foundation, either version 3 of the License, or
8+
** (at your option) any later version.
9+
**
10+
** This program is distributed in the hope that it will be useful,
11+
** but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
** GNU General Public License for more details.
14+
**
15+
** You should have received a copy of the GNU General Public License
16+
** along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
#include "Usp10Loader.h"
20+
21+
22+
CriticalSectionClass Usp10Loader::CriticalSection;
23+
HMODULE Usp10Loader::Module = HMODULE(nullptr);
24+
bool Usp10Loader::LoadAttempted = false;
25+
Usp10Loader::ScriptIsComplex_t Usp10Loader::ScriptIsComplexPtr = nullptr;
26+
Usp10Loader::ScriptStringAnalyse_t Usp10Loader::ScriptStringAnalysePtr = nullptr;
27+
Usp10Loader::ScriptStringFree_t Usp10Loader::ScriptStringFreePtr = nullptr;
28+
Usp10Loader::ScriptString_pSize_t Usp10Loader::ScriptString_pSizePtr = nullptr;
29+
Usp10Loader::ScriptStringOut_t Usp10Loader::ScriptStringOutPtr = nullptr;
30+
31+
32+
bool Usp10Loader::load()
33+
{
34+
CriticalSectionClass::LockClass lock(CriticalSection);
35+
36+
if (LoadAttempted) {
37+
return Module != HMODULE(nullptr);
38+
}
39+
LoadAttempted = true;
40+
41+
char dll_path[MAX_PATH];
42+
const char dll_name[] = "\\usp10.dll";
43+
const UINT path_length = ::GetSystemDirectoryA(dll_path, ARRAY_SIZE(dll_path));
44+
if (path_length == 0 || path_length + ARRAY_SIZE(dll_name) > ARRAY_SIZE(dll_path)) {
45+
return false;
46+
}
47+
strcpy(dll_path + path_length, dll_name);
48+
49+
Module = ::LoadLibraryA(dll_path);
50+
if (Module == HMODULE(nullptr)) {
51+
return false;
52+
}
53+
54+
ScriptIsComplexPtr = reinterpret_cast<ScriptIsComplex_t>(::GetProcAddress(Module, "ScriptIsComplex"));
55+
ScriptStringAnalysePtr = reinterpret_cast<ScriptStringAnalyse_t>(::GetProcAddress(Module, "ScriptStringAnalyse"));
56+
ScriptStringFreePtr = reinterpret_cast<ScriptStringFree_t>(::GetProcAddress(Module, "ScriptStringFree"));
57+
ScriptString_pSizePtr = reinterpret_cast<ScriptString_pSize_t>(::GetProcAddress(Module, "ScriptString_pSize"));
58+
ScriptStringOutPtr = reinterpret_cast<ScriptStringOut_t>(::GetProcAddress(Module, "ScriptStringOut"));
59+
60+
if (ScriptIsComplexPtr == nullptr || ScriptStringAnalysePtr == nullptr || ScriptStringFreePtr == nullptr ||
61+
ScriptString_pSizePtr == nullptr || ScriptStringOutPtr == nullptr)
62+
{
63+
::FreeLibrary(Module);
64+
Module = HMODULE(nullptr);
65+
return false;
66+
}
67+
68+
return true;
69+
}
70+
71+
72+
HRESULT Usp10Loader::ScriptIsComplex(const WCHAR *text, int text_length, DWORD flags)
73+
{
74+
return load() ? ScriptIsComplexPtr(text, text_length, flags) : E_FAIL;
75+
}
76+
77+
78+
HRESULT Usp10Loader::ScriptStringAnalyse(HDC dc, const void *text, int text_length, int glyph_count,
79+
int charset, DWORD flags, int required_width, ScriptControl *control, ScriptState *state,
80+
const int *spacing, ScriptTabDefinition *tabs, const BYTE *character_classes, ScriptStringAnalysis *analysis)
81+
{
82+
return load() ? ScriptStringAnalysePtr(dc, text, text_length, glyph_count, charset, flags, required_width,
83+
control, state, spacing, tabs, character_classes, analysis) : E_FAIL;
84+
}
85+
86+
87+
HRESULT Usp10Loader::ScriptStringFree(ScriptStringAnalysis *analysis)
88+
{
89+
return load() ? ScriptStringFreePtr(analysis) : E_FAIL;
90+
}
91+
92+
93+
const SIZE *Usp10Loader::ScriptString_pSize(ScriptStringAnalysis analysis)
94+
{
95+
return load() ? ScriptString_pSizePtr(analysis) : nullptr;
96+
}
97+
98+
99+
HRESULT Usp10Loader::ScriptStringOut(ScriptStringAnalysis analysis, int x, int y, UINT options,
100+
const RECT *rect, int minimum_selection, int maximum_selection, BOOL disabled)
101+
{
102+
return load() ? ScriptStringOutPtr(analysis, x, y, options, rect, minimum_selection, maximum_selection, disabled) : E_FAIL;
103+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
** Command & Conquer Generals Zero Hour(tm)
3+
** Copyright 2026 TheSuperHackers
4+
**
5+
** This program is free software: you can redistribute it and/or modify
6+
** it under the terms of the GNU General Public License as published by
7+
** the Free Software Foundation, either version 3 of the License, or
8+
** (at your option) any later version.
9+
**
10+
** This program is distributed in the hope that it will be useful,
11+
** but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
** GNU General Public License for more details.
14+
**
15+
** You should have received a copy of the GNU General Public License
16+
** along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
#pragma once
20+
21+
#include "mutex.h"
22+
#include "win.h"
23+
24+
25+
// This static class loads usp10.dll on first use and keeps it loaded for the process lifetime.
26+
27+
class Usp10Loader
28+
{
29+
public:
30+
31+
typedef void *ScriptStringAnalysis;
32+
struct ScriptControl;
33+
struct ScriptState;
34+
struct ScriptTabDefinition;
35+
36+
enum
37+
{
38+
SIC_COMPLEX = 0x00000001,
39+
SSA_FALLBACK = 0x00000020,
40+
SSA_GLYPHS = 0x00000080,
41+
SSA_RTL = 0x00000100,
42+
};
43+
44+
static HRESULT ScriptIsComplex(const WCHAR *text, int text_length, DWORD flags);
45+
static HRESULT ScriptStringAnalyse(HDC dc, const void *text, int text_length, int glyph_count,
46+
int charset, DWORD flags, int required_width, ScriptControl *control, ScriptState *state,
47+
const int *spacing, ScriptTabDefinition *tabs, const BYTE *character_classes,
48+
ScriptStringAnalysis *analysis);
49+
static HRESULT ScriptStringFree(ScriptStringAnalysis *analysis);
50+
static const SIZE *ScriptString_pSize(ScriptStringAnalysis analysis);
51+
static HRESULT ScriptStringOut(ScriptStringAnalysis analysis, int x, int y, UINT options,
52+
const RECT *rect, int minimum_selection, int maximum_selection, BOOL disabled);
53+
54+
private:
55+
56+
static bool load();
57+
58+
typedef HRESULT (WINAPI *ScriptIsComplex_t)(const WCHAR *, int, DWORD);
59+
typedef HRESULT (WINAPI *ScriptStringAnalyse_t)(HDC, const void *, int, int, int, DWORD, int,
60+
ScriptControl *, ScriptState *, const int *, ScriptTabDefinition *, const BYTE *, ScriptStringAnalysis *);
61+
typedef HRESULT (WINAPI *ScriptStringFree_t)(ScriptStringAnalysis *);
62+
typedef const SIZE *(WINAPI *ScriptString_pSize_t)(ScriptStringAnalysis);
63+
typedef HRESULT (WINAPI *ScriptStringOut_t)(ScriptStringAnalysis, int, int, UINT, const RECT *, int, int, BOOL);
64+
65+
static CriticalSectionClass CriticalSection;
66+
static HMODULE Module;
67+
static bool LoadAttempted;
68+
static ScriptIsComplex_t ScriptIsComplexPtr;
69+
static ScriptStringAnalyse_t ScriptStringAnalysePtr;
70+
static ScriptStringFree_t ScriptStringFreePtr;
71+
static ScriptString_pSize_t ScriptString_pSizePtr;
72+
static ScriptStringOut_t ScriptStringOutPtr;
73+
};

0 commit comments

Comments
 (0)