Skip to content
Merged
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: 1 addition & 1 deletion CoHModConfigUI.sln → ModConfigUI.sln
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.13.35825.156 d17.13
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CoHModConfigUI", "CoHModConfigUI.vcxproj", "{21002190-9E4B-40BF-902F-BD5C5497A891}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ModConfigUI", "ModConfigUI.vcxproj", "{21002190-9E4B-40BF-902F-BD5C5497A891}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
8 changes: 4 additions & 4 deletions CoHModConfigUI.vcxproj → ModConfigUI.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<VCProjectVersion>17.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{21002190-9e4b-40bf-902f-bd5c5497a891}</ProjectGuid>
<RootNamespace>FactionFixLoader</RootNamespace>
<RootNamespace>ModConfigUI</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
Expand Down Expand Up @@ -59,7 +59,7 @@
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;FACTIONFIXLOADER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;_DEBUG;MODCONFIGUI_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
Expand All @@ -78,7 +78,7 @@
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;FACTIONFIXLOADER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;NDEBUG;MODCONFIGUI_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
Expand All @@ -94,7 +94,7 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="src\CoHModConfigUI.cpp" />
<ClCompile Include="src\ModConfigUI.cpp" />
<ClCompile Include="src\ConfigCatalog.cpp" />
<ClCompile Include="src\dllmain.cpp" />
<ClCompile Include="src\UiInterop.cpp" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
<ClCompile Include="src\dllmain.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\CoHModConfigUI.cpp">
<ClCompile Include="src\ModConfigUI.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\ConfigCatalog.cpp">
Expand Down
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
# FactionFix Loader
# Mod Config UI

## 📜 Description

FactionFix Loader is a helper mod for `Company of Heroes Relaunch` built on top of `CoHModSDK` which
loads the `FactionFix.dll` library.
Mod Config UI is a mod for `Company of Heroes Relaunch` built on top of `CoHModSDK` which adds an ingame menu for mod settings configuration.

## 🔧 Installation

1. Install the `CoHModSDK` loader in your game directory
2. Copy `FactionFixLoader.dll` into the `mods` folder
3. Add `FactionFixLoader.dll` to `CoHModSDKLoader.ini`
2. Copy `ModConfigUI.dll` into the `mods` folder
3. Add `ModConfigUI.dll` to `CoHModSDKLoader.ini`
4. Start the game

## 🛠️ Building

If you want to build the mod yourself, open the solution in Visual Studio 2022 and build `Release|x86`.
If you want to build the mod yourself, open the solution in Visual Studio and build `Release|x86`.

## 📄 License

Expand Down
27 changes: 27 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# CoHModConfigUI TODO

## Dropdown Hairline On 4-Item Enums

Status: Deferred for later polish.

Context:
- In the 4-item enum case, the dropdown should behave like a no-scroll variant.
- The major scrollbar behavior is already correct: no active scrolling is needed until there are 5 or more choices.
- A very thin black vertical line can still appear at the right edge of the opened 4-item dropdown.

What was verified on 2026-03-30:
- Cheat Engine inspection showed the scrollbar subtree is effectively collapsed in the 4-item case.
- `btnDec` and `btnInc` were collapsed.
- `btnPgUp` and `btnPgDn` were not acting like a visible scrollbar lane.
- The cloned dropdown item widgets were already using the narrow content width.
- The remaining artifact appears to come from native dropdown/listbox root behavior, not from the populated item text path.

Current code assumptions:
- Scrollbar visibility is controlled by `choiceCount > 4` in `src/UiInterop.cpp`.
- For 4 or fewer choices, the code hides the scrollbar and narrows the no-scroll listbox root/content geometry.
- This fixed the major width mismatch, but not the last hairline seam.

