Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Core/Libraries/Source/WWVegas/WWLib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ if(WIN32)
rcfile.h
registry.cpp
registry.h
Usp10Loader.cpp
Usp10Loader.h
verchk.cpp
verchk.h
WWCOMUtil.cpp
Expand Down
157 changes: 157 additions & 0 deletions Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.cpp

Copy link
Copy Markdown

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

  • Uniscribe: This component is responsible for handling the proper formatting of Unicode text, especially for complex scripts.
  • Script Processor: It processes text layout and rendering, ensuring that text appears correctly on the screen.

Versioning

  • The "10" in USP10 indicates that it is the first version of the Uniscribe Script Processor, which is integral to the Windows operating system's ability to manage various languages and scripts.

Copy link
Copy Markdown

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.

Copy link
Copy Markdown
Author

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

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;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the loader lock held while invoking exports

When unload() runs concurrently with any shaping call, load() releases CriticalSection before this expression reads and invokes the function pointer. The unloading thread can therefore call FreeLibrary() and null the pointers in that gap, causing the caller to invoke either a null pointer or code from an unloaded module. All export wrappers have this race; keep the lock held through each invocation or otherwise prevent unloading while calls are active.

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;
}
121 changes: 121 additions & 0 deletions Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.h
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();
Comment thread
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;
};