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:
uberhalit 2019-03-26 23:24:24 +01:00
parent 7829d124ea
commit 9df5b8832b
6 changed files with 210 additions and 74 deletions

View file

@ -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)

View file

@ -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>

View file

@ -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,

View 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;
}
}

View file

@ -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")]

View file

@ -76,6 +76,7 @@
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="Offsets.cs" />
<Compile Include="PatternScan.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>