Skip to content

Commit 32dbec5

Browse files
authored
Release v1.0.0 (#1)
* chore: Delete unused screen file * refactor: Remove unused arch checks * fix: Shifting numbers * docs: Update README.md * refactor: Update log levels
1 parent 4677b1c commit 32dbec5

3 files changed

Lines changed: 24 additions & 124 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Match Timer is a mod for `Company of Heroes Relaunch` built on top of `CoHModSDK
1313

1414
## 🎥 Preview
1515

16-
| [![](https://ytcards.demolab.com/?id=YG_1o9S313g&title=Match+Timer+Demo&timestamp=1776170501&width=700 "Match Timer Demo")](https://www.youtube.com/watch?v=YG_1o9S313g) |
16+
| [![](https://ytcards.demolab.com/?id=iLNbJWbV0kc&title=Match+Timer+Demo&timestamp=1777330234&width=700 "Match Timer Demo")](https://www.youtube.com/watch?v=iLNbJWbV0kc) |
1717
|---|
1818

1919
## 🛠️ Building

res/matchtimer_data/Art/UI/Screens/matchtimer.screen

Lines changed: 0 additions & 86 deletions
This file was deleted.

src/dllmain.cpp

Lines changed: 23 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ namespace {
4040
using WidgetProxyBindFn = void(__thiscall*)(void*, void*);
4141
using WidgetProxySetVisibleFn = void(__thiscall*)(void*, bool);
4242
using WidgetProxySetEnabledFn = void(__thiscall*)(void*, bool);
43+
using WidgetProxySetTextHAlignFn = void(__thiscall*)(void*, int, const char*);
4344
using TextLabelCtorFn = void(__thiscall*)(void*);
4445
using TextLabelDtorFn = void(__thiscall*)(void*);
4546
using TextLabelSetTextFn = void(__thiscall*)(void*, const void*);
@@ -77,6 +78,7 @@ namespace {
7778
constexpr char kWidgetProxyBindExportName[] = "?Bind@WidgetProxy@UI@@IAEXPAVWidget@2@@Z";
7879
constexpr char kWidgetProxySetVisibleExportName[] = "?SetVisible@WidgetProxy@UI@@UAEX_N@Z";
7980
constexpr char kWidgetProxySetEnabledExportName[] = "?SetEnabled@WidgetProxy@UI@@UAEX_N@Z";
81+
constexpr char kWidgetProxySetTextHAlignExportName[] = "?SetTextHAlign@WidgetProxy@UI@@QAEXW4HAlign@RenderProxy@@PBD@Z";
8082
constexpr char kTextLabelCtorExportName[] = "??0TextLabel@UI@@QAE@XZ";
8183
constexpr char kTextLabelDtorExportName[] = "??1TextLabel@UI@@UAE@XZ";
8284
constexpr char kTextLabelSetTextExportName[] = "?SetText@TextLabel@UI@@QAEXABVLocString@@@Z";
@@ -102,11 +104,13 @@ namespace {
102104
constexpr char kTextLabelWidgetTypeName[] = "TextLabel";
103105
constexpr char kTimerPresentationDonorName[] = "txtInfo";
104106

105-
constexpr float kTimerLabelPositionX = 0.30260f;
106-
constexpr float kTimerLabelPositionY = 0.00000f;
107-
constexpr float kTimerLabelSizeX = 0.39480f;
107+
constexpr float kTimerLabelPositionX = 0.47500f;
108+
constexpr float kTimerLabelPositionY = -0.02000f;
109+
constexpr float kTimerLabelSizeX = 0.14000f;
108110
constexpr float kTimerLabelSizeY = 0.12600f;
109111

112+
constexpr int kRenderProxyHAlignLeft = 0;
113+
110114
constexpr std::size_t kOpaqueTextLabelStorageSize = 8192u;
111115
constexpr std::size_t kLocStringStorageSize = 256u;
112116

@@ -128,6 +132,7 @@ namespace {
128132
WidgetProxyBindFn g_widgetProxyBind = nullptr;
129133
WidgetProxySetVisibleFn g_widgetProxySetVisible = nullptr;
130134
WidgetProxySetEnabledFn g_widgetProxySetEnabled = nullptr;
135+
WidgetProxySetTextHAlignFn g_widgetProxySetTextHAlign = nullptr;
131136
TextLabelCtorFn g_textLabelCtor = nullptr;
132137
TextLabelDtorFn g_textLabelDtor = nullptr;
133138
TextLabelSetTextFn g_textLabelSetText = nullptr;
@@ -170,18 +175,9 @@ namespace {
170175
bool g_lastUiLabelVisible = false;
171176
bool g_uiLabelTextInitialized = false;
172177

173-
template <typename T>
174-
T ResolveExport(HMODULE module, const char* exportName) {
175-
if ((module == nullptr) || (exportName == nullptr)) {
176-
return nullptr;
177-
}
178-
179-
return reinterpret_cast<T>(GetProcAddress(module, exportName));
180-
}
181-
182178
template <typename T>
183179
bool ResolveRequiredExport(HMODULE module, const char* moduleName, const char* exportName, T& outFunction) {
184-
outFunction = ResolveExport<T>(module, exportName);
180+
outFunction = ModSDK::Memory::ResolveExport<T>(module, exportName);
185181
if (outFunction != nullptr) {
186182
return true;
187183
}
@@ -226,7 +222,7 @@ namespace {
226222
}
227223

228224
bool InstallExportHook(HMODULE module, const char* exportName, void* detour, void** originalFunction, const char* hookDisplayName) {
229-
void* target = ResolveExport<void*>(module, exportName);
225+
void* target = ModSDK::Memory::ResolveExport<void*>(module, exportName);
230226
if (target == nullptr) {
231227
char message[256] = {};
232228
std::snprintf(message, sizeof(message), "Match Timer failed to resolve %s", hookDisplayName);
@@ -268,7 +264,7 @@ namespace {
268264
if (milliseconds > 0u) {
269265
char message[96] = {};
270266
std::snprintf(message, sizeof(message), "Match Timer captured game time %.3f seconds", gameTimeSeconds);
271-
LogOnce(g_loggedTimeCapture, CoHModSDKLogLevel_Info, message);
267+
LogOnce(g_loggedTimeCapture, CoHModSDKLogLevel_Debug, message);
272268
}
273269
}
274270
}
@@ -342,7 +338,7 @@ namespace {
342338
ZeroWidgetField(g_timerLabelRaw, hitAreaOffset);
343339
LogOnce(
344340
g_loggedTimerLabelDetach,
345-
CoHModSDKLogLevel_Info,
341+
CoHModSDKLogLevel_Debug,
346342
"Match Timer detached shared presentation state from the native Taskbar timer label before unload"
347343
);
348344
}
@@ -393,7 +389,6 @@ namespace {
393389
}
394390

395391
void* widget = nullptr;
396-
#if defined(_M_IX86)
397392
void* createFunction = g_widgetFactoryCreateAddress;
398393
__asm {
399394
mov edi, widgetTypeName
@@ -402,9 +397,6 @@ namespace {
402397
call eax
403398
mov widget, eax
404399
}
405-
#else
406-
(void)widgetTypeName;
407-
#endif
408400
return widget;
409401
}
410402

@@ -438,7 +430,6 @@ namespace {
438430
return false;
439431
}
440432

441-
#if defined(_M_IX86)
442433
void* addRenderChildFunction = g_addRenderChildAddress;
443434
__asm {
444435
mov edi, parentRenderObject
@@ -448,12 +439,6 @@ namespace {
448439
call eax
449440
}
450441
return true;
451-
#else
452-
(void)parentRenderObject;
453-
(void)childWidget;
454-
(void)insertionIndex;
455-
return false;
456-
#endif
457442
}
458443

459444
bool AttachRenderChild(void* parentWidget, void* childWidget) {
@@ -554,6 +539,11 @@ namespace {
554539
g_textLabelSetMultiline(g_timerLabelProxyStorage.data(), false);
555540
}
556541

542+
if (g_widgetProxySetTextHAlign != nullptr) {
543+
// Anchor the changing digits against the right edge of the fixed label box.
544+
g_widgetProxySetTextHAlign(g_timerLabelProxyStorage.data(), kRenderProxyHAlignLeft, nullptr);
545+
}
546+
557547
ApplyTimerLabelVisibility(false);
558548
LogOnce(g_loggedTimerLabelBound, CoHModSDKLogLevel_Info, "Match Timer bound the native Taskbar timer label");
559549
return true;
@@ -690,7 +680,7 @@ namespace {
690680
g_uiLabelTextInitialized = true;
691681
g_lastUiLabelVisible = true;
692682
g_lastUiLabelSeconds = totalSeconds;
693-
LogOnce(g_loggedTimerLabelUpdated, CoHModSDKLogLevel_Info, "Match Timer updated the native Taskbar timer label");
683+
LogOnce(g_loggedTimerLabelUpdated, CoHModSDKLogLevel_Debug, "Match Timer updated the native Taskbar timer label");
694684
}
695685

696686
void __fastcall HookedWorldSimulate(void* world, void*) {
@@ -708,7 +698,6 @@ namespace {
708698
g_matchActive.store(true, std::memory_order_relaxed);
709699
g_gameOver.store(false, std::memory_order_relaxed);
710700
RefreshCachedMatchTime(simManager);
711-
LogOnce(g_loggedWorldSimulate, CoHModSDKLogLevel_Info, "Match Timer World::Simulate hook is active");
712701
}
713702

714703
void __fastcall HookedClearGameTicks(void* simManager, void*) {
@@ -728,8 +717,6 @@ namespace {
728717
}
729718

730719
void __fastcall HookedScreenManagerDraw(void* screenManager, void*, float deltaSeconds) {
731-
LogOnce(g_loggedUiDrawHook, CoHModSDKLogLevel_Info, "Match Timer UI::ScreenManager::Draw hook is active");
732-
733720
const bool shouldShow = ShouldShowTimer();
734721
if (EnsureTaskbarTimerLabel(screenManager)) {
735722
ApplyTimerLabelVisibility(shouldShow);
@@ -779,8 +766,8 @@ namespace {
779766
return ShowAndLogError("Match Timer failed to load SimEngine.dll");
780767
}
781768

782-
g_getGameTime = ResolveExport<SimManagerGetGameTimeFn>(simEngineModule, kGetGameTimeExportName);
783-
g_getWorldSimManager = ResolveExport<WorldGetSimManagerFn>(simEngineModule, kWorldGetSimManagerExportName);
769+
g_getGameTime = ModSDK::Memory::ResolveExport<SimManagerGetGameTimeFn>(simEngineModule, kGetGameTimeExportName);
770+
g_getWorldSimManager = ModSDK::Memory::ResolveExport<WorldGetSimManagerFn>(simEngineModule, kWorldGetSimManagerExportName);
784771
if ((g_getGameTime == nullptr) || (g_getWorldSimManager == nullptr)) {
785772
return ShowAndLogError("Match Timer failed to resolve SimManager timer exports");
786773
}
@@ -812,7 +799,6 @@ namespace {
812799
return false;
813800
}
814801

815-
ModSDK::Runtime::LogInfo("Match Timer hooked SimEngine timer exports");
816802
return true;
817803
}
818804

@@ -839,6 +825,7 @@ namespace {
839825
ResolveRequiredExport(userInterfaceModule, kUserInterfaceModuleName, kWidgetProxyBindExportName, g_widgetProxyBind) &&
840826
ResolveRequiredExport(userInterfaceModule, kUserInterfaceModuleName, kWidgetProxySetVisibleExportName, g_widgetProxySetVisible) &&
841827
ResolveRequiredExport(userInterfaceModule, kUserInterfaceModuleName, kWidgetProxySetEnabledExportName, g_widgetProxySetEnabled) &&
828+
ResolveRequiredExport(userInterfaceModule, kUserInterfaceModuleName, kWidgetProxySetTextHAlignExportName, g_widgetProxySetTextHAlign) &&
842829
ResolveRequiredExport(userInterfaceModule, kUserInterfaceModuleName, kTextLabelCtorExportName, g_textLabelCtor) &&
843830
ResolveRequiredExport(userInterfaceModule, kUserInterfaceModuleName, kTextLabelDtorExportName, g_textLabelDtor) &&
844831
ResolveRequiredExport(userInterfaceModule, kUserInterfaceModuleName, kTextLabelSetTextExportName, g_textLabelSetText) &&
@@ -851,8 +838,8 @@ namespace {
851838
return false;
852839
}
853840

854-
g_textLabelSetAutoSize = ResolveExport<TextLabelSetAutoSizeFn>(userInterfaceModule, kTextLabelSetAutoSizeExportName);
855-
g_textLabelSetMultiline = ResolveExport<TextLabelSetMultilineFn>(userInterfaceModule, kTextLabelSetMultilineExportName);
841+
g_textLabelSetAutoSize = ModSDK::Memory::ResolveExport<TextLabelSetAutoSizeFn>(userInterfaceModule, kTextLabelSetAutoSizeExportName);
842+
g_textLabelSetMultiline = ModSDK::Memory::ResolveExport<TextLabelSetMultilineFn>(userInterfaceModule, kTextLabelSetMultilineExportName);
856843

857844
const std::uintptr_t userInterfaceBase = reinterpret_cast<std::uintptr_t>(userInterfaceModule);
858845
g_widgetFactoryCreateAddress = reinterpret_cast<void*>(userInterfaceBase + kWidgetFactoryCreateRva);
@@ -887,7 +874,6 @@ namespace {
887874
return false;
888875
}
889876

890-
LogOnce(g_loggedUiHooksInstalled, CoHModSDKLogLevel_Info, "Match Timer hooked UI::ScreenManager::Draw for a native Taskbar timer");
891877
return true;
892878
}
893879

0 commit comments

Comments
 (0)