mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-26 10:29:18 +01:00
346 lines
14 KiB
C++
346 lines
14 KiB
C++
/*
|
|
* Copyright (C) 2021 Patrick Mours
|
|
* SPDX-License-Identifier: BSD-3-Clause OR MIT
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "reshade_events.hpp"
|
|
#include "reshade_overlay.hpp"
|
|
#include <charconv>
|
|
#include <Windows.h>
|
|
|
|
// Current version of the ReShade API
|
|
#define RESHADE_API_VERSION 8
|
|
|
|
// Optionally import ReShade API functions when 'RESHADE_API_LIBRARY' is defined instead of using header-only mode
|
|
#if defined(RESHADE_API_LIBRARY) || defined(RESHADE_API_LIBRARY_EXPORT)
|
|
|
|
#ifdef RESHADE_API_LIBRARY_EXPORT
|
|
#define RESHADE_API_LIBRARY_DECLSPEC extern "C" __declspec(dllexport)
|
|
#else
|
|
#define RESHADE_API_LIBRARY_DECLSPEC extern "C" __declspec(dllimport)
|
|
#endif
|
|
|
|
RESHADE_API_LIBRARY_DECLSPEC void ReShadeLogMessage(HMODULE module, int level, const char *message);
|
|
|
|
RESHADE_API_LIBRARY_DECLSPEC void ReShadeGetBasePath(char *path, size_t *path_size);
|
|
|
|
RESHADE_API_LIBRARY_DECLSPEC bool ReShadeGetConfigValue(HMODULE module, reshade::api::effect_runtime *runtime, const char *section, const char *key, char *value, size_t *value_size);
|
|
RESHADE_API_LIBRARY_DECLSPEC void ReShadeSetConfigValue(HMODULE module, reshade::api::effect_runtime *runtime, const char *section, const char *key, const char *value);
|
|
|
|
RESHADE_API_LIBRARY_DECLSPEC bool ReShadeRegisterAddon(HMODULE module, uint32_t api_version);
|
|
RESHADE_API_LIBRARY_DECLSPEC void ReShadeUnregisterAddon(HMODULE module);
|
|
|
|
RESHADE_API_LIBRARY_DECLSPEC void ReShadeRegisterEvent(reshade::addon_event ev, void *callback);
|
|
RESHADE_API_LIBRARY_DECLSPEC void ReShadeUnregisterEvent(reshade::addon_event ev, void *callback);
|
|
|
|
RESHADE_API_LIBRARY_DECLSPEC void ReShadeRegisterOverlay(const char *title, void(*callback)(reshade::api::effect_runtime *runtime));
|
|
RESHADE_API_LIBRARY_DECLSPEC void ReShadeUnregisterOverlay(const char *title, void(*callback)(reshade::api::effect_runtime *runtime));
|
|
|
|
#else
|
|
|
|
// Use the kernel32 variant of module enumeration functions so it can be safely called from 'DllMain'
|
|
extern "C" BOOL WINAPI K32EnumProcessModules(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded);
|
|
|
|
namespace reshade { namespace internal
|
|
{
|
|
/// <summary>
|
|
/// Gets the handle to the ReShade module.
|
|
/// </summary>
|
|
inline HMODULE get_reshade_module_handle(HMODULE reshade_module = nullptr)
|
|
{
|
|
static HMODULE handle = reshade_module;
|
|
if (handle == nullptr)
|
|
{
|
|
HMODULE modules[1024]; DWORD num = 0;
|
|
if (K32EnumProcessModules(GetCurrentProcess(), modules, sizeof(modules), &num))
|
|
{
|
|
if (num > sizeof(modules))
|
|
num = sizeof(modules);
|
|
|
|
for (DWORD i = 0; i < num / sizeof(HMODULE); ++i)
|
|
{
|
|
if (GetProcAddress(modules[i], "ReShadeRegisterAddon") &&
|
|
GetProcAddress(modules[i], "ReShadeUnregisterAddon"))
|
|
{
|
|
handle = modules[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return handle;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the handle to the current add-on module.
|
|
/// </summary>
|
|
inline HMODULE get_current_module_handle(HMODULE addon_module = nullptr)
|
|
{
|
|
static HMODULE handle = addon_module;
|
|
return handle;
|
|
}
|
|
} }
|
|
|
|
#endif
|
|
|
|
namespace reshade
|
|
{
|
|
/// <summary>
|
|
/// Available log severity levels.
|
|
/// </summary>
|
|
enum class log_level
|
|
{
|
|
error = 1,
|
|
warning = 2,
|
|
info = 3,
|
|
debug = 4
|
|
};
|
|
|
|
/// <summary>
|
|
/// Writes a message to ReShade's log.
|
|
/// </summary>
|
|
/// <param name="level">Severity level.</param>
|
|
/// <param name="message">A null-terminated message string.</param>
|
|
inline void log_message(log_level level, const char *message)
|
|
{
|
|
#if defined(RESHADE_API_LIBRARY) || defined(RESHADE_API_LIBRARY_EXPORT)
|
|
ReShadeLogMessage(nullptr, static_cast<int>(level), message);
|
|
#else
|
|
static const auto func = reinterpret_cast<void(*)(HMODULE, int, const char *)>(
|
|
GetProcAddress(internal::get_reshade_module_handle(), "ReShadeLogMessage"));
|
|
func(internal::get_current_module_handle(), static_cast<int>(level), message);
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the file path ReShade uses to resolve relative paths.
|
|
/// </summary>
|
|
/// <param name="path">Pointer to a string buffer that is filled with the file path to the preset, or <see langword="nullptr"/> to query the necessary size.</param>
|
|
/// <param name="path_size">Pointer to an integer that contains the size of the string buffer and is set to the actual length of the string, including the null-terminator.</param>
|
|
inline void get_reshade_base_path(char *path, size_t *path_size)
|
|
{
|
|
#if defined(RESHADE_API_LIBRARY) || defined(RESHADE_API_LIBRARY_EXPORT)
|
|
ReShadeGetBasePath(path, path_size);
|
|
#else
|
|
static const auto func = reinterpret_cast<bool(*)(char *, size_t *)>(
|
|
GetProcAddress(internal::get_reshade_module_handle(), "ReShadeGetBasePath"));
|
|
func(path, path_size);
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value from one of ReShade's config files.
|
|
/// </summary>
|
|
/// <param name="runtime">Optional effect runtime to use the config file from, or <see langword="nullptr"/> to use the global config file.</param>
|
|
/// <param name="section">Name of the config section.</param>
|
|
/// <param name="key">Name of the config value.</param>
|
|
/// <param name="value">Pointer to a string buffer that is filled with the config value, or <see langword="nullptr"/> to query the necessary size.</param>
|
|
/// <param name="value_size">Pointer to an integer that contains the size of the string buffer and is set to the actual length of the string, including the null-terminator.</param>
|
|
/// <returns><see langword="true"/> if the specified config value exists, <see cref="false"/> otherwise.</returns>
|
|
inline bool get_config_value(api::effect_runtime *runtime, const char *section, const char *key, char *value, size_t *value_size)
|
|
{
|
|
#if defined(RESHADE_API_LIBRARY) || defined(RESHADE_API_LIBRARY_EXPORT)
|
|
return ReShadeGetConfigValue(nullptr, runtime, section, key, value, value_size);
|
|
#else
|
|
static const auto func = reinterpret_cast<bool(*)(HMODULE, api::effect_runtime *, const char *, const char *, char *, size_t *)>(
|
|
GetProcAddress(internal::get_reshade_module_handle(), "ReShadeGetConfigValue"));
|
|
return func(internal::get_current_module_handle(), runtime, section, key, value, value_size);
|
|
#endif
|
|
}
|
|
#if _HAS_CXX17
|
|
template <typename T>
|
|
inline bool get_config_value(api::effect_runtime *runtime, const char *section, const char *key, T &value)
|
|
{
|
|
char value_string[32]; size_t value_length = sizeof(value_string) - 1;
|
|
if (!get_config_value(runtime, section, key, value_string, &value_length))
|
|
return false;
|
|
return std::from_chars(value_string, value_string + value_length, value).ec == std::errc {};
|
|
}
|
|
template <>
|
|
inline bool get_config_value<bool>(api::effect_runtime *runtime, const char *section, const char *key, bool &value)
|
|
{
|
|
int value_int = 0;
|
|
if (!get_config_value<int>(runtime, section, key, value_int))
|
|
return false;
|
|
value = (value_int != 0);
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// Sets and saves a value in one of ReShade's config files.
|
|
/// </summary>
|
|
/// <param name="runtime">Optional effect runtime to use the config file from, or <see langword="nullptr"/> to use the global config file.</param>
|
|
/// <param name="section">Name of the config section.</param>
|
|
/// <param name="key">Name of the config value.</param>
|
|
/// <param name="value">Config value to set.</param>
|
|
inline void set_config_value(api::effect_runtime *runtime, const char *section, const char *key, const char *value)
|
|
{
|
|
#if defined(RESHADE_API_LIBRARY) || defined(RESHADE_API_LIBRARY_EXPORT)
|
|
ReShadeSetConfigValue(nullptr, runtime, section, key, value);
|
|
#else
|
|
static const auto func = reinterpret_cast<void(*)(HMODULE, api::effect_runtime *, const char *, const char *, const char *)>(
|
|
GetProcAddress(internal::get_reshade_module_handle(), "ReShadeSetConfigValue"));
|
|
func(internal::get_current_module_handle(), runtime, section, key, value);
|
|
#endif
|
|
}
|
|
#if _HAS_CXX17
|
|
template <typename T>
|
|
inline void set_config_value(api::effect_runtime *runtime, const char *section, const char *key, const T &value)
|
|
{
|
|
char value_string[32] = "";
|
|
std::to_chars(value_string, value_string + sizeof(value_string) - 1, value);
|
|
set_config_value(runtime, section, key, static_cast<const char *>(value_string));
|
|
}
|
|
template <>
|
|
inline void set_config_value<bool>(api::effect_runtime *runtime, const char *section, const char *key, const bool &value)
|
|
{
|
|
set_config_value<int>(runtime, section, key, value ? 1 : 0);
|
|
}
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// Registers this module as an add-on with ReShade.
|
|
/// Call this in 'AddonInit' or 'DllMain' during process attach, before any of the other API functions!
|
|
/// </summary>
|
|
/// <param name="addon_module">Handle of the current module.</param>
|
|
/// <param name="reshade_module">Handle of the ReShade module in the process, or <see langword="nullptr"/> to find it automatically.</param>
|
|
inline bool register_addon(HMODULE addon_module, [[maybe_unused]] HMODULE reshade_module = nullptr)
|
|
{
|
|
#if defined(RESHADE_API_LIBRARY) || (defined(RESHADE_API_LIBRARY_EXPORT) && RESHADE_ADDON)
|
|
return ReShadeRegisterAddon(addon_module, RESHADE_API_VERSION);
|
|
#elif !defined(RESHADE_API_LIBRARY_EXPORT)
|
|
addon_module = internal::get_current_module_handle(addon_module);
|
|
reshade_module = internal::get_reshade_module_handle(reshade_module);
|
|
|
|
if (reshade_module == nullptr)
|
|
return false;
|
|
|
|
const auto func = reinterpret_cast<bool(*)(HMODULE, uint32_t)>(
|
|
GetProcAddress(reshade_module, "ReShadeRegisterAddon"));
|
|
// Check that the ReShade module supports the used API
|
|
if (func == nullptr || !func(addon_module, RESHADE_API_VERSION))
|
|
return false;
|
|
|
|
#if defined(IMGUI_VERSION_NUM)
|
|
const auto imgui_func = reinterpret_cast<const imgui_function_table *(*)(uint32_t)>(
|
|
GetProcAddress(reshade_module, "ReShadeGetImGuiFunctionTable"));
|
|
// Check that the ReShade module was built with Dear ImGui support and supports the used version
|
|
if (imgui_func == nullptr || !(imgui_function_table_instance() = imgui_func(IMGUI_VERSION_NUM)))
|
|
return false;
|
|
#endif
|
|
|
|
return true;
|
|
#else
|
|
UNREFERENCED_PARAMETER(addon_module);
|
|
UNREFERENCED_PARAMETER(reshade_module);
|
|
return false;
|
|
#endif
|
|
}
|
|
/// <summary>
|
|
/// Unregisters this module.
|
|
/// Call this in 'AddonUninit' or 'DllMain' during process detach, after any of the other API functions.
|
|
/// </summary>
|
|
/// <param name="addon_module">Handle of the current module.</param>
|
|
/// <param name="reshade_module">Handle of the ReShade module in the process, or <see langword="nullptr"/> to find it automatically.</param>
|
|
inline void unregister_addon(HMODULE addon_module, [[maybe_unused]] HMODULE reshade_module = nullptr)
|
|
{
|
|
#if defined(RESHADE_API_LIBRARY) || (defined(RESHADE_API_LIBRARY_EXPORT) && RESHADE_ADDON)
|
|
ReShadeUnregisterAddon(addon_module);
|
|
#elif !defined(RESHADE_API_LIBRARY_EXPORT)
|
|
addon_module = internal::get_current_module_handle(addon_module);
|
|
reshade_module = internal::get_reshade_module_handle(reshade_module);
|
|
|
|
if (reshade_module == nullptr)
|
|
return;
|
|
|
|
const auto func = reinterpret_cast<bool(*)(HMODULE)>(
|
|
GetProcAddress(reshade_module, "ReShadeUnregisterAddon"));
|
|
if (func != nullptr)
|
|
func(addon_module);
|
|
#else
|
|
UNREFERENCED_PARAMETER(addon_module);
|
|
UNREFERENCED_PARAMETER(reshade_module);
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Registers a callback for the specified event (via template) with ReShade.
|
|
/// <para>The callback function is then called whenever the application performs a task associated with this event (see also the <see cref="addon_event"/> enumeration).</para>
|
|
/// </summary>
|
|
/// <param name="callback">Pointer to the callback function.</param>
|
|
template <reshade::addon_event ev>
|
|
inline void register_event(typename reshade::addon_event_traits<ev>::decl callback)
|
|
{
|
|
#if defined(RESHADE_API_LIBRARY) || (defined(RESHADE_API_LIBRARY_EXPORT) && RESHADE_ADDON)
|
|
ReShadeRegisterEvent(ev, static_cast<void *>(callback));
|
|
#elif !defined(RESHADE_API_LIBRARY_EXPORT)
|
|
static const auto func = reinterpret_cast<void(*)(reshade::addon_event, void *)>(
|
|
GetProcAddress(internal::get_reshade_module_handle(), "ReShadeRegisterEvent"));
|
|
if (func != nullptr)
|
|
func(ev, static_cast<void *>(callback));
|
|
#else
|
|
UNREFERENCED_PARAMETER(callback);
|
|
#endif
|
|
}
|
|
/// <summary>
|
|
/// Unregisters a callback for the specified event (via template) that was previously registered via <see cref="register_event"/>.
|
|
/// </summary>
|
|
/// <param name="callback">Pointer to the callback function.</param>
|
|
template <reshade::addon_event ev>
|
|
inline void unregister_event(typename reshade::addon_event_traits<ev>::decl callback)
|
|
{
|
|
#if defined(RESHADE_API_LIBRARY) || (defined(RESHADE_API_LIBRARY_EXPORT) && RESHADE_ADDON)
|
|
ReShadeUnregisterEvent(ev, static_cast<void *>(callback));
|
|
#elif !defined(RESHADE_API_LIBRARY_EXPORT)
|
|
static const auto func = reinterpret_cast<void(*)(reshade::addon_event, void *)>(
|
|
GetProcAddress(internal::get_reshade_module_handle(), "ReShadeUnregisterEvent"));
|
|
if (func != nullptr)
|
|
func(ev, static_cast<void *>(callback));
|
|
#else
|
|
UNREFERENCED_PARAMETER(callback);
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Registers an overlay with ReShade.
|
|
/// <para>The callback function is then called when the overlay is visible and allows adding Dear ImGui widgets for user interaction.</para>
|
|
/// </summary>
|
|
/// <param name="title">Null-terminated title string, or <see langword="nullptr"/> to register a settings overlay for this add-on.</param>
|
|
/// <param name="callback">Pointer to the callback function.</param>
|
|
inline void register_overlay(const char *title, void(*callback)(reshade::api::effect_runtime *runtime))
|
|
{
|
|
#if defined(RESHADE_API_LIBRARY) || (defined(RESHADE_API_LIBRARY_EXPORT) && RESHADE_ADDON && RESHADE_GUI)
|
|
ReShadeRegisterOverlay(title, callback);
|
|
#elif !defined(RESHADE_API_LIBRARY_EXPORT)
|
|
static const auto func = reinterpret_cast<void(*)(const char *, void(*)(reshade::api::effect_runtime *))>(
|
|
GetProcAddress(internal::get_reshade_module_handle(), "ReShadeRegisterOverlay"));
|
|
if (func != nullptr)
|
|
func(title, callback);
|
|
#else
|
|
UNREFERENCED_PARAMETER(title);
|
|
UNREFERENCED_PARAMETER(callback);
|
|
#endif
|
|
}
|
|
/// <summary>
|
|
/// Unregisters an overlay that was previously registered via <see cref="register_overlay"/>.
|
|
/// </summary>
|
|
/// <param name="title">Null-terminated title string.</param>
|
|
/// <param name="callback">Pointer to the callback function.</param>
|
|
inline void unregister_overlay(const char *title, void(*callback)(reshade::api::effect_runtime *runtime))
|
|
{
|
|
#if defined(RESHADE_API_LIBRARY) || (defined(RESHADE_API_LIBRARY_EXPORT) && RESHADE_ADDON && RESHADE_GUI)
|
|
ReShadeUnregisterOverlay(title, callback);
|
|
#elif !defined(RESHADE_API_LIBRARY_EXPORT)
|
|
static const auto func = reinterpret_cast<void(*)(const char *, void(*)(reshade::api::effect_runtime *))>(
|
|
GetProcAddress(internal::get_reshade_module_handle(), "ReShadeUnregisterOverlay"));
|
|
if (func != nullptr)
|
|
func(title, callback);
|
|
#else
|
|
UNREFERENCED_PARAMETER(title);
|
|
UNREFERENCED_PARAMETER(callback);
|
|
#endif
|
|
}
|
|
}
|