Files
ReshadePluginsAddon/DeathStranding2/dllmain.cpp

308 lines
15 KiB
C++
Raw Normal View History

2026-03-22 17:23:49 +01:00
#define IMGUI_DISABLE_INCLUDE_IMCONFIG_H
#define IMGUI_HAS_DOCK 1
#include "CommonHeaders.h"
#include "HotkeysManager.h"
#include "DirectX.h"
#include <thread>
2026-03-22 17:23:49 +01:00
// Constants
constexpr ULONGLONG DECIMA_ENGINE_INIT_DELAY = 6000; // Use to wait for Decima engine to be ready
2026-03-22 17:23:49 +01:00
// Screen informations
static int screenHeight = GetSystemMetrics(SM_CYSCREEN);
// Core game dll functions declarations
typedef void (*SetBoolFn)(bool, bool);
typedef void (*SetFixesFn)(GameFixes, bool);
typedef void (*SetFloatFn)(GameSetting, float);
static HMODULE fixLib = nullptr;
static LONG g_coreInitialized = 0;
static SetBoolFn SetFixEnabled = nullptr;
static SetFixesFn SetFixes = nullptr;
static SetFloatFn SetValues = nullptr;
void SetFixesEnabled(GameFixes fix, bool value) { if (SetFixes) SetFixes(fix, value); }
static GetGameInfosStruct GetGameInfos = nullptr;
// Plugin variables for checkboxes and sliders
static bool fov_fix_enabled = false;
static bool ultrawide_fix_enabled = false;
2026-03-30 12:39:42 +02:00
static bool HUD_fix_enabled = false;
2026-03-22 22:52:31 +01:00
static bool camera_fix_enabled = false;
static bool DOF_fix_enabled = false;
static bool vignetting_fix_enabled = false;
2026-03-22 17:23:49 +01:00
static bool fix_enabled = false;
static int worldFOVvalue = 0;
2026-03-22 22:52:31 +01:00
static float cameraDistancevalue = 1.f;
2026-03-30 12:39:42 +02:00
static int HUDvalue = 0;
static int triggerDelayMs = 2000; // Default delay between shortcuts
2026-03-22 17:23:49 +01:00
// Overlays popups
static bool popup_Informations = false;
static bool show_log_overlay = false;
static std::string log_content;
// Plugin settings
const std::string SETTINGS_FILE = "./pluginsettings.ini";
2026-03-30 12:39:42 +02:00
const char* FIX_VERSION = "1.0.4";
const char* FIX_INFORMATIONS = "This fix allows to:\n - Control FOV in game.\n - Control camera distance.\n - Enable ultrawide.\n - Control HUD scaling.\n - Disable depth of field in cutscenes.\n - Disable vignetting.";
2026-03-22 17:23:49 +01:00
const char* DONATION_URL = "https://buymeacoffee.com/k4sh44";
// Scaling factor based on screen resolution
float scale = (float)screenHeight / 1200;
// Prepare arrays of checkboxes for ImGui
static FixToggle individualFixes[] = {
{ "FOV", &fov_fix_enabled, GameFixes::FOV },
{ "Depth of field", &DOF_fix_enabled, GameFixes::DOF },
{ "Vignetting", &vignetting_fix_enabled, GameFixes::Vignetting },
{ "Ultrawide", &ultrawide_fix_enabled, GameFixes::UltraWide, "This fix targets aspect ratios wider than 32:9.\n"
"It forces the engine to redraw the aspect by simulating shortcuts.\n"
"Use the threshold slider below to fine tune the delay between simulated shortcuts.\n"
"Increase the delay on slower PCs, decrease it on faster ones."},
2026-03-30 12:39:42 +02:00
{ "HUD", &HUD_fix_enabled, GameFixes::HUD },
2026-03-22 22:52:31 +01:00
{ "Camera", &camera_fix_enabled, GameFixes::Camera } };
2026-03-22 17:23:49 +01:00
// Prepare array of sliders for ImGui
2026-03-30 12:39:42 +02:00
static SliderFix2 sliders[4];
2026-03-22 17:23:49 +01:00
// Load and unload game core dll functions /!\ necessary
static void LoadFixDLL(reshade::api::effect_runtime* runtime) {
if (InterlockedCompareExchange(&g_coreInitialized, 1, 0) != 0)
return;
if (GetModuleHandleA("DeathStranding2Core.dll") == nullptr) {
fixLib = LoadLibraryA("DeathStranding2Core.dll");
if (!fixLib) {
MessageBoxA(nullptr, "Impossible to load game core dll", "Erreur", MB_OK);
return;
}
SetFixEnabled = (SetBoolFn)GetProcAddress(fixLib, "SetFixEnabled");
SetFixes = (SetFixesFn)GetProcAddress(fixLib, "SetFixesEnabled");
SetValues = (SetFloatFn)GetProcAddress(fixLib, "SetValues");
GetGameInfos = (GetGameInfosStruct)GetProcAddress(fixLib, "GetGameInfos");
// Apply initial values loaded from settings
if (SetValues) {
SetValues(GameSetting::FOV, worldFOVvalue);
2026-03-30 12:39:42 +02:00
SetValues(GameSetting::HUD, HUDvalue);
2026-03-22 22:52:31 +01:00
SetValues(GameSetting::CameraDistance, cameraDistancevalue);
2026-03-22 17:23:49 +01:00
}
if (SetFixEnabled) SetFixEnabled(fix_enabled, true);
if (SetFixes) {
SetFixes(GameFixes::FOV, fov_fix_enabled);
2026-03-30 12:39:42 +02:00
SetFixes(GameFixes::HUD, HUD_fix_enabled);
2026-03-22 22:52:31 +01:00
SetFixes(GameFixes::Camera, camera_fix_enabled);
SetFixes(GameFixes::DOF, DOF_fix_enabled);
SetFixes(GameFixes::Vignetting, vignetting_fix_enabled);
2026-03-22 17:23:49 +01:00
}
sliders[0] = { "In game additional FOV", "##FOVValue", SliderType::Int, &worldFOVvalue, -20, 50, GameSetting::FOV, SetValues };
2026-03-30 12:39:42 +02:00
sliders[1] = { "HUD scaling", "##HUDValue", SliderType::Int, &HUDvalue, -20, 40, GameSetting::HUD, SetValues };
sliders[2] = { "Camera distance", "##CamValue", SliderType::Float, &cameraDistancevalue, 0, 5, GameSetting::CameraDistance, SetValues, "%.1f",
2026-03-22 22:52:31 +01:00
"Value is a multiplier.\nFinal value reported below is in meters."};
2026-03-30 12:39:42 +02:00
sliders[3] = { "Ultrawide enforce delay", "##AltEnterValue", SliderType::Int, &triggerDelayMs, 1000, 5000, GameSetting::Threshold, SetValues, "",
"This value is a threshold in ms between the simulated alt + enter." };
2026-03-22 17:23:49 +01:00
}
}
// Settings functions
static void SaveSettings() {
ini::IniFile pluginIniFile;
pluginIniFile["1#General fix"].setComment(std::vector<std::string>{ "The following sections are saved by plugin",
"You should not need to modify them",
" ",
"Controls if fix mod (globally) is enabled" });
pluginIniFile["1#General fix"]["Enabled"] = fix_enabled;
pluginIniFile["1#General fix"]["TriggerDelayMs"] = triggerDelayMs;
2026-03-22 17:23:49 +01:00
pluginIniFile["2#Individual fix"].setComment("Controls each fix individually");
pluginIniFile["2#Individual fix"]["FOV"] = fov_fix_enabled;
pluginIniFile["2#Individual fix"]["UltraWide"] = ultrawide_fix_enabled;
2026-03-30 12:39:42 +02:00
pluginIniFile["2#Individual fix"]["HUD"] = HUD_fix_enabled;
2026-03-22 22:52:31 +01:00
pluginIniFile["2#Individual fix"]["Camera"] = camera_fix_enabled;
pluginIniFile["2#Individual fix"]["DOF"] = DOF_fix_enabled;
pluginIniFile["2#Individual fix"]["Vignetting"] = vignetting_fix_enabled;
2026-03-22 17:23:49 +01:00
pluginIniFile["3#Fixes tuning"].setComment("Individual fix fine tune");
pluginIniFile["3#Fixes tuning"]["World FOV"] = worldFOVvalue;
2026-03-30 12:39:42 +02:00
pluginIniFile["3#Fixes tuning"]["HUD scaling"] = HUDvalue;
2026-03-22 22:52:31 +01:00
pluginIniFile["3#Fixes tuning"]["Camera distance"] = cameraDistancevalue;
2026-03-22 17:23:49 +01:00
pluginIniFile.save(SETTINGS_FILE);
}
static void LoadSettings() {
ini::IniFile pluginIniFile;
try {
pluginIniFile.load(SETTINGS_FILE);
fix_enabled = pluginIniFile["1#General fix"]["Enabled"].as<bool>();
triggerDelayMs = pluginIniFile["1#General fix"]["TriggerDelayMs"].as<int>();
2026-03-22 17:23:49 +01:00
fov_fix_enabled = pluginIniFile["2#Individual fix"]["FOV"].as<bool>();
ultrawide_fix_enabled = pluginIniFile["2#Individual fix"]["UltraWide"].as<bool>();
2026-03-30 12:39:42 +02:00
HUD_fix_enabled = pluginIniFile["2#Individual fix"]["HUD"].as<bool>();
2026-03-22 22:52:31 +01:00
camera_fix_enabled = pluginIniFile["2#Individual fix"]["Camera"].as<bool>();
DOF_fix_enabled = pluginIniFile["2#Individual fix"]["DOF"].as<bool>();
vignetting_fix_enabled = pluginIniFile["2#Individual fix"]["Vignetting"].as<bool>();
2026-03-22 17:23:49 +01:00
worldFOVvalue = pluginIniFile["3#Fixes tuning"]["World FOV"].as<int>();
2026-03-30 12:39:42 +02:00
HUDvalue = pluginIniFile["3#Fixes tuning"]["HUD scaling"].as<int>();
2026-03-22 22:52:31 +01:00
cameraDistancevalue = pluginIniFile["3#Fixes tuning"]["Camera distance"].as<float>();
2026-03-22 17:23:49 +01:00
}
catch (const std::exception& e) {}
}
// Read plugin log file
void read_log_file(const std::string& filename) {
std::ifstream file(filename);
if (!file.is_open()) {
log_content = "Impossible to open file : " + filename;
return;
}
std::ostringstream ss;
ss << file.rdbuf();
log_content = ss.str();
}
// Initialize ImGui widgets for Reshade
static void on_overlay_draw(reshade::api::effect_runtime* runtime) {
ImGui::SetNextWindowSize(ImVec2(350 * scale, 150 * scale), ImGuiCond_Once);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.84f, 0.12f, 0.51f, 1.0f)); // pink
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.84f, 0.12f, 0.51f, 1.0f)); // pink
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.84f, 0.2f, 0.51f, 1.0f)); // pink
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.8f, 1.f, 1.f, 1.f)); // white
if (ImGui::Button("Like my work ? Consider donation")) ShellExecuteA(NULL, "open", DONATION_URL, NULL, NULL, SW_SHOWNORMAL); // Donation
ImGui::PopStyleColor(4); // Restore color
ImGui::SameLine();
if (ImGui::Button("Fix informations")) popup_Informations = true; // Fix information
ImGui::SameLine();
if (ImGui::Button("View logs")) {
read_log_file("DeathStranding2.log");
show_log_overlay = true; // Fix information
}
if (popup_Informations) {
ImGui::Begin("Informations", &popup_Informations, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Text("Version : %s", FIX_VERSION);
ImGui::Text(FIX_INFORMATIONS);
ImGui::End();
}
if (show_log_overlay) {
ImGui::Begin("Game log", &show_log_overlay, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::TextUnformatted(log_content.c_str());
ImGui::End();
}
if (ImGui::BeginTabBar("MainTabs")) {
if (ImGui::BeginTabItem("Fixes")) {
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(10 * scale, 10 * scale));
if (ImGui::BeginTable("FixesTable", 2, ImGuiTableFlags_SizingStretchSame)) {
ImGui::TableSetupColumn("LeftFix", ImGuiTableColumnFlags_WidthStretch, 0.4f);
ImGui::TableSetupColumn("RightFix", ImGuiTableColumnFlags_WidthStretch, 0.6f);
ImGui::TableNextRow();
// Drawing a left column with slider and general fix
ImGui::TableSetColumnIndex(0);
if (ImGui::CollapsingHeader("Enable fixes", ImGuiTreeNodeFlags_DefaultOpen))
if (ImGui::Checkbox("Fix enabled", &fix_enabled)) {
if (SetFixEnabled) SetFixEnabled(fix_enabled, false);
SaveSettings();
}
// Individual fixes
ImGui::TableSetColumnIndex(1);
if (ImGui::CollapsingHeader("Individual fixes", ImGuiTreeNodeFlags_DefaultOpen)) {
if (ImGui::BeginTable("IndividualFixesTable", 2, ImGuiTableFlags_SizingStretchSame)) {
ImGui::TableSetupColumn("IndFix1", ImGuiTableColumnFlags_WidthStretch, 0.4f);
ImGui::TableSetupColumn("IndFix2", ImGuiTableColumnFlags_WidthStretch, 0.6f);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
2026-03-30 12:39:42 +02:00
for (int i = 0; i < 3; ++i) DrawFixCheckbox(individualFixes[i]);
ImGui::TableSetColumnIndex(1);
2026-03-30 12:39:42 +02:00
for (int i = 3; i < IM_ARRAYSIZE(individualFixes); ++i) DrawFixCheckbox(individualFixes[i]);
2026-03-22 17:23:49 +01:00
ImGui::EndTable();
}
}
ImGui::EndTable();
}
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 2);
if (ImGui::BeginTable("FixesSliders", 2, ImGuiTableFlags_SizingStretchSame)) {
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
2026-03-30 12:39:42 +02:00
for (int i = 0; i < 2; ++i) DrawSlider2(sliders[i], 200);
2026-03-22 22:52:31 +01:00
ImGui::TableSetColumnIndex(1);
2026-03-30 12:39:42 +02:00
for (int i = 2; i < 4; ++i) DrawSlider2(sliders[i], 200);
2026-03-22 17:23:49 +01:00
ImGui::EndTable();
}
ImGui::PopStyleVar();
ImGui::EndTabItem();
}
// Fix status
if (ImGui::CollapsingHeader("Fix informations", ImGuiTreeNodeFlags_DefaultOpen)) {
if (GetGameInfos) {
GameInfos infos{};
GetGameInfos(&infos);
ImGui::Text("Screen width: %d, height: %d, aspect ratio: %.2f", infos.screenWidth, infos.screenHeight, infos.aspectRatio);
ImGui::TextColored(ImColor(48, 179, 25), "FOV In: %.2f, Out: %.2f", infos.FOVIn, infos.FOVOut);
2026-03-22 22:52:31 +01:00
ImGui::TextColored(ImColor(48, 179, 25), "Camera distance In: %.2f, Out: %.2f", infos.cameraIn, infos.cameraOut);
2026-03-22 17:23:49 +01:00
}
}
}
ImGui::PopStyleVar();
}
static std::atomic<bool> g_Started{ false };
static std::atomic<bool> g_Done{ false };
static void on_present(reshade::api::command_queue* queue, reshade::api::swapchain* swapchain,
const reshade::api::rect*, const reshade::api::rect*, uint32_t, const reshade::api::rect*) {
if (!ultrawide_fix_enabled || g_Done || !SetFixes || g_Started.exchange(true)) return;
void* hwnd = swapchain->get_hwnd(); // Retrieve DX12 swapchain
HWND hwnd_local = reinterpret_cast<HWND>(hwnd);
bool exclusive = IsExclusiveFullscreen(swapchain); // Retrieve display mode (fullscreen or exclusive fullscreen)
SetFixes(GameFixes::UltraWide, ultrawide_fix_enabled);
SetFixes(GameFixes::None, false);
std::thread([hwnd_local, exclusive]() {
std::this_thread::sleep_for(std::chrono::milliseconds(DECIMA_ENGINE_INIT_DELAY)); // Wait for Decima engine to be ready
if (!exclusive) {
SimulateAltEnter(); // 1st Alt+Enter
std::this_thread::sleep_for(std::chrono::milliseconds(triggerDelayMs)); // Give enough time to swapchain rebuild
SimulateAltEnter(); // 2nd Alt+Enter
}
else {
SimulateAltTab(); // Simulate ALT+Tab
std::this_thread::sleep_for(std::chrono::milliseconds(triggerDelayMs));
ShowWindow(hwnd_local, SW_RESTORE); // Restore game window
}
g_Done = true;
}).detach();
}
2026-03-22 17:23:49 +01:00
// Main dll intrance
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD ul_reason_for_call, LPVOID) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
if (!reshade::register_addon(hinstDLL))
return FALSE;
LoadSettings();
reshade::register_overlay("Death Stranding 2: On The Beach", &on_overlay_draw);
reshade::register_event<reshade::addon_event::present>(&on_present);
2026-03-22 17:23:49 +01:00
reshade::register_event<reshade::addon_event::init_effect_runtime>(&LoadFixDLL);
break;
case DLL_PROCESS_DETACH:
reshade::unregister_overlay("Death Stranding 2: On The Beach", &on_overlay_draw);
reshade::unregister_event<reshade::addon_event::present>(&on_present);
2026-03-22 17:23:49 +01:00
reshade::unregister_event<reshade::addon_event::init_effect_runtime>(&LoadFixDLL);
reshade::unregister_addon(hinstDLL);
break;
}
return TRUE;
}