#define IMGUI_DISABLE_INCLUDE_IMCONFIG_H #define IMGUI_HAS_DOCK 1 #include "CommonHeaders.h" #include "HotkeysManager.h" #include "DirectX.h" #include // 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 camera_fix_enabled = false; static bool DOF_fix_enabled = false; static bool vignetting_fix_enabled = false; static bool fix_enabled = false; static int worldFOVvalue = 0; static float cameraDistancevalue = 1.f; 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.3"; const char* FIX_INFORMATIONS = "This fix allows to:\n - Control FOV in game.\n - Control camera distance.\n - Enable ultrawide.\n - Disable depth of field in cutscenes.\n - Disable vignetting."; 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."}, { "Camera", &camera_fix_enabled, GameFixes::Camera } }; // Prepare array of sliders for ImGui static SliderFix2 sliders[3]; // 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::CameraDistance, cameraDistancevalue); } if (SetFixEnabled) SetFixEnabled(fix_enabled, true); if (SetFixes) { SetFixes(GameFixes::FOV, fov_fix_enabled); SetFixes(GameFixes::Camera, camera_fix_enabled); SetFixes(GameFixes::DOF, DOF_fix_enabled); SetFixes(GameFixes::Vignetting, vignetting_fix_enabled); } sliders[0] = { "In game additional FOV", "##FOVValue", SliderType::Int, &worldFOVvalue, -20, 50, GameSetting::FOV, SetValues }; sliders[1] = { "Camera distance", "##CamValue", SliderType::Float, &cameraDistancevalue, 0, 5, GameSetting::CameraDistance, SetValues, "%.1f", "Value is a multiplier.\nFinal value reported below is in meters."}; sliders[2] = { "Ultrawide enforce delay", "##AltEnterValue", SliderType::Int, &triggerDelayMs, 1000, 5000, GameSetting::Threshold, SetValues, "", "This value is a threshold in ms between the simulated alt + enter." }; } } // Settings functions static void SaveSettings() { ini::IniFile pluginIniFile; pluginIniFile["1#General fix"].setComment(std::vector{ "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"]["Camera"] = camera_fix_enabled; pluginIniFile["2#Individual fix"]["DOF"] = DOF_fix_enabled; pluginIniFile["2#Individual fix"]["Vignetting"] = vignetting_fix_enabled; pluginIniFile["3#Fixes tuning"].setComment("Individual fix fine tune"); pluginIniFile["3#Fixes tuning"]["World FOV"] = worldFOVvalue; pluginIniFile["3#Fixes tuning"]["Camera distance"] = cameraDistancevalue; pluginIniFile.save(SETTINGS_FILE); } static void LoadSettings() { ini::IniFile pluginIniFile; try { pluginIniFile.load(SETTINGS_FILE); fix_enabled = pluginIniFile["1#General fix"]["Enabled"].as(); triggerDelayMs = pluginIniFile["1#General fix"]["TriggerDelayMs"].as(); fov_fix_enabled = pluginIniFile["2#Individual fix"]["FOV"].as(); ultrawide_fix_enabled = pluginIniFile["2#Individual fix"]["UltraWide"].as(); camera_fix_enabled = pluginIniFile["2#Individual fix"]["Camera"].as(); DOF_fix_enabled = pluginIniFile["2#Individual fix"]["DOF"].as(); vignetting_fix_enabled = pluginIniFile["2#Individual fix"]["Vignetting"].as(); worldFOVvalue = pluginIniFile["3#Fixes tuning"]["World FOV"].as(); cameraDistancevalue = pluginIniFile["3#Fixes tuning"]["Camera distance"].as(); } 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); DrawFixCheckbox(individualFixes[0]); DrawFixCheckbox(individualFixes[1]); DrawFixCheckbox(individualFixes[2]); ImGui::TableSetColumnIndex(1); DrawFixCheckbox(individualFixes[3]); DrawFixCheckbox(individualFixes[4]); ImGui::EndTable(); } } ImGui::EndTable(); } ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 2); if (ImGui::BeginTable("FixesSliders", 2, ImGuiTableFlags_SizingStretchSame)) { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); DrawSlider2(sliders[0], 200); ImGui::TableSetColumnIndex(1); DrawSlider2(sliders[1], 200); DrawSlider2(sliders[2], 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 g_Started{ false }; static std::atomic 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); 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(&on_present); reshade::register_event(&LoadFixDLL); break; case DLL_PROCESS_DETACH: reshade::unregister_overlay("Death Stranding 2: On The Beach", &on_overlay_draw); reshade::unregister_event(&on_present); reshade::unregister_event(&LoadFixDLL); reshade::unregister_addon(hinstDLL); break; } return TRUE; }