diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..32ef8b7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+*.vs/
+*bin/
+*obj/
+*.ico
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ed14295
--- /dev/null
+++ b/README.md
@@ -0,0 +1,121 @@
+# Sekiro FPS Unlocker and more
+
+A small utility to remove frame rate limit, add custom resolutions with 21/9 widescreen support, increase field of view (FOV) (credits to jackfuste) and borderless window mode for [Sekiro: Shadows Die Twice](https://www.sekirothegame.com/) written in C#.
+Patches games memory while running, does not modify any game files. works with every game version (legit steam & oh-not-so-legit), should work with all future updates.
+
+![Sekiro FPS Unlocker and more]()
+
+## Download
+
+[Get the latest release here](https://github.com/uberhalit/SekiroFpsUnlockAndMore/releases)
+
+## Features
+
+* does not modify any game files, RAM patches only
+* 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
+
+## Usage
+
+The game enforces VSYNC and forces 60 Hz in fullscreen even on 144 Hz monitors so we have to override these.
+
+### Follow these steps (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
+
+### 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
+
+### 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
+
+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
+2. Start `Sekiro FPS Unlocker and more`, set you desired resolution and enable it by ticking the check box
+3. Set your custom resolution in the graphical settings, be aware that the ingame HUD will be limited to 16/9
+
+### To use the FOV changer
+1. Start the game
+2. Load up your save game
+3. Start `Sekiro FPS Unlocker and more`, set you desired FOV value and enable it by ticking the check box
+4. If you reload a save FOV will reset so patch game manually again
+
+### To use borderless window mode:
+1. Start the game
+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
+
+## Preview
+
+### Unlocking frame rate:
+![unlocked frame rate]()
+
+### Increasing FOV:
+![FOV increase on the fly and borderless window]()
+
+### See it in action:
+![preview]()
+
+## Prerequisites
+
+* .NET Framework 4.0
+* administrative privileges (for patching)
+* 64 bit OS
+
+## Building
+
+Use Visual Studio 2017 to build
+
+## Contributing
+
+Feel free to open an issue or create a pull request at any time
+
+## License
+
+This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
+
+## Credits
+
+* jackfuste for FOV findings and running speed fix
+* TyChii93 for AMD and widescreen testing
+* [Darius Dan](www.dariusdan.com) for the icon
+
+## Limitations
+
+* the game has forced VSYNC so unlocking the frame rate when your monitor has 60Hz will do nothing. You'll have to disable VSYNC in Nvidia Control Panel or AMD Driver first
+* your monitor has to support your custom resolution otherwise it show up correctly
+* due to how the game renders altering FOV will not move the HUD
+* unlocking framerate and custom resolutions only work in borderless window mode or with VSYNC force disabled
+
+## Version History
+
+* v1.0.0 (2019-03-25)
+ * Initial release
diff --git a/SekiroFpsUnlockAndMore.sln b/SekiroFpsUnlockAndMore.sln
new file mode 100644
index 0000000..179593d
--- /dev/null
+++ b/SekiroFpsUnlockAndMore.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.28307.168
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SekiroFpsUnlockAndMore", "SekiroFpsUnlockAndMore\SekiroFpsUnlockAndMore.csproj", "{DDB24669-982C-44D6-8375-6176CD97B7E2}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {DDB24669-982C-44D6-8375-6176CD97B7E2}.Debug|x64.ActiveCfg = Debug|x64
+ {DDB24669-982C-44D6-8375-6176CD97B7E2}.Debug|x64.Build.0 = Debug|x64
+ {DDB24669-982C-44D6-8375-6176CD97B7E2}.Release|x64.ActiveCfg = Release|x64
+ {DDB24669-982C-44D6-8375-6176CD97B7E2}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {7EBF1A6D-0BBD-4A3E-895E-001A3A9DCD6B}
+ EndGlobalSection
+EndGlobal
diff --git a/SekiroFpsUnlockAndMore/App.config b/SekiroFpsUnlockAndMore/App.config
new file mode 100644
index 0000000..74ade9d
--- /dev/null
+++ b/SekiroFpsUnlockAndMore/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/SekiroFpsUnlockAndMore/App.xaml b/SekiroFpsUnlockAndMore/App.xaml
new file mode 100644
index 0000000..f0507c3
--- /dev/null
+++ b/SekiroFpsUnlockAndMore/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/SekiroFpsUnlockAndMore/App.xaml.cs b/SekiroFpsUnlockAndMore/App.xaml.cs
new file mode 100644
index 0000000..9e8652f
--- /dev/null
+++ b/SekiroFpsUnlockAndMore/App.xaml.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace SekiroFpsUnlockAndMore
+{
+ ///
+ /// Interaktionslogik für "App.xaml"
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/SekiroFpsUnlockAndMore/MainWindow.xaml b/SekiroFpsUnlockAndMore/MainWindow.xaml
new file mode 100644
index 0000000..4493494
--- /dev/null
+++ b/SekiroFpsUnlockAndMore/MainWindow.xaml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ If your monitor is 60 Hz you will have to use
+ fullscreen mode and force disable VSYNC
+ with Nvidia Control panel or AMD Radeon Settings to get frame rate unlock working.
+ To avoid stuttering it's recommended to disable VSYNC even with a 144 Hz monitor.
+ If your monitor is >60 Hz you will have to use
+ borderless window mode or force the game to run on highest available refresh rate
+ using Nvidia Control panel or AMD Radeon Settings.
+ Borderless window mode requires "Window mode" in game settings first.
+ Custom resolution adds 21/9 support and overwrites default 1920x1080 resolution, hud will be limited to 16/9 though.
+ Borderless window mode with custom resolution requires you to patch and set resolution first, then add borderless patch afterwards.
+ For FOV patch to work you need to be ingame.
+ FOV patch resets after save game reload.
+ See the link below for detailed information, GSYNC support and an AMD fix.
+
+
+
+
+
+
+
diff --git a/SekiroFpsUnlockAndMore/MainWindow.xaml.cs b/SekiroFpsUnlockAndMore/MainWindow.xaml.cs
new file mode 100644
index 0000000..686f533
--- /dev/null
+++ b/SekiroFpsUnlockAndMore/MainWindow.xaml.cs
@@ -0,0 +1,600 @@
+using System;
+using System.IO;
+using System.Windows;
+using System.Diagnostics;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Interop;
+using System.Windows.Threading;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text.RegularExpressions;
+
+namespace SekiroFpsUnlockAndMore
+{
+ public partial class MainWindow : Window
+ {
+ 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 _fovMatrix = new Dictionary
+ {
+ { 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 readonly DispatcherTimer _dispatcherTimerCheck = new DispatcherTimer();
+ internal string logPath;
+ internal bool retryAccess = true;
+
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+
+ ///
+ /// On window loaded.
+ ///
+ private void Window_Loaded(object sender, RoutedEventArgs e)
+ {
+ logPath = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) + @"\SekiroFpsUnlockAndMore.log";
+
+ this.cbSelectFov.ItemsSource = _fovMatrix;
+ this.cbSelectFov.SelectedIndex = 0;
+
+ IntPtr hwnd = new WindowInteropHelper(this).Handle;
+ if (!RegisterHotKey(hwnd, 9009, MOD_CONTROL, VK_P))
+ MessageBox.Show("Hotkey is already in use, it may not work.", "Sekiro FPS Unlocker and more");
+
+ // add a hook for WindowsMessageQueue to recognize hotkey-press
+ ComponentDispatcher.ThreadFilterMessage += new ThreadMessageEventHandler(ComponentDispatcherThreadFilterMessage);
+
+ _dispatcherTimerCheck.Tick += new EventHandler(CheckGame);
+ _dispatcherTimerCheck.Interval = new TimeSpan(0, 0, 0, 3);
+ _dispatcherTimerCheck.Start();
+ }
+
+ ///
+ /// On window closing.
+ ///
+ private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
+ {
+ ComponentDispatcher.ThreadFilterMessage -= ComponentDispatcherThreadFilterMessage;
+ IntPtr hwnd = new WindowInteropHelper(this).Handle;
+ UnregisterHotKey(hwnd, 9009);
+ if (_gameProc != IntPtr.Zero)
+ CloseHandle(_gameProc);
+ }
+
+ ///
+ /// Windows Message queue (Wndproc) to catch HotKeyPressed
+ ///
+ private void ComponentDispatcherThreadFilterMessage(ref MSG msg, ref bool handled)
+ {
+ if (!handled)
+ {
+ if (msg.message == WM_HOTKEY_MSG_ID) // hotkeyevent
+ {
+ if (msg.wParam.ToInt32() == 9009) // patch game
+ {
+ handled = true;
+ PatchGame();
+ }
+ }
+ }
+ }
+
+ ///
+ /// Checks if game is running and initializes further functionality.
+ ///
+ private void CheckGame(object sender, EventArgs e)
+ {
+ Process[] procList = Process.GetProcessesByName(PROCESS_NAME);
+ if (procList.Length < 1)
+ return;
+
+ if (_running || _offset_framelock != 0x0)
+ return;
+
+ int gameIndex = -1;
+ for (int i = 0; i < procList.Length; i++)
+ {
+ if (procList[i].MainWindowTitle == PROCESS_TITLE && procList[i].MainModule.FileVersionInfo.FileDescription.Contains(PROCESS_DESCRIPTION))
+ {
+ gameIndex = i;
+ break;
+ }
+ }
+ if (gameIndex < 0)
+ {
+ UpdateStatus("no valid game process found...", Brushes.Red);
+ LogToFile("no valid game process found...");
+ for (int j = 0; j < procList.Length; j++)
+ {
+ LogToFile(string.Format("\tProcess #{0}: '{1}' | ({2})", j, procList[j].MainModule.FileName, procList[j].MainModule.FileVersionInfo.FileName));
+ LogToFile(string.Format("\tDescription #{0}: {1} | {2} | {3}", j, procList[j].MainWindowTitle, procList[j].MainModule.FileVersionInfo.CompanyName, procList[j].MainModule.FileVersionInfo.FileDescription));
+ LogToFile(string.Format("\tData #{0}: {1} | {2} | {3} | {4} | {5}", j, procList[j].MainModule.FileVersionInfo.FileVersion, procList[j].MainModule.ModuleMemorySize, procList[j].StartTime, procList[j].Responding, procList[j].HasExited));
+ }
+ return;
+ }
+
+ _game = procList[gameIndex];
+ _gameHwnd = procList[gameIndex].MainWindowHandle;
+ _gameProc = OpenProcess(PROCESS_ALL_ACCESS, false, (uint)procList[gameIndex].Id);
+ _gameProcStatic = _gameProc;
+ if (_gameHwnd == IntPtr.Zero || _gameProc == IntPtr.Zero || procList[gameIndex].MainModule.BaseAddress == IntPtr.Zero)
+ {
+ LogToFile("no access to game...");
+ LogToFile("Hwnd: " + _gameHwnd.ToString("X"));
+ LogToFile("Proc: " + _gameProc.ToString("X"));
+ LogToFile("Base: " + procList[gameIndex].MainModule.BaseAddress.ToString("X"));
+ if (!retryAccess)
+ {
+ UpdateStatus("no access to game...", Brushes.Red);
+ _dispatcherTimerCheck.Stop();
+ return;
+ }
+ _gameHwnd = IntPtr.Zero;
+ if (_gameProc != IntPtr.Zero)
+ {
+ CloseHandle(_gameProc);
+ _gameProc = IntPtr.Zero;
+ _gameProcStatic = IntPtr.Zero;
+ }
+ LogToFile("retrying...");
+ retryAccess = false;
+ return;
+ }
+
+ //string gameFileVersion = FileVersionInfo.GetVersionInfo(procList[0].MainModule.FileName).FileVersion;
+
+ _offset_framelock = PatternScan.FindPattern(_gameProc, procList[gameIndex].MainModule, PATTERN_FRAMELOCK, 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;
+ Debug.WriteLine("2. Framelock found at: 0x" + _offset_framelock.ToString("X"));
+ }
+ if (!IsValid(_offset_framelock))
+ {
+ UpdateStatus("framelock not found...", Brushes.Red);
+ LogToFile("framelock not found...");
+ 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;
+ Debug.WriteLine("Running fix found at: 0x" + _offset_framelock_running_fix.ToString("X"));
+ if (!IsValid(_offset_framelock_running_fix))
+ {
+ UpdateStatus("running fix not found...", Brushes.Red);
+ LogToFile("running fix not found...");
+ this.cbAddWidescreen.IsEnabled = false;
+ 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))
+ {
+ UpdateStatus("resolution not found...", Brushes.Red);
+ LogToFile("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, ' ');
+ Debug.WriteLine("Widescreen 21/9 found at: 0x" + _offset_widescreen_219.ToString("X"));
+ if (!IsValid(_offset_widescreen_219))
+ {
+ UpdateStatus("widescreen 21/9 not found...", Brushes.Red);
+ LogToFile("Widescreen 21/9 not found...");
+ this.cbAddWidescreen.IsEnabled = false;
+ this.cbAddWidescreen.IsChecked = false;
+ }
+
+ _offset_fovsetting = PatternScan.FindPattern(_gameProc, procList[gameIndex].MainModule, PATTERN_FOVSETTING, PATTERN_FOVSETTING_MASK, ' ') + PATTERN_FOVSETTING_OFFSET;
+ Debug.WriteLine("FOV found at: 0x" + _offset_fovsetting.ToString("X"));
+ if (!IsValid(_offset_fovsetting))
+ {
+ UpdateStatus("FOV not found...", Brushes.Red);
+ LogToFile("FOV not found...");
+ this.cbFov.IsEnabled = false;
+ this.cbFov.IsChecked = false;
+ }
+
+ _running = true;
+ _dispatcherTimerCheck.Stop();
+ PatchGame();
+ }
+
+ ///
+ /// Patch up this broken port
+ ///
+ private void PatchGame()
+ {
+ if (!_running)
+ return;
+
+ if (_game.HasExited)
+ {
+ _running = false;
+ _gameHwnd = IntPtr.Zero;
+ _gameProc = IntPtr.Zero;
+ _gameProcStatic = IntPtr.Zero;
+ _offset_framelock = 0x0;
+ _offset_framelock_running_fix = 0x0;
+ _offset_resolution = 0x0;
+ _offset_widescreen_219 = 0x0;
+ _offset_fovsetting = 0x0;
+ UpdateStatus("waiting for game...", Brushes.White);
+ _dispatcherTimerCheck.Start();
+ return;
+ }
+
+ if (this.cbUnlockFps.IsChecked == true)
+ {
+ int fps = -1;
+ bool isNumber = Int32.TryParse(this.tbFps.Text, out fps);
+ if (fps < 0 || !isNumber)
+ {
+ this.tbFps.Text = "60";
+ fps = 60;
+ }
+ else if (fps > 0 && fps < 30)
+ {
+ this.tbFps.Text = "30";
+ fps = 30;
+ }
+ else if (fps > 300)
+ {
+ this.tbFps.Text = "300";
+ fps = 300;
+ }
+
+ if (fps == 0)
+ {
+ WriteBytes(_gameProcStatic, _offset_framelock, PATCH_FRAMERATE_UNLIMITED);
+ WriteBytes(_gameProcStatic, _offset_framelock_running_fix, new byte[1] { 0xF8 }); // F8 is maximum
+ }
+ else
+ {
+ int speed = 144 + (int)Math.Ceiling((fps - 60) / 16f) * 8; // calculation from game functions
+ if (speed > 248)
+ speed = 248;
+ float deltaTime = (1000f / fps) / 1000f;
+ Debug.WriteLine("Deltatime hex: 0x" + getHexRepresentationFromFloat(deltaTime));
+ Debug.WriteLine("Speed hex: 0x" + speed.ToString("X"));
+ WriteBytes(_gameProcStatic, _offset_framelock, BitConverter.GetBytes(deltaTime));
+ WriteBytes(_gameProcStatic, _offset_framelock_running_fix, new byte[] { (byte)Convert.ToInt16(speed) });
+ }
+ }
+ else if (this.cbUnlockFps.IsChecked == false)
+ {
+ float deltaTime = (1000f / 60) / 1000f;
+ WriteBytes(_gameProcStatic, _offset_framelock, BitConverter.GetBytes(deltaTime));
+ WriteBytes(_gameProcStatic, _offset_framelock_running_fix, PATCH_FRAMERATE_RUNNING_FIX_DISABLE);
+ }
+
+ if (this.cbAddWidescreen.IsChecked == true)
+ {
+ int width = -1;
+ bool isNumber = Int32.TryParse(this.tbWidth.Text, out width);
+ if (width < 800 || !isNumber)
+ {
+ this.tbWidth.Text = "2560";
+ width = 2560;
+ }
+ else if (width > 5760)
+ {
+ this.tbWidth.Text = "5760";
+ width = 5760;
+ }
+ int height = -1;
+ isNumber = Int32.TryParse(this.tbHeight.Text, out height);
+ if (height < 450 || !isNumber)
+ {
+ this.tbHeight.Text = "1080";
+ height = 1080;
+ }
+ else if (height > 2160)
+ {
+ 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);
+ }
+ else if (this.cbAddWidescreen.IsChecked == false)
+ {
+ WriteBytes(_gameProcStatic, _offset_resolution, BitConverter.GetBytes(1920));
+ WriteBytes(_gameProcStatic, _offset_resolution + 4, BitConverter.GetBytes(1080));
+ WriteBytes(_gameProcStatic, _offset_widescreen_219, PATCH_WIDESCREEN_219_DISABLE);
+ }
+
+ if (this.cbFov.IsChecked == true)
+ {
+ byte[] fovByte = new byte[1];
+ fovByte[0] = ((KeyValuePair) this.cbSelectFov.SelectedItem).Key;
+ WriteBytes(_gameProcStatic, _offset_fovsetting, fovByte);
+ }
+ else if (this.cbFov.IsChecked == false)
+ {
+ WriteBytes(_gameProcStatic, _offset_fovsetting, PATCH_FOV_DISABLE);
+ }
+
+ if (this.cbBorderless.IsChecked == true)
+ {
+ if (!IsFullscreen(_gameHwnd))
+ SetWindowBorderless(_gameHwnd);
+ else
+ {
+ MessageBox.Show("Please exit fullscreen first before activating borderless window mode.", "Sekiro FPS Unlocker and more");
+ this.cbBorderless.IsChecked = false;
+ }
+ }
+ else if (this.cbBorderless.IsChecked == false && !IsFullscreen(_gameHwnd))
+ {
+ SetWindowWindowed(_gameHwnd);
+ }
+
+ if (this.cbUnlockFps.IsChecked == true || this.cbAddWidescreen.IsChecked == true || this.cbFov.IsChecked == true)
+ UpdateStatus(DateTime.Now.ToString("HH:mm:ss") + " Game patched!", Brushes.Green);
+ else
+ UpdateStatus(DateTime.Now.ToString("HH:mm:ss") + " Game unpatched!", Brushes.White);
+ }
+
+ ///
+ /// Returns the hexadecimal representation of an IEEE-754 floating point number
+ ///
+ /// The floating point number
+ /// The hexadecimal representation of the input
+ private string getHexRepresentationFromFloat(float input)
+ {
+ uint f = BitConverter.ToUInt32(BitConverter.GetBytes(input), 0);
+ return "0x" + f.ToString("X8");
+ }
+
+ ///
+ /// Checks if window is in fullscreen mode.
+ ///
+ /// The main window handle of the window.
+ ///
+ /// Fullscreen windows have WS_EX_TOPMOST flag set.
+ ///
+ /// True if window is run in fullscreen mode.
+ private bool IsFullscreen(IntPtr hwnd)
+ {
+ long wndStyle = GetWindowLongPtr(hwnd, GWL_STYLE).ToInt64();
+ long wndExStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE).ToInt64();
+
+ if (wndStyle == 0 || wndExStyle == 0)
+ return false;
+
+ if ((wndExStyle & WS_EX_TOPMOST) == 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;
+ }
+
+ ///
+ /// Sets a window to ordinary windowed mode
+ ///
+ /// The handle to the window.
+ private void SetWindowWindowed(IntPtr hwnd)
+ {
+ SetWindowLongPtr(hwnd, GWL_STYLE, WS_VISIBLE | WS_CAPTION | WS_BORDER | WS_CLIPSIBLINGS | WS_DLGFRAME | WS_SYSMENU | WS_GROUP | WS_MINIMIZEBOX);
+ }
+
+ ///
+ /// Sets a window to borderless windowed mode and moves it to position 0x0.
+ ///
+ /// The handle to the window.
+ private void SetWindowBorderless(IntPtr hwnd)
+ {
+ SetWindowLongPtr(hwnd, GWL_STYLE, WS_VISIBLE | WS_POPUP);
+ RECT rect;
+ GetWindowRect(hwnd, out rect);
+ SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, rect.Right - rect.Left, rect.Bottom - rect.Top, SWP_FRAMECHANGED | SWP_SHOWWINDOW);
+ }
+
+ ///
+ /// Checks if a pointer is valid.
+ ///
+ /// The address the pointer points to.
+ /// True if pointer points to a valid address.
+ private static bool IsValid(Int64 address)
+ {
+ return (address >= 0x10000 && address < 0x000F000000000000);
+ }
+
+ ///
+ /// Writes a given type and value to processes memory using a generic method.
+ ///
+ /// The process handle to read from.
+ /// The address to write from.
+ /// The byte array to write.
+ /// True if successful, false otherwise.
+ private static bool WriteBytes(IntPtr gameProc, Int64 lpBaseAddress, byte[] bytes)
+ {
+ IntPtr lpNumberOfBytesWritten;
+ return WriteProcessMemory(gameProc, lpBaseAddress, bytes, (ulong)bytes.Length, out lpNumberOfBytesWritten);
+ }
+
+ ///
+ /// Check whether input is numeric only.
+ ///
+ /// The text to check.
+ /// True if inout is numeric only.
+ private bool IsNumericInput(string text)
+ {
+ return Regex.IsMatch(text, "[^0-9]+");
+ }
+
+ private void UpdateStatus(string text, Brush color)
+ {
+ this.tbStatus.Background = color;
+ this.tbStatus.Text = text;
+ }
+
+ private void Numeric_PreviewTextInput(object sender, TextCompositionEventArgs e)
+ {
+ e.Handled = IsNumericInput(e.Text);
+ }
+
+ private void Numeric_PastingHandler(object sender, DataObjectPastingEventArgs e)
+ {
+ if (e.DataObject.GetDataPresent(typeof(String)))
+ {
+ String text = (String)e.DataObject.GetData(typeof(String));
+ if (IsNumericInput(text)) e.CancelCommand();
+ }
+ else e.CancelCommand();
+ }
+
+ private void CheckBoxChanged_Handler(object sender, RoutedEventArgs e)
+ {
+ PatchGame();
+ }
+
+ private void BPatch_Click(object sender, RoutedEventArgs e)
+ {
+ PatchGame();
+ }
+
+ private void Hyperlink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
+ {
+ Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
+ 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;
+ private const uint VK_P = 0x0050;
+ private const uint PROCESS_ALL_ACCESS = 0x001F0FFF;
+ private const int GWL_EXSTYLE = -20;
+ private const int GWL_STYLE = -16;
+ private const uint WS_CLIPSIBLINGS = 0x04000000;
+ private const uint WS_DLGFRAME = 0x00400000;
+ private const uint WS_SYSMENU = 0x00080000;
+ private const uint WS_GROUP = 0x00020000;
+ private const uint WS_MINIMIZEBOX = 0x00020000;
+ private const uint WS_POPUP = 0x80000000;
+ private const uint WS_VISIBLE = 0x10000000;
+ 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_NOTOPMOST = -2;
+ private const uint SWP_FRAMECHANGED = 0x0020;
+ private const uint SWP_SHOWWINDOW = 0x0040;
+
+ [DllImport("user32.dll")]
+ public static extern Boolean RegisterHotKey(IntPtr hWnd, Int32 id, UInt32 fsModifiers, UInt32 vlc);
+
+ [DllImport("user32.dll")]
+ public static extern Boolean UnregisterHotKey(IntPtr hWnd, Int32 id);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ private static extern IntPtr OpenProcess(
+ UInt32 dwDesiredAccess,
+ Boolean bInheritHandle,
+ UInt32 dwProcessId);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ private static extern Boolean CloseHandle(IntPtr hObject);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ private static extern IntPtr GetWindowLongPtr(IntPtr hWnd, Int32 nIndex);
+
+ [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")]
+ private static extern IntPtr SetWindowLongPtr(IntPtr hWnd, Int32 nIndex, Int64 dwNewLong);
+
+ [DllImport("user32.dll", EntryPoint = "SetWindowPos")]
+ public static extern IntPtr SetWindowPos(IntPtr hWnd, Int32 hWndInsertAfter, Int32 X, Int32 Y, Int32 cx, Int32 cy, UInt32 uFlags);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct RECT
+ {
+ public int Left; // x position of upper-left corner
+ public int Top; // y position of upper-left corner
+ public int Right; // x position of lower-right corner
+ public int Bottom; // y position of lower-right corner
+ }
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ internal static extern bool WriteProcessMemory(
+ IntPtr hProcess,
+ Int64 lpBaseAddress,
+ [In, Out] Byte[] lpBuffer,
+ UInt64 dwSize,
+ out IntPtr lpNumberOfBytesWritten);
+
+ #endregion
+ }
+}
diff --git a/SekiroFpsUnlockAndMore/PatternScan.cs b/SekiroFpsUnlockAndMore/PatternScan.cs
new file mode 100644
index 0000000..bcf24f2
--- /dev/null
+++ b/SekiroFpsUnlockAndMore/PatternScan.cs
@@ -0,0 +1,79 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace SekiroFpsUnlockAndMore
+{
+ class PatternScan
+ {
+ ///
+ /// Finds a pattern or signature inside another process's memory.
+ ///
+ /// Handle to the process in whose memory pattern will be searched for.
+ /// Module which will be searched for the pattern.
+ /// A character-delimited string representing the pattern to be found.
+ /// A string of 'x' (match), '!' (not-match), or '?' (wildcard).
+ /// Determines how the string will be split. If null, defaults to ' '.
+ /// The address of the beginning of the pattern if found, 0 if not found
+ internal static Int64 FindPattern(IntPtr hProcess, ProcessModule pModule, string szPattern, string szMask, char cDelimiter = ' ')
+ {
+ string[] saPattern = szPattern.Split(cDelimiter);
+ byte[] bPattern = new byte[saPattern.Length];
+ for (int i = 0; i < saPattern.Length; i++)
+ bPattern[i] = Convert.ToByte(saPattern[i], 0x10);
+
+ if (bPattern == null || bPattern.Length == 0)
+ throw new ArgumentNullException("Pattern's length is zero!");
+ if (bPattern.Length != szMask.Length)
+ throw new ArgumentException("Pattern's bytes and szMask must be of the same size!");
+
+ long dwStart = 0;
+ if (IntPtr.Size == 4)
+ dwStart = (uint)pModule.BaseAddress;
+ 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))
+ throw new Exception("ReadProcessMemory error!");
+ if (lpNumberOfBytesRead.ToInt64() != nSize)
+ throw new Exception("ReadProcessMemory error!");
+ if (bData == null || bData.Length == 0)
+ throw new Exception("Could not read memory in FindPattern.");
+
+ long ix;
+ int iy;
+ bool bFound = false;
+ int patternLength = bPattern.Length;
+ int dataLength = bData.Length - patternLength;
+
+ for (ix = 0; ix < dataLength; ix++)
+ {
+ bFound = true;
+ for (iy = 0; iy < patternLength; iy++)
+ {
+ if ((szMask[iy] == 'x' && bPattern[iy] != bData[ix + iy]) ||
+ (szMask[iy] == '!' && bPattern[iy] == bData[ix + iy]))
+ {
+ bFound = false;
+ break;
+ }
+ }
+ if (bFound)
+ return Convert.ToInt64((long)dwStart + ix);
+ }
+ return 0;
+ }
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ private static extern bool ReadProcessMemory(
+ IntPtr hProcess,
+ Int64 lpBaseAddress,
+ [Out] Byte[] lpBuffer,
+ Int64 dwSize,
+ out IntPtr lpNumberOfBytesRead);
+ }
+}
diff --git a/SekiroFpsUnlockAndMore/Properties/AssemblyInfo.cs b/SekiroFpsUnlockAndMore/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..6669805
--- /dev/null
+++ b/SekiroFpsUnlockAndMore/Properties/AssemblyInfo.cs
@@ -0,0 +1,25 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+[assembly: AssemblyTitle("SekiroFpsUnlockAndMore")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("SekiroFpsUnlockAndMore")]
+[assembly: AssemblyCopyright("Copyright © uberhalit 2019")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None,
+ ResourceDictionaryLocation.SourceAssembly
+)]
+
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/SekiroFpsUnlockAndMore/Properties/Resources.Designer.cs b/SekiroFpsUnlockAndMore/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..0706ba1
--- /dev/null
+++ b/SekiroFpsUnlockAndMore/Properties/Resources.Designer.cs
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace SekiroFpsUnlockAndMore.Properties {
+ using System;
+
+
+ ///
+ /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+ ///
+ // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
+ // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
+ // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
+ // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SekiroFpsUnlockAndMore.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
+ /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/SekiroFpsUnlockAndMore/Properties/Resources.resx b/SekiroFpsUnlockAndMore/Properties/Resources.resx
new file mode 100644
index 0000000..af7dbeb
--- /dev/null
+++ b/SekiroFpsUnlockAndMore/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/SekiroFpsUnlockAndMore/Properties/Settings.Designer.cs b/SekiroFpsUnlockAndMore/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..bec0dc6
--- /dev/null
+++ b/SekiroFpsUnlockAndMore/Properties/Settings.Designer.cs
@@ -0,0 +1,26 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.42000
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace SekiroFpsUnlockAndMore.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/SekiroFpsUnlockAndMore/Properties/Settings.settings b/SekiroFpsUnlockAndMore/Properties/Settings.settings
new file mode 100644
index 0000000..033d7a5
--- /dev/null
+++ b/SekiroFpsUnlockAndMore/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SekiroFpsUnlockAndMore/SekiroFpsUnlockAndMore.csproj b/SekiroFpsUnlockAndMore/SekiroFpsUnlockAndMore.csproj
new file mode 100644
index 0000000..074ec2a
--- /dev/null
+++ b/SekiroFpsUnlockAndMore/SekiroFpsUnlockAndMore.csproj
@@ -0,0 +1,110 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {DDB24669-982C-44D6-8375-6176CD97B7E2}
+ WinExe
+ SekiroFpsUnlockAndMore
+ SekiroFpsUnlockAndMore
+ v4.0
+ 512
+ {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 4
+ true
+
+
+
+ app.manifest
+
+
+ true
+ bin\x64\Debug\
+ DEBUG;TRACE
+ full
+ x64
+ prompt
+ MinimumRecommendedRules.ruleset
+ true
+
+
+ bin\x64\Release\
+ TRACE
+ true
+ pdbonly
+ x64
+ prompt
+ MinimumRecommendedRules.ruleset
+ true
+
+
+ icon.ico
+
+
+
+
+
+
+
+
+
+
+
+ 4.0
+
+
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+
+
+ MSBuild:Compile
+ Designer
+
+
+ App.xaml
+ Code
+
+
+ MainWindow.xaml
+ Code
+
+
+
+
+
+ Code
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ Settings.settings
+ True
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SekiroFpsUnlockAndMore/app.manifest b/SekiroFpsUnlockAndMore/app.manifest
new file mode 100644
index 0000000..8c24669
--- /dev/null
+++ b/SekiroFpsUnlockAndMore/app.manifest
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+