Compare commits

..

7 commits

Author SHA1 Message Date
uberhalit
d6312c6b0a added legacy support for death penalties 2020-11-03 15:31:17 +01:00
uberhalit
2344e280d4 updated to game version 1.5.0.0 2020-11-03 14:58:47 +01:00
uberhalit
53fd55c3b9 added support for windows' high contrast mode
fixed a file race condition with OBS
2020-02-04 19:39:05 +01:00
uberhalit
f485974b6b added feature to automatically loot enemies 2019-05-04 21:22:52 +02:00
uberhalit
6898302974 fixed readme for version 1.2.4.0 2019-05-02 18:44:23 +02:00
uberhalit
50d043c75a added feature to increase emblem cap on upgrades
changed way to obtain kills, fixes slow stats updates
improved pattern scan algorithm
2019-05-02 18:42:20 +02:00
uberhalit
e158678422 added hotkey to toggle speed modifiers
added game version 1.04 to supported versions
2019-04-26 19:33:12 +02:00
8 changed files with 429 additions and 90 deletions

View file

@ -22,14 +22,17 @@ Patches games memory while running, does not modify any game files. Works with e
* disable camera auto rotate adjustment on movement (intended for mouse users) * disable camera auto rotate adjustment on movement (intended for mouse users)
* disable centering of camera (cam reset) on lock-on if there is no target * disable centering of camera (cam reset) on lock-on if there is no target
* display hidden death/kill counters and optionally log them to file to display in OBS on stream * display hidden death/kill counters and optionally log them to file to display in OBS on stream
* automatically loot enemies
* game modifications * game modifications
* prevent dragonrot from increasing upon death * prevent dragonrot from increasing upon death
* disable death penalties like losing Sen or experience * disable death penalties like losing Sen or experience
* increase spirit emblem capacity on prosthetic upgrades
* global game speed modifier (increase or decrease) * global game speed modifier (increase or decrease)
* player speed modifier (increase or decrease) * player speed modifier (increase or decrease)
* automatically patch game on startup * automatically patch game on startup
* seamlessly switch between windowed, borderless and borderless fullscreen * seamlessly switch between windowed, borderless and borderless fullscreen
* hotkey for patching while in (borderless) window mode * hotkey for patching while in (borderless) window mode
* hotkey for toggling speed modifier while in (borderless) window mode
## Usage ## Usage
@ -68,12 +71,13 @@ The game enforces VSYNC and forces 60 Hz in fullscreen even on 144 Hz monitors s
6. Press the yellow star icon in the menu bar to create a new Profile (1) 6. Press the yellow star icon in the menu bar to create a new Profile (1)
7. [![Vertical sync Off and Preferred refresh rate Highest available](https://camo.githubusercontent.com/531be3614b0742e9065f8c2a19df7e31afcdc7ed/68747470733a2f2f692e696d6775722e636f6d2f6f75664a7239632e706e67)](#) 7. [![Vertical sync Off and Preferred refresh rate Highest available](https://camo.githubusercontent.com/531be3614b0742e9065f8c2a19df7e31afcdc7ed/68747470733a2f2f692e696d6775722e636f6d2f6f75664a7239632e706e67)](#)
8. Name it `Sekiro` and select it in dropdown 8. Name it `Sekiro` and select it in dropdown
9. Press the blue window icon with the plus symbol to add an application to this profile (2) 9. Press the blue window icon with the plus symbol to add an application to this profile (2)
10. Change file type to `Application Absolute Path`, navigate to your `sekiro.exe` and select it 10. Change file type to `Application Absolute Path`, navigate to your `sekiro.exe` and select it
11. [![Application Absolute Path](https://camo.githubusercontent.com/0bb8ace024658dfcb31c5f3347df1505854819ec/68747470733a2f2f692e696d6775722e636f6d2f545669495357562e706e67)](#) 11. [![Application Absolute Path](https://camo.githubusercontent.com/0bb8ace024658dfcb31c5f3347df1505854819ec/68747470733a2f2f692e696d6775722e636f6d2f545669495357562e706e67)](#)
12. Make sure that the file path to the game is correct (3) 12. Make sure that the file path to the game is correct (3)
13. Under `2 - Sync and Refresh` set `Prefered Refreshrate` to `Highest available` and `Vertical Sync` to `Force off` (4) 13. Under `2 - Sync and Refresh` set `Prefered Refreshrate` to `Highest available` and `Vertical Sync` to `Force off` (4)
14. Hit `Apply changes` and you are good to go (5) 14. Hit `Apply changes` and you are good to go (5)
15. The success rate of these steps depend on your GPU and monitor combination, most will work fine however some (mostly older ones) wont, if your monitor is still locked at 60Hz in fullscreen after this then your only option remaining is to play in windowed mode.
### Follow these steps on AMD: ### Follow these steps on AMD:
1. Right click on Desktop -> `Display settings` 1. Right click on Desktop -> `Display settings`
@ -144,24 +148,30 @@ This will completely disable the automatic camera rotation adjustments when you
### On 'Disable camera reset on lock-on': ### On 'Disable camera reset on lock-on':
If you press your target lock-on key and no target is in sight the game will reset and center the camera position and disable your input while it's doing so. Ticking this checkbox will remove this behaviour of the game. If you press your target lock-on key and no target is in sight the game will reset and center the camera position and disable your input while it's doing so. Ticking this checkbox will remove this behaviour of the game.
### On 'Automatically loot enemies':
Enabling this will pick up all loot an enemy drops automatically as long as you are in pick-up range.
### On 'Prevent dragonrot increase on death': ### On 'Prevent dragonrot increase on death':
This option will remove the effect dragonrot has on NPCs, if an NPC already got dragonrot then it will ensure that their condition won't worsen when you die. The internal dragonrot counter will however keep increasing, nobody will be affected by it though. Keep in mind that there are certain thresholds regarding amount of deaths between dragonrot levels, if you enable this feature and die a level might get skipped so even when you disable it afterwards the dragonrot level for all NPCs will only increase after you have hit the **next** threshold. This option will remove the effect dragonrot has on NPCs, if an NPC already got dragonrot then it will ensure that their condition won't worsen when you die. The internal dragonrot counter will however keep increasing, nobody will be affected by it though. Keep in mind that there are certain thresholds regarding amount of deaths between dragonrot levels, if you enable this feature and die a level might get skipped so even when you disable it afterwards the dragonrot level for all NPCs will only increase after you have hit the **next** threshold.
### On 'Disable death penalties (except dragonrot)': ### On 'Disable death penalties (except dragonrot)':
Like 'Unseen Aid' you will not lose any Sen or Experience upon death with this option enabled. Dragonrot will not be modified, check the option above to prevent dragonrot from increasing. Like 'Unseen Aid' you will not lose any Sen or Experience upon death with this option enabled. Dragonrot will not be modified, check the option above to prevent dragonrot from increasing.
### On 'Increase spirit emblem cap. on upgrades':
Whenever you upgrade your prosthetic arm the maximum spirit emblem capacity will now be increased by 1 **permanently**. Deactivating this option will not remove the additional capacity gained by it. With the base 15 capacity and +5 capacity by skills the normal maximal capacity is 20, with this feature enabled you can reach up to 50 max capacity as there are 30 prosthetic upgrades.
### On 'Game speed': ### On 'Game speed':
Slow down the game to beat a boss like a game journalist or speed it up and become gud. Game speed acts as a global time scale and is used by the game itself to create a dramatic effect in a few cutscenes. All game physics (even opening the menu) will be affected equally: all time-critical windows like dodge and deflect will be proportionally prolonged or shortened while the amount of damage given and taken as well as all other damage physics will be unaltered. A hit from an enemy on 150% game speed will do the exact same damage as on 80%, the deflect window on 50% is exactly twice as long as on 100% and so on. Of course, Sekiro himself will be affected by the speed too so even though a time window might me different now, the speed which you can react on it is different too. Slow down the game to beat a boss like a game journalist or speed it up and become gud. Game speed acts as a global time scale and is used by the game itself to create a dramatic effect in a few cutscenes. All game physics (even opening the menu) will be affected equally: all time-critical windows like dodge and deflect will be proportionally prolonged or shortened while the amount of damage given and taken as well as all other damage physics will be unaltered. A hit from an enemy on 150% game speed will do the exact same damage as on 80%, the deflect window on 50% is exactly twice as long as on 100% and so on. Of course, Sekiro himself will be affected by the speed too so even though a time window might be different now, the speed which you can react on it is different too. Can be toggled by pressing CTRL+M. Be aware that both speed modifications can potentially crash the game in certain cutscenes and NPC interactions so use them with caution.
Be aware that both speed modifications can potentially crash the game in certain cutscenes and NPC interactions so use them with caution.
### On 'Player speed': ### On 'Player speed':
This modifier enables you to control Sekiro's speed independently from general game speed. Combat physics however are not guaranteed to stay the same on every setting. For example if you increase player speed you will be able to react to an attack faster but your own 'deflect' window is shorter now because you move faster. Use this to explore the world or to keep player speed near normal while decreasing general game speed. Grappling as well as jumping is handled by game speed while falling damage is calculated based on player speed so take care when grappling to a lower level with a low game speed and high player speed as this could instantly kill you as the game thinks you fell to death. This modifier enables you to control Sekiro's speed independently from general game speed. Combat physics however are not guaranteed to stay the same on every setting. For example if you increase player speed you will be able to react to an attack faster but your own 'deflect' window is shorter now because you move faster. Use this to explore the world or to keep player speed near normal while altering general game speed. Grappling as well as jumping is handled by game speed while falling damage is calculated based on player speed so take care when grappling to a lower level with a low game speed and high player speed as this could instantly kill you as the game thinks you fell to death. Can be toggled by pressing CTRL+M.
Be aware that both speed modifications can potentially crash the game in certain cutscenes and NPC interactions so use them with caution. Be aware that both speed modifications can potentially crash the game in certain cutscenes and NPC interactions so use them with caution.
## Troubleshooting: ## Troubleshooting:
* Utility can't seem to find the game? - Make sure your game exe is called `sekiro.exe` * Utility can't seem to find the game? - Make sure your game exe is called `sekiro.exe`
* Make sure you followed the appropriate steps and didn't skip any (especially not the deletion of the Sekiro profile!) * Make sure you followed the appropriate steps and didn't skip any (especially not the deletion of the Sekiro profile!)
* Try disabling `Fullscreen optimization` for Sekiro: right mouse click on `sekiro.exe -> Compatibility-> tick 'Disable fullscreen optimizations'` * Try disabling `Fullscreen optimization` for Sekiro: right mouse click on `sekiro.exe -> Compatibility-> tick 'Disable fullscreen optimizations'`
* If you are using ReShade make sure your preset doesn't enforce 60 Hz, try removing ReShade and see if it solves the problem
* Try adding the whole game folder and `Sekiro FPS Unlocker and more` to your antivirus's exclusion list * Try adding the whole game folder and `Sekiro FPS Unlocker and more` to your antivirus's exclusion list
* Try disabling `Steam Broadcast` (streaming via overlay) * Try disabling `Steam Broadcast` (streaming via overlay)
* Try to force disable VSYNC even when you are using GSYNC * Try to force disable VSYNC even when you are using GSYNC
@ -180,7 +190,7 @@ Be aware that both speed modifications can potentially crash the game in certain
## Preview ## Preview
[![Sekiro FPS Unlocker and more](https://camo.githubusercontent.com/09e0d1ddd5c0216649cf20fcd7d92898492a04e6/68747470733a2f2f692e696d6775722e636f6d2f5774547632654d2e706e67)](#) [![Sekiro FPS Unlocker and more](https://camo.githubusercontent.com/825b3ef500ce18358566b0ec96a74555eddf2603/68747470733a2f2f692e696d6775722e636f6d2f4868396335634a2e706e67)](#)
### Unlocked framerate ### Unlocked framerate
[![Sekiro FPS Unlocker and more](https://camo.githubusercontent.com/53f52adb98a5111b31538466f91b58599d56d6d7/68747470733a2f2f692e696d6775722e636f6d2f4c4c524a5a554c2e706e67)](#) [![Sekiro FPS Unlocker and more](https://camo.githubusercontent.com/53f52adb98a5111b31538466f91b58599d56d6d7/68747470733a2f2f692e696d6775722e636f6d2f4c4c524a5a554c2e706e67)](#)
@ -219,6 +229,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
* 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 Radeon Settings first, see Usage * 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 Radeon Settings first, see Usage
* in fullscreen the game forces the monitor to 60 Hz so you'll have to handle this with driver override too, see Usage * in fullscreen the game forces the monitor to 60 Hz so you'll have to handle this with driver override too, see Usage
* if your monitor does not support Hz override (Preferred Refreshrate missing and Profile Inspector won't work either) you won't be able to play at a higher refresh rate in fullscreen, play in windowed mode as an alternative
* your monitor has to natively support your custom resolution otherwise it won't show up correctly * your monitor has to natively support your custom resolution otherwise it won't show up correctly
* if a custom resolution is used it has to be added and selected before enabling borderless window * if a custom resolution is used it has to be added and selected before enabling borderless window
* due to how the game renders the HUD is limited to 16:9 even on 21:9 resolutions * due to how the game renders the HUD is limited to 16:9 even on 21:9 resolutions
@ -229,6 +240,21 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
## Version History ## Version History
* v1.2.5.2 (2020-11-02)
* updated death penalties code to latest version 1.05
* added game version 1.05 (1.5.0.0) to supported versions
* v1.2.5.1 (2020-02-04)
* added support for windows' high contrast mode (thanks to [khvorov45](https://github.com/khvorov45) for pointing it out)
* focus of the game won't be stolen anymore when OBS and the utility try to write/read the stats log files at the same time
* v1.2.5.0 (2019-05-04)
* added feature to automatically loot enemies
* v1.2.4.0 (2019-05-02)
* added feature to increase spirit emblem capacity on prosthetic upgrades
* changed way to obtain player kills, fixes slow stats updates
* improved pattern scan algorithm
* v1.2.3.2 (2019-04-26)
* added hotkey to toggle speed modifiers (CTRL+M)
* added game version 1.04 (1.4.0.0) to supported versions
* v1.2.3.1 (2019-04-23) * v1.2.3.1 (2019-04-23)
* added game version 1.03 (1.3.0.0) to supported versions * added game version 1.03 (1.3.0.0) to supported versions
* v1.2.3.0 (2019-04-18) * v1.2.3.0 (2019-04-18)
@ -268,7 +294,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
* FOV will now stick even between loads * FOV will now stick even between loads
* Fixed a potential issue with unlimited frame rate unlock * Fixed a potential issue with unlimited frame rate unlock
* Fixed a potential issue when user tried to enable borderless while in minimized fullscreen * Fixed a potential issue when user tried to enable borderless while in minimized fullscreen
* Improved initial load time til game is patchable * Improved initial load time till game is patchable
* v1.0.2.0 (2019-03-26) * v1.0.2.0 (2019-03-26)
* Added option to reduce FOV (request) * Added option to reduce FOV (request)
* Added option to stretch borderless window to fullscreen regardless of window resolution * Added option to stretch borderless window to fullscreen regardless of window resolution

View file

@ -1,6 +0,0 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/AutoDetectedNamingRules/=Parameters/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/AutoDetectedNamingRules/=Locals/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/AutoDetectedNamingRules/=PrivateConstants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="PATTERN_" Suffix="" Style="AA_BB" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/AutoDetectedNamingRules/=Constants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="WS_" Suffix="" Style="AA_BB"&gt;&lt;ExtraRule Prefix="" Suffix="" Style="AA_BB" /&gt;&lt;/Policy&gt;</s:String>
<s:Int64 x:Key="/Default/CodeStyle/Naming/CSharpAutoNaming/AutoNamingCompletedVersion/@EntryValue">2</s:Int64></wpf:ResourceDictionary>

View file

@ -7,9 +7,11 @@ namespace SekiroFpsUnlockAndMore
internal const string PROCESS_NAME = "sekiro"; internal const string PROCESS_NAME = "sekiro";
internal const string PROCESS_TITLE = "Sekiro"; internal const string PROCESS_TITLE = "Sekiro";
internal const string PROCESS_DESCRIPTION = "Shadows Die Twice"; internal const string PROCESS_DESCRIPTION = "Shadows Die Twice";
internal const string PROCESS_EXE_VERSION = "1.3.0.0"; internal const string PROCESS_EXE_VERSION = "1.5.0.0";
internal static readonly string[] PROCESS_EXE_VERSION_SUPPORTED = new string[1] internal static readonly string[] PROCESS_EXE_VERSION_SUPPORTED_LEGACY = new string[3]
{ {
"1.4.0.0",
"1.3.0.0",
"1.2.0.0" "1.2.0.0"
}; };
@ -106,9 +108,9 @@ namespace SekiroFpsUnlockAndMore
/** /**
Reference pointer pCurrentResolutionWidth to <int>iInternalGameWidth (and <int>iInternalGameHeight which is +4 bytes). Reference pointer pCurrentResolutionWidth to <int>iInternalGameWidth (and <int>iInternalGameHeight which is +4 bytes).
000000014114B5C5 | 0F57D2 | xorps xmm2,xmm2 | 000000014114B5C5 | 0F57D2 | xorps xmm2,xmm2 |
000000014114B5C8 | 890D 521B7D02 | mov dword ptr ds:[14391D120],ecx | 000000014114B5C8 | 890D 521B7D02 | mov dword ptr ds:[14391D120],ecx | iInternalGameWidth
000000014114B5CE | 0F57C9 | xorps xmm1,xmm1 | 000000014114B5CE | 0F57C9 | xorps xmm1,xmm1 |
000000014114B5D1 | 8915 4D1B7D02 | mov dword ptr ds:[14391D124],edx | 000000014114B5D1 | 8915 4D1B7D02 | mov dword ptr ds:[14391D124],edx | iInternalGameHeight
000000014114AC88 (Version 1.2.0.0) 000000014114AC88 (Version 1.2.0.0)
*/ */
@ -175,20 +177,24 @@ namespace SekiroFpsUnlockAndMore
/** /**
Reference pointer pTotalKills to <int>iTotalKills, does not get updated on every kill but mostly on every 2nd, includes own player deaths... Reference pointer pPlayerStatsRelated to 2 more PlayerStatsRelated pointer, offset in struct to <int>iTotalKills.
0000000141152178 | 48:8D0D A9ACB302 | lea rcx,qword ptr ds:[143C8CE28] | pTotalKills->iTotalKills 00000001407BFE25 | 48:69D8 18020000 | imul rbx,rax,218 |
000000014115217F | 891481 | mov dword ptr ds:[rcx+rax*4],edx | 00000001407BFE2C | 48:8B05 FD8E3803 | mov rax,qword ptr ds:[143B48D30] | pPlayerStatsRelated->[PlayerStatsRelated1+0x08]->[PlayerStatsRelated2+0xDC]->iTotalKills
0000000141152182 | C3 | ret | 00000001407BFE33 | 48:03D9 | add rbx,rcx |
00000001407BFE36 | 48:897C24 20 | mov qword ptr ss:[rsp+20],rdi |
00000001407BFE3B | 48:8B78 08 | mov rdi,qword ptr ds:[rax+8] | offset PlayerStatsRelated1->PlayerStatsRelated2
0000000141151838 (Version 1.2.0.0) 0000000000000000 (Version 1.2.0.0)
*/ */
// credits to 'Me_TheCat' for original offset internal const string PATTERN_TOTAL_KILLS = "48 ?? D8 ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 ?? ?? 48 89 ?? ?? ?? 48 8B ?? 08";
internal const string PATTERN_TOTAL_KILLS = "48 8D 0D ?? ?? ?? ?? 89 14 81 C3"; internal const int PATTERN_TOTAL_KILLS_OFFSET = 7;
internal const int PATTERN_TOTAL_KILLS_INSTRUCTION_LENGTH = 7; internal const int PATTERN_TOTAL_KILLS_INSTRUCTION_LENGTH = 7;
internal const int PATTERN_TOTAL_KILLS_POINTER1_OFFSET = 0x0008;
internal const int PATTERN_TOTAL_KILLS_POINTER2_OFFSET = 0x00DC;
/** /**
Controls camera pitch. xmm4 holds new pitch from a calculation while rps+170 holds current one from mouse so we overwrite xmm4 with the old pitch value. Controls camera pitch. xmm4 holds new pitch from a calculation while rsi+170 holds current one from mouse so we overwrite xmm4 with the old pitch value.
000000014073AF86 | 0F29A5 70080000 | movaps xmmword ptr ss:[rbp+870],xmm4 | code inject overwrite from here 000000014073AF86 | 0F29A5 70080000 | movaps xmmword ptr ss:[rbp+870],xmm4 | code inject overwrite from here
000000014073AF8D | 0F29A5 80080000 | movaps xmmword ptr ss:[rbp+880],xmm4 | jump back here from code inject 000000014073AF8D | 0F29A5 80080000 | movaps xmmword ptr ss:[rbp+880],xmm4 | jump back here from code inject
000000014073AF94 | 0F29A6 70010000 | movaps xmmword ptr ds:[rsi+170],xmm4 | camPitch, newCamPitch 000000014073AF94 | 0F29A6 70010000 | movaps xmmword ptr ds:[rsi+170],xmm4 | camPitch, newCamPitch
@ -225,12 +231,6 @@ namespace SekiroFpsUnlockAndMore
Controls automatic camera pitch adjust on move on XY-axis. Controls automatic camera pitch adjust on move on XY-axis.
Pointer in rax holds new pitch while rsi+170 holds current one prior movement so we overwrite xmm0 with the old pitch value and then overwrite [rax] with xmm0. Pointer in rax holds new pitch while rsi+170 holds current one prior movement so we overwrite xmm0 with the old pitch value and then overwrite [rax] with xmm0.
Breaks Pitch on emulated controllers... Breaks Pitch on emulated controllers...
000000014073B476 | F3:0F1000 | movss xmm0,dword ptr ds:[rax] | newCamPitch | code inject overwrite from here
000000014073B47A | F3:0F1186 70010000 | movss dword ptr ds:[rsi+170],xmm0 | camPitch
000000014073B482 | F3:0F1085 E4120000 | movss xmm0,dword ptr ss:[rbp+12E4] | jump back here from code inject
000000014073B48A | E8 91BDFFFF | call sekiro.140737220 |
000000014073B48F | 0F28D0 | movaps xmm2,xmm0 |
000000014073B4D6 | F3:0F1000 | movss xmm0,dword ptr ds:[rax] | newCamPitch | code inject overwrite from here 000000014073B4D6 | F3:0F1000 | movss xmm0,dword ptr ds:[rax] | newCamPitch | code inject overwrite from here
000000014073B4DA | F3:0F1186 70010000 | movss dword ptr ds:[rsi+170],xmm0 | camePitch 000000014073B4DA | F3:0F1186 70010000 | movss dword ptr ds:[rsi+170],xmm0 | camePitch
000000014073B4E2 | F3:0F1085 E4120000 | movss xmm0,dword ptr ss:[rbp+12E4] | jump back here from code inject 000000014073B4E2 | F3:0F1085 E4120000 | movss xmm0,dword ptr ss:[rbp+12E4] | jump back here from code inject
@ -281,6 +281,20 @@ namespace SekiroFpsUnlockAndMore
internal static readonly byte[] PATCH_CAMRESET_LOCKON_ENABLE = new byte[1] { 0x01 }; // true internal static readonly byte[] PATCH_CAMRESET_LOCKON_ENABLE = new byte[1] { 0x01 }; // true
/**
Picking up enemy loot can be automated by setting key press indicator to 1.
0000000140910D14 | C685 30010000 01 | mov byte ptr ss:[rbp+130],1 |
0000000140910D1B | B0 01 | mov al,1 | triggers loot pickup
0000000140910D1D | EB 09 | jmp sekiro.140910D28 |
0000000140910D1F | C685 30010000 00 | mov byte ptr ss:[rbp+130],0 |
0000000140910D26 | 32C0 | xor al,al | resets loot pickup
*/
internal const string PATTERN_AUTOLOOT = "C6 85 ?? ?? ?? ?? ?? B0 01 EB ?? C6 85 ?? ?? ?? ?? ?? 32 C0";
internal const int PATTERN_AUTOLOOT_OFFSET = 18;
internal static readonly byte[] PATCH_AUTOLOOT_ENABLE = new byte[2] { 0xB0, 0x01}; // mov al,1
internal static readonly byte[] PATCH_AUTOLOOT_DISABLE = new byte[2] { 0x32, 0xC0 }; // xor al,al
/** /**
Whole dragonrot routine upon death is guarded by a conditional jump, there may be some events in the game where a true death shall not increase the disease so it's skippable as a whole. Whole dragonrot routine upon death is guarded by a conditional jump, there may be some events in the game where a true death shall not increase the disease so it's skippable as a whole.
We replace conditional jump with non-conditional one. We replace conditional jump with non-conditional one.
@ -316,12 +330,12 @@ namespace SekiroFpsUnlockAndMore
/** /**
sekiro.14066B520 is used to increase and decrease various player values, in this case it's used to decrease Sen so we skip the call. sekiro.14066DA30 is used to increase and decrease various player values, in this case it's used to decrease Sen so we skip the call.
0000000141189B74 | F344:0F2CE9 | cvttss2si r13d,xmm1 | 00000001411D32F7 | F344:0F2CE9 | cvttss2si r13d,xmm1 |
0000000141189B79 | 41:8BD5 | mov edx,r13d | 00000001411D32FC | 41:8BD5 | mov edx,r13d |
0000000141189B7C | 48:8BCB | mov rcx,rbx | 00000001411D32FF | 48:8BCF | mov rcx,rdi |
0000000141189B7F | E8 FC194EFF | call sekiro.14066B580 | -> ManipulatePlayerValues() 00000001411D3302 | E8 29A749FF | call sekiro.14066DA30 | -> ManipulatePlayerValues()
0000000141189B84 | 8BAB 60010000 | mov ebp,dword ptr ds:[rbx+160] | 00000001411D3307 | 8B87 60010000 | mov eax,dword ptr ds:[rdi+160] |
000000014118904F (Version 1.2.0.0) 000000014118904F (Version 1.2.0.0)
*/ */
@ -331,6 +345,20 @@ namespace SekiroFpsUnlockAndMore
internal static readonly byte[] PATCH_DEATHPENALTIES1_DISABLE = new byte[5] { 0x90, 0x90, 0x90, 0x90, 0x90 }; // nop internal static readonly byte[] PATCH_DEATHPENALTIES1_DISABLE = new byte[5] { 0x90, 0x90, 0x90, 0x90, 0x90 }; // nop
/** /**
Here ability points (AP) are decreased and virtual Sen & AP decrease is set. The later 2 values will be shown after death as an indicator on how much of each has been lost. Here ability points (AP) are decreased and virtual Sen & AP decrease is set. The later 2 values will be shown after death as an indicator on how much of each has been lost.
To not have the "Unseen Aid" screen shown everytime we overwrite an additional instruction.
00000001411D33BB | E8 E0110000 | call sekiro.1411D45A0 | -> OnDeath() ability points (AP) decrease
00000001411D33C0 | 45:2BE5 | sub r12d,r13d |
00000001411D33C3 | 44:89A424 A0000000 | mov dword ptr ss:[rsp+A0],r12d | virtual Sen decrease - shows how many Sen got lost after death
00000001411D33CB | 8B8424 90000000 | mov eax,dword ptr ss:[rsp+90] | current AP
00000001411D33D2 | 2BC3 | sub eax,ebx |
00000001411D33D4 | 898424 A4000000 | mov dword ptr ss:[rsp+A4],eax | virtual AP decrease - shows how many APs got lost after death
00000001411D33DB | E8 002B6FFF | call sekiro.1408C5EE0 |
00000001411D33E0 | 48:8B8C24 A0000000 | mov rcx,qword ptr ss:[rsp+A0] |
00000001411D33E8 | 48:8908 | mov qword ptr ds:[rax],rcx | checks if we have lost virtual sen and if not shows "Unseen Aid" screen next spawn
00000001411D33EB | 48:8B0D 562BB802 | mov rcx,qword ptr ds:[143D55F48] |
00000001411D33F2 | 48:85C9 | test rcx,rcx |
LEGACY
0000000141189C68 | 8B00 | mov eax,dword ptr ds:[rax] | 0000000141189C68 | 8B00 | mov eax,dword ptr ds:[rax] |
0000000141189C6A | 8983 60010000 | mov dword ptr ds:[rbx+160],eax | OnDeath() ability points (AP) decrease 0000000141189C6A | 8983 60010000 | mov dword ptr ds:[rbx+160],eax | OnDeath() ability points (AP) decrease
0000000141189C70 | 45:2BFD | sub r15d,r13d | 0000000141189C70 | 45:2BFD | sub r15d,r13d |
@ -341,10 +369,26 @@ namespace SekiroFpsUnlockAndMore
000000014118913A (Version 1.2.0.0) 000000014118913A (Version 1.2.0.0)
*/ */
internal const string PATTERN_DEATHPENALTIES2 = "8B ?? 89 83 ?? ?? ?? ?? 45 ?? ?? 44 89 ?? 24 ?? ?? 00 00 2B ?? 89 ?? 24 ?? ?? 00 00 E8"; internal const string PATTERN_DEATHPENALTIES2 = "E8 ?? ?? ?? ?? 45 ?? ?? 44 89 ?? 24 ?? ?? 00 00 8B ?? 24 ?? ?? 00 00 2B ?? 89 ?? 24 ?? ?? 00 00 E8 ?? ?? ?? ?? 48 ?? ?? 24 ?? ?? 00 00 48 ?? ?? 48";
internal const int PATTERN_DEATHPENALTIES2_OFFSET = 2; internal const int PATTERN_DEATHPENALTIES2_OFFSET = 0;
internal const int PATCH_DEATHPENALTIES2_INSTRUCTION_LENGTH = 26; internal const int PATCH_DEATHPENALTIES2_INSTRUCTION_LENGTH = 32;
internal static readonly byte[] PATCH_DEATHPENALTIES2_DISABLE = new byte[26] // nop internal static readonly byte[] PATCH_DEATHPENALTIES2_DISABLE = new byte[32] // nop
{
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90
};
internal const int PATTERN_DEATHPENALTIES3_OFFSET = 45;
internal const int PATCH_DEATHPENALTIES3_INSTRUCTION_LENGTH = 3;
internal static readonly byte[] PATCH_DEATHPENALTIES3_DISABLE = new byte[3] // nop
{
0x90, 0x90, 0x90
};
internal const string PATTERN_DEATHPENALTIES2_LEGACY = "8B ?? 89 83 ?? ?? ?? ?? 45 ?? ?? 44 89 ?? 24 ?? ?? 00 00 2B ?? 89 ?? 24 ?? ?? 00 00 E8";
internal const int PATTERN_DEATHPENALTIES2_OFFSET_LEGACY = 2;
internal const int PATCH_DEATHPENALTIES2_INSTRUCTION_LENGTH_LEGACY = 26;
internal static readonly byte[] PATCH_DEATHPENALTIES2_DISABLE_LEGACY = new byte[26] // nop
{ {
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
@ -368,6 +412,48 @@ namespace SekiroFpsUnlockAndMore
internal static readonly byte[] PATCH_DEATHSCOUNTER_ENABLE = new byte[4] { 0x84, 0xDB, 0x0F, 0x85 }; // test bl,bl; jne internal static readonly byte[] PATCH_DEATHSCOUNTER_ENABLE = new byte[4] { 0x84, 0xDB, 0x0F, 0x85 }; // test bl,bl; jne
/**
Whenever we upgrade a prosthetic or learn an ability the following function block will get called.
We inject a check to determine if case is prosthetic and set register affecting SkillEffect4 to 1 so that the upgrade increases our maximum spirit emblem capacity.
Type for struct SKILL_PARAM_ST is defined below.
0000000140A84C29 | 48:85C0 | test rax,rax |
0000000140A84C2C | 74 4A | je sekiro.140A84C78 | IncreaseSkill4OnUpgrade ?
0000000140A84C2E | 0FB650 37 | movzx edx,byte ptr ds:[rax+37] | get SKILL_PARAM_ST.SkillEffect4 to edx | code inject overwrite from here
0000000140A84C32 | 85D2 | test edx,edx | check if edx is 0
0000000140A84C34 | 74 42 | je sekiro.140A84C78 | if 0 jump here | jump back here from code inject
0000000140A84C36 | 48:8B0D F3400C03 | mov rcx,qword ptr ds:[143B48D30] | increase skill4 on upgrade routine
0000000140A84C3D | 48:8B49 08 | mov rcx,qword ptr ds:[rcx+8] |
0000000140A84C41 | 48:85C9 | test rcx,rcx |
0000000140A84C44 | 74 32 | je sekiro.140A84C78 |
0000000140A84C46 | 48:81C1 46010000 | add rcx,146 |
0000000140A84C4D | 66:0111 | add word ptr ds:[rcx],dx | increases Skill4 on upgrade, will get skipped if edx == 0
0000000000000000 (Version 1.2.0.0)
[StructLayout(LayoutKind.Explicit, Size = 0x0060)]
private struct SKILL_PARAM_ST
{
[FieldOffset(0x0030)]
private Int32 SkillFamily; // (Unk6) 2700000 for prosthetic upgrades
[FieldOffset(0x0037)]
private UInt16 SkillEffect4; // (Unk10) controls how much spirit emblem capacity rises on acquisition of skill/upgrade
}
*/
internal const string PATTERN_EMBLEMUPGRADE = "48 85 C0 74 ?? 0F B6 50 37 85 D2 74 ?? 48 8B 0D";
internal const int PATTERN_EMBLEMUPGRADE_OFFSET = 5;
internal const int INJECT_EMBLEMUPGRADE_OVERWRITE_LENGTH = 6;
internal static readonly byte[] INJECT_EMBLEMUPGRADE_SHELLCODE = new byte[]
{
0x81, 0x78, 0x30, 0xE0, 0x32, 0x29, 0x00, // cmp dword ptr ds:[rax+30],2932E0 | if (SKILL_PARAM_ST.SkillFamily == 2700000)
0x75, 0x07, // jne +7 | {
0xBA, 0x01, 0x00, 0x00, 0x00, // mov edx,1 | edx = 1
0xEB, 0x04, // jmp +4 | } else {
0x0F, 0xB6, 0x50, 0x37, // movzx edx,byte ptr ds:[rax+37] | edx = SKILL_PARAM_ST.SkillEffect4 }
0x85, 0xD2 // test edx,edx | check if edx is 0
};
/** /**
Reference pointer pTimeRelated to TimescaleManager pointer, offset in struct to <float>fTimescale which acts as a global speed scale for almost all ingame calculations. Reference pointer pTimeRelated to TimescaleManager pointer, offset in struct to <float>fTimescale which acts as a global speed scale for almost all ingame calculations.
000000014114A7C7 | 48:8B05 3A2BB402 | mov rax,qword ptr ds:[143C8D308] | pTimeRelated->[TimescaleManager+0x360]->fTimescale 000000014114A7C7 | 48:8B05 3A2BB402 | mov rax,qword ptr ds:[143C8D308] | pTimeRelated->[TimescaleManager+0x360]->fTimescale
@ -395,9 +481,9 @@ namespace SekiroFpsUnlockAndMore
// credits to 'Zullie the Witch' for original offset // credits to 'Zullie the Witch' for original offset
internal const string PATTERN_TIMESCALE_PLAYER = "48 8B 1D ?? ?? ?? ?? 48 85 DB 74 ?? 8B ?? 81 FA"; internal const string PATTERN_TIMESCALE_PLAYER = "48 8B 1D ?? ?? ?? ?? 48 85 DB 74 ?? 8B ?? 81 FA";
internal const int PATTERN_TIMESCALE_PLAYER_INSTRUCTION_LENGTH = 7; internal const int PATTERN_TIMESCALE_PLAYER_INSTRUCTION_LENGTH = 7;
internal const int PATTERN_TIMESCALE_POINTER2_OFFSET = 0x88; internal const int PATTERN_TIMESCALE_POINTER2_OFFSET = 0x0088;
internal const int PATTERN_TIMESCALE_POINTER3_OFFSET = 0x1FF8; internal const int PATTERN_TIMESCALE_POINTER3_OFFSET = 0x1FF8;
internal const int PATTERN_TIMESCALE_POINTER4_OFFSET = 0x28; internal const int PATTERN_TIMESCALE_POINTER4_OFFSET = 0x0028;
internal const int PATTERN_TIMESCALE_POINTER5_OFFSET = 0xD00; internal const int PATTERN_TIMESCALE_POINTER5_OFFSET = 0x0D00;
} }
} }

View file

@ -5,9 +5,9 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SekiroFpsUnlockAndMore" xmlns:local="clr-namespace:SekiroFpsUnlockAndMore"
mc:Ignorable="d" mc:Ignorable="d"
Title="Sekiro FPS Unlocker and more v1.2.3" Width="Auto" Height="Auto" SizeToContent="WidthAndHeight" ResizeMode="CanMinimize" Loaded="Window_Loaded" Closing="Window_Closing"> Title="Sekiro FPS Unlocker and more v1.2.5" Width="Auto" Height="Auto" SizeToContent="WidthAndHeight" ResizeMode="CanMinimize" Loaded="Window_Loaded" Closing="Window_Closing">
<Grid Background="#FFF9F9F9"> <Grid x:Name="gMainGrid" Background="#FFF9F9F9">
<DockPanel> <DockPanel>
<StackPanel DockPanel.Dock="Top" Margin="10,10,10,0" Width="300" Height="Auto"> <StackPanel DockPanel.Dock="Top" Margin="10,10,10,0" Width="300" Height="Auto">
<DockPanel LastChildFill="False"> <DockPanel LastChildFill="False">
@ -33,26 +33,28 @@
</StackPanel> </StackPanel>
<CheckBox x:Name="cbLogStats" Margin="0,5,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Log stats (Deaths, Kills) to file for OBS" ToolTip="Check the guide on how to display these on stream with OBS" Checked="CbStatChanged" Unchecked="CbStatChanged" TabIndex="10" /> <CheckBox x:Name="cbLogStats" Margin="0,5,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Log stats (Deaths, Kills) to file for OBS" ToolTip="Check the guide on how to display these on stream with OBS" Checked="CbStatChanged" Unchecked="CbStatChanged" TabIndex="10" />
<Expander x:Name="exGameMods" Margin="-3,5,0,0" Height="Auto" FontSize="14 px" Header="Modifications" IsExpanded="True" TabIndex="11"> <Expander x:Name="exGameMods" Margin="-3,5,0,0" Height="Auto" FontSize="14 px" Header="Modifications" IsExpanded="True" TabIndex="11">
<Grid Margin="3,1,0,0" Background="#FFF9F9F9"> <Grid x:Name="gSubGrid1" Margin="3,1,0,0" Background="#FFF9F9F9">
<StackPanel Width="Auto" Height="Auto"> <StackPanel Width="Auto" Height="Auto">
<CheckBox x:Name="cbCamAdjust" Margin="0,5,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Disable camera auto rotate on movement" ToolTip="Disables the annoying automatic camera adjustment on movement. Intended for mouse users" Checked="CbCamAdjust_Check_Handler" Unchecked="CbCamAdjust_Check_Handler" TabIndex="12" /> <CheckBox x:Name="cbCamAdjust" Margin="0,5,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Disable camera auto rotate on movement" ToolTip="Disables the annoying automatic camera adjustment on movement. Intended for mouse users" Checked="CbCamAdjust_Check_Handler" Unchecked="CbCamAdjust_Check_Handler" TabIndex="12" />
<CheckBox x:Name="cbCamReset" Margin="0,3,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Disable camera reset on lock-on" ToolTip="Disables the annoying camera centering on lock-on when there is no target" Checked="CbCamReset_Check_Handler" Unchecked="CbCamReset_Check_Handler" TabIndex="13" /> <CheckBox x:Name="cbCamReset" Margin="0,3,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Disable camera reset on lock-on" ToolTip="Disables the annoying camera centering on lock-on when there is no target" Checked="CbCamReset_Check_Handler" Unchecked="CbCamReset_Check_Handler" TabIndex="13" />
<CheckBox x:Name="cbDragonrot" Margin="0,3,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Prevent dragonrot increase on death" ToolTip="NPCs won't get the disease and/or their condition won't get worse" Checked="CbDragonrot_Check_Handler" Unchecked="CbDragonrot_Check_Handler" TabIndex="14" /> <CheckBox x:Name="cbAutoLoot" Margin="0,3,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Automatically loot enemies" ToolTip="Automatically pickup and collect all enemy loot/items" TabIndex="14" Checked="CbAutoLoot_Check_Handler" Unchecked="CbAutoLoot_Check_Handler" />
<CheckBox x:Name="cbDeathPenalty" Margin="0,3,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Disable death penalties (except dragonrot)" ToolTip="Disables Sen and Experience loss upon death" Checked="CbDeathPenalty_Check_Handler" Unchecked="CbDeathPenalty_Check_Handler" TabIndex="15" /> <CheckBox x:Name="cbDragonrot" Margin="0,3,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Prevent dragonrot increase on death" ToolTip="NPCs won't get the disease and/or their condition won't get worse" Checked="CbDragonrot_Check_Handler" Unchecked="CbDragonrot_Check_Handler" TabIndex="15" />
<CheckBox x:Name="cbDeathPenalty" Margin="0,3,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Disable death penalties (except dragonrot)" ToolTip="Disables Sen and Experience loss upon death" Checked="CbDeathPenalty_Check_Handler" Unchecked="CbDeathPenalty_Check_Handler" TabIndex="16" />
<CheckBox x:Name="cbDeathPenaltyHidden" Margin="0,3,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="(DEBUG) Disable hidden death penalties" ToolTip="DEBUG ONLY" Checked="CbDeathPenaltyHidden_Check_Handler" Unchecked="CbDeathPenaltyHidden_Check_Handler" IsTabStop="False" Visibility="Collapsed" /> <CheckBox x:Name="cbDeathPenaltyHidden" Margin="0,3,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="(DEBUG) Disable hidden death penalties" ToolTip="DEBUG ONLY" Checked="CbDeathPenaltyHidden_Check_Handler" Unchecked="CbDeathPenaltyHidden_Check_Handler" IsTabStop="False" Visibility="Collapsed" />
<CheckBox x:Name="cbEmblemUpgrade" Margin="0,3,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Increase spirit emblem cap. on upgrades" ToolTip="Every prosthetic upgrade will increase maximum emblem capacity by 1. PERMANENT INCREASE!" Checked="CbEmblemUpgrade_Check_Handler" Unchecked="CbEmblemUpgrade_Check_Handler" TabIndex="17"/>
<DockPanel Margin="0,3,0,0" LastChildFill="False"> <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 (%):" ToolTip="Increase or decrease. Can potentially crash the game in cutscenes, use with caution" Checked="CbGameSpeed_Check_Handler" Unchecked="CbGameSpeed_Check_Handler" TabIndex="16" /> <CheckBox x:Name="cbGameSpeed" DockPanel.Dock="Left" Margin="0,0,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Game speed (%):" ToolTip="Increase or decrease. Can potentially crash the game in cutscenes, use with caution" Checked="CbGameSpeed_Check_Handler" Unchecked="CbGameSpeed_Check_Handler" TabIndex="18" />
<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="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" /> <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" TabIndex="17" /> <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" TabIndex="19" />
<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="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" /> <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>
<DockPanel Margin="0,5,0,0" LastChildFill="False"> <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="Increase or decrease. To enable this start the game and load a save, then tick the checkbox. Can potentially crash the game in cutscenes, use with caution" Checked="CbPlayerSpeed_Check_Handler" Unchecked="CbPlayerSpeed_Check_Handler" TabIndex="18" /> <CheckBox x:Name="cbPlayerSpeed" DockPanel.Dock="Left" Margin="0,0,0,0" Height="25" FontSize="14 px" VerticalContentAlignment="Center" Content="Player speed (%):" ToolTip="Increase or decrease. To enable this start the game and load a save, then tick the checkbox. Can potentially crash the game in cutscenes, use with caution" Checked="CbPlayerSpeed_Check_Handler" Unchecked="CbPlayerSpeed_Check_Handler" TabIndex="20" />
<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="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" /> <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" TabIndex="19" /> <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" TabIndex="21" />
<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="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" /> <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> </DockPanel>
@ -62,7 +64,7 @@
<Button x:Name="bPatch" Margin="0,7,0,0" Width="300" Height="30" FontSize="14 px" IsEnabled="False" Content="Patch game (CTRL + P)" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" <Button x:Name="bPatch" Margin="0,7,0,0" Width="300" 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" TabIndex="20" IsTabStop="False" /> Focusable="False" Click="BPatch_Click" TabIndex="20" IsTabStop="False" />
<TextBox x:Name="tbStatus" Margin="0,5,0,0" Width="300" Height="25" FontSize="14 px" FontWeight="Bold" Text="waiting for game..." TextAlignment="Center" TextWrapping="NoWrap" IsEnabled="False" /> <TextBox x:Name="tbStatus" Margin="0,5,0,0" Width="300" Height="25" FontSize="14 px" FontWeight="Bold" Text="waiting for game..." TextAlignment="Center" TextWrapping="NoWrap" IsEnabled="False" />
<Expander x:Name="exGuide" Margin="0,8,0,0" Height="Auto" FontSize="14 px" Header="ReadMe" IsExpanded="True" TabIndex="21"> <Expander x:Name="exGuide" Margin="0,8,0,0" Height="Auto" FontSize="14 px" Header="ReadMe" IsExpanded="True" TabIndex="22">
<TextBlock Margin="2,6,2,0" FontSize="11 px" TextWrapping="WrapWithOverflow" IsEnabled="False"> <TextBlock Margin="2,6,2,0" FontSize="11 px" TextWrapping="WrapWithOverflow" IsEnabled="False">
<TextBlock.Inlines> <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" Foreground="#FF0046FF">This patcher does not modify game files, you have to start it every time.</Run>
@ -76,6 +78,9 @@
<Run FontWeight="Bold">Custom resolution adds 21:9 support and overwrites a default resolution, hud will be limited to 16:9.</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 activate borderless afterwards.</Run> <Run>Borderless window mode with custom resolution requires you to patch and set resolution first, then activate borderless afterwards.</Run>
<Run FontWeight="Bold">Disabling camera auto rotate is mostly intended for mouse users.</Run> <Run FontWeight="Bold">Disabling camera auto rotate is mostly intended for mouse users.</Run>
<Run>Spirit emblem capacity upgrades are</Run>
<Run FontWeight="Bold" Foreground="#FFF00000">permanent.</Run>
<Run>Both speed modifiers can be toggled by pressing hotkey CTRL+M.</Run>
<Run FontWeight="Bold">To enable Player Speed modification you have to be ingame first.</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> <Run FontWeight="Bold" Foreground="#FFF00000">See the link below for detailed information, guides, GSYNC support and an AMD fix.</Run>
</TextBlock.Inlines> </TextBlock.Inlines>
@ -83,7 +88,7 @@
</Expander> </Expander>
<Label HorizontalAlignment="Right" FontSize="12 px" TabIndex="22"> <Label HorizontalAlignment="Right" FontSize="12 px" TabIndex="22">
<Hyperlink NavigateUri="https://github.com/uberhalit/SekiroFpsUnlockAndMore" RequestNavigate="Hyperlink_RequestNavigate"> <Hyperlink NavigateUri="https://github.com/uberhalit/SekiroFpsUnlockAndMore" RequestNavigate="Hyperlink_RequestNavigate">
v1.2.3 - by uberhalit v1.2.5 - by uberhalit
</Hyperlink> </Hyperlink>
</Label> </Label>
</StackPanel> </StackPanel>

View file

@ -26,12 +26,14 @@ namespace SekiroFpsUnlockAndMore
internal long _offset_resolution = 0x0; internal long _offset_resolution = 0x0;
internal long _offset_resolution_default = 0x0; internal long _offset_resolution_default = 0x0;
internal long _offset_resolution_scaling_fix = 0x0; internal long _offset_resolution_scaling_fix = 0x0;
internal long _offset_total_kills = 0x0;
internal long _offset_player_deaths = 0x0; internal long _offset_player_deaths = 0x0;
internal long _offset_total_kills = 0x0;
internal long _offset_camera_reset = 0x0; internal long _offset_camera_reset = 0x0;
internal long _offset_autoloot = 0x0;
internal long _offset_dragonrot_routine = 0x0; internal long _offset_dragonrot_routine = 0x0;
internal long _offset_deathpenalties1 = 0x0; internal long _offset_deathpenalties1 = 0x0;
internal long _offset_deathpenalties2 = 0x0; internal long _offset_deathpenalties2 = 0x0;
internal long _offset_deathpenalties3 = 0x0;
internal long _offset_deathscounter_routine = 0x0; internal long _offset_deathscounter_routine = 0x0;
internal long _offset_timescale = 0x0; internal long _offset_timescale = 0x0;
internal long _offset_timescale_player = 0x0; internal long _offset_timescale_player = 0x0;
@ -39,6 +41,7 @@ namespace SekiroFpsUnlockAndMore
internal byte[] _patch_deathpenalties1_enable; internal byte[] _patch_deathpenalties1_enable;
internal byte[] _patch_deathpenalties2_enable; internal byte[] _patch_deathpenalties2_enable;
internal byte[] _patch_deathpenalties3_enable;
internal MemoryCaveGenerator _memoryCaveGenerator; internal MemoryCaveGenerator _memoryCaveGenerator;
internal SettingsService _settingsService; internal SettingsService _settingsService;
@ -54,6 +57,7 @@ namespace SekiroFpsUnlockAndMore
internal bool _dataCave_speedfix = false; internal bool _dataCave_speedfix = false;
internal bool _dataCave_fovsetting = false; internal bool _dataCave_fovsetting = false;
internal bool _codeCave_camadjust = false; internal bool _codeCave_camadjust = false;
internal bool _codeCave_emblemupgrade = false;
internal bool _retryAccess = true; internal bool _retryAccess = true;
internal bool _statLoggingEnabled = false; internal bool _statLoggingEnabled = false;
internal bool _initialStartup = true; internal bool _initialStartup = true;
@ -63,6 +67,7 @@ namespace SekiroFpsUnlockAndMore
internal string _path_killsLog; internal string _path_killsLog;
internal RECT _windowRect; internal RECT _windowRect;
internal Size _screenSize; internal Size _screenSize;
internal bool _isLegacyVersion = false;
internal const string _DATACAVE_SPEEDFIX_POINTER = "speedfixPointer"; internal const string _DATACAVE_SPEEDFIX_POINTER = "speedfixPointer";
internal const string _DATACAVE_FOV_POINTER = "fovPointer"; internal const string _DATACAVE_FOV_POINTER = "fovPointer";
@ -70,6 +75,7 @@ namespace SekiroFpsUnlockAndMore
internal const string _CODECAVE_CAMADJUST_YAW_Z = "camAdjustYawZ"; internal const string _CODECAVE_CAMADJUST_YAW_Z = "camAdjustYawZ";
internal const string _CODECAVE_CAMADJUST_PITCH_XY = "camAdjustPitchXY"; internal const string _CODECAVE_CAMADJUST_PITCH_XY = "camAdjustPitchXY";
internal const string _CODECAVE_CAMADJUST_YAW_XY = "camAdjustYawXY"; internal const string _CODECAVE_CAMADJUST_YAW_XY = "camAdjustYawXY";
internal const string _CODECAVE_EMBLEM_UPGRADE = "emblemCapUpgrade";
public MainWindow() public MainWindow()
{ {
@ -90,6 +96,25 @@ namespace SekiroFpsUnlockAndMore
} }
GC.KeepAlive(mutex); GC.KeepAlive(mutex);
try
{
HIGHCONTRAST highContrastInfo = new HIGHCONTRAST();
highContrastInfo.cbSize = Marshal.SizeOf(typeof(HIGHCONTRAST));
if (SystemParametersInfo(SPI_GETHIGHCONTRAST, (uint)highContrastInfo.cbSize, ref highContrastInfo, 0))
{
if ((highContrastInfo.dwFlags & HCF_HIGHCONTRASTON) == 1)
{
// high contrast mode is active, remove grid background color and let the OS handle it
gMainGrid.Background = null;
gSubGrid1.Background = null;
}
}
}
catch (Exception ex)
{
Debug.WriteLine("Could not fetch SystemParameters: " + ex.Message);
}
_path_logs = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) + @"\SekiroFpsUnlockAndMore.log"; _path_logs = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) + @"\SekiroFpsUnlockAndMore.log";
_path_deathsLog = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) + @"\DeathCounter.txt"; _path_deathsLog = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) + @"\DeathCounter.txt";
_path_killsLog = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) + @"\TotalKillsCounter.txt"; _path_killsLog = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) + @"\TotalKillsCounter.txt";
@ -100,8 +125,8 @@ namespace SekiroFpsUnlockAndMore
this.sbInput.Text = _settingsService.ApplicationSettings.peasantInput ? "Controller" : "Mouse"; this.sbInput.Text = _settingsService.ApplicationSettings.peasantInput ? "Controller" : "Mouse";
IntPtr hWnd = new WindowInteropHelper(this).Handle; IntPtr hWnd = new WindowInteropHelper(this).Handle;
if (!RegisterHotKey(hWnd, 9009, MOD_CONTROL, VK_P)) if (!RegisterHotKey(hWnd, 9009, MOD_CONTROL, VK_M) || !RegisterHotKey(hWnd, 9010, MOD_CONTROL, VK_P))
MessageBox.Show("Hotkey is already in use, it may not work.", "Sekiro FPS Unlocker and more", MessageBoxButton.OK, MessageBoxImage.Warning); MessageBox.Show("A Hotkey is already in use, it may not work.", "Sekiro FPS Unlocker and more", MessageBoxButton.OK, MessageBoxImage.Warning);
ComponentDispatcher.ThreadFilterMessage += new ThreadMessageEventHandler(ComponentDispatcherThreadFilterMessage); ComponentDispatcher.ThreadFilterMessage += new ThreadMessageEventHandler(ComponentDispatcherThreadFilterMessage);
@ -141,6 +166,7 @@ namespace SekiroFpsUnlockAndMore
ComponentDispatcher.ThreadFilterMessage -= ComponentDispatcherThreadFilterMessage; ComponentDispatcher.ThreadFilterMessage -= ComponentDispatcherThreadFilterMessage;
IntPtr hWnd = new WindowInteropHelper(this).Handle; IntPtr hWnd = new WindowInteropHelper(this).Handle;
UnregisterHotKey(hWnd, 9009); UnregisterHotKey(hWnd, 9009);
UnregisterHotKey(hWnd, 9010);
if (_gameAccessHwnd != IntPtr.Zero) if (_gameAccessHwnd != IntPtr.Zero)
CloseHandle(_gameAccessHwnd); CloseHandle(_gameAccessHwnd);
} }
@ -153,7 +179,23 @@ namespace SekiroFpsUnlockAndMore
if (handled) return; if (handled) return;
if (msg.message != WM_HOTKEY_MSG_ID) return; if (msg.message != WM_HOTKEY_MSG_ID) return;
if (msg.wParam.ToInt32() == 9009) // patch game if (msg.wParam.ToInt32() == 9009) // toggle speed modifier
{
if (this.cbGameSpeed.IsEnabled && this.cbPlayerSpeed.IsEnabled)
{
if (this.cbGameSpeed.IsChecked == false)
{
this.cbGameSpeed.IsChecked = true;
this.cbPlayerSpeed.IsChecked = true;
}
else
{
this.cbGameSpeed.IsChecked = false;
this.cbPlayerSpeed.IsChecked = false;
}
}
}
else if (msg.wParam.ToInt32() == 9010) // patch game
{ {
handled = true; handled = true;
PatchGame(); PatchGame();
@ -180,10 +222,12 @@ namespace SekiroFpsUnlockAndMore
this.exGameMods.IsExpanded = _settingsService.ApplicationSettings.exGameMods; this.exGameMods.IsExpanded = _settingsService.ApplicationSettings.exGameMods;
this.cbCamAdjust.IsChecked = _settingsService.ApplicationSettings.cbCamAdjust; this.cbCamAdjust.IsChecked = _settingsService.ApplicationSettings.cbCamAdjust;
this.cbCamReset.IsChecked = _settingsService.ApplicationSettings.cbCamReset; this.cbCamReset.IsChecked = _settingsService.ApplicationSettings.cbCamReset;
this.cbAutoLoot.IsChecked = _settingsService.ApplicationSettings.cbAutoLoot;
this.cbDragonrot.IsChecked = _settingsService.ApplicationSettings.cbDragonrot; this.cbDragonrot.IsChecked = _settingsService.ApplicationSettings.cbDragonrot;
this.cbDeathPenalty.IsChecked = _settingsService.ApplicationSettings.cbDeathPenalty; this.cbDeathPenalty.IsChecked = _settingsService.ApplicationSettings.cbDeathPenalty;
this.cbDeathPenaltyHidden.Visibility = _settingsService.ApplicationSettings.hiddenDPs == ZUH_HIDDEN_DP ? Visibility.Visible : Visibility.Collapsed; this.cbDeathPenaltyHidden.Visibility = _settingsService.ApplicationSettings.hiddenDPs == ZUH_HIDDEN_DP ? Visibility.Visible : Visibility.Collapsed;
if (_settingsService.ApplicationSettings.hiddenDPs == ZUH_HIDDEN_DP) { _debugMode = true; sbMode.Text = "DEBUG"; } if (_settingsService.ApplicationSettings.hiddenDPs == ZUH_HIDDEN_DP) { _debugMode = true; sbMode.Text = "DEBUG"; }
this.cbEmblemUpgrade.IsChecked = _settingsService.ApplicationSettings.cbEmblemUpgrade;
this.cbGameSpeed.IsChecked = _settingsService.ApplicationSettings.cbGameSpeed; this.cbGameSpeed.IsChecked = _settingsService.ApplicationSettings.cbGameSpeed;
this.tbGameSpeed.Text = _settingsService.ApplicationSettings.tbGameSpeed.ToString(); this.tbGameSpeed.Text = _settingsService.ApplicationSettings.tbGameSpeed.ToString();
this.cbPlayerSpeed.IsChecked = _settingsService.ApplicationSettings.cbPlayerSpeed; this.cbPlayerSpeed.IsChecked = _settingsService.ApplicationSettings.cbPlayerSpeed;
@ -209,8 +253,10 @@ namespace SekiroFpsUnlockAndMore
_settingsService.ApplicationSettings.exGameMods = this.exGameMods.IsExpanded; _settingsService.ApplicationSettings.exGameMods = this.exGameMods.IsExpanded;
_settingsService.ApplicationSettings.cbCamAdjust = this.cbCamAdjust.IsChecked == true; _settingsService.ApplicationSettings.cbCamAdjust = this.cbCamAdjust.IsChecked == true;
_settingsService.ApplicationSettings.cbCamReset = this.cbCamReset.IsChecked == true; _settingsService.ApplicationSettings.cbCamReset = this.cbCamReset.IsChecked == true;
_settingsService.ApplicationSettings.cbAutoLoot = this.cbAutoLoot.IsChecked == true;
_settingsService.ApplicationSettings.cbDragonrot = this.cbDragonrot.IsChecked == true; _settingsService.ApplicationSettings.cbDragonrot = this.cbDragonrot.IsChecked == true;
_settingsService.ApplicationSettings.cbDeathPenalty = this.cbDeathPenalty.IsChecked == true; _settingsService.ApplicationSettings.cbDeathPenalty = this.cbDeathPenalty.IsChecked == true;
_settingsService.ApplicationSettings.cbEmblemUpgrade = this.cbEmblemUpgrade.IsChecked == true;
_settingsService.ApplicationSettings.cbGameSpeed = this.cbGameSpeed.IsChecked == true; _settingsService.ApplicationSettings.cbGameSpeed = this.cbGameSpeed.IsChecked == true;
_settingsService.ApplicationSettings.tbGameSpeed = this.tbGameSpeed.Text != "" && !this.tbGameSpeed.Text.Contains(" ") ? Convert.ToInt32(this.tbGameSpeed.Text) : 100; _settingsService.ApplicationSettings.tbGameSpeed = this.tbGameSpeed.Text != "" && !this.tbGameSpeed.Text.Contains(" ") ? Convert.ToInt32(this.tbGameSpeed.Text) : 100;
_settingsService.ApplicationSettings.cbPlayerSpeed = this.cbPlayerSpeed.IsChecked == true; _settingsService.ApplicationSettings.cbPlayerSpeed = this.cbPlayerSpeed.IsChecked == true;
@ -237,9 +283,11 @@ namespace SekiroFpsUnlockAndMore
this.exGameMods.IsExpanded = true; this.exGameMods.IsExpanded = true;
this.cbCamAdjust.IsChecked = false; this.cbCamAdjust.IsChecked = false;
this.cbCamReset.IsChecked = false; this.cbCamReset.IsChecked = false;
this.cbAutoLoot.IsChecked = false;
this.cbDragonrot.IsChecked = false; this.cbDragonrot.IsChecked = false;
this.cbDeathPenalty.IsChecked = false; this.cbDeathPenalty.IsChecked = false;
this.cbDeathPenaltyHidden.Visibility = Visibility.Collapsed; this.cbDeathPenaltyHidden.Visibility = Visibility.Collapsed;
this.cbEmblemUpgrade.IsChecked = false;
this.cbGameSpeed.IsChecked = false; this.cbGameSpeed.IsChecked = false;
this.tbGameSpeed.Text = "100"; this.tbGameSpeed.Text = "100";
this.cbPlayerSpeed.IsChecked = false; this.cbPlayerSpeed.IsChecked = false;
@ -314,12 +362,24 @@ namespace SekiroFpsUnlockAndMore
} }
string gameFileVersion = FileVersionInfo.GetVersionInfo(procList[0].MainModule.FileName).FileVersion; string gameFileVersion = FileVersionInfo.GetVersionInfo(procList[0].MainModule.FileName).FileVersion;
if (gameFileVersion != GameData.PROCESS_EXE_VERSION && Array.IndexOf(GameData.PROCESS_EXE_VERSION_SUPPORTED, gameFileVersion) < 0 && !_settingsService.ApplicationSettings.gameVersionNotify) if (gameFileVersion != GameData.PROCESS_EXE_VERSION)
{ {
MessageBox.Show(string.Format("Unknown game version '{0}'.\nSome functions might not work properly or even crash the game. " + if (Array.IndexOf(GameData.PROCESS_EXE_VERSION_SUPPORTED_LEGACY, gameFileVersion) < 0)
"Check for updates on this utility regularly following the link at the bottom.", gameFileVersion), "Sekiro FPS Unlocker and more", MessageBoxButton.OK, MessageBoxImage.Warning); {
ClearConfiguration(); if (!_settingsService.ApplicationSettings.gameVersionNotify)
_settingsService.ApplicationSettings.gameVersionNotify = true; {
MessageBox.Show(string.Format("Unknown game version '{0}'.\nSome functions might not work properly or even crash the game. " +
"Check for updates on this utility regularly following the link at the bottom.", gameFileVersion), "Sekiro FPS Unlocker and more", MessageBoxButton.OK, MessageBoxImage.Warning);
ClearConfiguration();
_settingsService.ApplicationSettings.gameVersionNotify = true;
SaveConfiguration();
}
}
else
{
_isLegacyVersion = true;
_settingsService.ApplicationSettings.gameVersionNotify = false;
}
} }
else else
_settingsService.ApplicationSettings.gameVersionNotify = false; _settingsService.ApplicationSettings.gameVersionNotify = false;
@ -405,14 +465,30 @@ namespace SekiroFpsUnlockAndMore
if (!IsValidAddress(_offset_player_deaths)) if (!IsValidAddress(_offset_player_deaths))
_offset_player_deaths = 0x0; _offset_player_deaths = 0x0;
long ref_lpTotalKills = patternScan.FindPattern(GameData.PATTERN_TOTAL_KILLS); long ref_lpTotalKills = patternScan.FindPattern(GameData.PATTERN_TOTAL_KILLS) + GameData.PATTERN_TOTAL_KILLS_OFFSET;
Debug.WriteLine("ref_lpTotalKills found at: 0x" + ref_lpTotalKills.ToString("X")); Debug.WriteLine("ref_lpTotalKills found at: 0x" + ref_lpTotalKills.ToString("X"));
if (IsValidAddress(ref_lpTotalKills)) if (IsValidAddress(ref_lpTotalKills))
{ {
_offset_total_kills = DereferenceStaticX64Pointer(_gameAccessHwndStatic, ref_lpTotalKills, GameData.PATTERN_TOTAL_KILLS_INSTRUCTION_LENGTH); long lpPlayerStatsRelatedKills1 = DereferenceStaticX64Pointer(_gameAccessHwndStatic, ref_lpTotalKills, GameData.PATTERN_TOTAL_KILLS_INSTRUCTION_LENGTH);
if (!IsValidAddress(_offset_total_kills)) Debug.WriteLine("lpPlayerStatsRelatedKills found at: 0x" + lpPlayerStatsRelatedKills1.ToString("X"));
_offset_total_kills = 0x0; if (IsValidAddress(lpPlayerStatsRelatedKills1))
{
long lpPlayerStructRelatedKills2 = Read<Int64>(_gameAccessHwndStatic, lpPlayerStatsRelatedKills1) + GameData.PATTERN_TOTAL_KILLS_POINTER1_OFFSET;
Debug.WriteLine("lpPlayerStructRelatedKills2 found at: 0x" + lpPlayerStructRelatedKills2.ToString("X"));
if (IsValidAddress(lpPlayerStructRelatedKills2))
{
_offset_total_kills = Read<Int64>(_gameAccessHwndStatic, lpPlayerStructRelatedKills2) + GameData.PATTERN_TOTAL_KILLS_POINTER2_OFFSET;
Debug.WriteLine("iTotalKills found at: 0x" + _offset_total_kills.ToString("X"));
}
}
} }
if (!IsValidAddress(_offset_total_kills))
_offset_total_kills = 0x0;
_offset_autoloot = patternScan.FindPattern(GameData.PATTERN_AUTOLOOT) + GameData.PATTERN_AUTOLOOT_OFFSET;
Debug.WriteLine("lpAutoLoot found at: 0x" + _offset_autoloot.ToString("X"));
if (!IsValidAddress(_offset_autoloot))
_offset_autoloot = 0x0;
long lpCamAdjustPitch = patternScan.FindPattern(GameData.PATTERN_CAMADJUST_PITCH); long lpCamAdjustPitch = patternScan.FindPattern(GameData.PATTERN_CAMADJUST_PITCH);
long lpCamAdjustYawZ = patternScan.FindPattern(GameData.PATTERN_CAMADJUST_YAW_Z) + GameData.PATTERN_CAMADJUST_YAW_Z_OFFSET; long lpCamAdjustYawZ = patternScan.FindPattern(GameData.PATTERN_CAMADJUST_YAW_Z) + GameData.PATTERN_CAMADJUST_YAW_Z_OFFSET;
@ -460,15 +536,36 @@ namespace SekiroFpsUnlockAndMore
Debug.WriteLine("deathPenalties1 original instruction set: " + BitConverter.ToString(_patch_deathpenalties1_enable).Replace('-', ' ')); Debug.WriteLine("deathPenalties1 original instruction set: " + BitConverter.ToString(_patch_deathpenalties1_enable).Replace('-', ' '));
if (_patch_deathpenalties1_enable != null) if (_patch_deathpenalties1_enable != null)
{ {
_offset_deathpenalties2 = patternScan.FindPattern(GameData.PATTERN_DEATHPENALTIES2) + GameData.PATTERN_DEATHPENALTIES2_OFFSET; if (!_isLegacyVersion)
_offset_deathpenalties2 = patternScan.FindPattern(GameData.PATTERN_DEATHPENALTIES2) + GameData.PATTERN_DEATHPENALTIES2_OFFSET;
else
_offset_deathpenalties2 = patternScan.FindPattern(GameData.PATTERN_DEATHPENALTIES2_LEGACY) + GameData.PATTERN_DEATHPENALTIES2_OFFSET_LEGACY;
Debug.WriteLine("lpDeathPenalties2 found at: 0x" + _offset_deathpenalties2.ToString("X")); Debug.WriteLine("lpDeathPenalties2 found at: 0x" + _offset_deathpenalties2.ToString("X"));
if (IsValidAddress(_offset_deathpenalties2)) if (IsValidAddress(_offset_deathpenalties2))
{ {
_patch_deathpenalties2_enable = new byte[GameData.PATCH_DEATHPENALTIES2_INSTRUCTION_LENGTH]; ulong instrLength = (ulong)GameData.PATCH_DEATHPENALTIES2_INSTRUCTION_LENGTH;
if (!ReadProcessMemory(_gameAccessHwnd, _offset_deathpenalties2, _patch_deathpenalties2_enable, (ulong) GameData.PATCH_DEATHPENALTIES2_INSTRUCTION_LENGTH, out lpNumberOfBytesRead) || lpNumberOfBytesRead.ToInt32() != GameData.PATCH_DEATHPENALTIES2_INSTRUCTION_LENGTH) if (!_isLegacyVersion)
_patch_deathpenalties2_enable = new byte[GameData.PATCH_DEATHPENALTIES2_INSTRUCTION_LENGTH];
else
{
_patch_deathpenalties2_enable = new byte[GameData.PATCH_DEATHPENALTIES2_INSTRUCTION_LENGTH_LEGACY];
instrLength = (ulong)GameData.PATCH_DEATHPENALTIES2_INSTRUCTION_LENGTH_LEGACY;
}
if (!ReadProcessMemory(_gameAccessHwnd, _offset_deathpenalties2, _patch_deathpenalties2_enable, instrLength, out lpNumberOfBytesRead) || lpNumberOfBytesRead.ToInt32() != (long)instrLength)
_patch_deathpenalties2_enable = null; _patch_deathpenalties2_enable = null;
else else
{
Debug.WriteLine("deathPenalties2 original instruction set: " + BitConverter.ToString(_patch_deathpenalties2_enable).Replace('-', ' ')); Debug.WriteLine("deathPenalties2 original instruction set: " + BitConverter.ToString(_patch_deathpenalties2_enable).Replace('-', ' '));
if (!_isLegacyVersion)
{
_offset_deathpenalties3 = _offset_deathpenalties2 + GameData.PATTERN_DEATHPENALTIES3_OFFSET;
_patch_deathpenalties3_enable = new byte[GameData.PATCH_DEATHPENALTIES3_INSTRUCTION_LENGTH];
if (!ReadProcessMemory(_gameAccessHwnd, _offset_deathpenalties3, _patch_deathpenalties3_enable, (ulong)GameData.PATCH_DEATHPENALTIES3_INSTRUCTION_LENGTH, out lpNumberOfBytesRead) || lpNumberOfBytesRead.ToInt32() != GameData.PATCH_DEATHPENALTIES3_INSTRUCTION_LENGTH)
_patch_deathpenalties2_enable = null;
else
Debug.WriteLine("deathPenalties3 original instruction set: " + BitConverter.ToString(_patch_deathpenalties3_enable).Replace('-', ' '));
}
}
} }
else else
_offset_deathpenalties2 = 0x0; _offset_deathpenalties2 = 0x0;
@ -478,8 +575,10 @@ namespace SekiroFpsUnlockAndMore
{ {
_offset_deathpenalties1 = 0x0; _offset_deathpenalties1 = 0x0;
_offset_deathpenalties2 = 0x0; _offset_deathpenalties2 = 0x0;
_offset_deathpenalties3 = 0x0;
_patch_deathpenalties1_enable = null; _patch_deathpenalties1_enable = null;
_patch_deathpenalties2_enable = null; _patch_deathpenalties2_enable = null;
_patch_deathpenalties3_enable = null;
} }
if (_settingsService.ApplicationSettings.hiddenDPs == ZUH_HIDDEN_DP) if (_settingsService.ApplicationSettings.hiddenDPs == ZUH_HIDDEN_DP)
@ -490,6 +589,15 @@ namespace SekiroFpsUnlockAndMore
_offset_deathscounter_routine = 0x0; _offset_deathscounter_routine = 0x0;
} }
long lpSkill4OnUpgrade = patternScan.FindPattern(GameData.PATTERN_EMBLEMUPGRADE) + GameData.PATTERN_EMBLEMUPGRADE_OFFSET;
Debug.WriteLine("lpSkill4OnUpgrade found at: 0x" + lpSkill4OnUpgrade.ToString("X"));
if (IsValidAddress(lpSkill4OnUpgrade))
{
if (_memoryCaveGenerator.CreateNewCodeCave(_CODECAVE_EMBLEM_UPGRADE, lpSkill4OnUpgrade, GameData.INJECT_EMBLEMUPGRADE_OVERWRITE_LENGTH, GameData.INJECT_EMBLEMUPGRADE_SHELLCODE))
_codeCave_emblemupgrade = true;
Debug.WriteLine("lpSkill4OnUpgrade code cave at: 0x" + _memoryCaveGenerator.GetCodeCaveAddressByName(_CODECAVE_EMBLEM_UPGRADE).ToString("X"));
}
long ref_lpTimeRelated = patternScan.FindPattern(GameData.PATTERN_TIMESCALE); long ref_lpTimeRelated = patternScan.FindPattern(GameData.PATTERN_TIMESCALE);
Debug.WriteLine("ref_lpTimeRelated found at: 0x" + ref_lpTimeRelated.ToString("X")); Debug.WriteLine("ref_lpTimeRelated found at: 0x" + ref_lpTimeRelated.ToString("X"));
if (IsValidAddress(ref_lpTimeRelated)) if (IsValidAddress(ref_lpTimeRelated))
@ -614,6 +722,13 @@ namespace SekiroFpsUnlockAndMore
this.cbCamReset.IsEnabled = false; this.cbCamReset.IsEnabled = false;
} }
if (_offset_autoloot == 0x0)
{
UpdateStatus("auto loot not found...", Brushes.Red);
LogToFile("auto loot not found...");
this.cbAutoLoot.IsEnabled = false;
}
if (_offset_dragonrot_routine == 0x0) if (_offset_dragonrot_routine == 0x0)
{ {
UpdateStatus("dragonrot not found...", Brushes.Red); UpdateStatus("dragonrot not found...", Brushes.Red);
@ -631,6 +746,13 @@ namespace SekiroFpsUnlockAndMore
if (_offset_deathscounter_routine == 0x0) if (_offset_deathscounter_routine == 0x0)
this.cbDeathPenaltyHidden.IsEnabled = false; this.cbDeathPenaltyHidden.IsEnabled = false;
if (!_codeCave_emblemupgrade)
{
UpdateStatus("emblem upgrade not found...", Brushes.Red);
LogToFile("emblem upgrade not found...");
this.cbEmblemUpgrade.IsEnabled = false;
}
if (_offset_timescale == 0x0) if (_offset_timescale == 0x0)
{ {
UpdateStatus("timescale not found...", Brushes.Red); UpdateStatus("timescale not found...", Brushes.Red);
@ -647,7 +769,8 @@ namespace SekiroFpsUnlockAndMore
this.bPatch.IsEnabled = true; this.bPatch.IsEnabled = true;
_running = true; _running = true;
PatchGame(); PatchGame();
InjectToGame(); InjectCamAdjust();
InjectEmblemUpgrades();
} }
/// <summary> /// <summary>
@ -708,9 +831,11 @@ namespace SekiroFpsUnlockAndMore
_codeCave_camadjust = false; _codeCave_camadjust = false;
_offset_camera_reset = 0x0; _offset_camera_reset = 0x0;
_offset_dragonrot_routine = 0x0; _offset_dragonrot_routine = 0x0;
_offset_autoloot = 0x0;
_offset_deathpenalties1 = 0x0; _offset_deathpenalties1 = 0x0;
_offset_deathpenalties2 = 0x0; _offset_deathpenalties2 = 0x0;
_offset_deathscounter_routine = 0x0; _offset_deathscounter_routine = 0x0;
_codeCave_emblemupgrade = false;
_offset_timescale = 0x0; _offset_timescale = 0x0;
_offset_timescale_player = 0x0; _offset_timescale_player = 0x0;
_offset_timescale_player_pointer_start = 0x0; _offset_timescale_player_pointer_start = 0x0;
@ -941,6 +1066,29 @@ namespace SekiroFpsUnlockAndMore
return true; return true;
} }
/// <summary>
/// Patches the game's auto loot.
/// </summary>
/// <param name="showStatus">Determines if status should be updated from within method, default is true.</param>
private bool PatchAutoloot(bool showStatus = true)
{
if (!this.cbAutoLoot.IsEnabled || _offset_autoloot == 0x0 || !CanPatchGame()) return false;
if (this.cbAutoLoot.IsChecked == true)
{
WriteBytes(_gameAccessHwndStatic, _offset_autoloot, GameData.PATCH_AUTOLOOT_ENABLE);
}
else if (this.cbAutoLoot.IsChecked == false)
{
if (!_initialStartup)
WriteBytes(_gameAccessHwndStatic, _offset_autoloot, GameData.PATCH_AUTOLOOT_DISABLE);
if (showStatus) UpdateStatus(DateTime.Now.ToString("HH:mm:ss") + " Game unpatched!", Brushes.White);
return false;
}
if (showStatus) UpdateStatus(DateTime.Now.ToString("HH:mm:ss") + " Game patched!", Brushes.Green);
return true;
}
/// <summary> /// <summary>
/// Patches the game's dragonrot effect on NPCs. /// Patches the game's dragonrot effect on NPCs.
/// </summary> /// </summary>
@ -976,13 +1124,17 @@ namespace SekiroFpsUnlockAndMore
{ {
WriteBytes(_gameAccessHwndStatic, _offset_deathpenalties1, GameData.PATCH_DEATHPENALTIES1_DISABLE); WriteBytes(_gameAccessHwndStatic, _offset_deathpenalties1, GameData.PATCH_DEATHPENALTIES1_DISABLE);
WriteBytes(_gameAccessHwndStatic, _offset_deathpenalties2, GameData.PATCH_DEATHPENALTIES2_DISABLE); WriteBytes(_gameAccessHwndStatic, _offset_deathpenalties2, GameData.PATCH_DEATHPENALTIES2_DISABLE);
if (!_isLegacyVersion && _offset_deathpenalties3 != 0x0)
WriteBytes(_gameAccessHwndStatic, _offset_deathpenalties3, GameData.PATCH_DEATHPENALTIES3_DISABLE);
} }
else if (this.cbDeathPenalty.IsChecked == false) else if (this.cbDeathPenalty.IsChecked == false)
{ {
if (_initialStartup) if (!_initialStartup)
{ {
WriteBytes(_gameAccessHwndStatic, _offset_deathpenalties1, _patch_deathpenalties1_enable); WriteBytes(_gameAccessHwndStatic, _offset_deathpenalties1, _patch_deathpenalties1_enable);
WriteBytes(_gameAccessHwndStatic, _offset_deathpenalties2, _patch_deathpenalties2_enable); WriteBytes(_gameAccessHwndStatic, _offset_deathpenalties2, _patch_deathpenalties2_enable);
if (!_isLegacyVersion && _offset_deathpenalties3 != 0x0)
WriteBytes(_gameAccessHwndStatic, _offset_deathpenalties3, _patch_deathpenalties3_enable);
} }
if (showStatus) UpdateStatus(DateTime.Now.ToString("HH:mm:ss") + " Game unpatched!", Brushes.White); if (showStatus) UpdateStatus(DateTime.Now.ToString("HH:mm:ss") + " Game unpatched!", Brushes.White);
return false; return false;
@ -1119,6 +1271,7 @@ namespace SekiroFpsUnlockAndMore
PatchFov(false), PatchFov(false),
PatchWindow(false), PatchWindow(false),
PatchCamReset(false), PatchCamReset(false),
PatchAutoloot(false),
PatchDragonrot(false), PatchDragonrot(false),
PatchDeathPenalty(false), PatchDeathPenalty(false),
PatchGameSpeed(false), PatchGameSpeed(false),
@ -1132,9 +1285,9 @@ namespace SekiroFpsUnlockAndMore
} }
/// <summary> /// <summary>
/// Inject or eject code to game using code caves. /// Inject or eject code to control cam adjustment.
/// </summary> /// </summary>
private void InjectToGame() private void InjectCamAdjust()
{ {
if (!CanPatchGame() || !_codeCave_camadjust) return; if (!CanPatchGame() || !_codeCave_camadjust) return;
@ -1185,6 +1338,27 @@ namespace SekiroFpsUnlockAndMore
} }
} }
/// <summary>
/// Inject or eject code to control emblem upgrades.
/// </summary>
private void InjectEmblemUpgrades()
{
if (!CanPatchGame() || !_codeCave_emblemupgrade) return;
if (this.cbEmblemUpgrade.IsChecked == true)
{
this.cbEmblemUpgrade.IsEnabled = false;
_memoryCaveGenerator.ActivateCodeCaveByName(_CODECAVE_EMBLEM_UPGRADE);
this.cbEmblemUpgrade.IsEnabled = true;
}
else
{
this.cbEmblemUpgrade.IsEnabled = false;
_memoryCaveGenerator.DeactivateCodeCaveByName(_CODECAVE_EMBLEM_UPGRADE);
this.cbEmblemUpgrade.IsEnabled = true;
}
}
/// <summary> /// <summary>
/// Freeze values in memory that can't be patched to require no freezing easily. /// Freeze values in memory that can't be patched to require no freezing easily.
/// </summary> /// </summary>
@ -1226,6 +1400,7 @@ namespace SekiroFpsUnlockAndMore
if (_statLoggingEnabled) LogStatsFile(_path_deathsLog, playerDeaths.ToString()); if (_statLoggingEnabled) LogStatsFile(_path_deathsLog, playerDeaths.ToString());
int totalKills = Read<Int32>(_gameAccessHwndStatic, _offset_total_kills); int totalKills = Read<Int32>(_gameAccessHwndStatic, _offset_total_kills);
totalKills -= playerDeaths; // Since this value seems to track every death, including the player totalKills -= playerDeaths; // Since this value seems to track every death, including the player
if (totalKills < 0) totalKills = 0;
_statusViewModel.Kills = totalKills; _statusViewModel.Kills = totalKills;
if (_statLoggingEnabled) LogStatsFile(_path_killsLog, totalKills.ToString()); if (_statLoggingEnabled) LogStatsFile(_path_killsLog, totalKills.ToString());
} }
@ -1505,7 +1680,9 @@ namespace SekiroFpsUnlockAndMore
} }
catch (Exception ex) catch (Exception ex)
{ {
MessageBox.Show("Failed writing stats file: " + ex.Message, "Sekiro Fps Unlock And More"); LogToFile("Failed writing stats file: " + ex.Message);
// don't show a messagebox as this will potentially steal focus from game
//MessageBox.Show("Failed writing stats file: " + ex.Message, "Sekiro Fps Unlock And More");
} }
} }
@ -1612,7 +1789,7 @@ namespace SekiroFpsUnlockAndMore
private void CbCamAdjust_Check_Handler(object sender, RoutedEventArgs e) private void CbCamAdjust_Check_Handler(object sender, RoutedEventArgs e)
{ {
if (this.cbCamAdjust.IsEnabled) if (this.cbCamAdjust.IsEnabled)
InjectToGame(); InjectCamAdjust();
} }
private void CbCamReset_Check_Handler(object sender, RoutedEventArgs e) private void CbCamReset_Check_Handler(object sender, RoutedEventArgs e)
@ -1621,6 +1798,12 @@ namespace SekiroFpsUnlockAndMore
PatchCamReset(); PatchCamReset();
} }
private void CbAutoLoot_Check_Handler(object sender, RoutedEventArgs e)
{
if (this.cbAutoLoot.IsEnabled == true)
PatchAutoloot();
}
private void CbDragonrot_Check_Handler(object sender, RoutedEventArgs e) private void CbDragonrot_Check_Handler(object sender, RoutedEventArgs e)
{ {
if (this.cbDragonrot.IsEnabled) if (this.cbDragonrot.IsEnabled)
@ -1639,6 +1822,12 @@ namespace SekiroFpsUnlockAndMore
PatchDeathPenaltyHidden(); PatchDeathPenaltyHidden();
} }
private void CbEmblemUpgrade_Check_Handler(object sender, RoutedEventArgs e)
{
if (this.cbEmblemUpgrade.IsEnabled)
InjectEmblemUpgrades();
}
private void CbGameSpeed_Check_Handler(object sender, RoutedEventArgs e) private void CbGameSpeed_Check_Handler(object sender, RoutedEventArgs e)
{ {
PatchGameSpeed(); PatchGameSpeed();
@ -1724,6 +1913,7 @@ namespace SekiroFpsUnlockAndMore
private const int WM_HOTKEY_MSG_ID = 0x0312; private const int WM_HOTKEY_MSG_ID = 0x0312;
private const int MOD_CONTROL = 0x0002; private const int MOD_CONTROL = 0x0002;
private const uint VK_M = 0x004D;
private const uint VK_P = 0x0050; private const uint VK_P = 0x0050;
private const uint PROCESS_ALL_ACCESS = 0x001F0FFF; private const uint PROCESS_ALL_ACCESS = 0x001F0FFF;
private const int GWL_STYLE = -16; private const int GWL_STYLE = -16;
@ -1746,6 +1936,8 @@ namespace SekiroFpsUnlockAndMore
private const uint SWP_FRAMECHANGED = 0x0020; private const uint SWP_FRAMECHANGED = 0x0020;
private const uint SWP_SHOWWINDOW = 0x0040; private const uint SWP_SHOWWINDOW = 0x0040;
private const int ZUH_HIDDEN_DP = 0x7; private const int ZUH_HIDDEN_DP = 0x7;
private const uint SPI_GETHIGHCONTRAST = 0x0042;
private const int HCF_HIGHCONTRASTON = 0x00000001;
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern Boolean RegisterHotKey(IntPtr hWnd, Int32 id, UInt32 fsModifiers, UInt32 vlc); public static extern Boolean RegisterHotKey(IntPtr hWnd, Int32 id, UInt32 fsModifiers, UInt32 vlc);
@ -1762,6 +1954,18 @@ namespace SekiroFpsUnlockAndMore
[DllImport("kernel32.dll", SetLastError = true)] [DllImport("kernel32.dll", SetLastError = true)]
private static extern Boolean CloseHandle(IntPtr hObject); private static extern Boolean CloseHandle(IntPtr hObject);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct HIGHCONTRAST
{
public int cbSize;
public int dwFlags;
public IntPtr lpszDefaultScheme;
}
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SystemParametersInfo(UInt32 uiAction, UInt32 uiParam, ref HIGHCONTRAST pvParam, UInt32 fWinIni);
[DllImport("user32.dll", SetLastError = true)] [DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr GetWindowLongPtr(IntPtr hWnd, Int32 nIndex); private static extern IntPtr GetWindowLongPtr(IntPtr hWnd, Int32 nIndex);
@ -1799,7 +2003,6 @@ namespace SekiroFpsUnlockAndMore
UInt64 dwSize, UInt64 dwSize,
out IntPtr lpNumberOfBytesWritten); out IntPtr lpNumberOfBytesWritten);
#endregion #endregion
} }
} }

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -71,25 +72,45 @@ namespace SekiroFpsUnlockAndMore
long ix; long ix;
int iy; int iy;
bool bFound = false;
List<byte> not0PatternBytesList = new List<byte>();
List<int> not0PatternBytesIndexList = new List<int>();
int dataLength = bData.Length - bPattern.Length; int dataLength = bData.Length - bPattern.Length;
for (iy = bPattern.Length - 1; iy > -1; iy--)
{
if (szMask[iy] == 'x')
{
not0PatternBytesList.Add(bPattern[iy]);
not0PatternBytesIndexList.Add(iy);
}
}
byte[] not0PatternBytesArray = not0PatternBytesList.ToArray();
int not0PatternBytesL = not0PatternBytesArray.Length;
int[] not0PatternBytesIndexArray = not0PatternBytesIndexList.ToArray();
for (ix = 0; ix < dataLength; ix++) for (ix = 0; ix < dataLength; ix++)
{ {
bFound = true; if (not0PatternBytesArray[not0PatternBytesL - 1] != bData[ix]) continue;
for (iy = bPattern.Length - 1; iy > -1; iy--) bool check = true;
for (iy = not0PatternBytesArray.Length - 1; iy > -1; iy--)
{ {
if (szMask[iy] != 'x' || bPattern[iy] == bData[ix + iy]) if (not0PatternBytesArray[iy] == bData[ix + not0PatternBytesIndexArray[iy]])
continue; continue;
bFound = false; check = false;
break; break;
} }
if (bFound) if (check)
{
return dwStart + ix; return dwStart + ix;
}
} }
return 0; return -1;
} }

View file

@ -7,7 +7,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("uberhalit")] [assembly: AssemblyCompany("uberhalit")]
[assembly: AssemblyProduct("SekiroFpsUnlockAndMore")] [assembly: AssemblyProduct("SekiroFpsUnlockAndMore")]
[assembly: AssemblyCopyright("Copyright © uberhalit 2019")] [assembly: AssemblyCopyright("Copyright © uberhalit 2020")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
@ -18,5 +18,5 @@ using System.Runtime.InteropServices;
ResourceDictionaryLocation.SourceAssembly ResourceDictionaryLocation.SourceAssembly
)] )]
[assembly: AssemblyVersion("1.2.3.1")] [assembly: AssemblyVersion("1.2.5.2")]
[assembly: AssemblyFileVersion("1.2.3.1")] [assembly: AssemblyFileVersion("1.2.5.2")]

View file

@ -45,12 +45,16 @@ namespace SekiroFpsUnlockAndMore
[XmlElement] [XmlElement]
public bool cbCamReset { get; set; } public bool cbCamReset { get; set; }
[XmlElement] [XmlElement]
public bool cbAutoLoot { get; set; }
[XmlElement]
public bool cbDragonrot { get; set; } public bool cbDragonrot { get; set; }
[XmlElement] [XmlElement]
public bool cbDeathPenalty { get; set; } public bool cbDeathPenalty { get; set; }
[XmlElement] [XmlElement]
public int hiddenDPs { get; set; } public int hiddenDPs { get; set; }
[XmlElement] [XmlElement]
public bool cbEmblemUpgrade { get; set; }
[XmlElement]
public bool cbGameSpeed { get; set; } public bool cbGameSpeed { get; set; }
[XmlElement] [XmlElement]
public int tbGameSpeed { get; set; } public int tbGameSpeed { get; set; }