Likely follow-up when revisiting:
- Compare against a native in-game dropdown that opens with exactly 4 visible rows, if one can be found.
- Inspect whether the line is a border/separator from the listbox root Presentation rather than a true scrollbar child.
- If needed, adjust the no-scroll listbox root styling/state separately from the scrollbar subtree.
56 changes: 45 additions & 11 deletions lib/CoHModSDK/include/CoHModSDK.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ extern "C" {
using CoHModSDKConfigModVisitor = bool(*)(const char* modId, void* userData);
using CoHModSDKConfigOptionVisitor = bool(*)(const CoHModSDKConfigOptionV1* option, const CoHModSDKConfigValueV1* currentValue, void* userData);

struct CoHModSDKConfigModInfoV1 {
std::uint32_t abiVersion;
std::uint32_t size;
const char* modId;
const char* name;
const char* version;
const char* author;
};

struct CoHModSDKApiV1 {
std::uint32_t abiVersion;
std::uint32_t size;
Expand All @@ -132,13 +141,12 @@ extern "C" {
std::optional<std::uintptr_t> (*FindPattern)(const char* moduleName, const char* signature);
void (*PatchMemory)(void* destination, const void* source, std::size_t size);
bool (*CreateHook)(void* targetFunction, void* detourFunction, void** originalFunction);
bool (*EnableHook)(void* targetFunction);
bool (*DisableHook)(void* targetFunction);
bool (*RegisterConfigSchema)(const CoHModSDKConfigSchemaV1* schema);
bool (*GetConfigValue)(const char* modId, const char* optionId, CoHModSDKConfigValueV1* outValue);
bool (*SetConfigValue)(const char* modId, const char* optionId, const CoHModSDKConfigValueV1* value);
bool (*EnumerateConfigMods)(CoHModSDKConfigModVisitor visitor, void* userData);
bool (*EnumerateConfigOptions)(const char* modId, CoHModSDKConfigOptionVisitor visitor, void* userData);
bool (*GetConfigModInfo)(const char* modId, CoHModSDKConfigModInfoV1* outInfo);
};

struct CoHModSDKModuleV1 {
Expand All @@ -149,11 +157,11 @@ extern "C" {
const char* version;
const char* author;
bool (*OnInitialize)();
bool (*OnModsLoaded)();
void (*OnShutdown)();
};

COHMODSDK_RUNTIME_API bool CoHModSDKRuntime_Initialize(const CoHModSDKRuntimeInitV1* init);
COHMODSDK_RUNTIME_API void CoHModSDKRuntime_EnableAllHooks();
COHMODSDK_RUNTIME_API void CoHModSDKRuntime_Shutdown();
COHMODSDK_RUNTIME_API bool CoHModSDKRuntime_RegisterMod(HMODULE modHandle, const CoHModSDKModuleV1* module, const CoHModSDKModContextV1** outContext);
COHMODSDK_RUNTIME_API void CoHModSDKRuntime_UnregisterMod(HMODULE modHandle);
Expand Down Expand Up @@ -214,6 +222,22 @@ namespace ModSDK {
inline void Log(CoHModSDKLogLevel level, const char* message) {
Detail::GetApi().Log(Detail::GetModContext(), level, message);
}

inline void LogDebug(const char* message) {
Detail::GetApi().Log(Detail::GetModContext(), CoHModSDKLogLevel_Debug, message);
}

inline void LogInfo(const char* message) {
Detail::GetApi().Log(Detail::GetModContext(), CoHModSDKLogLevel_Info, message);
}

inline void LogWarning(const char* message) {
Detail::GetApi().Log(Detail::GetModContext(), CoHModSDKLogLevel_Warning, message);
}

inline void LogError(const char* message) {
Detail::GetApi().Log(Detail::GetModContext(), CoHModSDKLogLevel_Error, message);
}
}

namespace Dialogs {
Expand All @@ -230,19 +254,24 @@ namespace ModSDK {
inline void PatchMemory(void* destination, const void* source, std::size_t size) {
Detail::GetApi().PatchMemory(destination, source, size);
}
}

namespace Hooks {
inline bool CreateHook(void* targetFunction, void* detourFunction, void** originalFunction) {
return Detail::GetApi().CreateHook(targetFunction, detourFunction, originalFunction);
inline void* GetVTableEntry(void* instance, std::size_t index) {
return (*static_cast<void***>(instance))[index];
}

inline bool EnableHook(void* targetFunction) {
return Detail::GetApi().EnableHook(targetFunction);
template <typename T>
inline T ResolveExport(HMODULE module, const char* exportName) {
if ((module == nullptr) || (exportName == nullptr)) {
return nullptr;
}

return reinterpret_cast<T>(GetProcAddress(module, exportName));
}
}

inline bool DisableHook(void* targetFunction) {
return Detail::GetApi().DisableHook(targetFunction);
namespace Hooks {
inline bool CreateHook(void* targetFunction, void* detourFunction, void** originalFunction) {
return Detail::GetApi().CreateHook(targetFunction, detourFunction, originalFunction);
}
}

Expand All @@ -253,6 +282,7 @@ namespace ModSDK {
using Schema = CoHModSDKConfigSchemaV1;
using Type = CoHModSDKConfigType;
using Flags = CoHModSDKConfigFlags;
using ModInfo = CoHModSDKConfigModInfoV1;
using ChangedCallback = CoHModSDKConfigChangedCallback;
using ModVisitor = CoHModSDKConfigModVisitor;
using OptionVisitor = CoHModSDKConfigOptionVisitor;
Expand Down Expand Up @@ -304,5 +334,9 @@ namespace ModSDK {
inline bool EnumerateOptions(const char* modId, OptionVisitor visitor, void* userData) {
return Detail::GetApi().EnumerateConfigOptions(modId, visitor, userData);
}

inline bool GetModInfo(const char* modId, ModInfo* outInfo) {
return Detail::GetApi().GetConfigModInfo(modId, outInfo);
}
}
}
Binary file modified lib/CoHModSDK/lib/x86/CoHModSDK.lib
Binary file not shown.
Loading
Loading