updated guide

cleanup
This commit is contained in:
uberhalit 2019-03-27 15:57:23 +01:00
parent 9df5b8832b
commit 35dd34c9c5
6 changed files with 107 additions and 84 deletions

View file

@ -5,7 +5,7 @@ Patches games memory while running, does not modify any game files. works with e
## Download
[Get the latest release here](https://github.com/uberhalit/SekiroFpsUnlockAndMore/releases)
**[Get the latest release here](https://github.com/uberhalit/SekiroFpsUnlockAndMore/releases)**
### See it in action:
[![Video preview](https://camo.githubusercontent.com/99b882828d8bb814a126282d67f0394460259df0/68747470733a2f2f692e696d6775722e636f6d2f4b4e4674454d772e706e67)](https://giant.gfycat.com/DevotedArtisticKingsnake.webm)
@ -16,13 +16,9 @@ Patches games memory while running, does not modify any game files. works with e
* works with legit, unmodified steam version as well as with unpacked, not-so-legit versions
* GSYNC and FreeSync support even in borderless window mode
* unlock frame rate (remove FPS limit) by setting a new custom limit or setting lock to unlimited
* 60 Hz monitors: disable VSYNC via driver (use 'Enhanced Sync' on AMD) and use fullscreen
* high refresh rate monitors: use borderless or force monitor to always use highest available refresh rate and then use fullscreen
* add a custom resolution, 21/9 widescreen supported (will overwrite the default 1920x1080 resolution, HUD limited to 16/9)
* increase field of view (FOV) (credits to jackfuste)
* you have to be ingame with a loaded save game, FOV will reset after every time a save game loads
* 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
@ -30,34 +26,61 @@ Patches games memory while running, does not modify any game files. works with e
## Usage
The game enforces VSYNC and forces 60 Hz in fullscreen even on 144 Hz monitors so we have to override these.
#### 60 Hz monitors: disable VSYNC via driver (use 'Enhanced Sync' on AMD) and use fullscreen, see guide below
#### high refresh rate monitors: use borderless or force monitor to always use highest available refresh rate and then use fullscreen, see guide below
### Follow these steps (Nvidia):
### Follow these steps on Nvidia:
1. Open Nvidia Control Panel
2. Navigate to `Configurate 3D Settings -> Program settings`
3. Select Sekiro from the dropdown or add it manually if it's missing: `Add -> Select Sekiro`
4. **Set `Vertical sync` to `Off`**
5. **Set `Preferred refresh rate` to `Highest available`**
6. Start `Sekiro FPS Unlocker and more` and set FPS lock to your desired framerate
7. Start the game and use fullscreen or borderless window mode
8. These steps will force disable vsync so it won't limit your fps to monitor refresh rate and also force the monitor to ignore the games request to run at 60 Hz if in fullscreen
2. Navigate to `Display -> Change resolution`
3. **Make sure your monitor is set to the highest Refresh rate possible:**
4. [![Make sure your monitor is set to the highest Refresh rate possible](https://camo.githubusercontent.com/331eb420bee67f4e57d7e46601bfd51f462de68f/68747470733a2f2f692e696d6775722e636f6d2f625667767155372e706e67)](#)
5. Navigate to `3D Settings -> Manage 3D settings -> Program Settings`
6. Select Sekiro from the dropdown or add it manually if it's missing: `Add -> Select Sekiro -> Add Selected Program`
7. **Set `Vertical sync` to `Off`**
8. **Set `Preferred refresh rate` to `Highest available`**
9. [![Vertical sync Off and Preferred refresh rate Highest available](https://camo.githubusercontent.com/bae53a6199d5a6fc2b8e0c4f0bfab322ebecd648/68747470733a2f2f692e696d6775722e636f6d2f35446f526d55452e706e67)](#)
10. Hit apply and close Nvidia Control Panel
11. Start `Sekiro FPS Unlocker and more` and set FPS lock to your desired framerate
12. Start the game and use fullscreen or borderless window mode
13. These steps will force disable vsync so it won't limit your fps to monitor refresh rate and also force the monitor to ignore the games request to run at 60 Hz if in fullscreen
#### If you do not have 'Preferred refresh rate' or 'Vertical sync' follow these steps (Nvidia):
1. Download and extract the [Nvidia Inspector](https://www.techpowerup.com/download/nvidia-inspector/)
2. Start the Nvidia Profile Inspector
3. Under `2 - Sync and Refresh` set `Prefered Refreshrate` to `Highest available` and `Vertical Sync` to `Force off`
4. [![Vertical sync Off and Preferred refresh rate Highest available](https://camo.githubusercontent.com/560355d19113ad3e6782c75cae992e7c91a4e0fd/68747470733a2f2f692e696d6775722e636f6d2f4657424b57536e2e706e67)](#)
5. Hit `Apply changes` and you are good to go
### Or these (AMD):
1. Open Radeon Settings
2. Navigate to `Gaming -> Sekiro` or add it manually if it's missing: `Add -> Browse -> Sekiro`
3. **Set `Wait for Vertical Refresh` to `Enhanced Sync`**
4. Start `Sekiro FPS Unlocker and more` and set FPS lock to your desired frame rate
5. **Launch the game in windowed mode, then switch to fullscreen once in game**
6. The last step is important as AMD somehow does not correctly disable VSYNC otherwise
### Follow these steps on AMD:
1. Right click on Desktop -> `Display settings`
2. Scroll down an click `Advanced Display Settings -> Display Adapter Properties`
3. **Switch to `Monitor` tab and make sure your monitor is set to the highest Refresh rate possible:**
4. [![Make sure your monitor is set to the highest Refresh rate possible](https://camo.githubusercontent.com/8ba71a0b512eb68509f7e7506a92a78f3cd47537/68747470733a2f2f692e696d6775722e636f6d2f61774b4862774d2e706e67)](#)
5. Open Radeon Settings
6. Navigate to `Gaming -> Sekiro` or add it manually if it's missing: `Add -> Browse -> Sekiro`
7. **Set `Wait for Vertical Refresh` to `Enhanced Sync`**:
8. [![Wait for Vertical Refresh Enhanced Sync](https://camo.githubusercontent.com/7c00daebb59c7e46c455e30b6caa055c63185dcb/68747470733a2f2f692e696d6775722e636f6d2f456e77595146322e706e67)](#)
9. Apply and close Radeon Settings
10. Start `Sekiro FPS Unlocker and more` and set FPS lock to your desired frame rate
11. **Launch the game in windowed mode, then switch to fullscreen once in game**
12. The last step is important as AMD somehow does not correctly disable VSYNC otherwise
#### If you do not have 'Enhanced Sync' follow these steps (AMD):
1. Try setting `Wait for Vertical Refresh` to `Off` instead:
2. [![Wait for Vertical Refresh Off](https://camo.githubusercontent.com/c06be2d7a7be65e379996eee66685209b276dcea/68747470733a2f2f692e696d6775722e636f6d2f417a7a716545332e706e67)](#)
3. Be aware however that it seems like AMDs latest drivers are buggy in that regard
### To play the game with GSYNC do these additional steps (Nvidia):
1. Set `Monitor Technology` to `G-SYNC`
2. Make sure that `Preferred refresh rate` is still set to `Highest available`
3. Use a 3rd party frame rate limiter like [RTSS](https://www.guru3d.com/files-details/rtss-rivatuner-statistics-server-download.html) and set a frame rate limit just a few fps below your monitor refresh rate, on a 144Hz Monitor use 138
4. Start `Sekiro FPS Unlocker and more` and set FPS lock to your monitors refresh rate
5. Start the game and set it to Fullscreen
6. Enjoy perfectly tearing free variable high refresh rates without VSYNC
1. Under Nvidia Control Panel navigate to `3D Settings -> Manage 3D settings -> Program Settings -> Sekiro`
2. Set `Monitor Technology` to `G-SYNC`
3. Make sure that `Preferred refresh rate` is still set to `Highest available`
4. Make sure that `Vertical sync` is still set to `Off`
5. [![Control Panel](https://camo.githubusercontent.com/b7b491c24020fd3eca41d857bd58b1c0c2ee037f/68747470733a2f2f692e696d6775722e636f6d2f614a41744444632e706e67)](#)
6. Don't forget to Apply and close Nvidia Control Panel
7. Use a 3rd party frame rate limiter like [RTSS](https://www.guru3d.com/files-details/rtss-rivatuner-statistics-server-download.html) and set a frame rate limit just a few fps below your monitor refresh rate, on a 144Hz Monitor use 138
8. Start `Sekiro FPS Unlocker and more` and set FPS lock to your monitors refresh rate
9. Start the game and set it to Fullscreen
10. Enjoy perfectly tearing free variable high refresh rates without VSYNC
The graphic setup has to be done only once but as the patcher hot-patches the memory you have to start the patcher every time you want to unlock frame rate etc.
The graphic setup has to be done only once but as the patcher hot-patches the memory **you have to start the patcher every time you want to unlock frame rate etc**.
### To add a custom resolution
1. Start the game

View file

@ -5,23 +5,23 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SekiroFpsUnlockAndMore"
mc:Ignorable="d"
Title="Sekiro FPS Unlocker and more v1.0.0" Height="530" Width="306" ResizeMode="CanMinimize" Loaded="Window_Loaded" Closing="Window_Closing">
Title="Sekiro FPS Unlocker and more v1.0.2" Height="Auto" SizeToContent="WidthAndHeight" Width="Auto" ResizeMode="CanMinimize" Loaded="Window_Loaded" Closing="Window_Closing">
<Grid>
<Grid Background="#FFF9F9F9">
<CheckBox x:Name="cbUnlockFps" Content="FPS lock (0= unlimited):" IsChecked="True" HorizontalAlignment="Left" Margin="10,12,0,0" VerticalAlignment="Top" FontSize="14 px" Checked="CheckBoxChanged_Handler" Unchecked="CheckBoxChanged_Handler"/>
<TextBox x:Name="tbFps" Text="144" MaxLength="3" HorizontalAlignment="Left" Height="25" Margin="181,10,0,0" VerticalAlignment="Top" Width="106" FontSize="14 px" PreviewTextInput="Numeric_PreviewTextInput" DataObject.Pasting="Numeric_PastingHandler"/>
<TextBox x:Name="tbFps" Text="144" MaxLength="3" HorizontalAlignment="Left" Height="25" Margin="181,10,10,10" VerticalAlignment="Top" Width="106" FontSize="14 px" PreviewTextInput="Numeric_PreviewTextInput" DataObject.Pasting="Numeric_PastingHandler"/>
<CheckBox x:Name="cbAddWidescreen" Content="Add custom resolution:" HorizontalAlignment="Left" Margin="10,42,0,0" VerticalAlignment="Top" FontSize="14 px" Checked="CheckBoxChanged_Handler" Unchecked="CheckBoxChanged_Handler"/>
<TextBox x:Name="tbWidth" Text="2560" MaxLength="4" HorizontalAlignment="Left" Height="25" Margin="181,40,0,0" VerticalAlignment="Top" Width="45" FontSize="14 px" PreviewTextInput="Numeric_PreviewTextInput" DataObject.Pasting="Numeric_PastingHandler" />
<Label Content="x" HorizontalAlignment="Left" Margin="226,36,0,0" VerticalAlignment="Top" FontSize="14 px"/>
<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"/>
<ComboBox x:Name="cbSelectFov" HorizontalAlignment="Left" VerticalAlignment="Top" Width="106" Height="25" Margin="181,70,10,10" FontSize="14 px" SelectedValuePath="Key" DisplayMemberPath="Value"/>
<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"
<Button x:Name="bPatch" Content="Patch game (CTRL + P)" FontSize="14 px" HorizontalAlignment="Left" Margin="10,125,10,10" 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" />
<StackPanel HorizontalAlignment="Left" Height="305" Margin="13,195,0,0" VerticalAlignment="Top" Width="275">
<TextBox x:Name="tbStatus" Text="waiting for game..." TextAlignment="Center" HorizontalAlignment="Left" TextWrapping="NoWrap" Height="25" Margin="10,165,10,10" VerticalAlignment="Top" Width="277" FontSize="14 px" IsEnabled="False" FontWeight="Bold" />
<StackPanel HorizontalAlignment="Left" Height="Auto" Margin="12,195,12,5" VerticalAlignment="Top" Width="275">
<TextBlock HorizontalAlignment="Left" TextWrapping="WrapWithOverflow" VerticalAlignment="Top" FontSize="11 px" IsEnabled="False">
<TextBlock.Inlines>
<Run FontWeight="Bold">If your monitor is 60 Hz you will have to use</Run>

View file

@ -21,15 +21,15 @@ namespace SekiroFpsUnlockAndMore
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 };
internal Dictionary<byte, string> PATCH_FOVMATRIX = new Dictionary<byte, string>
internal byte[] PATCH_FOV_DISABLE = new byte[2] { 0x0C, 0xE7};
internal Dictionary<byte[], string> PATCH_FOVMATRIX = new Dictionary<byte[], string>
{
{ 0x00, "- 50%" },
{ 0x04, "- 10%" },
{ 0x10, "+ 15%" },
{ 0x14, "+ 40%" },
{ 0x18, "+ 75%" },
{ 0x1C, "+ 90%" },
{ new byte[2] {0x00, 0xE7}, "- 50%" },
{ new byte[2] {0x04, 0xE7}, "- 10%" },
{ new byte[2] {0x10, 0xE7}, "+ 15%" },
{ new byte[2] {0x14, 0xE7}, "+ 40%" },
{ new byte[2] {0x18, 0xE7}, "+ 75%" },
{ new byte[2] {0x1C, 0xE7}, "+ 90%" },
};
internal Process _game;
@ -261,12 +261,16 @@ namespace SekiroFpsUnlockAndMore
if (_game.HasExited)
{
_running = false;
if (_gameProc != IntPtr.Zero)
CloseHandle(_gameProc);
_game = null;
_gameHwnd = IntPtr.Zero;
_gameProc = IntPtr.Zero;
_gameProcStatic = IntPtr.Zero;
_offset_framelock = 0x0;
_offset_framelock_running_fix = 0x0;
_offset_resolution = 0x0;
_offset_resolution_default = 0x0;
_offset_widescreen_219 = 0x0;
_offset_fovsetting = 0x0;
UpdateStatus("waiting for game...", Brushes.White);
@ -320,8 +324,7 @@ namespace SekiroFpsUnlockAndMore
if (this.cbAddWidescreen.IsChecked == true)
{
int width = -1;
bool isNumber = Int32.TryParse(this.tbWidth.Text, out width);
bool isNumber = Int32.TryParse(this.tbWidth.Text, out int width);
if (width < 800 || !isNumber)
{
this.tbWidth.Text = "2560";
@ -332,8 +335,7 @@ namespace SekiroFpsUnlockAndMore
this.tbWidth.Text = "5760";
width = 5760;
}
int height = -1;
isNumber = Int32.TryParse(this.tbHeight.Text, out height);
isNumber = Int32.TryParse(this.tbHeight.Text, out int height);
if (height < 450 || !isNumber)
{
this.tbHeight.Text = "1080";
@ -357,8 +359,7 @@ namespace SekiroFpsUnlockAndMore
if (this.cbFov.IsChecked == true)
{
byte[] fovByte = new byte[1];
fovByte[0] = ((KeyValuePair<byte, string>) this.cbSelectFov.SelectedItem).Key;
byte[] fovByte = ((KeyValuePair<byte[], string>) this.cbSelectFov.SelectedItem).Key;
WriteBytes(_gameProcStatic, _offset_fovsetting, fovByte);
}
else if (this.cbFov.IsChecked == false)
@ -406,8 +407,8 @@ namespace SekiroFpsUnlockAndMore
/// <summary>
/// Returns the hexadecimal representation of an IEEE-754 floating point number
/// </summary>
/// <param name="input">The floating point number</param>
/// <returns>The hexadecimal representation of the input</returns>
/// <param name="input">The floating point number.</param>
/// <returns>The hexadecimal representation of the input.</returns>
private string getHexRepresentationFromFloat(float input)
{
uint f = BitConverter.ToUInt32(BitConverter.GetBytes(input), 0);
@ -514,8 +515,7 @@ namespace SekiroFpsUnlockAndMore
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);
ReadProcessMemory(gameProc, lpBaseAddress, lpBuffer, (ulong)lpBuffer.Length, out _);
GCHandle gcHandle = GCHandle.Alloc(lpBuffer, GCHandleType.Pinned);
T structure = (T)Marshal.PtrToStructure(gcHandle.AddrOfPinnedObject(), typeof(T));
gcHandle.Free();
@ -531,8 +531,7 @@ namespace SekiroFpsUnlockAndMore
/// <returns>True if successful, false otherwise.</returns>
private static bool WriteBytes(IntPtr gameProc, Int64 lpBaseAddress, byte[] bytes)
{
IntPtr lpNumberOfBytesWritten;
return WriteProcessMemory(gameProc, lpBaseAddress, bytes, (ulong)bytes.Length, out lpNumberOfBytesWritten);
return WriteProcessMemory(gameProc, lpBaseAddress, bytes, (ulong)bytes.Length, out _);
}
/// <summary>
@ -540,7 +539,7 @@ namespace SekiroFpsUnlockAndMore
/// </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>
/// <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)
@ -558,6 +557,27 @@ namespace SekiroFpsUnlockAndMore
return Regex.IsMatch(text, "[^0-9]+");
}
/// <summary>
/// Logs messages to log file
/// </summary>
/// <param name="msg">The message to write to file.</param>
private void LogToFile(string msg)
{
string timedMsg = "[" + DateTime.Now + "] " + msg;
Debug.WriteLine(timedMsg);
try
{
using (StreamWriter writer = new StreamWriter(_logPath, true))
{
writer.WriteLine(timedMsg);
}
}
catch (Exception ex)
{
MessageBox.Show("Writing to log file failed: " + ex.Message, "Sekiro Fps Unlock And More");
}
}
private void UpdateStatus(string text, Brush color)
{
this.tbStatus.Background = color;
@ -571,9 +591,9 @@ namespace SekiroFpsUnlockAndMore
private void Numeric_PastingHandler(object sender, DataObjectPastingEventArgs e)
{
if (e.DataObject.GetDataPresent(typeof(String)))
if (e.DataObject.GetDataPresent(typeof(string)))
{
String text = (String)e.DataObject.GetData(typeof(String));
string text = (string)e.DataObject.GetData(typeof(string));
if (IsNumericInput(text)) e.CancelCommand();
}
else e.CancelCommand();
@ -608,24 +628,6 @@ namespace SekiroFpsUnlockAndMore
e.Handled = true;
}
// log messages to file
private void LogToFile(string msg)
{
string timedMsg = "[" + DateTime.Now + "] " + msg;
Debug.WriteLine(timedMsg);
try
{
using (StreamWriter writer = new StreamWriter(_logPath, true))
{
writer.WriteLine(timedMsg);
}
}
catch (Exception ex)
{
MessageBox.Show("Writing to log file failed: " + ex.Message, "Sekiro Fps Unlock And More");
}
}
#region WINAPI
private const int WM_HOTKEY_MSG_ID = 0x0312;
private const int MOD_CONTROL = 0x0002;

View file

@ -24,8 +24,8 @@ namespace SekiroFpsUnlockAndMore
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 string PATTERN_FOVSETTING = "F3 0F 10 08 F3 0F 59 0D 00 00 9B 02"; // F3 0F 10 08 F3 0F 59 0D ?? ?? 9B 02
internal const string PATTERN_FOVSETTING_MASK = "xxxxxxxx??xx";
internal const int PATTERN_FOVSETTING_OFFSET = 8;
}
}

View file

@ -33,11 +33,9 @@ namespace SekiroFpsUnlockAndMore
else if (IntPtr.Size == 8)
dwStart = (long)pModule.BaseAddress;
int nSize = pModule.ModuleMemorySize;
IntPtr lpNumberOfBytesRead;
byte[] bData = new byte[nSize];
if (!ReadProcessMemory(hProcess, dwStart, bData, nSize, out lpNumberOfBytesRead))
if (!ReadProcessMemory(hProcess, dwStart, bData, nSize, out IntPtr lpNumberOfBytesRead))
throw new Exception("ReadProcessMemory error!");
if (lpNumberOfBytesRead.ToInt64() != nSize)
throw new Exception("ReadProcessMemory error!");

View file

@ -1,6 +1,6 @@
using System.Reflection;
using System.Windows;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
[assembly: AssemblyTitle("SekiroFpsUnlockAndMore")]
[assembly: AssemblyDescription("")]