2026-03-22 17:23:49 +01:00
# define IMGUI_DISABLE_INCLUDE_IMCONFIG_H
# define IMGUI_HAS_DOCK 1
# include "CommonHeaders.h"
2026-03-24 23:19:01 +01:00
# include "HotkeysManager.h"
# include "DirectX.h"
# include <thread>
2026-03-22 17:23:49 +01:00
2026-03-24 23:19:01 +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 ;
2026-03-24 23:19:01 +01:00
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 ;
2026-03-24 23:19:01 +01:00
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 } ,
2026-03-24 23:19:01 +01:00
{ " 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 ) ;
2026-03-24 23:19:01 +01:00
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. \n Final 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 , " " ,
2026-03-24 23:19:01 +01:00
" 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 ;
2026-03-24 23:19:01 +01:00
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 ;
2026-03-24 23:19:01 +01:00
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 > ( ) ;
2026-03-24 23:19:01 +01:00
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 > ( ) ;
2026-03-24 23:19:01 +01:00
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 ] ) ;
2026-03-24 23:19:01 +01:00
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 ( ) ;
}
2026-03-24 23:19:01 +01:00
static std : : atomic < bool > g_Started { false } ;
static std : : atomic < bool > g_Done { false } ;
2026-03-23 20:51:37 +01:00
static void on_present ( reshade : : api : : command_queue * queue , reshade : : api : : swapchain * swapchain ,
2026-03-24 23:19:01 +01:00
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-23 20:51:37 +01:00
}
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 ) ;
2026-03-23 20:51:37 +01:00
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 ) ;
2026-03-23 20:51:37 +01:00
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 ;
}