mirror of
https://github.com/uberhalit/SekiroFpsUnlockAndMore.git
synced 2026-06-13 01:47:54 +00:00
Added option to reduce FOV (request)
Added option to stretch borderless window to fullscreen regardless of window resolution Fixed borderless Z-order issue where task bar could be infront of window Fixed resolution issues in borderless
This commit is contained in:
parent
7829d124ea
commit
9df5b8832b
6 changed files with 210 additions and 74 deletions
11
README.md
11
README.md
|
|
@ -24,6 +24,8 @@ Patches games memory while running, does not modify any game files. works with e
|
|||
* set the game to borderless window mode
|
||||
* requires "Windowed" in ingame settings first
|
||||
* automatically patch game on startup
|
||||
* seamlessly switch between windowed, borderless and borderless fullscreen
|
||||
* hotkey for patching while in (borderless) window mode
|
||||
|
||||
## Usage
|
||||
|
||||
|
|
@ -73,6 +75,7 @@ The graphic setup has to be done only once but as the patcher hot-patches the me
|
|||
2. Go to `Settings -> Graphical settings -> Monitor Mode` and set it to `Windowed`
|
||||
3. Set your resolution
|
||||
4. Start `Sekiro FPS Unlocker and more` and enable borderless window mode
|
||||
5. If you want fullscreen borderless enable `Fullscreen stretch`
|
||||
|
||||
## Preview
|
||||
|
||||
|
|
@ -106,7 +109,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
|
|||
|
||||
* jackfuste for FOV findings and running speed fix
|
||||
* TyChii93#2376 for AMD and widescreen testing
|
||||
* [Darius Dan](https://www.dariusdan.com) for the icon
|
||||
* [Darius Dan](http://www.dariusdan.com) for the icon
|
||||
|
||||
## Limitations
|
||||
|
||||
|
|
@ -115,9 +118,15 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
|
|||
* your monitor has to support your custom resolution otherwise it won't show up correctly
|
||||
* due to how the game renders altering FOV will not move the HUD
|
||||
* the HUD is limited to 16/9 even on 21/9 resolutions
|
||||
* the hotkey won't work if the game runs in exclusive, true fullscreen mode
|
||||
|
||||
## Version History
|
||||
|
||||
* v1.0.2 (2019-03-26)
|
||||
* Added option to reduce FOV (request)
|
||||
* Added option to stretch borderless window to fullscreen regardless of window resolution
|
||||
* Fixed borderless Z-order issue where task bar could be infront of window (thanks to [Forkinator](https://github.com/Forkinator) for reporting)
|
||||
* Fixed resolution issues in borderless (thanks to King Henry V#6946 for reporting)
|
||||
* v1.0.1 (2019-03-26)
|
||||
* Fixed scaling issue in borderless window mode (thanks to Spacecop42#0947 for reporting)
|
||||
* v1.0.0 (2019-03-25)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@
|
|||
<TextBox x:Name="tbHeight" Text="1080" MaxLength="4" HorizontalAlignment="Left" Height="25" Margin="242,40,0,0" VerticalAlignment="Top" Width="45" FontSize="14 px" PreviewTextInput="Numeric_PreviewTextInput" DataObject.Pasting="Numeric_PastingHandler" />
|
||||
<CheckBox x:Name="cbFov" Content="Increase FOV by:" HorizontalAlignment="Left" Margin="10,72,0,0" VerticalAlignment="Top" FontSize="14 px" Checked="CheckBoxChanged_Handler" Unchecked="CheckBoxChanged_Handler"/>
|
||||
<ComboBox x:Name="cbSelectFov" HorizontalAlignment="Left" VerticalAlignment="Top" Width="106" Height="25" Margin="181,70,0,0" FontSize="14 px" SelectedValuePath="Key" DisplayMemberPath="Value"/>
|
||||
<CheckBox x:Name="cbBorderless" Content="Borderless window mode" HorizontalAlignment="Left" Margin="10,100,0,0" VerticalAlignment="Top" FontSize="14 px" Checked="CheckBoxChanged_Handler" Unchecked="CheckBoxChanged_Handler"/>
|
||||
<CheckBox x:Name="cbBorderless" Content="Borderless window" HorizontalAlignment="Left" Margin="10,100,0,0" VerticalAlignment="Top" FontSize="14 px" Checked="CbBorderless_Checked" Unchecked="CbBorderless_Unchecked"/>
|
||||
<CheckBox x:Name="cbBorderlessStretch" Content="Fullscreen stretch" IsEnabled="False" HorizontalAlignment="Left" Margin="152,100,0,0" VerticalAlignment="Top" FontSize="14 px" Checked="CheckBoxChanged_Handler" Unchecked="CheckBoxChanged_Handler"/>
|
||||
<Button x:Name="bPatch" Content="Patch game (CTRL + P)" FontSize="14 px" HorizontalAlignment="Left" Margin="10,125,0,0" VerticalAlignment="Top" Width="277" Height="30"
|
||||
BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Focusable="False" Click="BPatch_Click" />
|
||||
<TextBox x:Name="tbStatus" Text="waiting for game..." TextAlignment="Center" HorizontalAlignment="Left" TextWrapping="NoWrap" Height="25" Margin="10,165,0,0" VerticalAlignment="Top" Width="277" FontSize="14 px" IsEnabled="False" FontWeight="Bold" />
|
||||
|
|
@ -41,7 +42,7 @@
|
|||
|
||||
<Label HorizontalAlignment="Right" VerticalAlignment="Top" FontSize="12 px">
|
||||
<Hyperlink NavigateUri="https://github.com/uberhalit/SekiroFpsUnlockAndMore" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
v1.0.1 - by uberhalit
|
||||
v1.0.2 - by uberhalit
|
||||
</Hyperlink>
|
||||
</Label>
|
||||
</StackPanel>
|
||||
|
|
|
|||
|
|
@ -17,55 +17,37 @@ namespace SekiroFpsUnlockAndMore
|
|||
internal const string PROCESS_NAME = "sekiro";
|
||||
internal const string PROCESS_TITLE = "Sekiro";
|
||||
internal const string PROCESS_DESCRIPTION = "Shadows Die Twice";
|
||||
|
||||
internal const string PATTERN_FRAMELOCK = "00 88 88 3C 4C 89 AB 00"; // ?? 88 88 3C 4C 89 AB ?? // pattern/signature of frame rate limiter, first byte (last in mem) can can be 88/90 instead of 89 due to precision loss on floating point numbers
|
||||
internal const string PATTERN_FRAMELOCK_MASK = "?xxxxxx?"; // mask for frame rate limiter signature scanning
|
||||
internal const string PATTERN_FRAMELOCK_LONG = "44 88 6B 00 C7 43 00 89 88 88 3C 4C 89 AB 00 00 00 00"; // 44 88 6B ?? C7 43 ?? 89 88 88 3C 4C 89 AB ?? ?? ?? ??
|
||||
internal const string PATTERN_FRAMELOCK_LONG_MASK = "xxx?xx?xxxxxxx????";
|
||||
internal const int PATTERN_FRAMELOCK_LONG_OFFSET = 7;
|
||||
internal const string PATTERN_FRAMELOCK_FUZZY = "C7 43 00 00 00 00 00 4C 89 AB 00 00 00 00"; // C7 43 ?? ?? ?? ?? ?? 4C 89 AB ?? ?? ?? ??
|
||||
internal const string PATTERN_FRAMELOCK_FUZZY_MASK = "xx?????xxx????";
|
||||
internal const int PATTERN_FRAMELOCK_FUZZY_OFFSET = 3; // offset to byte array from found position
|
||||
internal const string PATTERN_FRAMELOCK_RUNNING_FIX = "F3 0F 59 05 00 30 92 02 0F 2F F8"; // F3 0F 59 05 ?? 30 92 02 0F 2F F8 | 0F 51 C2 F3 0F 59 05 ?? ?? ?? ?? 0F 2F F8
|
||||
internal const string PATTERN_FRAMELOCK_RUNNING_FIX_MASK = "xxxx?xxxxxx";
|
||||
internal const int PATTERN_FRAMELOCK_RUNNING_FIX_OFFSET = 4;
|
||||
internal const string PATTERN_RESOLUTION = "80 07 00 00 38 04"; // 1920x1080
|
||||
internal const string PATTERN_RESOLUTION_MASK = "xxxxxx";
|
||||
internal const string PATTERN_WIDESCREEN_219 = "00 47 47 8B 94 C7 1C 02 00 00"; // ?? 47 47 8B 94 C7 1C 02 00 00
|
||||
internal const string PATTERN_WIDESCREEN_219_MASK = "?xxxxxxxxx";
|
||||
internal byte[] PATCH_FRAMERATE_RUNNING_FIX_DISABLE = new byte[1] { 0x90 };
|
||||
internal byte[] PATCH_FRAMERATE_UNLIMITED = new byte[4] { 0x00, 0x00, 0x00, 0x00 };
|
||||
internal byte[] PATCH_WIDESCREEN_219_DISABLE = new byte[1] { 0x74 };
|
||||
internal byte[] PATCH_WIDESCREEN_219_ENABLE = new byte[1] { 0xEB };
|
||||
internal byte[] PATCH_FOV_DISABLE = new byte[1] { 0x0C };
|
||||
|
||||
// credits to jackfuste for FOV findings
|
||||
internal const string PATTERN_FOVSETTING = "F3 0F 10 08 F3 0F 59 0D 00 E7 9B 02"; // F3 0F 10 08 F3 0F 59 0D ?? E7 9B 02
|
||||
internal const string PATTERN_FOVSETTING_MASK = "xxxxxxxx?xxx";
|
||||
internal const int PATTERN_FOVSETTING_OFFSET = 8;
|
||||
internal Dictionary<byte, string> _fovMatrix = new Dictionary<byte, string>
|
||||
internal Dictionary<byte, string> PATCH_FOVMATRIX = new Dictionary<byte, string>
|
||||
{
|
||||
{ 0x00, "- 50%" },
|
||||
{ 0x04, "- 10%" },
|
||||
{ 0x10, "+ 15%" },
|
||||
{ 0x14, "+ 40%" },
|
||||
{ 0x18, "+ 75%" },
|
||||
{ 0x1C, "+ 90%" },
|
||||
};
|
||||
|
||||
internal long _offset_framelock = 0x0;
|
||||
internal long _offset_framelock_running_fix = 0x0;
|
||||
internal long _offset_resolution = 0x0;
|
||||
internal long _offset_widescreen_219 = 0x0;
|
||||
internal long _offset_fovsetting = 0x0;
|
||||
internal bool _running = false;
|
||||
internal Process _game;
|
||||
internal IntPtr _gameHwnd = IntPtr.Zero;
|
||||
internal IntPtr _gameProc = IntPtr.Zero;
|
||||
internal static IntPtr _gameProcStatic;
|
||||
internal long _offset_framelock = 0x0;
|
||||
internal long _offset_framelock_running_fix = 0x0;
|
||||
internal long _offset_resolution = 0x0;
|
||||
internal long _offset_resolution_default = 0x0;
|
||||
internal long _offset_widescreen_219 = 0x0;
|
||||
internal long _offset_fovsetting = 0x0;
|
||||
|
||||
internal readonly DispatcherTimer _dispatcherTimerCheck = new DispatcherTimer();
|
||||
internal bool _running = false;
|
||||
internal string _logPath;
|
||||
internal bool _retryAccess = true;
|
||||
internal RECT _windowRect;
|
||||
internal RECT _clientRect;
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
|
|
@ -79,8 +61,8 @@ namespace SekiroFpsUnlockAndMore
|
|||
{
|
||||
_logPath = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) + @"\SekiroFpsUnlockAndMore.log";
|
||||
|
||||
this.cbSelectFov.ItemsSource = _fovMatrix;
|
||||
this.cbSelectFov.SelectedIndex = 0;
|
||||
this.cbSelectFov.ItemsSource = PATCH_FOVMATRIX;
|
||||
this.cbSelectFov.SelectedIndex = 2;
|
||||
|
||||
IntPtr hwnd = new WindowInteropHelper(this).Handle;
|
||||
if (!RegisterHotKey(hwnd, 9009, MOD_CONTROL, VK_P))
|
||||
|
|
@ -90,7 +72,7 @@ namespace SekiroFpsUnlockAndMore
|
|||
ComponentDispatcher.ThreadFilterMessage += new ThreadMessageEventHandler(ComponentDispatcherThreadFilterMessage);
|
||||
|
||||
_dispatcherTimerCheck.Tick += new EventHandler(CheckGame);
|
||||
_dispatcherTimerCheck.Interval = new TimeSpan(0, 0, 0, 3);
|
||||
_dispatcherTimerCheck.Interval = new TimeSpan(0, 0, 0, 2);
|
||||
_dispatcherTimerCheck.Start();
|
||||
}
|
||||
|
||||
|
|
@ -188,11 +170,11 @@ namespace SekiroFpsUnlockAndMore
|
|||
|
||||
//string gameFileVersion = FileVersionInfo.GetVersionInfo(procList[0].MainModule.FileName).FileVersion;
|
||||
|
||||
_offset_framelock = PatternScan.FindPattern(_gameProc, procList[gameIndex].MainModule, PATTERN_FRAMELOCK, PATTERN_FRAMELOCK_MASK, ' ');
|
||||
_offset_framelock = PatternScan.FindPattern(_gameProc, procList[gameIndex].MainModule, Offsets.PATTERN_FRAMELOCK, Offsets.PATTERN_FRAMELOCK_MASK, ' ');
|
||||
Debug.WriteLine("1. Framelock found at: 0x" + _offset_framelock.ToString("X"));
|
||||
if (!IsValid(_offset_framelock))
|
||||
{
|
||||
_offset_framelock = PatternScan.FindPattern(_gameProc, procList[gameIndex].MainModule, PATTERN_FRAMELOCK_FUZZY, PATTERN_FRAMELOCK_FUZZY_MASK, ' ') + PATTERN_FRAMELOCK_FUZZY_OFFSET;
|
||||
_offset_framelock = PatternScan.FindPattern(_gameProc, procList[gameIndex].MainModule, Offsets.PATTERN_FRAMELOCK_FUZZY, Offsets.PATTERN_FRAMELOCK_FUZZY_MASK, ' ') + Offsets.PATTERN_FRAMELOCK_FUZZY_OFFSET;
|
||||
Debug.WriteLine("2. Framelock found at: 0x" + _offset_framelock.ToString("X"));
|
||||
}
|
||||
if (!IsValid(_offset_framelock))
|
||||
|
|
@ -202,7 +184,7 @@ namespace SekiroFpsUnlockAndMore
|
|||
this.cbUnlockFps.IsEnabled = false;
|
||||
this.cbUnlockFps.IsChecked = false;
|
||||
}
|
||||
_offset_framelock_running_fix = PatternScan.FindPattern(_gameProc, procList[gameIndex].MainModule, PATTERN_FRAMELOCK_RUNNING_FIX, PATTERN_FRAMELOCK_RUNNING_FIX_MASK, ' ') + PATTERN_FRAMELOCK_RUNNING_FIX_OFFSET;
|
||||
_offset_framelock_running_fix = PatternScan.FindPattern(_gameProc, procList[gameIndex].MainModule, Offsets.PATTERN_FRAMELOCK_RUNNING_FIX, Offsets.PATTERN_FRAMELOCK_RUNNING_FIX_MASK, ' ') + Offsets.PATTERN_FRAMELOCK_RUNNING_FIX_OFFSET;
|
||||
Debug.WriteLine("Running fix found at: 0x" + _offset_framelock_running_fix.ToString("X"));
|
||||
if (!IsValid(_offset_framelock_running_fix))
|
||||
{
|
||||
|
|
@ -212,16 +194,16 @@ namespace SekiroFpsUnlockAndMore
|
|||
this.cbAddWidescreen.IsChecked = false;
|
||||
}
|
||||
|
||||
_offset_resolution = PatternScan.FindPattern(_gameProc, procList[gameIndex].MainModule, PATTERN_RESOLUTION, PATTERN_RESOLUTION_MASK, ' ');
|
||||
Debug.WriteLine("Resolution found at: 0x" + _offset_resolution.ToString("X"));
|
||||
if (!IsValid(_offset_resolution))
|
||||
_offset_resolution_default = PatternScan.FindPattern(_gameProc, procList[gameIndex].MainModule, Offsets.PATTERN_RESOLUTION_DEFAULT, Offsets.PATTERN_RESOLUTION_DEFAULT_MASK, ' ');
|
||||
Debug.WriteLine("Default resolution found at: 0x" + _offset_resolution_default.ToString("X"));
|
||||
if (!IsValid(_offset_resolution_default))
|
||||
{
|
||||
UpdateStatus("resolution not found...", Brushes.Red);
|
||||
LogToFile("resolution not found...");
|
||||
UpdateStatus("default resolution not found...", Brushes.Red);
|
||||
LogToFile("default resolution not found...");
|
||||
this.cbAddWidescreen.IsEnabled = false;
|
||||
this.cbAddWidescreen.IsChecked = false;
|
||||
}
|
||||
_offset_widescreen_219 = PatternScan.FindPattern(_gameProc, procList[gameIndex].MainModule, PATTERN_WIDESCREEN_219, PATTERN_WIDESCREEN_219_MASK, ' ');
|
||||
_offset_widescreen_219 = PatternScan.FindPattern(_gameProc, procList[gameIndex].MainModule, Offsets.PATTERN_WIDESCREEN_219, Offsets.PATTERN_WIDESCREEN_219_MASK, ' ');
|
||||
Debug.WriteLine("Widescreen 21/9 found at: 0x" + _offset_widescreen_219.ToString("X"));
|
||||
if (!IsValid(_offset_widescreen_219))
|
||||
{
|
||||
|
|
@ -231,7 +213,29 @@ namespace SekiroFpsUnlockAndMore
|
|||
this.cbAddWidescreen.IsChecked = false;
|
||||
}
|
||||
|
||||
_offset_fovsetting = PatternScan.FindPattern(_gameProc, procList[gameIndex].MainModule, PATTERN_FOVSETTING, PATTERN_FOVSETTING_MASK, ' ') + PATTERN_FOVSETTING_OFFSET;
|
||||
long offset_resolution_pointer = PatternScan.FindPattern(_gameProc, procList[gameIndex].MainModule, Offsets.PATTERN_RESOLUTION_POINTER, Offsets.PATTERN_RESOLUTION_POINTER_MASK, ' ') + Offsets.PATTERN_RESOLUTION_POINTER_OFFSET;
|
||||
Debug.WriteLine("Resolution pointer found at: 0x" + offset_resolution_pointer.ToString("X"));
|
||||
if (!IsValid(offset_resolution_pointer))
|
||||
{
|
||||
UpdateStatus("Resolution pointer not found...", Brushes.Red);
|
||||
LogToFile("Resolution pointer not found...");
|
||||
this.cbBorderless.IsEnabled = false;
|
||||
this.cbBorderless.IsChecked = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_offset_resolution = FindOffsetToStaticPointer(_gameProc, offset_resolution_pointer, Offsets.PATTERN_RESOLUTION_POINTER_INSTRUCTION_LENGTH);
|
||||
Debug.WriteLine("Resolution found at: 0x" + _offset_resolution.ToString("X"));
|
||||
if (!IsValid(_offset_resolution))
|
||||
{
|
||||
UpdateStatus("Resolution not valid...", Brushes.Red);
|
||||
LogToFile("Resolution not valid...");
|
||||
this.cbBorderless.IsEnabled = false;
|
||||
this.cbBorderless.IsChecked = false;
|
||||
}
|
||||
}
|
||||
|
||||
_offset_fovsetting = PatternScan.FindPattern(_gameProc, procList[gameIndex].MainModule, Offsets.PATTERN_FOVSETTING, Offsets.PATTERN_FOVSETTING_MASK, ' ') + Offsets.PATTERN_FOVSETTING_OFFSET;
|
||||
Debug.WriteLine("FOV found at: 0x" + _offset_fovsetting.ToString("X"));
|
||||
if (!IsValid(_offset_fovsetting))
|
||||
{
|
||||
|
|
@ -241,8 +245,6 @@ namespace SekiroFpsUnlockAndMore
|
|||
this.cbFov.IsChecked = false;
|
||||
}
|
||||
|
||||
GetWindowRect(_gameHwnd, out _windowRect);
|
||||
GetClientRect(_gameHwnd, out _clientRect);
|
||||
_running = true;
|
||||
_dispatcherTimerCheck.Stop();
|
||||
PatchGame();
|
||||
|
|
@ -342,14 +344,14 @@ namespace SekiroFpsUnlockAndMore
|
|||
this.tbHeight.Text = "2160";
|
||||
height = 2160;
|
||||
}
|
||||
WriteBytes(_gameProcStatic, _offset_resolution, BitConverter.GetBytes(width));
|
||||
WriteBytes(_gameProcStatic, _offset_resolution + 4, BitConverter.GetBytes(height));
|
||||
WriteBytes(_gameProcStatic, _offset_widescreen_219, (float) width / (float) height > 1.9f ? PATCH_WIDESCREEN_219_ENABLE : PATCH_WIDESCREEN_219_DISABLE);
|
||||
WriteBytes(_gameProcStatic, _offset_resolution_default, BitConverter.GetBytes(width));
|
||||
WriteBytes(_gameProcStatic, _offset_resolution_default + 4, BitConverter.GetBytes(height));
|
||||
WriteBytes(_gameProcStatic, _offset_widescreen_219, PATCH_WIDESCREEN_219_ENABLE);
|
||||
}
|
||||
else if (this.cbAddWidescreen.IsChecked == false)
|
||||
{
|
||||
WriteBytes(_gameProcStatic, _offset_resolution, BitConverter.GetBytes(1920));
|
||||
WriteBytes(_gameProcStatic, _offset_resolution + 4, BitConverter.GetBytes(1080));
|
||||
WriteBytes(_gameProcStatic, _offset_resolution_default, BitConverter.GetBytes(1920));
|
||||
WriteBytes(_gameProcStatic, _offset_resolution_default + 4, BitConverter.GetBytes(1080));
|
||||
WriteBytes(_gameProcStatic, _offset_widescreen_219, PATCH_WIDESCREEN_219_DISABLE);
|
||||
}
|
||||
|
||||
|
|
@ -366,17 +368,33 @@ namespace SekiroFpsUnlockAndMore
|
|||
|
||||
if (this.cbBorderless.IsChecked == true)
|
||||
{
|
||||
if (!IsFullscreen(_gameHwnd))
|
||||
SetWindowBorderless(_gameHwnd);
|
||||
else
|
||||
if (IsFullscreen(_gameHwnd))
|
||||
{
|
||||
MessageBox.Show("Please exit fullscreen first before activating borderless window mode.", "Sekiro FPS Unlocker and more");
|
||||
this.cbBorderless.IsChecked = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!IsBorderless(_gameHwnd))
|
||||
GetWindowRect(_gameHwnd, out _windowRect);
|
||||
int width = Read<Int32>(_gameProc, _offset_resolution);
|
||||
int height = Read<Int32>(_gameProc, _offset_resolution + 4);
|
||||
Debug.WriteLine(string.Format("Client Resolution: {0}x{1}", width, height));
|
||||
if (this.cbBorderlessStretch.IsChecked == true)
|
||||
SetWindowBorderless(_gameHwnd, (int)SystemParameters.PrimaryScreenWidth, (int)SystemParameters.PrimaryScreenHeight, 0, 0);
|
||||
else
|
||||
SetWindowBorderless(_gameHwnd, width, height, _windowRect.Left, _windowRect.Top);
|
||||
}
|
||||
}
|
||||
else if (this.cbBorderless.IsChecked == false && !IsFullscreen(_gameHwnd))
|
||||
else if (this.cbBorderless.IsChecked == false && IsBorderless(_gameHwnd))
|
||||
{
|
||||
SetWindowWindowed(_gameHwnd);
|
||||
if (_windowRect.Bottom > 0)
|
||||
{
|
||||
int width = _windowRect.Right - _windowRect.Left;
|
||||
int height = _windowRect.Bottom - _windowRect.Top;
|
||||
Debug.WriteLine(string.Format("Window Resolution: {0}x{1}", width, height));
|
||||
SetWindowWindowed(_gameHwnd, width, height, _windowRect.Left, _windowRect.Top);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.cbUnlockFps.IsChecked == true || this.cbAddWidescreen.IsChecked == true || this.cbFov.IsChecked == true)
|
||||
|
|
@ -408,7 +426,6 @@ namespace SekiroFpsUnlockAndMore
|
|||
{
|
||||
long wndStyle = GetWindowLongPtr(hwnd, GWL_STYLE).ToInt64();
|
||||
long wndExStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE).ToInt64();
|
||||
|
||||
if (wndStyle == 0 || wndExStyle == 0)
|
||||
return false;
|
||||
|
||||
|
|
@ -424,30 +441,56 @@ namespace SekiroFpsUnlockAndMore
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if window is in borderless window mode.
|
||||
/// </summary>
|
||||
/// <param name="hwnd">The main window handle of the window.</param>
|
||||
/// <remarks>
|
||||
/// Borderless windows have WS_POPUP flag set.
|
||||
/// </remarks>
|
||||
/// <returns>True if window is run in borderless window mode.</returns>
|
||||
private bool IsBorderless(IntPtr hwnd)
|
||||
{
|
||||
long wndStyle = GetWindowLongPtr(hwnd, GWL_STYLE).ToInt64();
|
||||
if (wndStyle == 0)
|
||||
return false;
|
||||
|
||||
if ((wndStyle & WS_POPUP) == 0)
|
||||
return false;
|
||||
if ((wndStyle & WS_CAPTION) != 0)
|
||||
return false;
|
||||
if ((wndStyle & WS_BORDER) != 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a window to ordinary windowed mode
|
||||
/// </summary>
|
||||
/// <param name="hwnd">The handle to the window.</param>
|
||||
private void SetWindowWindowed(IntPtr hwnd)
|
||||
/// <param name="width">The desired window width.</param>
|
||||
/// <param name="height">The desired window height.</param>
|
||||
/// <param name="posX">The desired X position of the window.</param>
|
||||
/// <param name="posY">The desired Y position of the window.</param>
|
||||
private void SetWindowWindowed(IntPtr hwnd, int width, int height, int posX, int posY)
|
||||
{
|
||||
var width = _windowRect.Right - _windowRect.Left;
|
||||
var height = _windowRect.Bottom - _windowRect.Top;
|
||||
Debug.WriteLine(string.Format("Window Resolution: {0}x{1}", width, height));
|
||||
SetWindowLongPtr(hwnd, GWL_STYLE, WS_VISIBLE | WS_CAPTION | WS_BORDER | WS_CLIPSIBLINGS | WS_DLGFRAME | WS_SYSMENU | WS_GROUP | WS_MINIMIZEBOX);
|
||||
SetWindowPos(hwnd, HWND_NOTOPMOST, 40, 40, width, height, SWP_FRAMECHANGED | SWP_SHOWWINDOW);
|
||||
SetWindowPos(hwnd, HWND_NOTOPMOST, posX, posY, width, height, SWP_FRAMECHANGED | SWP_SHOWWINDOW);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a window to borderless windowed mode and moves it to position 0x0.
|
||||
/// </summary>
|
||||
/// <param name="hwnd">The handle to the window.</param>
|
||||
private void SetWindowBorderless(IntPtr hwnd)
|
||||
/// <param name="width">The desired window width.</param>
|
||||
/// <param name="height">The desired window height.</param>
|
||||
/// <param name="posX">The desired X position of the window.</param>
|
||||
/// <param name="posY">The desired Y position of the window.</param>
|
||||
private void SetWindowBorderless(IntPtr hwnd, int width, int height, int posX, int posY)
|
||||
{
|
||||
var width = _clientRect.Right - _clientRect.Left;
|
||||
var height = _clientRect.Bottom - _clientRect.Top;
|
||||
Debug.WriteLine(string.Format("Client Resolution: {0}x{1}", width, height));
|
||||
SetWindowLongPtr(hwnd, GWL_STYLE, WS_VISIBLE | WS_POPUP);
|
||||
SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, width, height, SWP_FRAMECHANGED | SWP_SHOWWINDOW);
|
||||
SetWindowPos(hwnd, HWND_TOP, posX, posY, width, height, SWP_FRAMECHANGED | SWP_SHOWWINDOW);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -460,6 +503,25 @@ namespace SekiroFpsUnlockAndMore
|
|||
return (address >= 0x10000 && address < 0x000F000000000000);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a given type from processes memory using a generic method.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The base type to read.</typeparam>
|
||||
/// <param name="gameProc">The process handle to read from.</param>
|
||||
/// <param name="lpBaseAddress">The address to read from.</param>
|
||||
/// <returns>The given base type read from memory.</returns>
|
||||
/// <remarks>GCHandle and Marshal are costy.</remarks>
|
||||
private static T Read<T>(IntPtr gameProc, Int64 lpBaseAddress)
|
||||
{
|
||||
byte[] lpBuffer = new byte[Marshal.SizeOf(typeof(T))];
|
||||
IntPtr lpNumberOfBytesRead;
|
||||
ReadProcessMemory(gameProc, lpBaseAddress, lpBuffer, (ulong)lpBuffer.Length, out lpNumberOfBytesRead);
|
||||
GCHandle gcHandle = GCHandle.Alloc(lpBuffer, GCHandleType.Pinned);
|
||||
T structure = (T)Marshal.PtrToStructure(gcHandle.AddrOfPinnedObject(), typeof(T));
|
||||
gcHandle.Free();
|
||||
return structure;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a given type and value to processes memory using a generic method.
|
||||
/// </summary>
|
||||
|
|
@ -473,6 +535,19 @@ namespace SekiroFpsUnlockAndMore
|
|||
return WriteProcessMemory(gameProc, lpBaseAddress, bytes, (ulong)bytes.Length, out lpNumberOfBytesWritten);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the static offset to a desired object instead of an offset to a pointer.
|
||||
/// </summary>
|
||||
/// <param name="hProcess">Handle to the process in whose memory the pattern has been found.</param>
|
||||
/// <param name="lpPatternAddress">The address where the pattern has been found.</param>
|
||||
/// <param name="instructionLength">The length of the instruction including the 4 bytes pointer</param>
|
||||
/// <remarks>Static pointers in x86-64 are relative offsets from the instruction address. </remarks>
|
||||
/// <returns>The static offset from the process to desired object).</returns>
|
||||
internal static Int64 FindOffsetToStaticPointer(IntPtr hProcess, Int64 lpPatternAddress, int instructionLength)
|
||||
{
|
||||
return lpPatternAddress + Read<Int32>(hProcess, lpPatternAddress + (instructionLength -0x04)) + instructionLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check whether input is numeric only.
|
||||
/// </summary>
|
||||
|
|
@ -509,6 +584,19 @@ namespace SekiroFpsUnlockAndMore
|
|||
PatchGame();
|
||||
}
|
||||
|
||||
private void CbBorderless_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.cbBorderlessStretch.IsEnabled = true;
|
||||
PatchGame();
|
||||
}
|
||||
|
||||
private void CbBorderless_Unchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.cbBorderlessStretch.IsEnabled = false;
|
||||
this.cbBorderlessStretch.IsChecked = false;
|
||||
PatchGame();
|
||||
}
|
||||
|
||||
private void BPatch_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
PatchGame();
|
||||
|
|
@ -555,7 +643,8 @@ namespace SekiroFpsUnlockAndMore
|
|||
private const uint WS_CAPTION = 0x00C00000;
|
||||
private const uint WS_BORDER = 0x00800000;
|
||||
private const uint WS_EX_TOPMOST = 0x00000008;
|
||||
private const uint WS_EX_WINDOWEDGE = 0x00000100;
|
||||
private const int HWND_TOP = 0;
|
||||
private const int HWND_TOPMOST = -1;
|
||||
private const int HWND_NOTOPMOST = -2;
|
||||
private const uint SWP_FRAMECHANGED = 0x0020;
|
||||
private const uint SWP_SHOWWINDOW = 0x0040;
|
||||
|
|
@ -587,9 +676,6 @@ namespace SekiroFpsUnlockAndMore
|
|||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RECT
|
||||
{
|
||||
|
|
@ -599,6 +685,14 @@ namespace SekiroFpsUnlockAndMore
|
|||
public int Bottom; // y position of lower-right corner
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern Boolean ReadProcessMemory(
|
||||
IntPtr hProcess,
|
||||
Int64 lpBaseAddress,
|
||||
[Out] Byte[] lpBuffer,
|
||||
UInt64 dwSize,
|
||||
out IntPtr lpNumberOfBytesRead);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern bool WriteProcessMemory(
|
||||
IntPtr hProcess,
|
||||
|
|
|
|||
31
SekiroFpsUnlockAndMore/Offsets.cs
Normal file
31
SekiroFpsUnlockAndMore/Offsets.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
namespace SekiroFpsUnlockAndMore
|
||||
{
|
||||
internal class Offsets
|
||||
{
|
||||
internal const string PATTERN_FRAMELOCK = "00 88 88 3C 4C 89 AB 00"; // ?? 88 88 3C 4C 89 AB ?? // pattern/signature of frame rate limiter, first byte (last in mem) can can be 88/90 instead of 89 due to precision loss on floating point numbers
|
||||
internal const string PATTERN_FRAMELOCK_MASK = "?xxxxxx?"; // mask for frame rate limiter signature scanning
|
||||
internal const string PATTERN_FRAMELOCK_LONG = "44 88 6B 00 C7 43 00 89 88 88 3C 4C 89 AB 00 00 00 00"; // 44 88 6B ?? C7 43 ?? 89 88 88 3C 4C 89 AB ?? ?? ?? ??
|
||||
internal const string PATTERN_FRAMELOCK_LONG_MASK = "xxx?xx?xxxxxxx????";
|
||||
internal const int PATTERN_FRAMELOCK_LONG_OFFSET = 7;
|
||||
internal const string PATTERN_FRAMELOCK_FUZZY = "C7 43 00 00 00 00 00 4C 89 AB 00 00 00 00"; // C7 43 ?? ?? ?? ?? ?? 4C 89 AB ?? ?? ?? ??
|
||||
internal const string PATTERN_FRAMELOCK_FUZZY_MASK = "xx?????xxx????";
|
||||
internal const int PATTERN_FRAMELOCK_FUZZY_OFFSET = 3; // offset to byte array from found position
|
||||
internal const string PATTERN_FRAMELOCK_RUNNING_FIX = "F3 0F 59 05 00 30 92 02 0F 2F F8"; // F3 0F 59 05 ?? 30 92 02 0F 2F F8 | 0F 51 C2 F3 0F 59 05 ?? ?? ?? ?? 0F 2F F8
|
||||
internal const string PATTERN_FRAMELOCK_RUNNING_FIX_MASK = "xxxx?xxxxxx";
|
||||
internal const int PATTERN_FRAMELOCK_RUNNING_FIX_OFFSET = 4;
|
||||
internal const string PATTERN_RESOLUTION_POINTER = "0F 57 D2 89 0D 00 00 00 00 0F 57 C9 89 15 00 00 00 00"; // 0F 57 D2 89 0D ?? ?? ?? ?? 0F 57 C9 89 15 ?? ?? ?? ??
|
||||
internal const string PATTERN_RESOLUTION_POINTER_MASK = "xxxxx????xxxxx????";
|
||||
internal const int PATTERN_RESOLUTION_POINTER_OFFSET = 3;
|
||||
internal const int PATTERN_RESOLUTION_POINTER_INSTRUCTION_LENGTH = 6;
|
||||
internal const string PATTERN_RESOLUTION_DEFAULT = "80 07 00 00 38 04"; // 1920x1080
|
||||
internal const string PATTERN_RESOLUTION_DEFAULT_MASK = "xxxxxx";
|
||||
internal const string PATTERN_WIDESCREEN_219 = "00 47 47 8B 94 C7 1C 02 00 00"; // ?? 47 47 8B 94 C7 1C 02 00 00
|
||||
internal const string PATTERN_WIDESCREEN_219_MASK = "?xxxxxxxxx";
|
||||
|
||||
// credits to jackfuste for FOV findings
|
||||
internal const string PATTERN_FOVSETTING = "F3 0F 10 08 F3 0F 59 0D 00 E7 9B 02"; // F3 0F 10 08 F3 0F 59 0D ?? E7 9B 02
|
||||
internal const string PATTERN_FOVSETTING_MASK = "xxxxxxxx?xxx";
|
||||
internal const int PATTERN_FOVSETTING_OFFSET = 8;
|
||||
}
|
||||
}
|
||||
|
|
@ -21,5 +21,5 @@ using System.Windows;
|
|||
ResourceDictionaryLocation.SourceAssembly
|
||||
)]
|
||||
|
||||
[assembly: AssemblyVersion("1.0.1.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.1.0")]
|
||||
[assembly: AssemblyVersion("1.0.2.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.2.0")]
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@
|
|||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Offsets.cs" />
|
||||
<Compile Include="PatternScan.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue