Added FreezeMem for fTimescalePlayer

ApplicationSettings are decoupled from base class now
Cleanup
Refactor
This commit is contained in:
uberhalit 2019-04-01 04:11:18 +02:00
parent 12b6c52149
commit 772b69c1fb
7 changed files with 1743 additions and 1641 deletions

View file

@ -1,196 +1,218 @@
using System;
using System.Linq;
using System.Collections.Generic;
namespace SekiroFpsUnlockAndMore
{
internal class GameData
{
internal const string PROCESS_NAME = "sekiro";
internal const string PROCESS_TITLE = "Sekiro";
internal const string PROCESS_DESCRIPTION = "Shadows Die Twice";
/**
<float>fFrameTick determines default frame rate limit in seconds
000000014116168D | C743 18 8988883C | mov dword ptr ds:[rbx+18],3C888889 | fFrameTick
0000000141161694 | 4C:89AB 70020000 | mov qword ptr ds:[rbx+270],r13 |
*/
internal const string PATTERN_FRAMELOCK = "88 88 3C 4C 89 AB"; // 88 88 3C 4C 89 AB // 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 int PATTERN_FRAMELOCK_OFFSET = -1; // offset to byte array from found position
internal const string PATTERN_FRAMELOCK_FUZZY = "C7 43 00 00 00 00 00 4C 89 AB"; // C7 43 ?? ?? ?? ?? ?? 4C 89 AB
internal const string PATTERN_FRAMELOCK_FUZZY_MASK = "xx?????xxx";
internal const int PATTERN_FRAMELOCK_FUZZY_OFFSET = 3;
/**
Reference pointer pFrametimeRunningSpeed to speed table entry that gets used in calculations. Add or remove multiplications of 4bytes to pFrametimeRunningSpeed address to use a higher or lower <float>fFrametimeCriticalRunningSpeed from table
fFrametimeCriticalRunningSpeed should be roughly half the frame rate: 30 @ 60FPS limit, 50 @ 100FPS limit...
00000001407D4E08 | F3:0F5905 90309202 | mulss xmm0,dword ptr ds:[1430F7EA0] | pFrametimeRunningSpeed->fFrametimeCriticalRunningSpeed
*/
internal const string PATTERN_FRAMELOCK_SPEED_FIX = "F3 0F 59 05 00 30 92 02"; // F3 0F 59 05 ?? 30 92 02 | 0F 51 C2 F3 0F 59 05 ?? ?? ?? ?? 0F 2F F8
internal const string PATTERN_FRAMELOCK_SPEED_FIX_MASK = "xxxx?xxx";
internal const int PATTERN_FRAMELOCK_SPEED_FIX_OFFSET = 4;
/**
00000001430F7E10
Key: Patch to pFrametimeRunningSpeed last byte
Value: Value resolve in float table from pFrametimeRunningSpeed->fFrametimeCriticalRunningSpeed
Hardcoded cause lazy -> if anyone knows how the table is calculated then tell me and I'll buy you a beer
*/
internal static Dictionary<byte[], float> PATCH_FRAMELOCK_SPEED_FIX_MATRIX = new Dictionary<byte[], float>
{
{ new byte[1] {0x68}, 15f },
{ new byte[1] {0x6C}, 16f },
{ new byte[1] {0x70}, 16.6667f },
{ new byte[1] {0x74}, 18f },
{ new byte[1] {0x78}, 18.6875f },
{ new byte[1] {0x7C}, 18.8516f },
{ new byte[1] {0x80}, 20f },
{ new byte[1] {0x84}, 24f },
{ new byte[1] {0x88}, 25f },
{ new byte[1] {0x8C}, 27.5f },
{ new byte[1] {0x90}, 30f },
{ new byte[1] {0x94}, 32f },
{ new byte[1] {0x98}, 38.5f },
{ new byte[1] {0x9C}, 40f },
{ new byte[1] {0xA0}, 48f },
{ new byte[1] {0xA4}, 49.5f },
{ new byte[1] {0xA8}, 50f },
{ new byte[1] {0xAC}, 57.2958f },
{ new byte[1] {0xB0}, 60f },
{ new byte[1] {0xB4}, 64f },
{ new byte[1] {0xB8}, 66.75f },
{ new byte[1] {0xBC}, 67f },
{ new byte[1] {0xC0}, 78.8438f },
{ new byte[1] {0xC4}, 80f },
{ new byte[1] {0xC8}, 84f },
{ new byte[1] {0xCC}, 90f },
{ new byte[1] {0xD0}, 93.8f },
{ new byte[1] {0xD4}, 100f },
{ new byte[1] {0xD8}, 120f },
{ new byte[1] {0xDC}, 127f },
{ new byte[1] {0xE0}, 128f },
{ new byte[1] {0xE4}, 130f },
{ new byte[1] {0xE8}, 140f },
{ new byte[1] {0xEC}, 144f },
{ new byte[1] {0xF0}, 150f }
};
internal static byte[] PATCH_FRAMELOCK_SPEED_FIX_DISABLE = new byte[1] { 0x90 }; // 30f
/// <summary>
/// Finds closest valid speed fix value for a frame rate limit.
/// </summary>
/// <param name="frameLimit">The set frame rate limit.</param>
/// <returns>The byte patch of the closest speed fix.</returns>
internal static byte[] FindSpeedFixForRefreshRate(int frameLimit)
{
float idealSpeedFix = frameLimit / 2f;
KeyValuePair<byte[], float> closestSpeedFix = new KeyValuePair<byte[], float>(PATCH_FRAMELOCK_SPEED_FIX_DISABLE, 30f);
foreach (var speedFix in PATCH_FRAMELOCK_SPEED_FIX_MATRIX.OrderBy(kvp => kvp.Value))
{
if (Math.Abs(idealSpeedFix - speedFix.Value) < Math.Abs(idealSpeedFix - closestSpeedFix.Value))
closestSpeedFix = speedFix;
}
return closestSpeedFix.Key;
}
/**
Reference pointer pCurrentResolutionWidth to <int>iInternalGameWidth (and <int>iInternalGameHeight which is +4 bytes)
000000014114AC85 | 0F57D2 | xorps xmm2,xmm2 |
000000014114AC88 | 890D 92147D02 | mov dword ptr ds:[14391C120],ecx | pCurrentResolutionWidth->iInternalGameWidth
000000014114AC8E | 0F57C9 | xorps xmm1,xmm1 |
000000014114AC91 | 8915 8D147D02 | mov dword ptr ds:[14391C124],edx | pCurrentResolutionHeight->iInternalGameHeight
*/
internal const string PATTERN_RESOLUTION_POINTER = "0F 57 D2 89 0D 00 00 00 00 0F 57 C9"; // 0F 57 D2 89 0D ?? ?? ?? ?? 0F 57 C9
internal const string PATTERN_RESOLUTION_POINTER_MASK = "xxxxx????xxx";
internal const int PATTERN_RESOLUTION_POINTER_OFFSET = 3;
internal const int PATTERN_RESOLUTION_POINTER_INSTRUCTION_LENGTH = 6;
/**
DATA SECTION. All resolutions are listed in memory as <int>width1 <int>height1 <int>width2 <int>height2 ...
Overwrite an unused one with desired new one. Some glitches, 1920x1080 and 1280x720 works best
*/
internal const string PATTERN_RESOLUTION_DEFAULT = "80 07 00 00 38 04 00 00"; // 1920x1080
internal const string PATTERN_RESOLUTION_DEFAULT_720 = "40 06 00 00 84 03 00 00"; // 1280x720
internal const string PATTERN_RESOLUTION_DEFAULT_MASK = "xxxxxxxx";
internal static byte[] PATCH_RESOLUTION_DEFAULT_DISABLE = new byte[8] { 0x80, 0x07, 0x00, 0x00, 0x38, 0x04, 0x00, 0x00 };
internal static byte[] PATCH_RESOLUTION_DEFAULT_DISABLE_720 = new byte[8] { 0x40, 0x06, 0x00, 0x00, 0x84, 0x03, 0x00, 0x00 };
/**
Conditional jump instruction that determines if 16/9 scaling for game is enforced or not, overwrite with non conditional JMP so widescreen won't get clinched
000000014012967A | 74 47 | je sekiro.1401296C3 | conditional jump
000000014012967C | 47:8B94C7 1C020000 | mov r10d,dword ptr ds:[r15+r8*8+21C] | start of long resolution scaling calculation method within jump
*/
internal const string PATTERN_RESOLUTION_SCALING_FIX = "47 47 8B 94 C7 1C 02 00 00"; // 47 47 8B 94 C7 1C 02 00 00
internal const string PATTERN_RESOLUTION_SCALING_FIX_MASK = "xxxxxxxxx";
internal const int PATTERN_RESOLUTION_SCALING_FIX_OFFSET = -1;
internal static byte[] PATCH_RESOLUTION_SCALING_FIX_ENABLE = new byte[1] { 0xEB }; // JMP
internal static byte[] PATCH_RESOLUTION_SCALING_FIX_DISABLE = new byte[1] { 0x74 }; // JE
/**
Reference pointer pFovTableEntry to FOV entry in game FOV table that gets used in FOV calculations. Overwrite pFovTableEntry address to use a higher or lower <float>fFOV from table
0000000140739548 | F3:0F1008 | movss xmm1,dword ptr ds:[rax] |
000000014073954C | F3:0F590D 0CE79B02 | mulss xmm1,dword ptr ds:[1430F7C60] | pFovTableEntry->fFov
*/
// credits to 'jackfuste' for original offset
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;
/**
00000001430F7C60
Key: Patch to pFovTableEntry last 2 bytes
Value: Value resolve in float table from pFovTableEntry->fFov
*/
internal static Dictionary<byte[], string> PATCH_FOVSETTING_MATRIX = new Dictionary<byte[], string>
{
{ 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 static byte[] PATCH_FOVSETTING_DISABLE = new byte[2] { 0x0C, 0xE7 }; // + 0%
/**
Reference pointer pTimeRelated to pTimescaleManager pointer, offset in struct to <float>fTimescale which acts as a global speed scale for almost all ingame calculations
0000000141149E87 | 48:8B05 3A24B402 | mov rax,qword ptr ds:[143C8C2C8] | pTimeRelated->[pTimescaleManager+0x360]->fTimescale
0000000141149E8E | F3:0F1088 60030000 | movss xmm1,dword ptr ds:[rax+360] | offset from TimescaleManager->fTimescale
0000000141149E96 | F3:0F5988 68020000 | mulss xmm1,dword ptr ds:[rax+268] |
*/
// credits to 'Zullie the Witch' for original offset
internal const string PATTERN_TIMESCALE = "48 8B 05 00 00 00 00 F3 0F 10 88 00 00 00 00 F3 0F"; // 48 8B 05 ?? ?? ?? ?? F3 0F 10 88 ?? ?? ?? ?? F3 0F
internal const string PATTERN_TIMESCALE_MASK = "xxx????xxxx????xx";
internal const int PATTERN_TIMESCALE_INSTRUCTION_LENGTH = 7;
internal const int PATTERN_TIMESCALE_POINTER_OFFSET_OFFSET = 11;
/**
Reference pointer pPlayerStructRelated1 to 4 more pointers up to player data class, offset in struct to <float>fTimescalePlayer which acts as a speed scale for the player character
00000001406BF1D7 | 48:8B1D 128C4A03 | mov rbx,qword ptr ds:[143B67DF0] | pPlayerStructRelated1->[pPlayerStructRelated2+0x88]->[pPlayerStructRelated3+0x1FF8]->[pPlayerStructRelated4+0x28]->[pPlayerStructRelated5+0xD00]->fTimescalePlayer
00000001406BF1DE | 48:85DB | test rbx,rbx |
00000001406BF1E1 | 74 3C | je sekiro.1406BF21F |
00000001406BF1E3 | 8B17 | mov edx,dword ptr ds:[rdi] |
*/
// credits to 'Zullie the Witch' for original offset
internal const string PATTERN_TIMESCALE_PLAYER = "48 8B 1D 00 00 00 00 48 85 DB 74 3C 8B 17"; // 48 8B 1D ?? ?? ?? ?? 48 85 DB 74 3C 8B 17
internal const string PATTERN_TIMESCALE_PLAYER_MASK = "xxx????xxxxxxx";
internal const int PATTERN_TIMESCALE_PLAYER_INSTRUCTION_LENGTH = 7;
internal const int PATTERN_TIMESCALE_POINTER2_OFFSET = 0x88;
internal const int PATTERN_TIMESCALE_POINTER3_OFFSET = 0x1FF8;
internal const int PATTERN_TIMESCALE_POINTER4_OFFSET = 0x28;
using System;
using System.Linq;
using System.Collections.Generic;
namespace SekiroFpsUnlockAndMore
{
internal class GameData
{
internal const string PROCESS_NAME = "sekiro";
internal const string PROCESS_TITLE = "Sekiro";
internal const string PROCESS_DESCRIPTION = "Shadows Die Twice";
/**
<float>fFrameTick determines default frame rate limit in seconds
000000014116168D | C743 18 8988883C | mov dword ptr ds:[rbx+18],3C888889 | fFrameTick
0000000141161694 | 4C:89AB 70020000 | mov qword ptr ds:[rbx+270],r13 |
*/
internal const string PATTERN_FRAMELOCK = "88 88 3C 4C 89 AB"; // 88 88 3C 4C 89 AB // 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 int PATTERN_FRAMELOCK_OFFSET = -1; // offset to byte array from found position
internal const string PATTERN_FRAMELOCK_FUZZY = "C7 43 00 00 00 00 00 4C 89 AB"; // C7 43 ?? ?? ?? ?? ?? 4C 89 AB
internal const string PATTERN_FRAMELOCK_FUZZY_MASK = "xx?????xxx";
internal const int PATTERN_FRAMELOCK_FUZZY_OFFSET = 3;
/**
Reference pointer pFrametimeRunningSpeed to speed table entry that gets used in calculations. Add or remove multiplications of 4bytes to pFrametimeRunningSpeed address to use a higher or lower <float>fFrametimeCriticalRunningSpeed from table
fFrametimeCriticalRunningSpeed should be roughly half the frame rate: 30 @ 60FPS limit, 50 @ 100FPS limit...
00000001407D4E08 | F3:0F5905 90309202 | mulss xmm0,dword ptr ds:[1430F7EA0] | pFrametimeRunningSpeed->fFrametimeCriticalRunningSpeed
*/
internal const string PATTERN_FRAMELOCK_SPEED_FIX = "F3 0F 59 05 00 30 92 02"; // F3 0F 59 05 ?? 30 92 02 | 0F 51 C2 F3 0F 59 05 ?? ?? ?? ?? 0F 2F F8
internal const string PATTERN_FRAMELOCK_SPEED_FIX_MASK = "xxxx?xxx";
internal const int PATTERN_FRAMELOCK_SPEED_FIX_OFFSET = 4;
/**
00000001430F7E10
Key: Patch to pFrametimeRunningSpeed last byte
Value: Value resolve in float table from pFrametimeRunningSpeed->fFrametimeCriticalRunningSpeed
Hardcoded cause lazy -> if anyone knows how the table is calculated then tell me and I'll buy you a beer
*/
internal static Dictionary<byte[], float> PATCH_FRAMELOCK_SPEED_FIX_MATRIX = new Dictionary<byte[], float>
{
{ new byte[1] {0x68}, 15f },
{ new byte[1] {0x6C}, 16f },
{ new byte[1] {0x70}, 16.6667f },
{ new byte[1] {0x74}, 18f },
{ new byte[1] {0x78}, 18.6875f },
{ new byte[1] {0x7C}, 18.8516f },
{ new byte[1] {0x80}, 20f },
{ new byte[1] {0x84}, 24f },
{ new byte[1] {0x88}, 25f },
{ new byte[1] {0x8C}, 27.5f },
{ new byte[1] {0x90}, 30f },
{ new byte[1] {0x94}, 32f },
{ new byte[1] {0x98}, 38.5f },
{ new byte[1] {0x9C}, 40f },
{ new byte[1] {0xA0}, 48f },
{ new byte[1] {0xA4}, 49.5f },
{ new byte[1] {0xA8}, 50f },
{ new byte[1] {0xAC}, 57.2958f },
{ new byte[1] {0xB0}, 60f },
{ new byte[1] {0xB4}, 64f },
{ new byte[1] {0xB8}, 66.75f },
{ new byte[1] {0xBC}, 67f },
{ new byte[1] {0xC0}, 78.8438f },
{ new byte[1] {0xC4}, 80f },
{ new byte[1] {0xC8}, 84f },
{ new byte[1] {0xCC}, 90f },
{ new byte[1] {0xD0}, 93.8f },
{ new byte[1] {0xD4}, 100f },
{ new byte[1] {0xD8}, 120f },
{ new byte[1] {0xDC}, 127f },
{ new byte[1] {0xE0}, 128f },
{ new byte[1] {0xE4}, 130f },
{ new byte[1] {0xE8}, 140f },
{ new byte[1] {0xEC}, 144f },
{ new byte[1] {0xF0}, 150f }
};
internal static byte[] PATCH_FRAMELOCK_SPEED_FIX_DISABLE = new byte[1] { 0x90 }; // 30f
/// <summary>
/// Finds closest valid speed fix value for a frame rate limit.
/// </summary>
/// <param name="frameLimit">The set frame rate limit.</param>
/// <returns>The byte patch of the closest speed fix.</returns>
internal static byte[] FindSpeedFixForRefreshRate(int frameLimit)
{
float idealSpeedFix = frameLimit / 2f;
KeyValuePair<byte[], float> closestSpeedFix = new KeyValuePair<byte[], float>(PATCH_FRAMELOCK_SPEED_FIX_DISABLE, 30f);
foreach (var speedFix in PATCH_FRAMELOCK_SPEED_FIX_MATRIX.OrderBy(kvp => kvp.Value))
{
if (Math.Abs(idealSpeedFix - speedFix.Value) < Math.Abs(idealSpeedFix - closestSpeedFix.Value))
closestSpeedFix = speedFix;
}
return closestSpeedFix.Key;
}
/**
Reference pointer pCurrentResolutionWidth to <int>iInternalGameWidth (and <int>iInternalGameHeight which is +4 bytes)
000000014114AC85 | 0F57D2 | xorps xmm2,xmm2 |
000000014114AC88 | 890D 92147D02 | mov dword ptr ds:[14391C120],ecx | pCurrentResolutionWidth->iInternalGameWidth
000000014114AC8E | 0F57C9 | xorps xmm1,xmm1 |
000000014114AC91 | 8915 8D147D02 | mov dword ptr ds:[14391C124],edx | pCurrentResolutionHeight->iInternalGameHeight
*/
internal const string PATTERN_RESOLUTION_POINTER = "0F 57 D2 89 0D 00 00 00 00 0F 57 C9"; // 0F 57 D2 89 0D ?? ?? ?? ?? 0F 57 C9
internal const string PATTERN_RESOLUTION_POINTER_MASK = "xxxxx????xxx";
internal const int PATTERN_RESOLUTION_POINTER_OFFSET = 3;
internal const int PATTERN_RESOLUTION_POINTER_INSTRUCTION_LENGTH = 6;
/**
DATA SECTION. All resolutions are listed in memory as <int>width1 <int>height1 <int>width2 <int>height2 ...
Overwrite an unused one with desired new one. Some glitches, 1920x1080 and 1280x720 works best
*/
internal const string PATTERN_RESOLUTION_DEFAULT = "80 07 00 00 38 04 00 00"; // 1920x1080
internal const string PATTERN_RESOLUTION_DEFAULT_720 = "40 06 00 00 84 03 00 00"; // 1280x720
internal const string PATTERN_RESOLUTION_DEFAULT_MASK = "xxxxxxxx";
internal static byte[] PATCH_RESOLUTION_DEFAULT_DISABLE = new byte[8] { 0x80, 0x07, 0x00, 0x00, 0x38, 0x04, 0x00, 0x00 };
internal static byte[] PATCH_RESOLUTION_DEFAULT_DISABLE_720 = new byte[8] { 0x40, 0x06, 0x00, 0x00, 0x84, 0x03, 0x00, 0x00 };
/**
Conditional jump instruction that determines if 16/9 scaling for game is enforced or not, overwrite with non conditional JMP so widescreen won't get clinched
000000014012967A | 74 47 | je sekiro.1401296C3 | conditional jump
000000014012967C | 47:8B94C7 1C020000 | mov r10d,dword ptr ds:[r15+r8*8+21C] | start of long resolution scaling calculation method within jump
*/
internal const string PATTERN_RESOLUTION_SCALING_FIX = "47 47 8B 94 C7 1C 02 00 00"; // 47 47 8B 94 C7 1C 02 00 00
internal const string PATTERN_RESOLUTION_SCALING_FIX_MASK = "xxxxxxxxx";
internal const int PATTERN_RESOLUTION_SCALING_FIX_OFFSET = -1;
internal static byte[] PATCH_RESOLUTION_SCALING_FIX_ENABLE = new byte[1] { 0xEB }; // JMP
internal static byte[] PATCH_RESOLUTION_SCALING_FIX_DISABLE = new byte[1] { 0x74 }; // JE
/**
Reference pointer pFovTableEntry to FOV entry in game FOV table that gets used in FOV calculations. Overwrite pFovTableEntry address to use a higher or lower <float>fFOV from table
0000000140739548 | F3:0F1008 | movss xmm1,dword ptr ds:[rax] |
000000014073954C | F3:0F590D 0CE79B02 | mulss xmm1,dword ptr ds:[1430F7C60] | pFovTableEntry->fFov
*/
// credits to 'jackfuste' for original offset
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;
/**
00000001430F7C60
Key: Patch to pFovTableEntry last 2 bytes
Value: Value resolve in float table from pFovTableEntry->fFov
*/
internal static Dictionary<byte[], string> PATCH_FOVSETTING_MATRIX = new Dictionary<byte[], string>
{
{ 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 static byte[] PATCH_FOVSETTING_DISABLE = new byte[2] { 0x0C, 0xE7 }; // + 0%
/**
Reference pointer pPlayerStatsRelated to PlayerStats pointer, offset in struct to <int>iPlayerDeaths
00000001407AAC92 | 0FB648 7A | movzx ecx,byte ptr ds:[rax+7A] |
00000001407AAC96 | 888B F7000000 | mov byte ptr ds:[rbx+F7],cl |
00000001407AAC9C | 48:8B05 4DD03903 | mov rax,qword ptr ds:[143B47CF0] |
00000001407AACA3 | 8B88 8C000000 | mov ecx,dword ptr ds:[rax+8C] |
00000001407AACA9 | 898B F8000000 | mov dword ptr ds:[rbx+F8],ecx |
00000001407AACAF | 48:8B05 3AD03903 | mov rax,qword ptr ds:[143B47CF0] | pPlayerStatsRelated->[PlayerStats+0x90]->iPlayerDeaths
00000001407AACB6 | 8B88 90000000 | mov ecx,dword ptr ds:[rax+90] | offset pPlayerStats->iPlayerDeaths
*/
// credits to 'Me_TheCat' for original offset
internal const string PATTERN_PLAYER_DEATHS = "0F B6 48 00 88 8B 00 00 00 00 48 8B 05 00 00 00 00 8B 88 00 00 00 00 89 8B 00 00 00 00 48 8B 05 00 00 00 00 8B 88 00 00 00 00"; // 0F B6 48 ?? 88 8B ?? ?? 00 00 48 8B 05 ?? ?? ?? ?? 8B 88 ?? ?? 00 00 89 8B ?? ?? 00 00 48 8B 05 ?? ?? ?? ?? 8B 88 ?? ?? 00 00
internal const string PATTERN_PLAYER_DEATHS_MASK = "xxx?xx??xxxxx????xx??xxxx??xxxxx????xx??xx";
internal const int PATTERN_PLAYER_DEATHS_OFFSET = 29;
internal const int PATTERN_PLAYER_DEATHS_INSTRUCTION_LENGTH = 7;
internal const int PATTERN_PLAYER_DEATHS_POINTER_OFFSET_OFFSET = 9;
/**
Reference pointer pTotalKills to <int>iTotalKills, does not get updated on every kill but mostly on every 2nd, includes own player deaths...
0000000141151838 | 48:8D0D A9A5B302 | lea rcx,qword ptr ds:[143C8BDE8] | pTotalKills->iTotalKills
000000014115183F | 891481 | mov dword ptr ds:[rcx+rax*4],edx |
0000000141151842 | C3 | ret |
*/
// credits to 'Me_TheCat' for original offset
internal const string PATTERN_TOTAL_KILLS = "48 8D 0D 00 00 00 00 89 14 81 C3"; // 48 8D 0D ?? ?? ?? ?? 89 14 81 C3
internal const string PATTERN_TOTAL_KILLS_MASK = "xxx????xxxx";
internal const int PATTERN_TOTAL_KILLS_INSTRUCTION_LENGTH = 7;
/**
Reference pointer pTimeRelated to TimescaleManager pointer, offset in struct to <float>fTimescale which acts as a global speed scale for almost all ingame calculations
0000000141149E87 | 48:8B05 3A24B402 | mov rax,qword ptr ds:[143C8C2C8] | pTimeRelated->[TimescaleManager+0x360]->fTimescale
0000000141149E8E | F3:0F1088 60030000 | movss xmm1,dword ptr ds:[rax+360] | offset TimescaleManager->fTimescale
0000000141149E96 | F3:0F5988 68020000 | mulss xmm1,dword ptr ds:[rax+268] |
*/
// credits to 'Zullie the Witch' for original offset
internal const string PATTERN_TIMESCALE = "48 8B 05 00 00 00 00 F3 0F 10 88 00 00 00 00 F3 0F"; // 48 8B 05 ?? ?? ?? ?? F3 0F 10 88 ?? ?? ?? ?? F3 0F
internal const string PATTERN_TIMESCALE_MASK = "xxx????xxxx????xx";
internal const int PATTERN_TIMESCALE_INSTRUCTION_LENGTH = 7;
internal const int PATTERN_TIMESCALE_POINTER_OFFSET_OFFSET = 11;
/**
Reference pointer pPlayerStructRelated1 to 4 more pointers up to player data class, offset in struct to <float>fTimescalePlayer which acts as a speed scale for the player character
00000001406BF1D7 | 48:8B1D 128C4A03 | mov rbx,qword ptr ds:[143B67DF0] | pPlayerStructRelated1->[pPlayerStructRelated2+0x88]->[pPlayerStructRelated3+0x1FF8]->[pPlayerStructRelated4+0x28]->[pPlayerStructRelated5+0xD00]->fTimescalePlayer
00000001406BF1DE | 48:85DB | test rbx,rbx |
00000001406BF1E1 | 74 3C | je sekiro.1406BF21F |
00000001406BF1E3 | 8B17 | mov edx,dword ptr ds:[rdi] |
*/
// credits to 'Zullie the Witch' for original offset
internal const string PATTERN_TIMESCALE_PLAYER = "48 8B 1D 00 00 00 00 48 85 DB 74 3C 8B 17"; // 48 8B 1D ?? ?? ?? ?? 48 85 DB 74 3C 8B 17
internal const string PATTERN_TIMESCALE_PLAYER_MASK = "xxx????xxxxxxx";
internal const int PATTERN_TIMESCALE_PLAYER_INSTRUCTION_LENGTH = 7;
internal const int PATTERN_TIMESCALE_POINTER2_OFFSET = 0x88;
internal const int PATTERN_TIMESCALE_POINTER3_OFFSET = 0x1FF8;
internal const int PATTERN_TIMESCALE_POINTER4_OFFSET = 0x28;
internal const int PATTERN_TIMESCALE_POINTER5_OFFSET = 0xD00;
// game stat values by Me_TheCat
internal const string PATTERN_PLAYER_DEATHS = "8B 88 90 00 00 00 89 8B FC 00 00 00 48 8B";
internal const string PATTERN_PLAYER_DEATHS_MASK = "xxxxxxxxx???xx";
internal const string PATTERN_TOTAL_KILLS = "48 8D 0D 00 00 00 00 89 14 81 C3";
internal const string PATTERN_TOTAL_KILLS_MASK = "xxx????xxxx";
internal const int PATTERN_TOTAL_KILLS_OFFSET = 7;
}
}
}
}

View file

@ -1,117 +1,109 @@
<Window x:Class="SekiroFpsUnlockAndMore.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SekiroFpsUnlockAndMore"
mc:Ignorable="d"
Title="Sekiro FPS Unlocker and more v1.1.0" Width="Auto" Height="Auto" SizeToContent="WidthAndHeight" ResizeMode="CanMinimize" Loaded="Window_Loaded" Closing="Window_Closing">
<Grid Background="#FFF9F9F9">
<StackPanel Margin="10,10,10,0" Width="290" Height="Auto">
<DockPanel LastChildFill="False">
<CheckBox x:Name="cbFramelock" DockPanel.Dock="Left" Margin="0,0,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Frame rate lock:" IsChecked="True" Checked="CbFramelock_Check_Handler" Unchecked="CbFramelock_Check_Handler" />
<TextBox x:Name="tbFramelock" DockPanel.Dock="Right" Margin="0,0,0,0" Width="116" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Text="144" MaxLength="3" PreviewTextInput="Numeric_PreviewTextInput" DataObject.Pasting="Numeric_PastingHandler" />
</DockPanel>
<DockPanel Margin="0,5,0,0" LastChildFill="False">
<CheckBox x:Name="cbAddResolution" DockPanel.Dock="Left" Margin="0,0,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Add custom resolution:" Checked="CbAddResolution_Check_Handler" Unchecked="CbAddResolution_Check_Handler" />
<TextBox x:Name="tbHeight" DockPanel.Dock="Right" Margin="0,0,0,0" Width="50" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Text="1080" MaxLength="4" PreviewTextInput="Numeric_PreviewTextInput" DataObject.Pasting="Numeric_PastingHandler" />
<Label DockPanel.Dock="Right" Margin="0,0,0,0" Height="25" FontSize="14 px" Content="x" />
<TextBox x:Name="tbWidth" DockPanel.Dock="Right" Margin="0,0,0,0" Width="50" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Text="2560" MaxLength="4" PreviewTextInput="Numeric_PreviewTextInput" DataObject.Pasting="Numeric_PastingHandler" />
</DockPanel>
<DockPanel Margin="0,5,0,0" LastChildFill="False">
<CheckBox x:Name="cbFov" DockPanel.Dock="Left" Margin="0,0,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Increase FOV by:" Checked="CbFov_Check_Handler" Unchecked="CbFov_Check_Handler" />
<ComboBox x:Name="cbSelectFov" DockPanel.Dock="Right" Margin="0,0,0,0" Width="116" Height="25" FontSize="14 px" SelectedValuePath="Key" DisplayMemberPath="Value" DropDownClosed="CbSelectFov_DropDownClosed"/>
</DockPanel>
<StackPanel Margin="0,5,0,0" Orientation="Horizontal">
<CheckBox x:Name="cbBorderless" DockPanel.Dock="Left" Margin="0,0,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Borderless window" IsEnabled="False" Checked="CbBorderless_Checked" Unchecked="CbBorderless_Unchecked"/>
<CheckBox x:Name="cbBorderlessStretch" DockPanel.Dock="Right" Margin="10,0,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Fullscreen stretch" IsEnabled="False" Checked="CbBorderlessStretch_Check_Handler" Unchecked="CbBorderlessStretch_Check_Handler" />
</StackPanel>
<DockPanel Margin="0,5,0,0" LastChildFill="False">
<CheckBox x:Name="cbLogStats" Content="Log stats (Deaths, Enemies killed)" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" FontSize="14 px" Checked="CbStatChanged" Unchecked="CbStatChanged" />
</DockPanel>
<Expander x:Name="exGameMods" Margin="-3,4,0,0" Height="Auto" FontSize="14 px" Header="Game modifications" IsExpanded="False">
<Grid Margin="3,0,0,0" Background="#FFF9F9F9">
<StackPanel Width="Auto" Height="Auto">
<DockPanel Margin="0,3,0,0" LastChildFill="False">
<CheckBox x:Name="cbGameSpeed" DockPanel.Dock="Left" Margin="0,0,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Game speed (%):" Checked="CbGameSpeed_Check_Handler" Unchecked="CbGameSpeed_Check_Handler" />
<Button x:Name="bGs100" DockPanel.Dock="Right" Content="100" Margin="0,0,0,0" Width="30" Height="25" FontSize="14 px" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
Focusable="False" Click="BGs100_Click" />
<Button x:Name="bGsHigher" DockPanel.Dock="Right" Content="&gt;" Margin="0,0,3,0" Width="25" Height="25" FontSize="14 px" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
Focusable="False" Click="BGsHigher_Click" />
<TextBox x:Name="tbGameSpeed" DockPanel.Dock="Right" Margin="0,0,3,0" Width="30" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Text="100" TextAlignment="Center" MaxLength="3" PreviewTextInput="Numeric_PreviewTextInput" DataObject.Pasting="Numeric_PastingHandler" />
<Button x:Name="bGsLower" DockPanel.Dock="Right" Content="&lt;" Margin="0,0,3,0" Width="25" Height="25" FontSize="14 px" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
Focusable="False" Click="BGsLower_Click" />
<Button x:Name="bGs0" DockPanel.Dock="Right" Content="0" Margin="0,0,3,0" Width="30" Height="25" FontSize="14 px" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
Focusable="False" Click="BGs0_Click" />
</DockPanel>
<DockPanel Margin="0,5,0,0" LastChildFill="False">
<CheckBox x:Name="cbPlayerSpeed" DockPanel.Dock="Left" Margin="0,0,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Player speed (%):" ToolTip="To enable this start the game and load a save, then start the patcher." Checked="CbPlayerSpeed_Check_Handler" Unchecked="CbPlayerSpeed_Check_Handler" />
<Button x:Name="bPs100" DockPanel.Dock="Right" Content="100" Margin="0,0,0,0" Width="30" Height="25" FontSize="14 px" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
Focusable="False" Click="BPs100_Click" />
<Button x:Name="bPsHigher" DockPanel.Dock="Right" Content="&gt;" Margin="0,0,3,0" Width="25" Height="25" FontSize="14 px" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
Focusable="False" Click="BPsHigher_Click" />
<TextBox x:Name="tbPlayerSpeed" DockPanel.Dock="Right" Margin="0,0,3,0" Width="30" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Text="100" TextAlignment="Center" MaxLength="3" PreviewTextInput="Numeric_PreviewTextInput" DataObject.Pasting="Numeric_PastingHandler" />
<Button x:Name="bPsLower" DockPanel.Dock="Right" Content="&lt;" Margin="0,0,3,0" Width="25" Height="25" FontSize="14 px" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
Focusable="False" Click="BPsLower_Click" />
<Button x:Name="bPs0" DockPanel.Dock="Right" Content="0" Margin="0,0,3,0" Width="30" Height="25" FontSize="14 px" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
Focusable="False" Click="BPs0_Click" />
</DockPanel>
</StackPanel>
</Grid>
</Expander>
<Button x:Name="bPatch" Margin="0,7,0,0" Width="290" Height="30" FontSize="14 px" IsEnabled="False" Content="Patch game (CTRL + P)" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
Focusable="False" Click="BPatch_Click" />
<TextBox x:Name="tbStatus" Margin="0,5,0,0" Width="290" Height="25" FontSize="14 px" FontWeight="Bold" Text="waiting for game..." TextAlignment="Center" TextWrapping="NoWrap" IsEnabled="False" />
<TextBlock HorizontalAlignment="Left" TextWrapping="WrapWithOverflow" Margin="2,5,2,0" VerticalAlignment="Top" FontSize="11 px" IsEnabled="False">
<TextBlock.Inlines>
<Run FontWeight="Bold" Foreground="#FF0046FF">This patcher does not modify game files, you have to start it every time.</Run>
<Run FontWeight="Bold">If your monitor is 60 Hz you will have to use</Run>
<Run FontWeight="Bold" Foreground="#FFF00000">fullscreen mode and force disable VSYNC</Run>
<Run FontWeight="Bold">with Nvidia Control panel or AMD Radeon Settings to get frame rate unlock working.</Run>
<Run>To avoid stuttering it's recommended to disable VSYNC even with a 144 Hz monitor.</Run>
<Run FontWeight="Bold">If your monitor is >60 Hz you will have to use</Run>
<Run FontWeight="Bold" Foreground="#FFF00000">borderless window mode or force the game to run on highest available refresh rate</Run>
<Run FontWeight="Bold">using Nvidia Control panel or AMD Radeon Settings.</Run>
<Run>Borderless window mode requires "Windowed" in game settings first.</Run>
<Run FontWeight="Bold">Custom resolution adds 21/9 support and overwrites a default resolution, hud will be limited to 16/9.</Run>
<Run>Borderless window mode with custom resolution requires you to patch and set resolution first, then add borderless patch afterwards.</Run>
<Run FontWeight="Bold">To enable Player Speed modification you have to be ingame first.</Run>
<Run FontWeight="Bold" Foreground="#FFF00000">See the link below for detailed information, guides, GSYNC support and an AMD fix.</Run>
</TextBlock.Inlines>
</TextBlock>
<Label HorizontalAlignment="Right" FontSize="12 px">
<Hyperlink NavigateUri="https://github.com/uberhalit/SekiroFpsUnlockAndMore" RequestNavigate="Hyperlink_RequestNavigate">
v1.1.0 - by uberhalit
</Hyperlink>
</Label>
<StatusBar DockPanel.Dock="Bottom" Margin="-10,0,-10,0" VerticalAlignment="Bottom">
<StatusBar.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<Window x:Class="SekiroFpsUnlockAndMore.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SekiroFpsUnlockAndMore"
mc:Ignorable="d"
Title="Sekiro FPS Unlocker and more v1.1.1" Width="Auto" Height="Auto" SizeToContent="WidthAndHeight" ResizeMode="CanMinimize" Loaded="Window_Loaded" Closing="Window_Closing">
<StatusBarItem Grid.Column="0">
<TextBlock Name="sbDeathCount">
<Run Text="Deaths:"/>
<Run Text="{Binding Deaths}"/>
</TextBlock>
</StatusBarItem>
<Separator Grid.Column="1" />
<StatusBarItem Grid.Column="2">
<TextBlock Name="sbKillCount">
<Run Text="Kills:"/>
<Run Text="{Binding Kills}"/>
</TextBlock>
</StatusBarItem>
</StatusBar>
</StackPanel>
</Grid>
</Window>
<Grid Background="#FFF9F9F9">
<DockPanel>
<StackPanel DockPanel.Dock="Top" Margin="10,10,10,0" Width="290" Height="Auto">
<DockPanel LastChildFill="False">
<CheckBox x:Name="cbFramelock" DockPanel.Dock="Left" Margin="0,0,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Frame rate lock:" IsChecked="True" Checked="CbFramelock_Check_Handler" Unchecked="CbFramelock_Check_Handler" />
<TextBox x:Name="tbFramelock" DockPanel.Dock="Right" Margin="0,0,0,0" Width="116" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Text="144" MaxLength="3" PreviewTextInput="Numeric_PreviewTextInput" DataObject.Pasting="Numeric_PastingHandler" />
</DockPanel>
<DockPanel Margin="0,5,0,0" LastChildFill="False">
<CheckBox x:Name="cbAddResolution" DockPanel.Dock="Left" Margin="0,0,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Add custom resolution:" Checked="CbAddResolution_Check_Handler" Unchecked="CbAddResolution_Check_Handler" />
<TextBox x:Name="tbHeight" DockPanel.Dock="Right" Margin="0,0,0,0" Width="50" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Text="1080" MaxLength="4" PreviewTextInput="Numeric_PreviewTextInput" DataObject.Pasting="Numeric_PastingHandler" />
<Label DockPanel.Dock="Right" Margin="0,0,0,0" Height="25" FontSize="14 px" Content="x" />
<TextBox x:Name="tbWidth" DockPanel.Dock="Right" Margin="0,0,0,0" Width="50" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Text="2560" MaxLength="4" PreviewTextInput="Numeric_PreviewTextInput" DataObject.Pasting="Numeric_PastingHandler" />
</DockPanel>
<DockPanel Margin="0,5,0,0" LastChildFill="False">
<CheckBox x:Name="cbFov" DockPanel.Dock="Left" Margin="0,0,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Increase FOV by:" Checked="CbFov_Check_Handler" Unchecked="CbFov_Check_Handler" />
<ComboBox x:Name="cbSelectFov" DockPanel.Dock="Right" Margin="0,0,0,0" Width="116" Height="25" FontSize="14 px" SelectedValuePath="Key" DisplayMemberPath="Value" DropDownClosed="CbSelectFov_DropDownClosed"/>
</DockPanel>
<StackPanel Margin="0,5,0,0" Orientation="Horizontal">
<CheckBox x:Name="cbBorderless" DockPanel.Dock="Left" Margin="0,0,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Borderless window" ToolTip="To enable this set 'Windowed' mode in game options first." IsEnabled="False" Checked="CbBorderless_Checked" Unchecked="CbBorderless_Unchecked"/>
<CheckBox x:Name="cbBorderlessStretch" DockPanel.Dock="Right" Margin="10,0,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Fullscreen stretch" IsEnabled="False" Checked="CbBorderlessStretch_Check_Handler" Unchecked="CbBorderlessStretch_Check_Handler" />
</StackPanel>
<CheckBox x:Name="cbLogStats" Margin="0,5,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Log stats to file (Deaths, Kills)" ToolTip="Check the guide on how to display these with OBS." Checked="CbStatChanged" Unchecked="CbStatChanged" />
<Expander x:Name="exGameMods" Margin="-3,5,0,0" Height="Auto" FontSize="14 px" Header="Game modifications" IsExpanded="False">
<Grid Margin="3,0,0,0" Background="#FFF9F9F9">
<StackPanel Width="Auto" Height="Auto">
<DockPanel Margin="0,3,0,0" LastChildFill="False">
<CheckBox x:Name="cbGameSpeed" DockPanel.Dock="Left" Margin="0,0,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Game speed (%):" Checked="CbGameSpeed_Check_Handler" Unchecked="CbGameSpeed_Check_Handler" />
<Button x:Name="bGs100" DockPanel.Dock="Right" Content="100" Margin="0,0,0,0" Width="30" Height="25" FontSize="14 px" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
Focusable="False" Click="BGs100_Click" />
<Button x:Name="bGsHigher" DockPanel.Dock="Right" Content="&gt;" Margin="0,0,3,0" Width="25" Height="25" FontSize="14 px" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
Focusable="False" Click="BGsHigher_Click" />
<TextBox x:Name="tbGameSpeed" DockPanel.Dock="Right" Margin="0,0,3,0" Width="30" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Text="100" TextAlignment="Center" MaxLength="3" PreviewTextInput="Numeric_PreviewTextInput" DataObject.Pasting="Numeric_PastingHandler" />
<Button x:Name="bGsLower" DockPanel.Dock="Right" Content="&lt;" Margin="0,0,3,0" Width="25" Height="25" FontSize="14 px" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
Focusable="False" Click="BGsLower_Click" />
<Button x:Name="bGs0" DockPanel.Dock="Right" Content="0" Margin="0,0,3,0" Width="30" Height="25" FontSize="14 px" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
Focusable="False" Click="BGs0_Click" />
</DockPanel>
<DockPanel Margin="0,5,0,0" LastChildFill="False">
<CheckBox x:Name="cbPlayerSpeed" DockPanel.Dock="Left" Margin="0,0,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Player speed (%):" ToolTip="To enable this start the game and load a save, then tick the checkbox." Checked="CbPlayerSpeed_Check_Handler" Unchecked="CbPlayerSpeed_Check_Handler" />
<Button x:Name="bPs100" DockPanel.Dock="Right" Content="100" Margin="0,0,0,0" Width="30" Height="25" FontSize="14 px" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
Focusable="False" Click="BPs100_Click" />
<Button x:Name="bPsHigher" DockPanel.Dock="Right" Content="&gt;" Margin="0,0,3,0" Width="25" Height="25" FontSize="14 px" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
Focusable="False" Click="BPsHigher_Click" />
<TextBox x:Name="tbPlayerSpeed" DockPanel.Dock="Right" Margin="0,0,3,0" Width="30" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Text="100" TextAlignment="Center" MaxLength="3" PreviewTextInput="Numeric_PreviewTextInput" DataObject.Pasting="Numeric_PastingHandler" />
<Button x:Name="bPsLower" DockPanel.Dock="Right" Content="&lt;" Margin="0,0,3,0" Width="25" Height="25" FontSize="14 px" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
Focusable="False" Click="BPsLower_Click" />
<Button x:Name="bPs0" DockPanel.Dock="Right" Content="0" Margin="0,0,3,0" Width="30" Height="25" FontSize="14 px" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
Focusable="False" Click="BPs0_Click" />
</DockPanel>
</StackPanel>
</Grid>
</Expander>
<Button x:Name="bPatch" Margin="0,7,0,0" Width="290" Height="30" FontSize="14 px" IsEnabled="False" Content="Patch game (CTRL + P)" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
Focusable="False" Click="BPatch_Click" />
<TextBox x:Name="tbStatus" Margin="0,5,0,0" Width="290" Height="25" FontSize="14 px" FontWeight="Bold" Text="waiting for game..." TextAlignment="Center" TextWrapping="NoWrap" IsEnabled="False" />
<TextBlock Margin="2,5,2,0" FontSize="11 px" TextWrapping="WrapWithOverflow" IsEnabled="False">
<TextBlock.Inlines>
<Run FontWeight="Bold" Foreground="#FF0046FF">This patcher does not modify game files, you have to start it every time.</Run>
<Run FontWeight="Bold">If your monitor is 60 Hz you will have to use</Run>
<Run FontWeight="Bold" Foreground="#FFF00000">fullscreen mode and force disable VSYNC</Run>
<Run FontWeight="Bold">with Nvidia Control panel or AMD Radeon Settings to get frame rate unlock working.</Run>
<Run>To avoid stuttering it's recommended to disable VSYNC even with a 144 Hz monitor.</Run>
<Run FontWeight="Bold">If your monitor is >60 Hz you will have to use</Run>
<Run FontWeight="Bold" Foreground="#FFF00000">borderless window mode or force the game to run on highest available refresh rate</Run>
<Run FontWeight="Bold">using Nvidia Control panel or AMD Radeon Settings.</Run>
<Run>Borderless window mode requires "Windowed" in game settings first.</Run>
<Run FontWeight="Bold">Custom resolution adds 21/9 support and overwrites a default resolution, hud will be limited to 16/9.</Run>
<Run>Borderless window mode with custom resolution requires you to patch and set resolution first, then add borderless patch afterwards.</Run>
<Run FontWeight="Bold">To enable Player Speed modification you have to be ingame first.</Run>
<Run FontWeight="Bold" Foreground="#FFF00000">See the link below for detailed information, guides, GSYNC support and an AMD fix.</Run>
</TextBlock.Inlines>
</TextBlock>
<Label HorizontalAlignment="Right" FontSize="12 px">
<Hyperlink NavigateUri="https://github.com/uberhalit/SekiroFpsUnlockAndMore" RequestNavigate="Hyperlink_RequestNavigate">
v1.1.1 - by uberhalit
</Hyperlink>
</Label>
</StackPanel>
<StatusBar DockPanel.Dock="Bottom" Height="25">
<StatusBarItem DockPanel.Dock="Left">
<TextBlock Name="sbDeathCount">
<Run Text="Deaths:" />
<Run Text="{Binding Deaths}" />
</TextBlock>
</StatusBarItem>
<Separator Margin="5,4,5,4" Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}"/>
<StatusBarItem DockPanel.Dock="Left">
<TextBlock Name="sbKillCount">
<Run Text="Kills:" />
<Run Text="{Binding Kills}" />
</TextBlock>
</StatusBarItem>
<Separator Margin="5,4,5,4" Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}"/>
<StatusBarItem DockPanel.Dock="Right" HorizontalAlignment="Right">
<TextBlock Name="sbStatus" />
</StatusBarItem>
</StatusBar>
</DockPanel>
</Grid>
</Window>

File diff suppressed because it is too large Load diff

View file

@ -21,5 +21,5 @@ using System.Runtime.InteropServices;
ResourceDictionaryLocation.SourceAssembly
)]
[assembly: AssemblyVersion("1.1.0.1")]
[assembly: AssemblyFileVersion("1.1.0.1")]
[assembly: AssemblyVersion("1.1.1.0")]
[assembly: AssemblyFileVersion("1.1.1.0")]

