Files
2026-03-31 21:07:57 +02:00

318 lines
16 KiB
C++

#define IMGUI_DISABLE_INCLUDE_IMCONFIG_H
#define IMGUI_HAS_DOCK 1
#include "CommonHeaders.h"
#include "HotkeysManager.h"
#include "DirectX.h"
#include <thread>
// Constants
constexpr ULONGLONG DECIMA_ENGINE_INIT_DELAY = 6000; // Use to wait for Decima engine to be ready
// 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;
static bool HUD_fix_enabled = false;
static bool camera_fix_enabled = false;
static bool DOF_fix_enabled = false;
static bool vignetting_fix_enabled = false;
static bool sharpening_fix_enabled = false;
static bool fix_enabled = false;
static int worldFOVvalue = 0;
static float cameraDistancevalue = 1.f;
static float sharpeningvalue = 1.f;
static int HUDvalue = 0;
static int triggerDelayMs = 2000; // Default delay between shortcuts
// 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";
const char* FIX_VERSION = "1.0.4.1";
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.\n - Control image sharpening.";
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 },
{ "Sharpening", &sharpening_fix_enabled, GameFixes::Sharpening, "This fix will override game setting."},
{ "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 slider below to fine tune the delay between simulated shortcuts.\n"
"Increase the delay on slower PCs, decrease it on faster ones."},
{ "HUD", &HUD_fix_enabled, GameFixes::HUD, "Use this fix to reposition HUD for ultrawide displays."},
{ "Camera", &camera_fix_enabled, GameFixes::Camera } };
// Prepare array of sliders for ImGui
static SliderFix2 sliders[5];
// 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);
SetValues(GameSetting::HUD, HUDvalue);
SetValues(GameSetting::CameraDistance, cameraDistancevalue);
SetValues(GameSetting::Sharpening, sharpeningvalue);
}
if (SetFixEnabled) SetFixEnabled(fix_enabled, true);
if (SetFixes) {
SetFixes(GameFixes::FOV, fov_fix_enabled);
SetFixes(GameFixes::HUD, HUD_fix_enabled);
SetFixes(GameFixes::Camera, camera_fix_enabled);
SetFixes(GameFixes::DOF, DOF_fix_enabled);
SetFixes(GameFixes::Vignetting, vignetting_fix_enabled);
SetFixes(GameFixes::Sharpening, sharpening_fix_enabled);
}
sliders[0] = { "In game additional FOV", "##FOVValue", SliderType::Int, &worldFOVvalue, -20, 50, GameSetting::FOV, SetValues };
sliders[1] = { "HUD scaling", "##HUDValue", SliderType::Int, &HUDvalue, -20, 40, GameSetting::HUD, SetValues };
sliders[2] = { "Sharpening value", "##SharpeningValue", SliderType::Float, &sharpeningvalue, 0, 1.5, GameSetting::Sharpening, SetValues, "%.1f" };
sliders[3] = { "Camera distance", "##CamValue", SliderType::Float, &cameraDistancevalue, 0, 5, GameSetting::CameraDistance, SetValues, "%.1f",
"Value is a multiplier.\nFinal value reported below is in meters."};
sliders[4] = { "Ultrawide enforce delay", "##AltEnterValue", SliderType::Int, &triggerDelayMs, 1000, 5000, GameSetting::Threshold, SetValues, "",
"This value is a delay in ms between the simulated shortcuts to enforce aspect ratio on launch." };
}
}
// 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;
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;
pluginIniFile["2#Individual fix"]["HUD"] = HUD_fix_enabled;
pluginIniFile["2#Individual fix"]["Camera"] = camera_fix_enabled;
pluginIniFile["2#Individual fix"]["DOF"] = DOF_fix_enabled;
pluginIniFile["2#Individual fix"]["Vignetting"] = vignetting_fix_enabled;
pluginIniFile["2#Individual fix"]["Sharpening"] = sharpening_fix_enabled;
pluginIniFile["3#Fixes tuning"].setComment("Individual fix fine tune");
pluginIniFile["3#Fixes tuning"]["World FOV"] = worldFOVvalue;
pluginIniFile["3#Fixes tuning"]["HUD scaling"] = HUDvalue;
pluginIniFile["3#Fixes tuning"]["Camera distance"] = cameraDistancevalue;
pluginIniFile["3#Fixes tuning"]["Sharpening value"] = sharpeningvalue;
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>();
fov_fix_enabled = pluginIniFile["2#Individual fix"]["FOV"].as<bool>();
ultrawide_fix_enabled = pluginIniFile["2#Individual fix"]["UltraWide"].as<bool>();
HUD_fix_enabled = pluginIniFile["2#Individual fix"]["HUD"].as<bool>();
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>();
sharpening_fix_enabled = pluginIniFile["2#Individual fix"]["Sharpening"].as<bool>();
worldFOVvalue = pluginIniFile["3#Fixes tuning"]["World FOV"].as<int>();
HUDvalue = pluginIniFile["3#Fixes tuning"]["HUD scaling"].as<int>();
cameraDistancevalue = pluginIniFile["3#Fixes tuning"]["Camera distance"].as<float>();
sharpeningvalue = pluginIniFile["3#Fixes tuning"]["Sharpening value"].as<float>();
}
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);
for (int i = 0; i < 4; ++i) DrawFixCheckbox(individualFixes[i]);
ImGui::TableSetColumnIndex(1);
for (int i = 4; i < IM_ARRAYSIZE(individualFixes); ++i) DrawFixCheckbox(individualFixes[i]);
ImGui::EndTable();
}
}
ImGui::EndTable();
}
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 2);
if (ImGui::BeginTable("FixesSliders", 2, ImGuiTableFlags_SizingStretchSame)) {
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
for (int i = 0; i < 3; ++i) DrawSlider2(sliders[i], 200);
ImGui::TableSetColumnIndex(1);
for (int i = 3; i < 5; ++i) DrawSlider2(sliders[i], 200);
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);
ImGui::TextColored(ImColor(48, 179, 25), "Camera distance In: %.2f, Out: %.2f", infos.cameraIn, infos.cameraOut);
}
}
}
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();
}
// 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);
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);
reshade::unregister_event<reshade::addon_event::init_effect_runtime>(&LoadFixDLL);
reshade::unregister_addon(hinstDLL);
break;
}
return TRUE;
}