View file

@ -63,7 +63,7 @@
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="SettingsService.cs" />
<Compile Include="StatViewModel.cs" />
<Compile Include="StatusViewModel.cs" />
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>

View file

@ -1,112 +1,109 @@
using System;
using System.IO;
using System.Windows;
using System.Xml.Serialization;
namespace SekiroFpsUnlockAndMore
{
[XmlRoot("SekiroFpsUnlockAndMore")]
[Serializable]
public class SettingsService
{
private readonly string sConfigurationPath = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) + @"\config.xml";
/// <summary>
/// Read and store settings here.
/// </summary>
public SettingsService settings;
/**
* Settings definition
*/
[XmlElement]
public bool cbFramelock { get; set; }
[XmlElement]
public int tbFramelock { get; set; }
[XmlElement]
public bool cbAddResolution { get; set; }
[XmlElement]
public int tbWidth { get; set; }
[XmlElement]
public int tbHeight { get; set; }
[XmlElement]
public bool cbFov { get; set; }
[XmlElement]
public int cbSelectFov { get; set; }
[XmlElement]
public bool cbBorderless { get; set; }
[XmlElement]
public bool cbBorderlessStretch { get; set; }
[XmlElement]
public bool exGameMods { get; set; }
[XmlElement]
public bool cbGameSpeed { get; set; }
[XmlElement]
public int tbGameSpeed { get; set; }
[XmlElement]
public bool cbPlayerSpeed { get; set; }
[XmlElement]
public int tbPlayerSpeed { get; set; }
[XmlElement]
public bool cbLogStats { get; set; }
using System;
using System.IO;
using System.Windows;
using System.Xml.Serialization;
public SettingsService() { }
/// <summary>
/// Create a settings provider to load and save settings.
/// </summary>
/// <param name="settingsFilePath">The file path to the settings file.</param>
public SettingsService(string settingsFilePath = null)
{
if (settingsFilePath != null)
{
sConfigurationPath = settingsFilePath;
settings = new SettingsService();
}
}
/// <summary>
/// Load settings from file into settings property.
/// </summary>
/// <returns></returns>
internal bool Load()
{
if (!File.Exists(sConfigurationPath)) return false;
XmlSerializer xmlSerializer = new XmlSerializer(typeof(SettingsService));
using (StreamReader streamReader = new StreamReader(sConfigurationPath))
{
try
{
settings = (SettingsService)xmlSerializer.Deserialize(streamReader);
return true;
}
catch (Exception ex)
{
MessageBox.Show("Error while loading configuration file:\n" + ex.Message, "Sekiro FPS Unlocker and more");
}
}
return false;
}
/// <summary>
/// Save settings from settings property to file.
/// </summary>
internal void Save()
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(SettingsService));
using (StreamWriter streamReader = new StreamWriter(sConfigurationPath))
{
try
{
xmlSerializer.Serialize(streamReader, settings);
}
catch (Exception ex)
{
MessageBox.Show("Error while writing configuration file:\n" + ex.Message, "Sekiro FPS Unlocker and more");
}
}
}
}
}
namespace SekiroFpsUnlockAndMore
{
[XmlRoot("SekiroFpsUnlockAndMore")]
[Serializable]
public class ApplicationSettings
{
/**
* Settings definition
*/
[XmlElement]
public bool cbFramelock { get; set; }
[XmlElement]
public int tbFramelock { get; set; }
[XmlElement]
public bool cbAddResolution { get; set; }
[XmlElement]
public int tbWidth { get; set; }
[XmlElement]
public int tbHeight { get; set; }
[XmlElement]
public bool cbFov { get; set; }
[XmlElement]
public int cbSelectFov { get; set; }
[XmlElement]
public bool cbBorderless { get; set; }
[XmlElement]
public bool cbBorderlessStretch { get; set; }
[XmlElement]
public bool cbLogStats { get; set; }
[XmlElement]
public bool exGameMods { get; set; }
[XmlElement]
public bool cbGameSpeed { get; set; }
[XmlElement]
public int tbGameSpeed { get; set; }
[XmlElement]
public bool cbPlayerSpeed { get; set; }
[XmlElement]
public int tbPlayerSpeed { get; set; }
}
public class SettingsService
{
private readonly string _sConfigurationPath = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) + @"\config.xml";
/// <summary>
/// Read and store settings here.
/// </summary>
public ApplicationSettings ApplicationSettings;
/// <summary>
/// Create a settings provider to load and save settings.
/// </summary>
/// <param name="settingsFilePath">The file path to the settings file.</param>
public SettingsService(string settingsFilePath = null)
{
if (settingsFilePath != null) _sConfigurationPath = settingsFilePath;
ApplicationSettings = new ApplicationSettings();
}
/// <summary>
/// Load settings from file into settings property.
/// </summary>
/// <returns></returns>
internal bool Load()
{
if (!File.Exists(_sConfigurationPath)) return false;
XmlSerializer xmlSerializer = new XmlSerializer(typeof(ApplicationSettings));
using (StreamReader streamReader = new StreamReader(_sConfigurationPath))
{
try
{
ApplicationSettings = (ApplicationSettings)xmlSerializer.Deserialize(streamReader);
return true;
}
catch (Exception ex)
{
MessageBox.Show("Error while loading configuration file:\n" + ex.Message, "Sekiro FPS Unlocker and more");
}
}
return false;
}
/// <summary>
/// Save settings from settings property to file.
/// </summary>
internal void Save()
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(ApplicationSettings));
using (StreamWriter streamReader = new StreamWriter(_sConfigurationPath))
{
try
{
xmlSerializer.Serialize(streamReader, ApplicationSettings);
}
catch (Exception ex)
{
MessageBox.Show("Error while writing configuration file:\n" + ex.Message, "Sekiro FPS Unlocker and more");
}
}
}
}
}

View file

@ -1,43 +1,39 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
namespace SekiroFpsUnlockAndMore
{
/// <summary>
/// For Status bar display
/// </summary>
class StatViewModel : INotifyPropertyChanged
{
private int _deaths = 0;
public int Deaths
{
get { return _deaths; }
set
{
_deaths = value;
OnPropertyChanged(new PropertyChangedEventArgs("Deaths"));
}
}
private int _kills = 0;
public int Kills
{
get { return _kills; }
set
{
_kills = value;
OnPropertyChanged(new PropertyChangedEventArgs("Kills"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
}
}
using System.ComponentModel;
namespace SekiroFpsUnlockAndMore
{
/// <summary>
/// For Status bar display
/// </summary>
class StatusViewModel : INotifyPropertyChanged
{
private int _deaths = 0;
public int Deaths
{
get { return _deaths; }
set
{
_deaths = value;
OnPropertyChanged(new PropertyChangedEventArgs("Deaths"));
}
}
private int _kills = 0;
public int Kills
{
get { return _kills; }
set
{
_kills = value;
OnPropertyChanged(new PropertyChangedEventArgs("Kills"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
}
}