├── Relocation ├── GlobalFunctions.h ├── main.cpp ├── GlobalFunctions.cpp ├── GLobalVariables.h ├── Relocation.vcxproj.filters ├── GlobalVariables.cpp ├── Relocation.cpp ├── Relocation.h ├── Pattern.h ├── Pattern.cpp ├── Relocation.vcxproj └── RVA.h ├── SkyrimUncapper ├── Utilities.cpp ├── exports.def ├── SkyrimUncapper.rc ├── resource.h ├── SafeWrite.h ├── Hook_Skill.h ├── BranchTrampoline.h ├── GameSettings.h ├── SafeWrite.cpp ├── GameSettings.cpp ├── Relocation.cpp ├── Relocation.h ├── main.cpp ├── Settings.h ├── SkyrimUncapper.vcxproj.filters ├── Utilities.h ├── xbyak_bin2hex.h ├── BranchTrampoline.cpp ├── NiTypes.cpp ├── NiTypes.h ├── SkyrimUncapper.vcxproj ├── PluginAPI.h ├── xbyak_util.h └── Hook_Skill.cpp ├── README.md ├── xbyak ├── COPYRIGHT ├── xbyak_bin2hex.h └── xbyak_util.h ├── .gitattributes ├── SkyrimUncapper.sln └── .gitignore /Relocation/GlobalFunctions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | -------------------------------------------------------------------------------- /Relocation/main.cpp: -------------------------------------------------------------------------------- 1 | void main() 2 | { 3 | 4 | } -------------------------------------------------------------------------------- /Relocation/GlobalFunctions.cpp: -------------------------------------------------------------------------------- 1 | #include "GlobalFunctions.h" 2 | -------------------------------------------------------------------------------- /SkyrimUncapper/Utilities.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kassent/SkyrimUncapper/HEAD/SkyrimUncapper/Utilities.cpp -------------------------------------------------------------------------------- /SkyrimUncapper/exports.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | StartSkyrimUncapper 3 | SKSEPlugin_Query 4 | SKSEPlugin_Load 5 | LoadINIConfig -------------------------------------------------------------------------------- /SkyrimUncapper/SkyrimUncapper.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kassent/SkyrimUncapper/HEAD/SkyrimUncapper/SkyrimUncapper.rc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SkyrimUncapper 2 | a DLL(SKSE64) plugin for SkyrimSE that unlocks the skill level caps of 100 and makes some relevant leveling tweaks. 3 | 4 | Release:http://www.nexusmods.com/skyrimspecialedition/mods/8889/? 5 | -------------------------------------------------------------------------------- /Relocation/GLobalVariables.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "RVA.h" 3 | 4 | class BSScaleformManager; 5 | class MenuControls; 6 | class UI; 7 | class InputManager; 8 | class GameVM; 9 | class DataHandler; 10 | 11 | namespace GLobalVariables 12 | { 13 | void Init(); 14 | } -------------------------------------------------------------------------------- /SkyrimUncapper/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by SkyrimUncapper.rc 4 | 5 | // Next default values for new objects 6 | // 7 | #ifdef APSTUDIO_INVOKED 8 | #ifndef APSTUDIO_READONLY_SYMBOLS 9 | #define _APS_NEXT_RESOURCE_VALUE 101 10 | #define _APS_NEXT_COMMAND_VALUE 40001 11 | #define _APS_NEXT_CONTROL_VALUE 1001 12 | #define _APS_NEXT_SYMED_VALUE 101 13 | #endif 14 | #endif 15 | -------------------------------------------------------------------------------- /SkyrimUncapper/SafeWrite.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void SafeWriteBuf(uintptr_t addr, void * data, size_t len); 4 | void SafeWrite8(uintptr_t addr, UInt8 data); 5 | void SafeWrite16(uintptr_t addr, UInt16 data); 6 | void SafeWrite32(uintptr_t addr, UInt32 data); 7 | void SafeWrite64(uintptr_t addr, UInt64 data); 8 | 9 | // ### warning: if you try to branch more than +/- 2GB with these, they will fail and return false 10 | // ### this is a limitation of the 'jmp' instruction and more generally the x64 ISA 11 | // 5 bytes written to src 12 | bool SafeWriteJump(uintptr_t src, uintptr_t dst); 13 | bool SafeWriteCall(uintptr_t src, uintptr_t dst); 14 | -------------------------------------------------------------------------------- /SkyrimUncapper/Hook_Skill.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class ActorValueOwner 4 | { 5 | public: 6 | //00007FF71DFA71B0 + 28 = 00007FF71DFA71D8 7 | virtual ~ActorValueOwner(){} 8 | //C0 86 F8 1C F7 7F 00 00 7FF71CF886C0 9 | 10 | // Argument is the ActorValue ID 11 | virtual float GetCurrent(UInt32 arg); 12 | virtual float GetMaximum(UInt32 arg); 13 | virtual float GetBase(UInt32 arg); 14 | virtual void SetBase(UInt32 arg0, float arg1); 15 | virtual void ModBase(UInt32 arg0, float arg1); 16 | virtual void Unk_06(UInt32 arg0, UInt32 arg1, UInt32 arg2);// Force/Mod AV? 17 | virtual void SetCurrent(UInt32 arg0, float arg1); 18 | virtual bool Unk_08(void); 19 | 20 | // void ** _vtbl; // 00 21 | }; 22 | 23 | class PlayerSkills 24 | { 25 | public: 26 | 27 | enum { 28 | kAdvanceableSkillOffset = 6, // 29 | kNumAdvanceableSkills = 18 30 | }; 31 | 32 | struct StatData { 33 | struct LevelData { 34 | float level; // 00 35 | float points; // 04 36 | float pointsMax; // 08 37 | }; 38 | 39 | float levelPoints; // 00 40 | float levelPointsMax; // 04 41 | LevelData levelData[kNumAdvanceableSkills]; // 08 42 | UInt32 legendaryLevel[kNumAdvanceableSkills]; // 90 43 | }; 44 | 45 | StatData * data; 46 | }; 47 | 48 | 49 | void Hook_Skill_Init(); 50 | void Hook_Skill_Commit(); -------------------------------------------------------------------------------- /SkyrimUncapper/BranchTrampoline.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class BranchTrampoline 4 | { 5 | public: 6 | BranchTrampoline(); 7 | ~BranchTrampoline(); 8 | 9 | bool Create(size_t len, void * module = NULL); 10 | void Destroy(); 11 | 12 | // allocate unsized 13 | void * StartAlloc(); 14 | void EndAlloc(const void * end); 15 | 16 | void * Allocate(size_t size = sizeof(void *)); 17 | 18 | size_t Remain() { return m_len - m_allocated; } 19 | 20 | // takes 6 bytes of space at src, 8 bytes in trampoline 21 | bool Write6Branch(uintptr_t src, uintptr_t dst); 22 | bool Write6Call(uintptr_t src, uintptr_t dst); 23 | 24 | // takes 5 bytes of space at src, 14 bytes in trampoline 25 | bool Write5Branch(uintptr_t src, uintptr_t dst); 26 | bool Write5Call(uintptr_t src, uintptr_t dst); 27 | 28 | private: 29 | // takes 6 bytes of space at src, 8 bytes in trampoline 30 | bool Write6Branch_Internal(uintptr_t src, uintptr_t dst, UInt8 op); 31 | 32 | // takes 5 bytes of space at src, 14 bytes in trampoline 33 | bool Write5Branch_Internal(uintptr_t src, uintptr_t dst, UInt8 op); 34 | 35 | void * m_base; 36 | size_t m_len; // bytes 37 | size_t m_allocated; // bytes 38 | 39 | void * m_curAlloc; // currently active StartAlloc base 40 | }; 41 | 42 | extern BranchTrampoline g_branchTrampoline; 43 | extern BranchTrampoline g_localTrampoline; 44 | -------------------------------------------------------------------------------- /SkyrimUncapper/GameSettings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "skse64_common/Utilities.h" 4 | #include "NiTypes.h" 5 | 6 | // 18 7 | class Setting 8 | { 9 | public: 10 | Setting(); 11 | virtual ~Setting(); 12 | 13 | enum 14 | { 15 | kType_Unknown = 0, 16 | kType_Integer, 17 | kType_Float, 18 | kType_String, 19 | kType_Bool, 20 | kType_ID6, // need to find an example of this 21 | kType_ID, 22 | }; 23 | 24 | union Data 25 | { 26 | UInt32 u32; 27 | SInt32 s32; 28 | float f32; 29 | UInt8 u8; // used for bool 30 | char * s; 31 | }; 32 | 33 | // void ** _vtbl; // 00 34 | Data data; // 08 35 | char * name; // 10 36 | 37 | UInt32 GetType(void) const; 38 | 39 | bool GetDouble(double * out) const; 40 | bool SetDouble(double value); 41 | 42 | bool SetString(const char * value); 43 | }; 44 | 45 | 46 | // 138 47 | class SettingCollectionMap 48 | { 49 | public: 50 | virtual ~SettingCollectionMap(); 51 | 52 | Setting * Get(const char * name); 53 | 54 | // void ** _vtbl; // 000 55 | UInt64 pad008[(0x118 - 0x008) >> 3]; 56 | NiTMap items; // 118 - actually BSTCaseInsensitiveStringMap but that only changes the virtual functions 57 | }; 58 | STATIC_ASSERT(offsetof(SettingCollectionMap, items) == 0x118); 59 | 60 | Setting * GetINISetting(const char * name); 61 | 62 | extern RelocPtr g_gameSettingCollection; 63 | 64 | -------------------------------------------------------------------------------- /SkyrimUncapper/SafeWrite.cpp: -------------------------------------------------------------------------------- 1 | #include "SafeWrite.h" 2 | 3 | void SafeWriteBuf(uintptr_t addr, void * data, size_t len) 4 | { 5 | UInt32 oldProtect; 6 | 7 | VirtualProtect((void *)addr, len, PAGE_EXECUTE_READWRITE, &oldProtect); 8 | memcpy((void *)addr, data, len); 9 | VirtualProtect((void *)addr, len, oldProtect, &oldProtect); 10 | } 11 | 12 | void SafeWrite8(uintptr_t addr, UInt8 data) 13 | { 14 | SafeWriteBuf(addr, &data, sizeof(data)); 15 | } 16 | 17 | void SafeWrite16(uintptr_t addr, UInt16 data) 18 | { 19 | SafeWriteBuf(addr, &data, sizeof(data)); 20 | } 21 | 22 | void SafeWrite32(uintptr_t addr, UInt32 data) 23 | { 24 | SafeWriteBuf(addr, &data, sizeof(data)); 25 | } 26 | 27 | void SafeWrite64(uintptr_t addr, UInt64 data) 28 | { 29 | SafeWriteBuf(addr, &data, sizeof(data)); 30 | } 31 | 32 | static bool SafeWriteJump_Internal(uintptr_t src, uintptr_t dst, UInt8 op) 33 | { 34 | #pragma pack(push, 1) 35 | struct Code 36 | { 37 | UInt8 op; 38 | SInt32 displ; 39 | }; 40 | #pragma pack(pop) 41 | 42 | STATIC_ASSERT(sizeof(Code) == 5); 43 | 44 | ptrdiff_t delta = dst - (src + sizeof(Code)); 45 | if((delta < INT_MIN) || (delta > INT_MAX)) 46 | return false; 47 | 48 | Code code; 49 | 50 | code.op = op; 51 | code.displ = delta; 52 | 53 | SafeWriteBuf(src, &code, sizeof(code)); 54 | 55 | return true; 56 | } 57 | 58 | bool SafeWriteJump(uintptr_t src, uintptr_t dst) 59 | { 60 | return SafeWriteJump_Internal(src, dst, 0xE9); 61 | } 62 | 63 | bool SafeWriteCall(uintptr_t src, uintptr_t dst) 64 | { 65 | return SafeWriteJump_Internal(src, dst, 0xE8); 66 | } 67 | -------------------------------------------------------------------------------- /Relocation/Relocation.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files 40 | 41 | 42 | Header Files 43 | 44 | 45 | -------------------------------------------------------------------------------- /Relocation/GlobalVariables.cpp: -------------------------------------------------------------------------------- 1 | //#include "GLobalVariables.h" 2 | //#include "f4se_common/Relocation.h" 3 | //#include "f4se_common/f4se_version.h" 4 | // 5 | //#define GET_RVA(relocPtr) relocPtr.GetUIntPtr() - RelocationManager::s_baseAddr 6 | // 7 | ///* 8 | //This file makes globals version-independent. 9 | //Initialization order is important for this file. 10 | //Since RelocPtrs are static globals with constructors they are initialized during the dynamic initialization phase. 11 | //Static initialization order is undefined for variables in different translation units, so it is not possible to deterministically obtain the value of a RelocPtr during static init. 12 | //Initialization must thus be done explicitly: 13 | //Call G::Init() in the plugin load routine before calling RVAManager::UpdateAddresses(). 14 | //Doing so ensures that all RelocPtrs have been initialized and can be used to initialize an RVA. 15 | //*/ 16 | // 17 | //#include "f4se/GameData.h" 18 | //#include "f4se/GameInput.h" 19 | //#include "f4se/GameMenus.h" 20 | //#include "f4se/PapyrusVM.h" 21 | //#include "f4se/ScaleformLoader.h" 22 | // 23 | //namespace GLobalVariables 24 | //{ 25 | // void Init() 26 | // { 27 | // g_scaleformManager = RVA(g_scaleformManager.GetUIntPtr(), "48 8B 0D ? ? ? ? 48 8D 05 ? ? ? ? 48 8B D3", 0, 3, 7); 28 | // g_menuControls = RVA(g_menuControls.GetUIntPtr(), "48 8B 0D ? ? ? ? E8 ? ? ? ? 80 3D ? ? ? ? ? 0F B6 F8", 0, 3, 7); 29 | // g_ui = RVA(g_ui.GetUIntPtr(), "48 8B 0D ? ? ? ? BA ? ? ? ? 8B 1C 16", 0, 3, 7); 30 | // g_inputMgr = RVA(g_inputMgr.GetUIntPtr(), "48 83 3D ? ? ? ? ? 74 3F 48 83 C1 40", 0, 3, 8); 31 | // g_gameVM = RVA(g_gameVM.GetUIntPtr(), "4C 8B 05 ? ? ? ? 48 8B F9", 0, 3, 7); 32 | // g_dataHandler = RVA(g_dataHandler.GetUIntPtr(), "48 8B 05 ? ? ? ? 8B 13", 0, 3, 7); 33 | // } 34 | //} -------------------------------------------------------------------------------- /SkyrimUncapper/GameSettings.cpp: -------------------------------------------------------------------------------- 1 | #include "GameSettings.h" 2 | 3 | 4 | // FE086A335E2EB844894F7D900623A149B381723F+6B 5 | RelocPtr g_gameSettingCollection(0x02EDEE30); 6 | 7 | UInt32 Setting::GetType(void) const 8 | { 9 | if (!name || !name[0]) return kType_Unknown; 10 | 11 | switch (name[0]) 12 | { 13 | case 'b': return kType_Bool; 14 | case 'c': return kType_Unknown; 15 | case 'h': return kType_Unknown; 16 | case 'i': return kType_Integer; 17 | case 'u': return kType_Unknown; 18 | case 'f': return kType_Float; 19 | case 'S': return kType_String; // dynamically allocated string 20 | case 's': return kType_String; // statically allocated string 21 | case 'r': return kType_ID6; 22 | case 'a': return kType_ID; 23 | } 24 | 25 | return kType_Unknown; 26 | } 27 | 28 | bool Setting::GetDouble(double * out) const 29 | { 30 | switch (GetType()) 31 | { 32 | case kType_Integer: *out = data.s32; break; 33 | case kType_Float: *out = data.f32; break; 34 | case kType_String: return false; 35 | case kType_Bool: *out = data.u8 ? 1 : 0; break; 36 | case kType_ID6: *out = data.u32 >> 8; break; 37 | case kType_ID: *out = data.u32; break; 38 | default: return false; 39 | case kType_Unknown: return false; 40 | } 41 | 42 | return true; 43 | } 44 | 45 | bool Setting::SetDouble(double value) 46 | { 47 | switch (GetType()) 48 | { 49 | case kType_Integer: data.s32 = value; break; 50 | case kType_Float: data.f32 = value; break; 51 | case kType_String: return false; 52 | case kType_Bool: data.u8 = value ? 1 : 0; break; 53 | case kType_ID6: data.u32 = ((UInt32)value) << 8; break; 54 | case kType_ID: data.u32 = value; break; 55 | default: return false; 56 | case kType_Unknown: return false; 57 | } 58 | 59 | return true; 60 | } 61 | 62 | Setting * SettingCollectionMap::Get(const char * name) 63 | { 64 | return items.Get(name); 65 | } 66 | 67 | -------------------------------------------------------------------------------- /Relocation/Relocation.cpp: -------------------------------------------------------------------------------- 1 | #include "Relocation.h" 2 | 3 | // the goal of this file is to support pointers in to a relocated binary with as little runtime overhead, code bloat, and hassle as possible 4 | // 5 | // since the main executable will always be loaded before the dll, the easiest solution is to perform the relocation in the constructor of 6 | // a pointer class that supports conversion to T*. however, since we can't control anything about initialization order, each constructor 7 | // must call GetModuleHandle(NULL) locally, which sucks. each pointer will need an entry in the static init table, and nothing can be done 8 | // with the pointers in any other static constructors. 9 | // 10 | // one solution to this problem is init_seg(lib). any objects constructed in a file containing this pragma will be constructed before standard 11 | // 'user' level code. this means we can use the constructor of that object to call GetModuleHandle once and initialize a global with the load 12 | // address, which the pointer class constructor then references to fix up the addresses. this still creates an entry in the static init table 13 | // for each pointer, but only calls GetModuleHandle once. pointers are not fixed up until all static init has finished, so other static ctors 14 | // can't safely use pointers. 15 | // 16 | // the problem can't be solved further without moving the RelocPtr constructors in to init_seg(lib), which doesn't appear to be possible 17 | // without forcing all pointers to be defined in a file with init_seg(lib). that is really ugly and doesn't seem like a good idea. 18 | 19 | // anything in this file will initialized after the crt but before any user code 20 | #pragma warning(disable: 4073) // yes this is intentional 21 | #pragma init_seg(lib) 22 | 23 | static RelocationManager s_relocMgr; 24 | 25 | uintptr_t RelocationManager::s_baseAddr = 0; 26 | 27 | RelocationManager::RelocationManager() 28 | { 29 | s_baseAddr = reinterpret_cast(GetModuleHandleA(NULL)); 30 | } 31 | -------------------------------------------------------------------------------- /SkyrimUncapper/Relocation.cpp: -------------------------------------------------------------------------------- 1 | #include "Relocation.h" 2 | 3 | // the goal of this file is to support pointers in to a relocated binary with as little runtime overhead, code bloat, and hassle as possible 4 | // 5 | // since the main executable will always be loaded before the dll, the easiest solution is to perform the relocation in the constructor of 6 | // a pointer class that supports conversion to T*. however, since we can't control anything about initialization order, each constructor 7 | // must call GetModuleHandle(NULL) locally, which sucks. each pointer will need an entry in the static init table, and nothing can be done 8 | // with the pointers in any other static constructors. 9 | // 10 | // one solution to this problem is init_seg(lib). any objects constructed in a file containing this pragma will be constructed before standard 11 | // 'user' level code. this means we can use the constructor of that object to call GetModuleHandle once and initialize a global with the load 12 | // address, which the pointer class constructor then references to fix up the addresses. this still creates an entry in the static init table 13 | // for each pointer, but only calls GetModuleHandle once. pointers are not fixed up until all static init has finished, so other static ctors 14 | // can't safely use pointers. 15 | // 16 | // the problem can't be solved further without moving the RelocPtr constructors in to init_seg(lib), which doesn't appear to be possible 17 | // without forcing all pointers to be defined in a file with init_seg(lib). that is really ugly and doesn't seem like a good idea. 18 | 19 | // anything in this file will initialized after the crt but before any user code 20 | #pragma warning(disable: 4073) // yes this is intentional 21 | #pragma init_seg(lib) 22 | 23 | static RelocationManager s_relocMgr; 24 | 25 | uintptr_t RelocationManager::s_baseAddr = 0; 26 | 27 | RelocationManager::RelocationManager() 28 | { 29 | s_baseAddr = reinterpret_cast(GetModuleHandle(NULL)); 30 | } 31 | -------------------------------------------------------------------------------- /SkyrimUncapper/Relocation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class RelocationManager 4 | { 5 | public: 6 | RelocationManager(); 7 | 8 | static uintptr_t s_baseAddr; 9 | }; 10 | 11 | // use this for addresses that represent pointers to a type T 12 | template 13 | class RelocPtr 14 | { 15 | public: 16 | RelocPtr(uintptr_t offset) 17 | :m_offset(offset + RelocationManager::s_baseAddr) 18 | { 19 | // 20 | } 21 | operator uintptr_t() const 22 | { 23 | return m_offset; 24 | } 25 | 26 | operator T *() const 27 | { 28 | return GetPtr(); 29 | } 30 | 31 | T * operator->() const 32 | { 33 | return GetPtr(); 34 | } 35 | 36 | T * GetPtr() const 37 | { 38 | return reinterpret_cast (m_offset); 39 | } 40 | 41 | const T * GetConst() const 42 | { 43 | return reinterpret_cast (m_offset); 44 | } 45 | 46 | uintptr_t GetUIntPtr() const 47 | { 48 | return m_offset; 49 | } 50 | 51 | private: 52 | uintptr_t m_offset; 53 | 54 | // hide 55 | RelocPtr(); 56 | RelocPtr(RelocPtr & rhs); 57 | RelocPtr & operator=(RelocPtr & rhs); 58 | }; 59 | 60 | // use this for direct addresses to types T. needed to avoid ambiguity 61 | template 62 | class RelocAddr 63 | { 64 | public: 65 | RelocAddr(uintptr_t offset) 66 | :m_offset(reinterpret_cast (offset + RelocationManager::s_baseAddr)) 67 | { 68 | // 69 | } 70 | 71 | operator T() 72 | { 73 | return reinterpret_cast (m_offset); 74 | } 75 | 76 | uintptr_t GetUIntPtr() const 77 | { 78 | return reinterpret_cast (m_offset); 79 | } 80 | 81 | private: 82 | // apparently you can't reinterpret_cast from a type to the same type 83 | // that's kind of stupid and makes it impossible to use this for uintptr_ts if I use the same type 84 | // so we make a new type by storing the data in a pointer to this useless struct 85 | // at least this is hidden by a wrapper 86 | struct BlockConversionType { }; 87 | BlockConversionType * m_offset; 88 | 89 | // hide 90 | RelocAddr(); 91 | RelocAddr(RelocAddr & rhs); 92 | RelocAddr & operator=(RelocAddr & rhs); 93 | }; 94 | -------------------------------------------------------------------------------- /xbyak/COPYRIGHT: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2007 MITSUNARI Shigeo 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | Neither the name of the copyright owner nor the names of its contributors may 14 | be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 | THE POSSIBILITY OF SUCH DAMAGE. 28 | ----------------------------------------------------------------------------- 29 | ソースコード形式かバイナリ形式か、変更するかしないかを問わず、以下の条件を満た 30 | す場合に限り、再頒布および使用が許可されます。 31 | 32 | ソースコードを再頒布する場合、上記の著作権表示、本条件一覧、および下記免責条項 33 | を含めること。 34 | バイナリ形式で再頒布する場合、頒布物に付属のドキュメント等の資料に、上記の著作 35 | 権表示、本条件一覧、および下記免責条項を含めること。 36 | 書面による特別の許可なしに、本ソフトウェアから派生した製品の宣伝または販売促進 37 | に、著作権者の名前またはコントリビューターの名前を使用してはならない。 38 | 本ソフトウェアは、著作権者およびコントリビューターによって「現状のまま」提供さ 39 | れており、明示黙示を問わず、商業的な使用可能性、および特定の目的に対する適合性 40 | に関する暗黙の保証も含め、またそれに限定されない、いかなる保証もありません。 41 | 著作権者もコントリビューターも、事由のいかんを問わず、 損害発生の原因いかんを 42 | 問わず、かつ責任の根拠が契約であるか厳格責任であるか(過失その他の)不法行為で 43 | あるかを問わず、仮にそのような損害が発生する可能性を知らされていたとしても、 44 | 本ソフトウェアの使用によって発生した(代替品または代用サービスの調達、使用の 45 | 喪失、データの喪失、利益の喪失、業務の中断も含め、またそれに限定されない)直接 46 | 損害、間接損害、偶発的な損害、特別損害、懲罰的損害、または結果損害について、 47 | 一切責任を負わないものとします。 48 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /Relocation/Relocation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common/IPrefix.h" 3 | 4 | class RelocationManager 5 | { 6 | public: 7 | RelocationManager(); 8 | 9 | static uintptr_t s_baseAddr; 10 | }; 11 | 12 | // use this for addresses that represent pointers to a type T 13 | template 14 | class RelocPtr 15 | { 16 | public: 17 | RelocPtr(uintptr_t offset) 18 | :m_offset(offset + RelocationManager::s_baseAddr) 19 | { 20 | // 21 | } 22 | 23 | operator T *() const 24 | { 25 | return GetPtr(); 26 | } 27 | 28 | T * operator->() const 29 | { 30 | return GetPtr(); 31 | } 32 | 33 | T * GetPtr() const 34 | { 35 | return reinterpret_cast (m_offset); 36 | } 37 | 38 | const T * GetConst() const 39 | { 40 | return reinterpret_cast (m_offset); 41 | } 42 | 43 | uintptr_t GetUIntPtr() const 44 | { 45 | return m_offset; 46 | } 47 | 48 | RelocPtr & operator=(RelocPtr & rhs) 49 | { 50 | m_offset = rhs.m_offset; 51 | } 52 | 53 | RelocPtr & operator=(T * rhs) 54 | { 55 | m_offset = reinterpret_cast(rhs); 56 | _MESSAGE("%s=%08X", __FUNCTION__, reinterpret_cast(rhs) - RelocationManager::s_baseAddr); 57 | return (*this); 58 | } 59 | private: 60 | uintptr_t m_offset; 61 | 62 | // hide 63 | RelocPtr(); 64 | RelocPtr(RelocPtr & rhs); 65 | }; 66 | 67 | // use this for direct addresses to types T. needed to avoid ambiguity 68 | template 69 | class RelocAddr 70 | { 71 | public: 72 | RelocAddr(uintptr_t offset) 73 | :m_offset(reinterpret_cast (offset + RelocationManager::s_baseAddr)) 74 | { 75 | // 76 | } 77 | 78 | operator T() 79 | { 80 | return reinterpret_cast (m_offset); 81 | } 82 | 83 | uintptr_t GetUIntPtr() const 84 | { 85 | return reinterpret_cast (m_offset); 86 | } 87 | 88 | RelocAddr& operator=(T rhs) 89 | { 90 | m_offset = reinterpret_cast (rhs); 91 | _MESSAGE("%s=%08X", __FUNCTION__, reinterpret_cast(rhs) - RelocationManager::s_baseAddr); 92 | return (*this); 93 | } 94 | 95 | RelocAddr& operator=(const RelocAddr& rhs) 96 | { 97 | m_offset = rhs.m_offset; 98 | return (*this); 99 | } 100 | 101 | RelocAddr& operator+=(intptr_t offset) 102 | { 103 | m_offset = reinterpret_cast(reinterpret_cast(m_offset) + offset); 104 | return (*this); 105 | } 106 | private: 107 | // apparently you can't reinterpret_cast from a type to the same type 108 | // that's kind of stupid and makes it impossible to use this for uintptr_ts if I use the same type 109 | // so we make a new type by storing the data in a pointer to this useless struct 110 | // at least this is hidden by a wrapper 111 | struct BlockConversionType { }; 112 | BlockConversionType * m_offset; 113 | 114 | // hide 115 | RelocAddr(); 116 | RelocAddr(RelocAddr & rhs); 117 | //RelocAddr & operator=(RelocAddr & rhs); 118 | }; 119 | -------------------------------------------------------------------------------- /SkyrimUncapper/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "skse64_common/Relocation.h" 3 | #include "skse64_common/BranchTrampoline.h" 4 | #include "Hook_Skill.h" 5 | #include "Settings.h" 6 | #include "Utilities.h" 7 | #include "PluginAPI.h" 8 | 9 | #ifdef _DEBUG 10 | #include "ScanMemory.h" 11 | #endif 12 | 13 | #define GAME_EXE_NAME "SkyrimSE.exe" 14 | 15 | IDebugLog gLog; 16 | void * g_moduleHandle = nullptr; 17 | 18 | 19 | UInt32 g_pluginHandle = kPluginHandle_Invalid; 20 | 21 | 22 | void SkyrimUncapper_Initialize(void) 23 | { 24 | static bool isInit = false; 25 | if (isInit) return; 26 | isInit = true; 27 | 28 | gLog.OpenRelative(CSIDL_MYDOCUMENTS, "\\My Games\\Skyrim Special Edition\\SkyrimUncapper\\SkyrimUncapper.log"); 29 | 30 | _MESSAGE("imagebase = %016I64X ===============", GetModuleHandle(NULL)); 31 | _MESSAGE("reloc mgr imagebase = %016I64X ==============", RelocationManager::s_baseAddr); 32 | 33 | std::string version; 34 | 35 | if (GetFileVersion(GAME_EXE_NAME, version)) 36 | { 37 | 38 | } 39 | else 40 | { 41 | MessageBox(NULL, "Can't access SkyrimSE.exe's version info, SkyrimUncapper won't be initialized", "SkyrimUncapper", MB_OK); 42 | return; 43 | } 44 | 45 | if (!g_branchTrampoline.Create(1024 * 64)) 46 | { 47 | _ERROR("couldn't create branch trampoline. this is fatal. skipping remainder of init process."); 48 | return; 49 | } 50 | 51 | if (!g_localTrampoline.Create(1024 * 64, g_moduleHandle)) 52 | { 53 | _ERROR("couldn't create codegen buffer. this is fatal. skipping remainder of init process."); 54 | return; 55 | } 56 | settings.ReadConfig(); 57 | 58 | Hook_Skill_Commit(); 59 | 60 | _MESSAGE("Init complete"); 61 | } 62 | 63 | 64 | extern "C" { 65 | 66 | BOOL WINAPI DllMain(HANDLE hDllHandle, DWORD dwReason, LPVOID lpreserved) 67 | { 68 | switch (dwReason) 69 | { 70 | case DLL_PROCESS_ATTACH: 71 | g_moduleHandle = (void *)hDllHandle; 72 | SkyrimUncapper_Initialize(); 73 | break; 74 | 75 | case DLL_PROCESS_DETACH: 76 | break; 77 | }; 78 | 79 | return TRUE; 80 | } 81 | 82 | bool StartSkyrimUncapper(void) 83 | { 84 | g_moduleHandle = reinterpret_cast(GetModuleHandleA("SkyrimUncapper.dll")); 85 | SkyrimUncapper_Initialize(); 86 | return true; 87 | } 88 | 89 | bool SKSEPlugin_Query(const SKSEInterface * skse, PluginInfo * info) 90 | { 91 | 92 | g_moduleHandle = reinterpret_cast(GetModuleHandleA("SkyrimUncapper.dll")); 93 | 94 | SkyrimUncapper_Initialize(); 95 | 96 | info->infoVersion = PluginInfo::kInfoVersion; 97 | info->name = "SkyrimUncapper"; 98 | info->version = 1; 99 | 100 | g_pluginHandle = skse->GetPluginHandle(); 101 | 102 | if (skse->isEditor) 103 | { 104 | _ERROR("loaded in editor, marking as incompatible"); 105 | 106 | return false; 107 | } 108 | return true; 109 | } 110 | 111 | bool SKSEPlugin_Load(const void * skse) 112 | { 113 | return true; 114 | } 115 | 116 | void LoadINIConfig() 117 | { 118 | #ifdef _DEBUG 119 | settings.ReadConfig(); 120 | #endif 121 | } 122 | }; -------------------------------------------------------------------------------- /SkyrimUncapper/Settings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "SimpleIni.h" 4 | #include 5 | #include 6 | #include 7 | 8 | #define CONFIG_VERSION 4 9 | 10 | template 11 | struct SettingList : public std::map < UInt32, T> 12 | { 13 | typename SettingList::mapped_type GetValue(const typename SettingList::key_type& index) 14 | { 15 | typename SettingList::mapped_type result = NULL; 16 | for (auto it = rbegin(); it != rend(); ++it) 17 | { 18 | if (it->first <= index) 19 | { 20 | result = it->second; 21 | break; 22 | } 23 | } 24 | return result; 25 | } 26 | 27 | float GetDecimal(const typename SettingList::key_type& index) 28 | { 29 | float decimal = 0.00f; 30 | for (auto iterator = rbegin(); iterator != rend(); ++iterator) 31 | { 32 | if (iterator->first < index) 33 | { 34 | if (iterator != --rend()) 35 | { 36 | decimal += (index - iterator->first) * (iterator->second); 37 | auto it = iterator; 38 | ++it; 39 | for (; it != rend(); ++it) 40 | { 41 | auto nextIterator = it; 42 | --nextIterator; 43 | if (it != --rend()) 44 | decimal += (nextIterator->first - it->first) * (it->second); 45 | else 46 | decimal += (nextIterator->first - it->first - 1) * (it->second); 47 | } 48 | } 49 | else 50 | decimal += (index - iterator->first - 1) * (iterator->second); 51 | break; 52 | } 53 | } 54 | return decimal - static_cast(decimal); 55 | } 56 | }; 57 | 58 | struct SettingsGeneral 59 | { 60 | UInt32 version; 61 | std::string author; 62 | }; 63 | 64 | struct SettingsLegendarySkill 65 | { 66 | bool bLegendaryKeepSkillLevel; 67 | bool bHideLegendaryButton; 68 | UInt32 iSkillLevelEnableLegendary; 69 | UInt32 iSkillLevelAfterLegendary; 70 | }; 71 | 72 | struct Settings 73 | { 74 | enum { 75 | kAdvanceableSkillOffset = 6, 76 | kNumAdvanceableSkills = 18 77 | }; 78 | 79 | Settings(); 80 | void ReadConfig(); 81 | void SaveConfig(CSimpleIniA* ini, const std::string& path); 82 | 83 | SettingsGeneral settingsGeneral; 84 | SettingsLegendarySkill settingsLegendarySkill; 85 | SettingList settingsSkillCaps; 86 | SettingList settingsSkillFormulaCaps; 87 | SettingList settingsSkillExpGainMults; 88 | SettingList settingsLevelSkillExpMults; 89 | SettingList settingsPerksAtLevelUp; 90 | SettingList settingsHealthAtLevelUp; 91 | SettingList settingsMagickaAtLevelUp; 92 | SettingList settingsStaminaAtLevelUp; 93 | SettingList settingsCarryWeightAtHealthLevelUp; 94 | SettingList settingsCarryWeightAtMagickaLevelUp; 95 | SettingList settingsCarryWeightAtStaminaLevelUp; 96 | SettingList settingsSkillExpGainMultsWithSkills[kNumAdvanceableSkills]; 97 | SettingList settingsSkillExpGainMultsWithPCLevel[kNumAdvanceableSkills]; 98 | SettingList settingsLevelSkillExpMultsWithSkills[kNumAdvanceableSkills]; 99 | SettingList settingsLevelSkillExpMultsWithPCLevel[kNumAdvanceableSkills]; 100 | }; 101 | 102 | extern Settings settings; 103 | -------------------------------------------------------------------------------- /Relocation/Pattern.h: -------------------------------------------------------------------------------- 1 | #ifndef __PATTERN_H__ 2 | #define __PATTERN_H__ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | 10 | // from boost someplace 11 | template 12 | struct basic_fnv_1 { 13 | 14 | uint64_t operator()(std::string const& text) const 15 | { 16 | 17 | uint64_t hash = OffsetBasis; 18 | for (std::string::const_iterator it = text.begin(), end = text.end(); it != end; ++it) { 19 | 20 | hash *= FnvPrime; 21 | hash ^= *it; 22 | } 23 | 24 | return hash; 25 | } 26 | }; 27 | 28 | const uint64_t fnv_prime = 1099511628211u; 29 | const uint64_t fnv_offset_basis = 14695981039346656037u; 30 | 31 | typedef basic_fnv_1 fnv_1; 32 | 33 | namespace Utility { 34 | 35 | void TransformPattern(const std::string & pattern, std::string & data, std::string & mask); 36 | 37 | class executable_meta { 38 | private: 39 | 40 | uintptr_t m_begin; 41 | uintptr_t m_end; 42 | 43 | public: 44 | 45 | executable_meta() 46 | : m_begin(0), m_end(0) { 47 | } 48 | 49 | void EnsureInit(); 50 | 51 | inline uintptr_t begin() { return m_begin; } 52 | inline uintptr_t end() { return m_end; } 53 | }; 54 | 55 | class pattern_match { 56 | private: 57 | 58 | void * m_pointer; 59 | 60 | public: 61 | 62 | inline pattern_match(void * pointer) { 63 | 64 | m_pointer = pointer; 65 | } 66 | 67 | template 68 | T * get(int offset) { 69 | 70 | if (m_pointer == nullptr) { 71 | return nullptr; 72 | } 73 | 74 | char * ptr = reinterpret_cast(m_pointer); 75 | return reinterpret_cast(ptr + offset); 76 | } 77 | 78 | template 79 | T * get() { 80 | 81 | return get(0); 82 | } 83 | }; 84 | 85 | typedef std::vector matchVec; 86 | 87 | class pattern { 88 | private: 89 | 90 | std::string m_bytes; 91 | std::string m_mask; 92 | 93 | uint64_t m_hash; 94 | 95 | size_t m_size; 96 | 97 | matchVec m_matches; 98 | 99 | bool m_matched; 100 | 101 | private: 102 | 103 | void Initialize(const char* pattern, size_t length); 104 | 105 | bool ConsiderMatch(uintptr_t offset); 106 | 107 | void EnsureMatches(int maxCount); 108 | 109 | public: 110 | 111 | pattern(const char* pattern) { 112 | 113 | Initialize(pattern, strlen(pattern)); 114 | } 115 | 116 | inline pattern & count(int expected) { 117 | 118 | if (!m_matched) { 119 | EnsureMatches(expected); 120 | } 121 | 122 | return *this; 123 | } 124 | 125 | inline size_t size() { 126 | 127 | if (!m_matched) { 128 | EnsureMatches(INT_MAX); 129 | } 130 | 131 | return m_matches.size(); 132 | } 133 | 134 | inline pattern_match & get(int index) { 135 | 136 | if (!m_matched) { 137 | EnsureMatches(INT_MAX); 138 | } 139 | 140 | if (m_matches.size() == 0) { 141 | 142 | m_matches.push_back(pattern_match(nullptr)); 143 | return m_matches[0]; 144 | } 145 | 146 | return m_matches[index]; 147 | } 148 | 149 | public: 150 | // define a hint 151 | static void hint(uint64_t hash, uintptr_t address); 152 | }; 153 | } 154 | 155 | #endif // __PATTERN_H__ 156 | -------------------------------------------------------------------------------- /SkyrimUncapper/SkyrimUncapper.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {a3da341a-f9f1-4b46-83b0-e09bf2c453a8} 18 | 19 | 20 | {84613649-3cc1-4ce3-a099-ea611d9d6183} 21 | 22 | 23 | {6db92acb-0d47-42fe-8ff8-87e1cd933bdd} 24 | 25 | 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | SimpleIni 35 | 36 | 37 | SKSE 38 | 39 | 40 | Header Files 41 | 42 | 43 | Header Files 44 | 45 | 46 | JIT 47 | 48 | 49 | Header Files 50 | 51 | 52 | Header Files 53 | 54 | 55 | Header Files 56 | 57 | 58 | 59 | 60 | Source Files 61 | 62 | 63 | Source Files 64 | 65 | 66 | Source Files 67 | 68 | 69 | SKSE 70 | 71 | 72 | Source Files 73 | 74 | 75 | Source Files 76 | 77 | 78 | Source Files 79 | 80 | 81 | 82 | 83 | Resource Files 84 | 85 | 86 | 87 | 88 | Resource Files 89 | 90 | 91 | -------------------------------------------------------------------------------- /SkyrimUncapper/Utilities.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "skse64_common/Relocation.h" 5 | 6 | 7 | // this has been tested to work for non-varargs functions 8 | // varargs functions end up with 'this' passed as the last parameter (ie. probably broken) 9 | // do NOT use with classes that have multiple inheritance 10 | 11 | // if many member functions are to be declared, use MEMBER_FN_PREFIX to create a type with a known name 12 | // so it doesn't need to be restated throughout the member list 13 | 14 | // all of the weirdness with the _GetType function is because you can't declare a static const pointer 15 | // inside the class definition. we sadly can't inline anymore because of relocation. 16 | 17 | // RelocPtr only works at a global scope, which we can't handle or we'd be bypassing the function route altogether 18 | 19 | #define MEMBER_FN_PREFIX(className) \ 20 | typedef className _MEMBER_FN_BASE_TYPE 21 | 22 | #define DEFINE_MEMBER_FN_LONG(className, functionName, retnType, address, ...) \ 23 | typedef retnType (className::* _##functionName##_type)(__VA_ARGS__); \ 24 | \ 25 | inline _##functionName##_type * _##functionName##_GetPtr(void) \ 26 | { \ 27 | static uintptr_t _address; \ 28 | _address = address + RelocationManager::s_baseAddr; \ 29 | return (_##functionName##_type *)&_address; \ 30 | } 31 | 32 | #define DEFINE_MEMBER_FN(functionName, retnType, address, ...) \ 33 | DEFINE_MEMBER_FN_LONG(_MEMBER_FN_BASE_TYPE, functionName, retnType, address, __VA_ARGS__) 34 | 35 | #define DEFINE_STATIC_HEAP(staticAllocate, staticFree) \ 36 | static void * operator new(std::size_t size) \ 37 | { \ 38 | return staticAllocate(size); \ 39 | } \ 40 | static void * operator new(std::size_t size, const std::nothrow_t &) \ 41 | { \ 42 | return staticAllocate(size); \ 43 | } \ 44 | static void * operator new(std::size_t size, void * ptr) \ 45 | { \ 46 | return ptr; \ 47 | } \ 48 | static void operator delete(void * ptr) \ 49 | { \ 50 | staticFree(ptr); \ 51 | } \ 52 | static void operator delete(void * ptr, const std::nothrow_t &) \ 53 | { \ 54 | staticFree(ptr); \ 55 | } \ 56 | static void operator delete(void *, void *) \ 57 | { \ 58 | } 59 | 60 | #define CALL_MEMBER_FN(obj, fn) \ 61 | ((*(obj)).*(*((obj)->_##fn##_GetPtr()))) 62 | 63 | // this is the solution to getting a pointer-to-member-function pointer 64 | template 65 | uintptr_t GetFnAddr(T src) 66 | { 67 | union 68 | { 69 | uintptr_t u; 70 | T t; 71 | } data; 72 | 73 | data.t = src; 74 | 75 | return data.u; 76 | } 77 | 78 | std::string GetRuntimePath(); 79 | std::string GetRuntimeName(); 80 | const std::string & GetRuntimeDirectory(); 81 | 82 | std::string GetCurrentDLLDirectory(); 83 | bool GetFileVersion(const std::string& szModuleName, std::string& RetStr); 84 | 85 | const std::string & GetConfigPath(); 86 | std::string GetConfigOption(const char * section, const char * key); 87 | bool GetConfigOption_UInt32(const char * section, const char * key, UInt32 * dataOut); 88 | 89 | const std::string & GetOSInfoStr(); 90 | uintptr_t BinarySearch(const std::vector& binary); 91 | 92 | void * GetIATAddr(void * module, const char * searchDllName, const char * searchImportName); 93 | 94 | const char * GetObjectClassName(void * objBase); 95 | void DumpClass(void * theClassPtr, UInt64 nIntsToDump); 96 | -------------------------------------------------------------------------------- /SkyrimUncapper.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common_vc11", "..\common\common_vc11.vcxproj", "{D4C128A1-73DC-4941-A453-CE55AF239BA8}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SkyrimUncapper", "SkyrimUncapper\SkyrimUncapper.vcxproj", "{372607D0-4DA8-41D0-B34A-D53E016DC12A}" 9 | EndProject 10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TEST", "TEST\TEST.vcxproj", "{92DA6CF1-F9AE-4CC1-B6E3-8175848DF894}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|ARM = Debug|ARM 15 | Debug|Mixed Platforms = Debug|Mixed Platforms 16 | Debug|Win32 = Debug|Win32 17 | Debug|x64 = Debug|x64 18 | Release|ARM = Release|ARM 19 | Release|Mixed Platforms = Release|Mixed Platforms 20 | Release|Win32 = Release|Win32 21 | Release|x64 = Release|x64 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {D4C128A1-73DC-4941-A453-CE55AF239BA8}.Debug|ARM.ActiveCfg = Debug|Win32 25 | {D4C128A1-73DC-4941-A453-CE55AF239BA8}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 26 | {D4C128A1-73DC-4941-A453-CE55AF239BA8}.Debug|Mixed Platforms.Build.0 = Debug|Win32 27 | {D4C128A1-73DC-4941-A453-CE55AF239BA8}.Debug|Win32.ActiveCfg = Debug|Win32 28 | {D4C128A1-73DC-4941-A453-CE55AF239BA8}.Debug|Win32.Build.0 = Debug|Win32 29 | {D4C128A1-73DC-4941-A453-CE55AF239BA8}.Debug|x64.ActiveCfg = Debug|x64 30 | {D4C128A1-73DC-4941-A453-CE55AF239BA8}.Debug|x64.Build.0 = Debug|x64 31 | {D4C128A1-73DC-4941-A453-CE55AF239BA8}.Release|ARM.ActiveCfg = Release|Win32 32 | {D4C128A1-73DC-4941-A453-CE55AF239BA8}.Release|Mixed Platforms.ActiveCfg = Release|Win32 33 | {D4C128A1-73DC-4941-A453-CE55AF239BA8}.Release|Mixed Platforms.Build.0 = Release|Win32 34 | {D4C128A1-73DC-4941-A453-CE55AF239BA8}.Release|Win32.ActiveCfg = Release|Win32 35 | {D4C128A1-73DC-4941-A453-CE55AF239BA8}.Release|Win32.Build.0 = Release|Win32 36 | {D4C128A1-73DC-4941-A453-CE55AF239BA8}.Release|x64.ActiveCfg = Release|x64 37 | {D4C128A1-73DC-4941-A453-CE55AF239BA8}.Release|x64.Build.0 = Release|x64 38 | {372607D0-4DA8-41D0-B34A-D53E016DC12A}.Debug|ARM.ActiveCfg = Debug|Win32 39 | {372607D0-4DA8-41D0-B34A-D53E016DC12A}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 40 | {372607D0-4DA8-41D0-B34A-D53E016DC12A}.Debug|Mixed Platforms.Build.0 = Debug|Win32 41 | {372607D0-4DA8-41D0-B34A-D53E016DC12A}.Debug|Win32.ActiveCfg = Debug|Win32 42 | {372607D0-4DA8-41D0-B34A-D53E016DC12A}.Debug|Win32.Build.0 = Debug|Win32 43 | {372607D0-4DA8-41D0-B34A-D53E016DC12A}.Debug|x64.ActiveCfg = Debug|x64 44 | {372607D0-4DA8-41D0-B34A-D53E016DC12A}.Debug|x64.Build.0 = Debug|x64 45 | {372607D0-4DA8-41D0-B34A-D53E016DC12A}.Debug|x64.Deploy.0 = Debug|x64 46 | {372607D0-4DA8-41D0-B34A-D53E016DC12A}.Release|ARM.ActiveCfg = Release|Win32 47 | {372607D0-4DA8-41D0-B34A-D53E016DC12A}.Release|Mixed Platforms.ActiveCfg = Release|Win32 48 | {372607D0-4DA8-41D0-B34A-D53E016DC12A}.Release|Mixed Platforms.Build.0 = Release|Win32 49 | {372607D0-4DA8-41D0-B34A-D53E016DC12A}.Release|Win32.ActiveCfg = Release|Win32 50 | {372607D0-4DA8-41D0-B34A-D53E016DC12A}.Release|Win32.Build.0 = Release|Win32 51 | {372607D0-4DA8-41D0-B34A-D53E016DC12A}.Release|x64.ActiveCfg = Release|x64 52 | {372607D0-4DA8-41D0-B34A-D53E016DC12A}.Release|x64.Build.0 = Release|x64 53 | {92DA6CF1-F9AE-4CC1-B6E3-8175848DF894}.Debug|ARM.ActiveCfg = Debug|Win32 54 | {92DA6CF1-F9AE-4CC1-B6E3-8175848DF894}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 55 | {92DA6CF1-F9AE-4CC1-B6E3-8175848DF894}.Debug|Mixed Platforms.Build.0 = Debug|Win32 56 | {92DA6CF1-F9AE-4CC1-B6E3-8175848DF894}.Debug|Win32.ActiveCfg = Debug|Win32 57 | {92DA6CF1-F9AE-4CC1-B6E3-8175848DF894}.Debug|Win32.Build.0 = Debug|Win32 58 | {92DA6CF1-F9AE-4CC1-B6E3-8175848DF894}.Debug|x64.ActiveCfg = Debug|Win32 59 | {92DA6CF1-F9AE-4CC1-B6E3-8175848DF894}.Release|ARM.ActiveCfg = Release|Win32 60 | {92DA6CF1-F9AE-4CC1-B6E3-8175848DF894}.Release|Mixed Platforms.ActiveCfg = Release|Win32 61 | {92DA6CF1-F9AE-4CC1-B6E3-8175848DF894}.Release|Mixed Platforms.Build.0 = Release|Win32 62 | {92DA6CF1-F9AE-4CC1-B6E3-8175848DF894}.Release|Win32.ActiveCfg = Release|Win32 63 | {92DA6CF1-F9AE-4CC1-B6E3-8175848DF894}.Release|Win32.Build.0 = Release|Win32 64 | {92DA6CF1-F9AE-4CC1-B6E3-8175848DF894}.Release|x64.ActiveCfg = Release|Win32 65 | EndGlobalSection 66 | GlobalSection(SolutionProperties) = preSolution 67 | HideSolutionNode = FALSE 68 | EndGlobalSection 69 | EndGlobal 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Microsoft Azure ApplicationInsights config file 170 | ApplicationInsights.config 171 | 172 | # Windows Store app package directory 173 | AppPackages/ 174 | BundleArtifacts/ 175 | 176 | # Visual Studio cache files 177 | # files ending in .cache can be ignored 178 | *.[Cc]ache 179 | # but keep track of directories ending in .cache 180 | !*.[Cc]ache/ 181 | 182 | # Others 183 | ClientBin/ 184 | [Ss]tyle[Cc]op.* 185 | ~$* 186 | *~ 187 | *.dbmdl 188 | *.dbproj.schemaview 189 | *.pfx 190 | *.publishsettings 191 | node_modules/ 192 | orleans.codegen.cs 193 | 194 | # RIA/Silverlight projects 195 | Generated_Code/ 196 | 197 | # Backup & report files from converting an old project file 198 | # to a newer Visual Studio version. Backup files are not needed, 199 | # because we have git ;-) 200 | _UpgradeReport_Files/ 201 | Backup*/ 202 | UpgradeLog*.XML 203 | UpgradeLog*.htm 204 | 205 | # SQL Server files 206 | *.mdf 207 | *.ldf 208 | 209 | # Business Intelligence projects 210 | *.rdl.data 211 | *.bim.layout 212 | *.bim_*.settings 213 | 214 | # Microsoft Fakes 215 | FakesAssemblies/ 216 | 217 | # GhostDoc plugin setting file 218 | *.GhostDoc.xml 219 | 220 | # Node.js Tools for Visual Studio 221 | .ntvs_analysis.dat 222 | 223 | # Visual Studio 6 build log 224 | *.plg 225 | 226 | # Visual Studio 6 workspace options file 227 | *.opt 228 | 229 | # Visual Studio LightSwitch build output 230 | **/*.HTMLClient/GeneratedArtifacts 231 | **/*.DesktopClient/GeneratedArtifacts 232 | **/*.DesktopClient/ModelManifest.xml 233 | **/*.Server/GeneratedArtifacts 234 | **/*.Server/ModelManifest.xml 235 | _Pvt_Extensions 236 | 237 | # LightSwitch generated files 238 | GeneratedArtifacts/ 239 | ModelManifest.xml 240 | 241 | # Paket dependency manager 242 | .paket/paket.exe 243 | 244 | # FAKE - F# Make 245 | .fake/ -------------------------------------------------------------------------------- /xbyak/xbyak_bin2hex.h: -------------------------------------------------------------------------------- 1 | enum { 2 | B00000000= 0, 3 | B00000001= 1, 4 | B00000010= 2, 5 | B00000011= 3, 6 | B00000100= 4, 7 | B00000101= 5, 8 | B00000110= 6, 9 | B00000111= 7, 10 | B00001000= 8, 11 | B00001001= 9, 12 | B00001010= 10, 13 | B00001011= 11, 14 | B00001100= 12, 15 | B00001101= 13, 16 | B00001110= 14, 17 | B00001111= 15, 18 | B00010000= 16, 19 | B00010001= 17, 20 | B00010010= 18, 21 | B00010011= 19, 22 | B00010100= 20, 23 | B00010101= 21, 24 | B00010110= 22, 25 | B00010111= 23, 26 | B00011000= 24, 27 | B00011001= 25, 28 | B00011010= 26, 29 | B00011011= 27, 30 | B00011100= 28, 31 | B00011101= 29, 32 | B00011110= 30, 33 | B00011111= 31, 34 | B00100000= 32, 35 | B00100001= 33, 36 | B00100010= 34, 37 | B00100011= 35, 38 | B00100100= 36, 39 | B00100101= 37, 40 | B00100110= 38, 41 | B00100111= 39, 42 | B00101000= 40, 43 | B00101001= 41, 44 | B00101010= 42, 45 | B00101011= 43, 46 | B00101100= 44, 47 | B00101101= 45, 48 | B00101110= 46, 49 | B00101111= 47, 50 | B00110000= 48, 51 | B00110001= 49, 52 | B00110010= 50, 53 | B00110011= 51, 54 | B00110100= 52, 55 | B00110101= 53, 56 | B00110110= 54, 57 | B00110111= 55, 58 | B00111000= 56, 59 | B00111001= 57, 60 | B00111010= 58, 61 | B00111011= 59, 62 | B00111100= 60, 63 | B00111101= 61, 64 | B00111110= 62, 65 | B00111111= 63, 66 | B01000000= 64, 67 | B01000001= 65, 68 | B01000010= 66, 69 | B01000011= 67, 70 | B01000100= 68, 71 | B01000101= 69, 72 | B01000110= 70, 73 | B01000111= 71, 74 | B01001000= 72, 75 | B01001001= 73, 76 | B01001010= 74, 77 | B01001011= 75, 78 | B01001100= 76, 79 | B01001101= 77, 80 | B01001110= 78, 81 | B01001111= 79, 82 | B01010000= 80, 83 | B01010001= 81, 84 | B01010010= 82, 85 | B01010011= 83, 86 | B01010100= 84, 87 | B01010101= 85, 88 | B01010110= 86, 89 | B01010111= 87, 90 | B01011000= 88, 91 | B01011001= 89, 92 | B01011010= 90, 93 | B01011011= 91, 94 | B01011100= 92, 95 | B01011101= 93, 96 | B01011110= 94, 97 | B01011111= 95, 98 | B01100000= 96, 99 | B01100001= 97, 100 | B01100010= 98, 101 | B01100011= 99, 102 | B01100100= 100, 103 | B01100101= 101, 104 | B01100110= 102, 105 | B01100111= 103, 106 | B01101000= 104, 107 | B01101001= 105, 108 | B01101010= 106, 109 | B01101011= 107, 110 | B01101100= 108, 111 | B01101101= 109, 112 | B01101110= 110, 113 | B01101111= 111, 114 | B01110000= 112, 115 | B01110001= 113, 116 | B01110010= 114, 117 | B01110011= 115, 118 | B01110100= 116, 119 | B01110101= 117, 120 | B01110110= 118, 121 | B01110111= 119, 122 | B01111000= 120, 123 | B01111001= 121, 124 | B01111010= 122, 125 | B01111011= 123, 126 | B01111100= 124, 127 | B01111101= 125, 128 | B01111110= 126, 129 | B01111111= 127, 130 | B10000000= 128, 131 | B10000001= 129, 132 | B10000010= 130, 133 | B10000011= 131, 134 | B10000100= 132, 135 | B10000101= 133, 136 | B10000110= 134, 137 | B10000111= 135, 138 | B10001000= 136, 139 | B10001001= 137, 140 | B10001010= 138, 141 | B10001011= 139, 142 | B10001100= 140, 143 | B10001101= 141, 144 | B10001110= 142, 145 | B10001111= 143, 146 | B10010000= 144, 147 | B10010001= 145, 148 | B10010010= 146, 149 | B10010011= 147, 150 | B10010100= 148, 151 | B10010101= 149, 152 | B10010110= 150, 153 | B10010111= 151, 154 | B10011000= 152, 155 | B10011001= 153, 156 | B10011010= 154, 157 | B10011011= 155, 158 | B10011100= 156, 159 | B10011101= 157, 160 | B10011110= 158, 161 | B10011111= 159, 162 | B10100000= 160, 163 | B10100001= 161, 164 | B10100010= 162, 165 | B10100011= 163, 166 | B10100100= 164, 167 | B10100101= 165, 168 | B10100110= 166, 169 | B10100111= 167, 170 | B10101000= 168, 171 | B10101001= 169, 172 | B10101010= 170, 173 | B10101011= 171, 174 | B10101100= 172, 175 | B10101101= 173, 176 | B10101110= 174, 177 | B10101111= 175, 178 | B10110000= 176, 179 | B10110001= 177, 180 | B10110010= 178, 181 | B10110011= 179, 182 | B10110100= 180, 183 | B10110101= 181, 184 | B10110110= 182, 185 | B10110111= 183, 186 | B10111000= 184, 187 | B10111001= 185, 188 | B10111010= 186, 189 | B10111011= 187, 190 | B10111100= 188, 191 | B10111101= 189, 192 | B10111110= 190, 193 | B10111111= 191, 194 | B11000000= 192, 195 | B11000001= 193, 196 | B11000010= 194, 197 | B11000011= 195, 198 | B11000100= 196, 199 | B11000101= 197, 200 | B11000110= 198, 201 | B11000111= 199, 202 | B11001000= 200, 203 | B11001001= 201, 204 | B11001010= 202, 205 | B11001011= 203, 206 | B11001100= 204, 207 | B11001101= 205, 208 | B11001110= 206, 209 | B11001111= 207, 210 | B11010000= 208, 211 | B11010001= 209, 212 | B11010010= 210, 213 | B11010011= 211, 214 | B11010100= 212, 215 | B11010101= 213, 216 | B11010110= 214, 217 | B11010111= 215, 218 | B11011000= 216, 219 | B11011001= 217, 220 | B11011010= 218, 221 | B11011011= 219, 222 | B11011100= 220, 223 | B11011101= 221, 224 | B11011110= 222, 225 | B11011111= 223, 226 | B11100000= 224, 227 | B11100001= 225, 228 | B11100010= 226, 229 | B11100011= 227, 230 | B11100100= 228, 231 | B11100101= 229, 232 | B11100110= 230, 233 | B11100111= 231, 234 | B11101000= 232, 235 | B11101001= 233, 236 | B11101010= 234, 237 | B11101011= 235, 238 | B11101100= 236, 239 | B11101101= 237, 240 | B11101110= 238, 241 | B11101111= 239, 242 | B11110000= 240, 243 | B11110001= 241, 244 | B11110010= 242, 245 | B11110011= 243, 246 | B11110100= 244, 247 | B11110101= 245, 248 | B11110110= 246, 249 | B11110111= 247, 250 | B11111000= 248, 251 | B11111001= 249, 252 | B11111010= 250, 253 | B11111011= 251, 254 | B11111100= 252, 255 | B11111101= 253, 256 | B11111110= 254, 257 | B11111111= 255 258 | }; 259 | -------------------------------------------------------------------------------- /SkyrimUncapper/xbyak_bin2hex.h: -------------------------------------------------------------------------------- 1 | enum { 2 | B00000000= 0, 3 | B00000001= 1, 4 | B00000010= 2, 5 | B00000011= 3, 6 | B00000100= 4, 7 | B00000101= 5, 8 | B00000110= 6, 9 | B00000111= 7, 10 | B00001000= 8, 11 | B00001001= 9, 12 | B00001010= 10, 13 | B00001011= 11, 14 | B00001100= 12, 15 | B00001101= 13, 16 | B00001110= 14, 17 | B00001111= 15, 18 | B00010000= 16, 19 | B00010001= 17, 20 | B00010010= 18, 21 | B00010011= 19, 22 | B00010100= 20, 23 | B00010101= 21, 24 | B00010110= 22, 25 | B00010111= 23, 26 | B00011000= 24, 27 | B00011001= 25, 28 | B00011010= 26, 29 | B00011011= 27, 30 | B00011100= 28, 31 | B00011101= 29, 32 | B00011110= 30, 33 | B00011111= 31, 34 | B00100000= 32, 35 | B00100001= 33, 36 | B00100010= 34, 37 | B00100011= 35, 38 | B00100100= 36, 39 | B00100101= 37, 40 | B00100110= 38, 41 | B00100111= 39, 42 | B00101000= 40, 43 | B00101001= 41, 44 | B00101010= 42, 45 | B00101011= 43, 46 | B00101100= 44, 47 | B00101101= 45, 48 | B00101110= 46, 49 | B00101111= 47, 50 | B00110000= 48, 51 | B00110001= 49, 52 | B00110010= 50, 53 | B00110011= 51, 54 | B00110100= 52, 55 | B00110101= 53, 56 | B00110110= 54, 57 | B00110111= 55, 58 | B00111000= 56, 59 | B00111001= 57, 60 | B00111010= 58, 61 | B00111011= 59, 62 | B00111100= 60, 63 | B00111101= 61, 64 | B00111110= 62, 65 | B00111111= 63, 66 | B01000000= 64, 67 | B01000001= 65, 68 | B01000010= 66, 69 | B01000011= 67, 70 | B01000100= 68, 71 | B01000101= 69, 72 | B01000110= 70, 73 | B01000111= 71, 74 | B01001000= 72, 75 | B01001001= 73, 76 | B01001010= 74, 77 | B01001011= 75, 78 | B01001100= 76, 79 | B01001101= 77, 80 | B01001110= 78, 81 | B01001111= 79, 82 | B01010000= 80, 83 | B01010001= 81, 84 | B01010010= 82, 85 | B01010011= 83, 86 | B01010100= 84, 87 | B01010101= 85, 88 | B01010110= 86, 89 | B01010111= 87, 90 | B01011000= 88, 91 | B01011001= 89, 92 | B01011010= 90, 93 | B01011011= 91, 94 | B01011100= 92, 95 | B01011101= 93, 96 | B01011110= 94, 97 | B01011111= 95, 98 | B01100000= 96, 99 | B01100001= 97, 100 | B01100010= 98, 101 | B01100011= 99, 102 | B01100100= 100, 103 | B01100101= 101, 104 | B01100110= 102, 105 | B01100111= 103, 106 | B01101000= 104, 107 | B01101001= 105, 108 | B01101010= 106, 109 | B01101011= 107, 110 | B01101100= 108, 111 | B01101101= 109, 112 | B01101110= 110, 113 | B01101111= 111, 114 | B01110000= 112, 115 | B01110001= 113, 116 | B01110010= 114, 117 | B01110011= 115, 118 | B01110100= 116, 119 | B01110101= 117, 120 | B01110110= 118, 121 | B01110111= 119, 122 | B01111000= 120, 123 | B01111001= 121, 124 | B01111010= 122, 125 | B01111011= 123, 126 | B01111100= 124, 127 | B01111101= 125, 128 | B01111110= 126, 129 | B01111111= 127, 130 | B10000000= 128, 131 | B10000001= 129, 132 | B10000010= 130, 133 | B10000011= 131, 134 | B10000100= 132, 135 | B10000101= 133, 136 | B10000110= 134, 137 | B10000111= 135, 138 | B10001000= 136, 139 | B10001001= 137, 140 | B10001010= 138, 141 | B10001011= 139, 142 | B10001100= 140, 143 | B10001101= 141, 144 | B10001110= 142, 145 | B10001111= 143, 146 | B10010000= 144, 147 | B10010001= 145, 148 | B10010010= 146, 149 | B10010011= 147, 150 | B10010100= 148, 151 | B10010101= 149, 152 | B10010110= 150, 153 | B10010111= 151, 154 | B10011000= 152, 155 | B10011001= 153, 156 | B10011010= 154, 157 | B10011011= 155, 158 | B10011100= 156, 159 | B10011101= 157, 160 | B10011110= 158, 161 | B10011111= 159, 162 | B10100000= 160, 163 | B10100001= 161, 164 | B10100010= 162, 165 | B10100011= 163, 166 | B10100100= 164, 167 | B10100101= 165, 168 | B10100110= 166, 169 | B10100111= 167, 170 | B10101000= 168, 171 | B10101001= 169, 172 | B10101010= 170, 173 | B10101011= 171, 174 | B10101100= 172, 175 | B10101101= 173, 176 | B10101110= 174, 177 | B10101111= 175, 178 | B10110000= 176, 179 | B10110001= 177, 180 | B10110010= 178, 181 | B10110011= 179, 182 | B10110100= 180, 183 | B10110101= 181, 184 | B10110110= 182, 185 | B10110111= 183, 186 | B10111000= 184, 187 | B10111001= 185, 188 | B10111010= 186, 189 | B10111011= 187, 190 | B10111100= 188, 191 | B10111101= 189, 192 | B10111110= 190, 193 | B10111111= 191, 194 | B11000000= 192, 195 | B11000001= 193, 196 | B11000010= 194, 197 | B11000011= 195, 198 | B11000100= 196, 199 | B11000101= 197, 200 | B11000110= 198, 201 | B11000111= 199, 202 | B11001000= 200, 203 | B11001001= 201, 204 | B11001010= 202, 205 | B11001011= 203, 206 | B11001100= 204, 207 | B11001101= 205, 208 | B11001110= 206, 209 | B11001111= 207, 210 | B11010000= 208, 211 | B11010001= 209, 212 | B11010010= 210, 213 | B11010011= 211, 214 | B11010100= 212, 215 | B11010101= 213, 216 | B11010110= 214, 217 | B11010111= 215, 218 | B11011000= 216, 219 | B11011001= 217, 220 | B11011010= 218, 221 | B11011011= 219, 222 | B11011100= 220, 223 | B11011101= 221, 224 | B11011110= 222, 225 | B11011111= 223, 226 | B11100000= 224, 227 | B11100001= 225, 228 | B11100010= 226, 229 | B11100011= 227, 230 | B11100100= 228, 231 | B11100101= 229, 232 | B11100110= 230, 233 | B11100111= 231, 234 | B11101000= 232, 235 | B11101001= 233, 236 | B11101010= 234, 237 | B11101011= 235, 238 | B11101100= 236, 239 | B11101101= 237, 240 | B11101110= 238, 241 | B11101111= 239, 242 | B11110000= 240, 243 | B11110001= 241, 244 | B11110010= 242, 245 | B11110011= 243, 246 | B11110100= 244, 247 | B11110101= 245, 248 | B11110110= 246, 249 | B11110111= 247, 250 | B11111000= 248, 251 | B11111001= 249, 252 | B11111010= 250, 253 | B11111011= 251, 254 | B11111100= 252, 255 | B11111101= 253, 256 | B11111110= 254, 257 | B11111111= 255 258 | }; 259 | -------------------------------------------------------------------------------- /Relocation/Pattern.cpp: -------------------------------------------------------------------------------- 1 | #include "Pattern.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static std::multimap g_hints; 10 | 11 | void Utility::executable_meta::EnsureInit() { 12 | 13 | if (m_begin) { 14 | return; 15 | } 16 | 17 | HMODULE gameModule = GetModuleHandleA(NULL); 18 | 19 | m_begin = reinterpret_cast(gameModule); 20 | const IMAGE_DOS_HEADER * dosHeader = reinterpret_cast(gameModule); 21 | const IMAGE_NT_HEADERS * ntHeader = reinterpret_cast(reinterpret_cast(dosHeader) + dosHeader->e_lfanew); 22 | m_end = m_begin + ntHeader->OptionalHeader.SizeOfCode; 23 | } 24 | 25 | void Utility::TransformPattern(const std::string & pattern, std::string & data, std::string & mask) { 26 | 27 | std::stringstream dataStr; 28 | std::stringstream maskStr; 29 | 30 | uint8_t tempDigit = 0; 31 | bool tempFlag = false; 32 | 33 | for (auto & ch : pattern) { 34 | 35 | if (ch == ' ') { 36 | continue; 37 | } 38 | else if (ch == '?') { 39 | 40 | dataStr << '\x00'; 41 | maskStr << '?'; 42 | } 43 | else if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f')) { 44 | 45 | char str[] = { ch, 0 }; 46 | int thisDigit = strtol(str, nullptr, 16); 47 | 48 | if (!tempFlag) { 49 | 50 | tempDigit = (thisDigit << 4); 51 | tempFlag = true; 52 | } 53 | else { 54 | 55 | tempDigit |= thisDigit; 56 | tempFlag = false; 57 | 58 | dataStr << tempDigit; 59 | maskStr << 'x'; 60 | } 61 | } 62 | } 63 | 64 | data = dataStr.str(); 65 | mask = maskStr.str(); 66 | } 67 | 68 | void Utility::pattern::Initialize(const char* pattern, size_t length) { 69 | 70 | // get the hash for the base pattern 71 | std::string baseString(pattern, length); 72 | m_hash = fnv_1()(baseString); 73 | 74 | m_matched = false; 75 | 76 | // transform the base pattern from IDA format to canonical format 77 | TransformPattern(baseString, m_bytes, m_mask); 78 | 79 | m_size = m_mask.size(); 80 | 81 | // if there's hints, try those first 82 | auto range = g_hints.equal_range(m_hash); 83 | 84 | if (range.first != range.second) { 85 | 86 | std::for_each(range.first, range.second, [&](const std::pair & hint) { 87 | ConsiderMatch(hint.second); 88 | }); 89 | 90 | // if the hints succeeded, we don't need to do anything more 91 | if (m_matches.size() > 0) { 92 | m_matched = true; 93 | return; 94 | } 95 | } 96 | } 97 | 98 | bool Utility::pattern::ConsiderMatch(uintptr_t offset) { 99 | 100 | const char * pattern = m_bytes.c_str(); 101 | const char * mask = m_mask.c_str(); 102 | 103 | char * ptr = reinterpret_cast(offset); 104 | 105 | for (size_t i = 0; i < m_size; i++) { 106 | 107 | if (mask[i] == '?') { 108 | continue; 109 | } 110 | 111 | if (pattern[i] != ptr[i]) { 112 | return false; 113 | } 114 | } 115 | 116 | m_matches.push_back(pattern_match(ptr)); 117 | 118 | return true; 119 | } 120 | 121 | void Utility::pattern::EnsureMatches(int maxCount) { 122 | 123 | if (m_matched) { 124 | return; 125 | } 126 | 127 | // Scan the executable for code 128 | static executable_meta executable; 129 | 130 | executable.EnsureInit(); 131 | 132 | // Check if SSE 4.2 is supported 133 | int cpuid[4]; 134 | __cpuid(cpuid, 0); 135 | 136 | bool sse42 = false; 137 | 138 | if (m_mask.size() <= 16) { 139 | 140 | if (cpuid[0] >= 1) { 141 | 142 | __cpuidex(cpuid, 1, 0); 143 | 144 | sse42 = (cpuid[2] & (1 << 20)) == 1; 145 | } 146 | } 147 | 148 | auto matchSuccess = [&](uintptr_t address) { 149 | 150 | g_hints.insert(std::make_pair(m_hash, address)); 151 | 152 | return (m_matches.size() == maxCount); 153 | }; 154 | 155 | LARGE_INTEGER ticks; 156 | QueryPerformanceCounter(&ticks); 157 | 158 | uint64_t startTicksOld = ticks.QuadPart; 159 | 160 | if (!sse42) { 161 | 162 | for (uintptr_t i = executable.begin(); i <= executable.end(); i++) { 163 | 164 | if (ConsiderMatch(i)) { 165 | 166 | if (matchSuccess(i)) { 167 | break; 168 | } 169 | } 170 | } 171 | } 172 | else { 173 | 174 | __declspec(align(16)) char desiredMask[16] = { 0 }; 175 | 176 | for (int i = 0; i < m_mask.size(); i++) { 177 | desiredMask[i / 8] |= ((m_mask[i] == '?') ? 0 : 1) << (i % 8); 178 | } 179 | 180 | __m128i mask = _mm_load_si128(reinterpret_cast(desiredMask)); 181 | __m128i comparand = _mm_loadu_si128(reinterpret_cast(m_bytes.c_str())); 182 | 183 | for (uintptr_t i = executable.begin(); i <= executable.end(); i++) { 184 | 185 | __m128i value = _mm_loadu_si128(reinterpret_cast(i)); 186 | __m128i result = _mm_cmpestrm(value, 16, comparand, (int)m_bytes.size(), _SIDD_CMP_EQUAL_EACH); 187 | 188 | // As the result can match more bits than the mask contains 189 | __m128i matches = _mm_and_si128(mask, result); 190 | __m128i equivalence = _mm_xor_si128(mask, matches); 191 | 192 | if (_mm_test_all_zeros(equivalence, equivalence)) { 193 | 194 | m_matches.push_back(pattern_match((void*)i)); 195 | 196 | if (matchSuccess(i)) { 197 | break; 198 | } 199 | } 200 | } 201 | } 202 | 203 | m_matched = true; 204 | } 205 | 206 | void Utility::pattern::hint(uint64_t hash, uintptr_t address) { 207 | 208 | auto range = g_hints.equal_range(hash); 209 | 210 | for (auto it = range.first; it != range.second; it++) { 211 | 212 | if (it->second == address) { 213 | return; 214 | } 215 | } 216 | 217 | g_hints.insert(std::make_pair(hash, address)); 218 | } -------------------------------------------------------------------------------- /SkyrimUncapper/BranchTrampoline.cpp: -------------------------------------------------------------------------------- 1 | #include "BranchTrampoline.h" 2 | #include "SafeWrite.h" 3 | #include 4 | 5 | BranchTrampoline g_branchTrampoline; 6 | BranchTrampoline g_localTrampoline; 7 | 8 | BranchTrampoline::BranchTrampoline() 9 | :m_base(nullptr) 10 | ,m_len(0) 11 | ,m_allocated(0) 12 | ,m_curAlloc(nullptr) 13 | { 14 | // 15 | } 16 | 17 | BranchTrampoline::~BranchTrampoline() 18 | { 19 | Destroy(); 20 | } 21 | 22 | bool BranchTrampoline::Create(size_t len, void * module) 23 | { 24 | if(!module) module = GetModuleHandle(NULL); 25 | 26 | // search backwards from module base 27 | uintptr_t moduleBase = uintptr_t(module); 28 | uintptr_t addr = moduleBase; 29 | uintptr_t lowestOKAddress = moduleBase - 0x80000000 + (1024 * 1024 * 128); // largest 32-bit displacement with 128MB scratch space 30 | addr--; 31 | 32 | while(!m_base) 33 | { 34 | MEMORY_BASIC_INFORMATION info; 35 | 36 | if(!VirtualQuery((void *)addr, &info, sizeof(info))) 37 | { 38 | _ERROR("VirtualQuery failed: %08X", GetLastError()); 39 | break; 40 | } 41 | 42 | if(info.State == MEM_FREE) 43 | { 44 | // free block, big enough? 45 | if(info.RegionSize >= len) 46 | { 47 | // try to allocate it 48 | addr = ((uintptr_t)info.BaseAddress) + info.RegionSize - len; 49 | 50 | m_base = (void *)VirtualAlloc((void *)addr, len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); 51 | if(m_base) 52 | { 53 | m_len = len; 54 | m_allocated = 0; 55 | } 56 | else 57 | { 58 | _WARNING("trampoline alloc %016I64Xx%016I64X failed (%08X)", addr, len, GetLastError()); 59 | } 60 | } 61 | } 62 | 63 | // move back and try again 64 | if(!m_base) 65 | { 66 | addr = ((uintptr_t)info.BaseAddress) - 1; 67 | } 68 | 69 | if(addr < lowestOKAddress) 70 | { 71 | _ERROR("couldn't allocate trampoline, no free space before image"); 72 | break; 73 | } 74 | } 75 | 76 | return m_base != nullptr; 77 | } 78 | 79 | void BranchTrampoline::Destroy() 80 | { 81 | if(m_base) 82 | { 83 | VirtualFree(m_base, 0, MEM_RELEASE); 84 | m_base = nullptr; 85 | } 86 | } 87 | 88 | void * BranchTrampoline::StartAlloc() 89 | { 90 | ASSERT(m_base); 91 | ASSERT(!m_curAlloc); 92 | 93 | m_curAlloc = ((UInt8 *)m_base) + m_allocated; 94 | 95 | return m_curAlloc; 96 | } 97 | 98 | void BranchTrampoline::EndAlloc(const void * end) 99 | { 100 | ASSERT(m_base); 101 | ASSERT(m_curAlloc); 102 | 103 | size_t len = uintptr_t(end) - uintptr_t(m_curAlloc); 104 | ASSERT(len <= Remain()); 105 | 106 | m_allocated += len; 107 | m_curAlloc = nullptr; 108 | } 109 | 110 | void * BranchTrampoline::Allocate(size_t size) 111 | { 112 | ASSERT(m_base); 113 | 114 | void * result = nullptr; 115 | 116 | if(size <= Remain()) 117 | { 118 | result = ((UInt8 *)m_base) + m_allocated; 119 | m_allocated += size; 120 | } 121 | 122 | return result; 123 | } 124 | 125 | bool BranchTrampoline::Write6Branch(uintptr_t src, uintptr_t dst) 126 | { 127 | return Write6Branch_Internal(src, dst, 0x25); 128 | } 129 | 130 | bool BranchTrampoline::Write6Call(uintptr_t src, uintptr_t dst) 131 | { 132 | return Write6Branch_Internal(src, dst, 0x15); 133 | } 134 | 135 | bool BranchTrampoline::Write5Branch(uintptr_t src, uintptr_t dst) 136 | { 137 | return Write5Branch_Internal(src, dst, 0xE9); 138 | } 139 | 140 | bool BranchTrampoline::Write5Call(uintptr_t src, uintptr_t dst) 141 | { 142 | return Write5Branch_Internal(src, dst, 0xE8); 143 | } 144 | 145 | bool BranchTrampoline::Write6Branch_Internal(uintptr_t src, uintptr_t dst, UInt8 op) 146 | { 147 | bool result = false; 148 | 149 | uintptr_t * trampoline = (uintptr_t *)Allocate(); 150 | if(trampoline) 151 | { 152 | uintptr_t trampolineAddr = (uintptr_t)trampoline; 153 | uintptr_t nextInstr = src + 6; 154 | ptrdiff_t trampolineDispl = trampolineAddr - nextInstr; 155 | 156 | if((trampolineDispl >= _I32_MIN) && (trampolineDispl <= _I32_MAX)) 157 | { 158 | UInt8 code[6]; 159 | 160 | // jmp [rip+imm32] 161 | code[0] = 0xFF; 162 | code[1] = op; 163 | *((SInt32 *)&code[2]) = (SInt32)trampolineDispl; 164 | 165 | SafeWriteBuf(src, code, sizeof(code)); 166 | 167 | *trampoline = dst; 168 | 169 | result = true; 170 | 171 | #if defined(_DEBUG) 172 | _MESSAGE("Write6Branch: %016I64X %016I64X %016I64X", src, dst, trampoline); 173 | #endif 174 | } 175 | } 176 | 177 | // do this for now so it's obvious when something goes wrong 178 | ASSERT(result); 179 | 180 | return result; 181 | } 182 | 183 | bool BranchTrampoline::Write5Branch_Internal(uintptr_t src, uintptr_t dst, UInt8 op) 184 | { 185 | bool result = false; 186 | 187 | #pragma pack(push, 1) 188 | // code placed in trampoline 189 | struct TrampolineCode 190 | { 191 | // jmp [rip] 192 | UInt8 escape; // FF 193 | UInt8 modrm; // 25 194 | UInt32 displ; // 00000000 195 | // rip points here 196 | UInt64 dst; // target 197 | 198 | void Init(uintptr_t _dst) 199 | { 200 | escape = 0xFF; 201 | modrm = 0x25; 202 | displ = 0; 203 | dst = _dst; 204 | } 205 | }; 206 | 207 | struct HookCode 208 | { 209 | // jmp disp32 210 | UInt8 op; // E9 for jmp, E8 for call 211 | SInt32 displ; // 212 | 213 | void Init(SInt32 _displ, UInt8 _op) 214 | { 215 | op = _op; 216 | displ = _displ; 217 | } 218 | }; 219 | #pragma pack(pop) 220 | 221 | STATIC_ASSERT(sizeof(TrampolineCode) == 14); 222 | STATIC_ASSERT(sizeof(HookCode) == 5); 223 | 224 | TrampolineCode * trampolineCode = (TrampolineCode *)Allocate(sizeof(TrampolineCode)); 225 | if(trampolineCode) 226 | { 227 | trampolineCode->Init(dst); 228 | 229 | HookCode hookCode; 230 | 231 | uintptr_t trampolineAddr = uintptr_t(trampolineCode); 232 | uintptr_t nextInstr = src + sizeof(hookCode); 233 | ptrdiff_t trampolineDispl = trampolineAddr - nextInstr; 234 | 235 | // should never fail because we're branching in to the trampoline 236 | ASSERT((trampolineDispl >= _I32_MIN) && (trampolineDispl <= _I32_MAX)); 237 | 238 | hookCode.Init(trampolineDispl, op); 239 | 240 | SafeWriteBuf(src, &hookCode, sizeof(hookCode)); 241 | 242 | #if defined(_DEBUG) 243 | _MESSAGE("Write5Branch: %016I64X %016I64X %016I64X", src, dst, trampolineCode); 244 | #endif 245 | 246 | result = true; 247 | } 248 | 249 | // do this for now so it's obvious when something goes wrong 250 | ASSERT(result); 251 | 252 | return result; 253 | } 254 | -------------------------------------------------------------------------------- /SkyrimUncapper/NiTypes.cpp: -------------------------------------------------------------------------------- 1 | #include "NiTypes.h" 2 | 3 | NiPoint3::NiPoint3() 4 | { 5 | x = 0.0f; 6 | y = 0.0f; 7 | z = 0.0f; 8 | } 9 | 10 | NiPoint3 NiPoint3::operator- () const 11 | { 12 | return NiPoint3(-x, -y, -z); 13 | } 14 | 15 | NiPoint3 NiPoint3::operator+ (const NiPoint3& pt) const 16 | { 17 | return NiPoint3(x + pt.x, y + pt.y, z + pt.z); 18 | } 19 | 20 | NiPoint3 NiPoint3::operator- (const NiPoint3& pt) const 21 | { 22 | return NiPoint3(x - pt.x, y - pt.y, z - pt.z); 23 | } 24 | 25 | NiPoint3& NiPoint3::operator+= (const NiPoint3& pt) 26 | { 27 | x += pt.x; 28 | y += pt.y; 29 | z += pt.z; 30 | return *this; 31 | } 32 | NiPoint3& NiPoint3::operator-= (const NiPoint3& pt) 33 | { 34 | x -= pt.x; 35 | y -= pt.y; 36 | z -= pt.z; 37 | return *this; 38 | } 39 | 40 | // Scalar operations 41 | NiPoint3 NiPoint3::operator* (float scalar) const 42 | { 43 | return NiPoint3(scalar * x, scalar * y, scalar * z); 44 | } 45 | NiPoint3 NiPoint3::operator/ (float scalar) const 46 | { 47 | float invScalar = 1.0f / scalar; 48 | return NiPoint3(invScalar * x, invScalar * y, invScalar * z); 49 | } 50 | 51 | NiPoint3& NiPoint3::operator*= (float scalar) 52 | { 53 | x *= scalar; 54 | y *= scalar; 55 | z *= scalar; 56 | return *this; 57 | } 58 | NiPoint3& NiPoint3::operator/= (float scalar) 59 | { 60 | float invScalar = 1.0f / scalar; 61 | x *= invScalar; 62 | y *= invScalar; 63 | z *= invScalar; 64 | return *this; 65 | } 66 | 67 | void NiMatrix33::Identity() 68 | { 69 | data[0][0] = 1.0f; 70 | data[0][1] = 0.0f; 71 | data[0][2] = 0.0f; 72 | data[1][0] = 0.0f; 73 | data[1][1] = 1.0f; 74 | data[1][2] = 0.0f; 75 | data[2][0] = 0.0f; 76 | data[2][1] = 0.0f; 77 | data[2][2] = 1.0f; 78 | } 79 | 80 | NiMatrix33 NiMatrix33::operator* (const NiMatrix33& rhs) const 81 | { 82 | NiMatrix33 tmp; 83 | tmp.data[0][0] = 84 | data[0][0] * rhs.data[0][0] + 85 | data[0][1] * rhs.data[1][0] + 86 | data[0][2] * rhs.data[2][0]; 87 | tmp.data[1][0] = 88 | data[1][0] * rhs.data[0][0] + 89 | data[1][1] * rhs.data[1][0] + 90 | data[1][2] * rhs.data[2][0]; 91 | tmp.data[2][0] = 92 | data[2][0] * rhs.data[0][0] + 93 | data[2][1] * rhs.data[1][0] + 94 | data[2][2] * rhs.data[2][0]; 95 | tmp.data[0][1] = 96 | data[0][0] * rhs.data[0][1] + 97 | data[0][1] * rhs.data[1][1] + 98 | data[0][2] * rhs.data[2][1]; 99 | tmp.data[1][1] = 100 | data[1][0] * rhs.data[0][1] + 101 | data[1][1] * rhs.data[1][1] + 102 | data[1][2] * rhs.data[2][1]; 103 | tmp.data[2][1] = 104 | data[2][0] * rhs.data[0][1] + 105 | data[2][1] * rhs.data[1][1] + 106 | data[2][2] * rhs.data[2][1]; 107 | tmp.data[0][2] = 108 | data[0][0] * rhs.data[0][2] + 109 | data[0][1] * rhs.data[1][2] + 110 | data[0][2] * rhs.data[2][2]; 111 | tmp.data[1][2] = 112 | data[1][0] * rhs.data[0][2] + 113 | data[1][1] * rhs.data[1][2] + 114 | data[1][2] * rhs.data[2][2]; 115 | tmp.data[2][2] = 116 | data[2][0] * rhs.data[0][2] + 117 | data[2][1] * rhs.data[1][2] + 118 | data[2][2] * rhs.data[2][2]; 119 | return tmp; 120 | } 121 | 122 | NiMatrix33 NiMatrix33::operator* (float scalar) const 123 | { 124 | NiMatrix33 result; 125 | result.data[0][0] = data[0][0] * scalar; 126 | result.data[0][1] = data[0][1] * scalar; 127 | result.data[0][2] = data[0][2] * scalar; 128 | result.data[1][0] = data[1][0] * scalar; 129 | result.data[1][1] = data[1][1] * scalar; 130 | result.data[1][2] = data[1][2] * scalar; 131 | result.data[2][0] = data[2][0] * scalar; 132 | result.data[2][1] = data[2][1] * scalar; 133 | result.data[2][2] = data[2][2] * scalar; 134 | return result; 135 | } 136 | 137 | NiPoint3 NiMatrix33::operator* (const NiPoint3& pt) const 138 | { 139 | return NiPoint3 140 | ( 141 | data[0][0] * pt.x + data[0][1] * pt.y + data[0][2] * pt.z, 142 | data[1][0] * pt.x + data[1][1] * pt.y + data[1][2] * pt.z, 143 | data[2][0] * pt.x + data[2][1] * pt.y + data[2][2] * pt.z 144 | ); 145 | } 146 | 147 | NiMatrix33 NiMatrix33::Transpose() const 148 | { 149 | NiMatrix33 result; 150 | result.data[0][0] = data[0][0]; 151 | result.data[0][1] = data[1][0]; 152 | result.data[0][2] = data[2][0]; 153 | result.data[1][0] = data[0][1]; 154 | result.data[1][1] = data[1][1]; 155 | result.data[1][2] = data[2][1]; 156 | result.data[2][0] = data[0][2]; 157 | result.data[2][1] = data[1][2]; 158 | result.data[2][2] = data[2][2]; 159 | return result; 160 | } 161 | 162 | // Converted from Java to C 163 | // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToEuler/index.htm 164 | void NiMatrix33::GetEulerAngles(float * heading, float * attitude, float * bank) 165 | { 166 | if (data[1][0] > 0.998) { // singularity at north pole 167 | *heading = atan2(data[0][2], data[2][2]); 168 | *attitude = MATH_PI / 2; 169 | *bank = 0; 170 | } 171 | else if (data[1][0] < -0.998) { // singularity at south pole 172 | *heading = atan2(data[0][2], data[2][2]); 173 | *attitude = -MATH_PI / 2; 174 | *bank = 0; 175 | } 176 | else { 177 | *heading = atan2(-data[2][0], data[0][0]); 178 | *bank = atan2(-data[1][2], data[1][1]); 179 | *attitude = asin(data[1][0]); 180 | } 181 | } 182 | 183 | // Converted from Java to C 184 | // http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToMatrix/index.htm 185 | void NiMatrix33::SetEulerAngles(float heading, float attitude, float bank) 186 | { 187 | double ch = cos(heading); 188 | double sh = sin(heading); 189 | double ca = cos(attitude); 190 | double sa = sin(attitude); 191 | double cb = cos(bank); 192 | double sb = sin(bank); 193 | 194 | data[0][0] = ch * ca; 195 | data[0][1] = sh * sb - ch * sa * cb; 196 | data[0][2] = ch * sa * sb + sh * cb; 197 | data[1][0] = sa; 198 | data[1][1] = ca * cb; 199 | data[1][2] = -ca * sb; 200 | data[2][0] = -sh * ca; 201 | data[2][1] = sh * sa * cb + ch * sb; 202 | data[2][2] = -sh * sa * sb + ch * cb; 203 | } 204 | 205 | NiTransform::NiTransform() 206 | { 207 | rot.Identity(); 208 | scale = 1.0f; 209 | } 210 | 211 | NiTransform NiTransform::operator*(const NiTransform &rhs) const 212 | { 213 | NiTransform tmp; 214 | tmp.scale = scale * rhs.scale; 215 | tmp.rot = rot * rhs.rot; 216 | tmp.pos = pos + (rot * rhs.pos) * scale; 217 | return tmp; 218 | } 219 | 220 | NiPoint3 NiTransform::operator*(const NiPoint3 & pt) const 221 | { 222 | return (((rot * pt) * scale) + pos); 223 | } 224 | 225 | void NiTransform::Invert(NiTransform& kDest) const 226 | { 227 | kDest.rot = rot.Transpose(); 228 | kDest.scale = 1.0f / scale; 229 | kDest.pos = (kDest.rot * -pos) * kDest.scale; 230 | } 231 | -------------------------------------------------------------------------------- /Relocation/Relocation.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {83D27534-600C-47B8-8C39-05BA34C3224E} 24 | Relocation 25 | 10.0.15063.0 26 | 27 | 28 | 29 | Application 30 | true 31 | v141 32 | MultiByte 33 | 34 | 35 | Application 36 | false 37 | v141 38 | true 39 | MultiByte 40 | 41 | 42 | Application 43 | true 44 | v141 45 | MultiByte 46 | 47 | 48 | StaticLibrary 49 | false 50 | v141 51 | true 52 | MultiByte 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Level3 76 | MaxSpeed 77 | true 78 | true 79 | true 80 | $(SolutionDir);$(SolutionDir)\.. 81 | MultiThreaded 82 | 83 | 84 | true 85 | true 86 | 87 | 88 | 89 | 90 | Level3 91 | Disabled 92 | true 93 | 94 | 95 | 96 | 97 | Level3 98 | Disabled 99 | true 100 | $(SolutionDir);$(SolutionDir)\..;%(AdditionalIncludeDirectories) 101 | 102 | 103 | 104 | 105 | Level3 106 | MaxSpeed 107 | true 108 | true 109 | true 110 | 111 | 112 | true 113 | true 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /Relocation/RVA.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include "skse64_common/skse_version.h" 6 | #include "Relocation.h" 7 | 8 | #include "Pattern.h" 9 | 10 | //============================================================================================= 11 | //======================================= RVAScan.h ========================================= 12 | //============================================================================================= 13 | //==== An RVAScan encapsulates a version-specific relative virtual address and a ==== 14 | //==== version-independent address signature pattern. ==== 15 | //============================================================================================= 16 | //==== If addresses are explicitly specified for the current runtime version, ==== 17 | //==== they will be used. ==== 18 | //==== If no addresses are available for the current runtime version, ==== 19 | //==== then a search will be performed against the provided signature in memory. ==== 20 | //============================================================================================= 21 | //==== Call RVAManager::UpdateAddresses(runtimeVersion) during plugin load ==== 22 | //==== to resolve addresses for all declared RVAs. ==== 23 | //============================================================================================= 24 | //==== Author: registrator2000 ==== 25 | //============================================================================================= 26 | 27 | #define SHOW_ADDR 2 28 | #define GET_RVA(relocPtr) relocPtr.GetUIntPtr() - RelocationManager::s_baseAddr 29 | // 0 - Default - use addresses for the runtime version first, and sigscan if the addresses for the runtime version are not available. No logging. 30 | // 1 - Always do sigscan and print address to log even if an address is present for the runtime version. 31 | // 2 - Default + print addresses only if sigscan is performed 32 | 33 | //------------------------ 34 | // Forward Declarations 35 | //------------------------ 36 | namespace RVAUtils 37 | { 38 | inline bool ReadMemory(uintptr_t addr, void* data, size_t len); 39 | } 40 | 41 | //------------------------ 42 | // Data Structures 43 | //------------------------ 44 | 45 | struct RVAData 46 | { 47 | std::unordered_map addr; // Map of runtime version to RVAScan. 48 | const char* sig = nullptr; // Signature 49 | uintptr_t effectiveAddress = 0; 50 | int offset = 0; 51 | int indirectOffset = 0; 52 | int instructionLength = 0; 53 | }; 54 | 55 | //------------------------ 56 | // Utility Classes 57 | //------------------------ 58 | 59 | namespace RVAUtils 60 | { 61 | class Timer 62 | { 63 | public: 64 | Timer() 65 | { 66 | QueryPerformanceCounter(&countStart); 67 | QueryPerformanceFrequency(&frequency); 68 | } 69 | ~Timer() 70 | { 71 | QueryPerformanceCounter(&countEnd); 72 | _MESSAGE(">> sigscan time elapsed: %llu ms...", (countEnd.QuadPart - countStart.QuadPart) / (frequency.QuadPart / 1000)); 73 | } 74 | 75 | private: 76 | LARGE_INTEGER countStart, countEnd, frequency; 77 | }; 78 | } 79 | 80 | //------------------------ 81 | // Address Manager 82 | //------------------------ 83 | 84 | class RVAManager 85 | { 86 | public: 87 | static void UpdateAddresses(UInt32 runtimeVersion) { 88 | RVAUtils::Timer tmr; 89 | 90 | for (auto rvaData : m_rvaDataVec()) 91 | { 92 | if (rvaData->effectiveAddress) continue; 93 | UpdateSingle(rvaData, runtimeVersion); 94 | } 95 | } 96 | 97 | static void UpdateSingle(std::shared_ptr rvaData, UInt32 runtimeVersion = 0) { 98 | if (SHOW_ADDR != 1 && rvaData->addr.count(runtimeVersion) > 0) { 99 | rvaData->effectiveAddress = GetEffectiveAddress(rvaData->addr[runtimeVersion]); 100 | } 101 | else { 102 | // Sigscan 103 | if (rvaData->sig) 104 | { 105 | rvaData->effectiveAddress = (uintptr_t)Utility::pattern(rvaData->sig).count(1).get(0).get(rvaData->offset); 106 | 107 | if (rvaData->indirectOffset != 0) 108 | { 109 | SInt32 rel32 = 0; 110 | RVAUtils::ReadMemory(rvaData->effectiveAddress + rvaData->indirectOffset, &rel32, sizeof(SInt32)); 111 | rvaData->effectiveAddress = rvaData->effectiveAddress + rvaData->instructionLength + rel32; 112 | } 113 | 114 | #if SHOW_ADDR 115 | _MESSAGE("---"); 116 | _MESSAGE("sig: %s", rvaData->sig); 117 | _MESSAGE("effective address: %p", rvaData->effectiveAddress); 118 | _MESSAGE("RVAScan: 0x%08X", rvaData->effectiveAddress - RelocationManager::s_baseAddr); 119 | _MESSAGE("---"); 120 | #endif 121 | 122 | } 123 | else 124 | { 125 | _MESSAGE("Warning: No signature and no addresses for runtime."); 126 | for (auto addr : rvaData->addr) { 127 | _MESSAGE("Runtime version: %08X \tAddress: %08X", addr.first, addr.second); 128 | } 129 | } 130 | } 131 | } 132 | 133 | static uintptr_t GetEffectiveAddress(uintptr_t rva) { 134 | return RelocationManager::s_baseAddr + rva; 135 | } 136 | 137 | static void Add(std::shared_ptr data) { 138 | m_rvaDataVec().push_back(data); 139 | } 140 | 141 | private: 142 | using RVADataVec = std::vector>; 143 | static RVADataVec& m_rvaDataVec() { static RVADataVec v; return v; } 144 | }; 145 | 146 | //------------------------ 147 | // Address Type 148 | //------------------------ 149 | 150 | template 151 | class RVAScan 152 | { 153 | public: 154 | using AddressMap = std::unordered_map; 155 | 156 | RVAScan(AddressMap addr, const char* sig, int offset = 0, int indirectOffset = 0, int instructionLength = 0) { 157 | init(addr, sig, offset, indirectOffset, instructionLength); 158 | } 159 | 160 | RVAScan(const char* sig, int offset, AddressMap addr) { 161 | init(addr, sig, offset); 162 | } 163 | 164 | RVAScan(const char* sig, AddressMap addr) 165 | { 166 | init(addr, sig, 0); 167 | } 168 | 169 | // Address map only 170 | RVAScan(AddressMap addr) { 171 | init(addr, NULL, 0); 172 | } 173 | 174 | // Address only 175 | RVAScan(uintptr_t rva) { 176 | AddressMap addr = { { CURRENT_RELEASE_RUNTIME, rva } }; 177 | init(addr, NULL, 0); 178 | } 179 | 180 | // Address + sig 181 | RVAScan(uintptr_t rva, const char* sig, int offset = 0, int indirectOffset = 0, int instructionLength = 0) { 182 | AddressMap addr/* = { { CURRENT_RELEASE_RUNTIME, rva } }*/; 183 | init(addr, sig, offset, indirectOffset, instructionLength); 184 | } 185 | 186 | // Signature only 187 | RVAScan(const char* sig, int offset = 0, int indirectOffset = 0, int instructionLength = 0) { 188 | AddressMap addr; 189 | init(addr, sig, offset, indirectOffset, instructionLength); 190 | } 191 | 192 | // Default constructor (empty) 193 | RVAScan() 194 | { 195 | // do nothing 196 | } 197 | 198 | T& operator* () 199 | { 200 | return *GetPtr(); 201 | } 202 | 203 | operator T() 204 | { 205 | return reinterpret_cast(data->effectiveAddress); 206 | } 207 | 208 | T* operator->() const 209 | { 210 | return GetPtr(); 211 | } 212 | 213 | T* GetPtr() const 214 | { 215 | return reinterpret_cast(data->effectiveAddress); 216 | } 217 | 218 | const T * GetConst() const 219 | { 220 | return reinterpret_cast(data->effectiveAddress); 221 | } 222 | 223 | uintptr_t GetUIntPtr() const 224 | { 225 | return data->effectiveAddress; 226 | } 227 | 228 | bool IsResolved() const { 229 | return (data->effectiveAddress != NULL); 230 | } 231 | 232 | void Resolve(UInt32 runtimeVersion) { 233 | if (!IsResolved()) RVAManager::UpdateSingle(data, runtimeVersion); 234 | } 235 | 236 | void Set(uintptr_t rva) { 237 | data->effectiveAddress = RVAManager::GetEffectiveAddress(rva); 238 | } 239 | 240 | void SetEffective(uintptr_t ea) { 241 | data->effectiveAddress = ea; 242 | } 243 | 244 | //void operator=(RVAScan const&) = delete; 245 | 246 | private: 247 | std::shared_ptr data; 248 | 249 | void init(AddressMap addr, const char* sig, int offset, int indirectOffset = 0, int instructionLength = 0) 250 | { 251 | data = std::make_shared(); 252 | data->addr = addr; 253 | data->sig = sig; 254 | data->offset = offset; 255 | data->indirectOffset = indirectOffset; 256 | data->instructionLength = instructionLength; 257 | 258 | RVAManager::UpdateSingle(data, CURRENT_RELEASE_RUNTIME); 259 | //RVAManager::Add(data); 260 | } 261 | }; 262 | 263 | //-------------------- 264 | // Utility Functions 265 | //-------------------- 266 | 267 | namespace RVAUtils 268 | { 269 | inline bool ReadMemory(uintptr_t addr, void* data, size_t len) 270 | { 271 | UInt32 oldProtect; 272 | if (VirtualProtect((void *)addr, len, PAGE_EXECUTE_READWRITE, &oldProtect)) 273 | { 274 | memcpy(data, (void*)addr, len); 275 | if (VirtualProtect((void *)addr, len, oldProtect, &oldProtect)) 276 | return true; 277 | } 278 | return false; 279 | } 280 | } -------------------------------------------------------------------------------- /SkyrimUncapper/NiTypes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 4 | template 5 | class NiPointer 6 | { 7 | public: 8 | T * m_pObject; // 00 9 | 10 | inline NiPointer(T* pObject = (T*)0) 11 | { 12 | m_pObject = pObject; 13 | if (m_pObject) m_pObject->IncRef(); 14 | } 15 | 16 | inline ~NiPointer() 17 | { 18 | if (m_pObject) m_pObject->DecRef(); 19 | } 20 | 21 | inline operator T *() const 22 | { 23 | return m_pObject; 24 | } 25 | 26 | inline T & operator*() const 27 | { 28 | return *m_pObject; 29 | } 30 | 31 | inline T * operator->() const 32 | { 33 | return m_pObject; 34 | } 35 | 36 | inline NiPointer & operator=(const NiPointer & rhs) 37 | { 38 | if (m_pObject != rhs.m_pObject) 39 | { 40 | if (rhs) rhs.m_pObject->IncRef(); 41 | if (m_pObject) m_pObject->DecRef(); 42 | 43 | m_pObject = rhs.m_pObject; 44 | } 45 | 46 | return *this; 47 | } 48 | 49 | inline NiPointer & operator=(T * rhs) 50 | { 51 | if (m_pObject != rhs) 52 | { 53 | if (rhs) rhs->IncRef(); 54 | if (m_pObject) m_pObject->DecRef(); 55 | 56 | m_pObject = rhs; 57 | } 58 | 59 | return *this; 60 | } 61 | 62 | inline bool operator==(T * pObject) const 63 | { 64 | return m_pObject == pObject; 65 | } 66 | 67 | inline bool operator!=(T * pObject) const 68 | { 69 | return m_pObject != pObject; 70 | } 71 | 72 | inline bool operator==(const NiPointer & ptr) const 73 | { 74 | return m_pObject == ptr.m_pObject; 75 | } 76 | 77 | inline bool operator!=(const NiPointer & ptr) const 78 | { 79 | return m_pObject != ptr.m_pObject; 80 | } 81 | }; 82 | 83 | #define MAKE_NI_POINTER(x) class x; typedef NiPointer x##Ptr 84 | 85 | template 86 | T_to * niptr_cast(const T_from & src) 87 | { 88 | return static_cast (src.m_pObject); 89 | } 90 | 91 | // 10 92 | template 93 | class NiRect 94 | { 95 | public: 96 | T m_left; // 00 97 | T m_right; // 04 98 | T m_top; // 08 99 | T m_bottom; // 0C 100 | }; 101 | 102 | // 1C 103 | class NiFrustum 104 | { 105 | public: 106 | float m_fLeft; // 00 107 | float m_fRight; // 04 108 | float m_fTop; // 08 109 | float m_fBottom; // 0C 110 | float m_fNear; // 10 111 | float m_fFar; // 14 112 | bool m_bOrtho; // 18 113 | }; 114 | 115 | // 10 116 | class NiQuaternion 117 | { 118 | public: 119 | // w is first 120 | 121 | float m_fW; // 0 122 | float m_fX; // 4 123 | float m_fY; // 8 124 | float m_fZ; // C 125 | }; 126 | 127 | // 8 128 | class NiPoint2 129 | { 130 | public: 131 | float x; // 0 132 | float y; // 4 133 | }; 134 | 135 | // C 136 | class NiPoint3 137 | { 138 | public: 139 | float x; // 0 140 | float y; // 4 141 | float z; // 8 142 | 143 | NiPoint3(); 144 | NiPoint3(float X, float Y, float Z) : x(X), y(Y), z(Z) { }; 145 | 146 | // Negative 147 | NiPoint3 operator- () const; 148 | 149 | // Basic operations 150 | NiPoint3 operator+ (const NiPoint3& pt) const; 151 | NiPoint3 operator- (const NiPoint3& pt) const; 152 | 153 | NiPoint3& operator+= (const NiPoint3& pt); 154 | NiPoint3& operator-= (const NiPoint3& pt); 155 | 156 | // Scalar operations 157 | NiPoint3 operator* (float fScalar) const; 158 | NiPoint3 operator/ (float fScalar) const; 159 | 160 | NiPoint3& operator*= (float fScalar); 161 | NiPoint3& operator/= (float fScalar); 162 | }; 163 | 164 | // 0C 165 | class NiColor 166 | { 167 | public: 168 | float r; // 0 169 | float g; // 4 170 | float b; // 8 171 | }; 172 | 173 | // 10 174 | class NiColorA 175 | { 176 | public: 177 | float r; // 0 178 | float g; // 4 179 | float b; // 8 180 | float a; // C 181 | }; 182 | 183 | // math.h 184 | #define MATH_PI 3.14159265358979323846 185 | 186 | // 24 187 | class NiMatrix33 188 | { 189 | public: 190 | union 191 | { 192 | float data[3][3]; 193 | float arr[9]; 194 | }; 195 | 196 | void Identity(); 197 | 198 | // Addition/Subtraction 199 | NiMatrix33 operator+(const NiMatrix33& mat) const; 200 | NiMatrix33 operator-(const NiMatrix33& mat) const; 201 | 202 | // Matric mult 203 | NiMatrix33 operator*(const NiMatrix33& mat) const; 204 | 205 | // Vector mult 206 | NiPoint3 operator*(const NiPoint3& pt) const; 207 | 208 | // Scalar multiplier 209 | NiMatrix33 operator*(float fScalar) const; 210 | 211 | NiMatrix33 Transpose() const; 212 | 213 | void GetEulerAngles(float * heading, float * attitude, float * bank); 214 | void SetEulerAngles(float heading, float attitude, float bank); 215 | }; 216 | 217 | STATIC_ASSERT(sizeof(NiMatrix33) == 0x24); 218 | 219 | // 34 220 | class NiTransform 221 | { 222 | public: 223 | NiMatrix33 rot; // 00 224 | NiPoint3 pos; // 24 225 | float scale; // 30 226 | 227 | NiTransform(); 228 | 229 | // Multiply transforms 230 | NiTransform operator*(const NiTransform &rhs) const; 231 | 232 | // Transform point 233 | NiPoint3 operator*(const NiPoint3 &pt) const; 234 | 235 | // Invert 236 | void Invert(NiTransform& kDest) const; 237 | }; 238 | 239 | STATIC_ASSERT(sizeof(NiTransform) == 0x34); 240 | 241 | // 10 242 | class NiBound 243 | { 244 | public: 245 | NiPoint3 pos; 246 | float radius; 247 | }; 248 | 249 | STATIC_ASSERT(sizeof(NiBound) == 0x10); 250 | 251 | // 20 252 | // derives from NiTMapBase, we don't bother 253 | template 254 | class NiTMap 255 | { 256 | public: 257 | virtual ~NiTMap(); 258 | 259 | struct NiTMapItem 260 | { 261 | NiTMapItem * next; 262 | T_key key; 263 | T_data data; 264 | }; 265 | 266 | T_data Get(T_key key) 267 | { 268 | UInt32 bucket = GetBucket(key); 269 | 270 | for (NiTMapItem * iter = buckets[bucket]; iter; iter = iter->next) 271 | { 272 | if (Compare(iter->key, key)) 273 | { 274 | return iter->data; 275 | } 276 | } 277 | 278 | return T_data(); 279 | } 280 | 281 | virtual UInt32 GetBucket(T_key key); // return hash % numBuckets; 282 | virtual bool Compare(T_key lhs, T_key rhs); // return lhs == rhs; 283 | virtual void FillItem(NiTMapItem * item, T_key key, T_data data); 284 | // item->key = key; item->data = data; 285 | virtual void Fn_04(UInt32 arg); // nop 286 | virtual NiTMapItem * AllocItem(void); // return new NiTMapItem; 287 | virtual void FreeItem(NiTMapItem * item); // item->data = 0; delete item; 288 | 289 | // void ** _vtbl; // 00 290 | UInt32 numBuckets; // 08 291 | UInt32 unk0C; // 0C 292 | NiTMapItem ** buckets; // 10 293 | UInt32 numEntries; // 18 294 | UInt32 unk1C; // 1C 295 | }; 296 | 297 | // 10 298 | template 299 | class NiTPointerMap : NiTMap 300 | { 301 | public: 302 | }; 303 | 304 | // 10 305 | template 306 | class NiTArray 307 | { 308 | public: 309 | NiTArray(); 310 | virtual ~NiTArray(); 311 | 312 | // sparse array, can have NULL entries that should be skipped 313 | // iterate from 0 to m_emptyRunStart - 1 314 | 315 | // void ** _vtbl; // 00 316 | T * m_data; // 04 317 | UInt16 m_arrayBufLen; // 08 - max elements storable in m_data 318 | UInt16 m_emptyRunStart; // 0A - index of beginning of empty slot run 319 | UInt16 m_size; // 0C - number of filled slots 320 | UInt16 m_growSize; // 0E - number of slots to grow m_data by 321 | }; 322 | 323 | //STATIC_ASSERT(sizeof(NiTArray ) == 0x10); 324 | 325 | // 18 326 | template 327 | class NiTLargeArray 328 | { 329 | public: 330 | NiTLargeArray(); 331 | virtual ~NiTLargeArray(); 332 | 333 | // fast, doesn't search for empty slots 334 | void NiTLargeArray::Append(T item) 335 | { 336 | if (m_emptyRunStart == m_arrayBufLen) 337 | { 338 | // need to expand the array 339 | Resize(m_arrayBufLen + 1); 340 | } 341 | 342 | m_data[m_emptyRunStart] = item; 343 | m_emptyRunStart++; 344 | m_size++; 345 | } 346 | 347 | void NiTLargeArray::Resize(UInt32 size) 348 | { 349 | // not reclaiming memory yet 350 | if (size <= m_size) return; 351 | 352 | ASSERT(m_growSize); 353 | 354 | // obey min grow size 355 | UInt32 growSize = size - m_size; 356 | if (growSize < m_growSize) 357 | growSize = m_growSize; 358 | 359 | size = m_arrayBufLen + growSize; 360 | 361 | // create new array 362 | T * newData = (T *)FormHeap_Allocate(sizeof(T) * size); 363 | 364 | for (UInt32 i = 0; i < size; i++) 365 | { 366 | new (&newData[i]) T; 367 | newData[i] = 0; 368 | } 369 | 370 | // copy over data, compacting as we go 371 | UInt32 iter = 0; 372 | 373 | for (UInt32 i = 0; i < m_emptyRunStart; i++) 374 | { 375 | if (m_data[i]) 376 | { 377 | newData[iter] = m_data[i]; 378 | iter++; 379 | } 380 | } 381 | 382 | // update pointers 383 | T * oldData = m_data; 384 | UInt32 oldDataLen = m_emptyRunStart; 385 | 386 | m_data = newData; 387 | m_arrayBufLen = size; 388 | m_emptyRunStart = m_size; 389 | 390 | // delete old array 391 | if (oldData) 392 | { 393 | for (UInt32 i = 0; i < oldDataLen; i++) 394 | if (oldData[i]) 395 | oldData[i].~T(); 396 | 397 | FormHeap_Free(oldData); 398 | } 399 | } 400 | 401 | // void ** _vtbl; // 00 402 | T * m_data; // 04 403 | UInt32 m_arrayBufLen; // 08 - max elements storable in m_data 404 | UInt32 m_emptyRunStart; // 0C - index of beginning of empty slot run 405 | UInt32 m_size; // 10 - number of filled slots 406 | UInt32 m_growSize; // 14 - number of slots to grow m_data by 407 | }; 408 | -------------------------------------------------------------------------------- /SkyrimUncapper/SkyrimUncapper.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {372607D0-4DA8-41D0-B34A-D53E016DC12A} 23 | SkyrimUncapper 24 | 10.0.15063.0 25 | 26 | 27 | 28 | DynamicLibrary 29 | true 30 | v141 31 | MultiByte 32 | 33 | 34 | DynamicLibrary 35 | true 36 | v141 37 | MultiByte 38 | 39 | 40 | Application 41 | false 42 | v141 43 | true 44 | MultiByte 45 | 46 | 47 | DynamicLibrary 48 | false 49 | v141 50 | true 51 | MultiByte 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | $(IncludePath) 70 | $(SourcePath) 71 | 72 | 73 | E:\SteamLibrary\steamapps\common\Skyrim Special Edition\Data\SKSE\Plugins\ 74 | 75 | 76 | 77 | Level3 78 | Disabled 79 | true 80 | common/IPrefix.h 81 | $(SolutionDir);$(SolutionDir)\.. 82 | 83 | 84 | true 85 | 86 | 87 | 88 | 89 | Level3 90 | Disabled 91 | false 92 | $(SolutionDir);$(SolutionDir)\.. 93 | MultiThreadedDebug 94 | common/IPrefix.h 95 | 96 | 97 | true 98 | exports.def 99 | 100 | 101 | 102 | 103 | Level3 104 | MaxSpeed 105 | true 106 | true 107 | true 108 | 109 | 110 | true 111 | true 112 | true 113 | 114 | 115 | 116 | 117 | Level3 118 | MaxSpeed 119 | true 120 | true 121 | false 122 | common/IPrefix.h 123 | $(SolutionDir);$(SolutionDir)\.. 124 | _WINDLL;%(PreprocessorDefinitions) 125 | MultiThreaded 126 | false 127 | false 128 | false 129 | 130 | 131 | true 132 | true 133 | true 134 | exports.def 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | {472e19ab-def0-42df-819b-18722e8dc822} 169 | 170 | 171 | {83d27534-600c-47b8-8c39-05ba34c3224e} 172 | 173 | 174 | {5fd1c08d-db80-480c-a1c6-f0920005cd13} 175 | 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /SkyrimUncapper/PluginAPI.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef UInt32 PluginHandle; // treat this as an opaque type 4 | class GFxMovieView; 5 | class GFxValue; 6 | class TaskDelegate; 7 | class UIDelegate_v1; 8 | class InventoryEntryData; 9 | class SKSEDelayFunctorManager; 10 | class SKSEObjectRegistry; 11 | class SKSEPersistentObjectStorage; 12 | 13 | enum 14 | { 15 | kPluginHandle_Invalid = 0xFFFFFFFF 16 | }; 17 | 18 | enum 19 | { 20 | kInterface_Invalid = 0, 21 | kInterface_Scaleform, 22 | kInterface_Papyrus, 23 | kInterface_Serialization, 24 | kInterface_Task, 25 | kInterface_Messaging, 26 | kInterface_Object, 27 | kInterface_Max, 28 | }; 29 | 30 | struct SKSEInterface 31 | { 32 | UInt32 skseVersion; 33 | UInt32 runtimeVersion; 34 | UInt32 editorVersion; 35 | UInt32 isEditor; 36 | void * (*QueryInterface)(UInt32 id); 37 | 38 | // call during your Query or Load functions to get a PluginHandle uniquely identifying your plugin 39 | // invalid if called at any other time, so call it once and save the result 40 | PluginHandle(*GetPluginHandle)(void); 41 | 42 | // returns the SKSE build's release index 43 | UInt32(*GetReleaseIndex)(void); 44 | }; 45 | 46 | struct SKSEScaleformInterface 47 | { 48 | enum 49 | { 50 | kInterfaceVersion = 2 51 | }; 52 | 53 | UInt32 interfaceVersion; 54 | 55 | // This callback will be called once for every new menu that is created. 56 | // Create your objects relative to the 'root' GFxValue parameter. 57 | typedef bool(*RegisterCallback)(GFxMovieView * view, GFxValue * root); 58 | typedef void(*RegisterInventoryCallback)(GFxMovieView * view, GFxValue * object, InventoryEntryData * item); 59 | 60 | // Register your plugin's scaleform API creation callback here. 61 | // The "name" parameter will be used to create an object with the path: 62 | // "skse.plugins.name" that will be passed to the callback. 63 | // Make sure that the memory it points to is valid from the point the callback 64 | // is registered until the game exits. 65 | bool(*Register)(const char * name, RegisterCallback callback); 66 | 67 | // Registers your plugin for when item data is extended to the UI 68 | // either favorites menu, or inventory menu 69 | void(*RegisterForInventory)(RegisterInventoryCallback callback); 70 | }; 71 | 72 | struct SKSESerializationInterface 73 | { 74 | enum 75 | { 76 | kVersion = 4, 77 | }; 78 | 79 | typedef void(*EventCallback)(SKSESerializationInterface * intfc); 80 | 81 | typedef void(*FormDeleteCallback)(UInt64 handle); 82 | 83 | UInt32 version; 84 | 85 | void(*SetUniqueID)(PluginHandle plugin, UInt32 uid); 86 | 87 | void(*SetRevertCallback)(PluginHandle plugin, EventCallback callback); 88 | void(*SetSaveCallback)(PluginHandle plugin, EventCallback callback); 89 | void(*SetLoadCallback)(PluginHandle plugin, EventCallback callback); 90 | void(*SetFormDeleteCallback)(PluginHandle plugin, FormDeleteCallback callback); 91 | 92 | bool(*WriteRecord)(UInt32 type, UInt32 version, const void * buf, UInt32 length); 93 | bool(*OpenRecord)(UInt32 type, UInt32 version); 94 | bool(*WriteRecordData)(const void * buf, UInt32 length); 95 | 96 | bool(*GetNextRecordInfo)(UInt32 * type, UInt32 * version, UInt32 * length); 97 | UInt32(*ReadRecordData)(void * buf, UInt32 length); 98 | bool(*ResolveHandle)(UInt64 handle, UInt64 * handleOut); 99 | bool(*ResolveFormId)(UInt32 formId, UInt32 * formIdOut); 100 | }; 101 | 102 | struct SKSETaskInterface 103 | { 104 | enum 105 | { 106 | kInterfaceVersion = 2 107 | }; 108 | 109 | UInt32 interfaceVersion; 110 | 111 | // Derive your type from TaskDelegate or UIDelegate 112 | // Allocate before adding 113 | // Define your Run function 114 | // Delete your object in the Dispose call 115 | void(*AddTask)(TaskDelegate * task); 116 | void(*AddUITask)(UIDelegate_v1 * task); 117 | }; 118 | 119 | //#ifdef _PPAPI 120 | 121 | // ### this code is unsupported and will be changed in the future 122 | 123 | class VMClassRegistry; 124 | 125 | struct SKSEPapyrusInterface 126 | { 127 | enum 128 | { 129 | kInterfaceVersion = 1 130 | }; 131 | UInt32 interfaceVersion; 132 | typedef bool(*RegisterFunctions)(VMClassRegistry * registry); 133 | bool(*Register)(RegisterFunctions callback); 134 | }; 135 | 136 | //#endif 137 | 138 | 139 | /**** Messaging API docs ******************************************************************** 140 | * 141 | * Messaging API allows inter-plugin communication at run-time. A plugin may register 142 | * one callback for each plugin from which it wishes to receive messages, specifying 143 | * the sender by name in the call to RegisterListener(). RegisterListener returns false 144 | * if the specified plugin is not loaded, true otherwise. Any messages dispatched by 145 | * the specified plugin will then be forwarded to the listener as they occur. Passing NULL as 146 | * the sender registers the calling plugin as a listener to every loaded plugin. 147 | * 148 | * Messages may be dispatched via Dispatch() to either a specific listener (specified 149 | * by name) or to all listeners (with NULL passed as the receiver). The contents and format of 150 | * messageData are left up to the sender, and the receiver is responsible for casting the message 151 | * to the expected type. If no plugins are registered as listeners for the sender, 152 | * Dispatch() returns false, otherwise it returns true. 153 | * 154 | * Calling RegisterListener() or Dispatch() during plugin load is not advised as the requested plugin 155 | * may not yet be loaded at that point. Instead, if you wish to register as a listener or dispatch a 156 | * message immediately after plugin load, use RegisterListener() during load to register to receive 157 | * messages from SKSE (sender name: "SKSE"). You will then receive a message from SKSE once 158 | * all plugins have been loaded, at which point it is safe to establish communications between 159 | * plugins. 160 | * 161 | * Some plugin authors may wish to use strings instead of integers to denote message type. In 162 | * that case the receiver can pass the address of the string as an integer and require the receiver 163 | * to cast it back to a char* on receipt. 164 | * 165 | *********************************************************************************************/ 166 | 167 | struct SKSEMessagingInterface 168 | { 169 | struct Message { 170 | const char * sender; 171 | UInt32 type; 172 | UInt32 dataLen; 173 | void * data; 174 | }; 175 | 176 | typedef void(*EventCallback)(Message* msg); 177 | 178 | enum { 179 | kInterfaceVersion = 2 180 | }; 181 | 182 | // SKSE messages 183 | enum { 184 | kMessage_PostLoad, // sent to registered plugins once all plugins have been loaded (no data) 185 | kMessage_PostPostLoad, // sent right after kMessage_PostLoad to facilitate the correct dispatching/registering of messages/listeners 186 | kMessage_PreLoadGame, // dispatched immediately before savegame is read by Fallout 187 | // dataLen: length of file path, data: char* file path of .ess savegame file 188 | kMessage_PostLoadGame, //dispatched after an attempt to load a saved game has finished (the game's LoadGame() routine 189 | //has returned). You will probably want to handle this event if your plugin uses a Preload callback 190 | //as there is a chance that after that callback is invoked the game will encounter an error 191 | //while loading the saved game (eg. corrupted save) which may require you to reset some of your 192 | //plugin state. 193 | //data: bool, true if game successfully loaded, false otherwise 194 | // plugins may register as listeners during the first callback while deferring dispatches until the next 195 | kMessage_SaveGame, 196 | kMessage_DeleteGame, // sent right before deleting the .skse cosave and the .ess save. 197 | // dataLen: length of file path, data: char* file path of .ess savegame file 198 | kMessage_InputLoaded, // sent right after game input is loaded, right before the main menu initializes 199 | kMessage_NewGame, // sent after a new game is created, before the game has loaded (Sends CharGen TESQuest pointer) 200 | kMessage_DataLoaded // send after the data handler has loaded all its forms 201 | }; 202 | 203 | UInt32 interfaceVersion; 204 | bool(*RegisterListener)(PluginHandle listener, const char* sender, EventCallback handler); 205 | bool(*Dispatch)(PluginHandle sender, UInt32 messageType, void * data, UInt32 dataLen, const char* receiver); 206 | 207 | enum 208 | { 209 | kDispatcher_ModEvent = 0, 210 | kDispatcher_CameraEvent, 211 | kDispatcher_CrosshairEvent, 212 | kDispatcher_ActionEvent, 213 | kDispatcher_NiNodeUpdateEvent 214 | }; 215 | 216 | // Use this to acquire SKSE's internal EventDispatchers so that you can sink to them 217 | void * (*GetEventDispatcher)(UInt32 dispatcherId); 218 | }; 219 | 220 | struct SKSEObjectInterface 221 | { 222 | enum 223 | { 224 | kInterfaceVersion = 1 225 | }; 226 | 227 | UInt32 interfaceVersion; 228 | 229 | SKSEDelayFunctorManager & (*GetDelayFunctorManager)(); 230 | SKSEObjectRegistry & (*GetObjectRegistry)(); 231 | SKSEPersistentObjectStorage & (*GetPersistentObjectStorage)(); 232 | }; 233 | 234 | struct PluginInfo 235 | { 236 | enum 237 | { 238 | kInfoVersion = 1 239 | }; 240 | 241 | UInt32 infoVersion; 242 | const char * name; 243 | UInt32 version; 244 | }; 245 | 246 | typedef bool(*_SKSEPlugin_Query)(const SKSEInterface * skse, PluginInfo * info); 247 | typedef bool(*_SKSEPlugin_Load)(const SKSEInterface * skse); 248 | 249 | /**** plugin API docs ********************************************************** 250 | * 251 | * The base API is pretty simple. Create a project based on the 252 | * skse_plugin_example project included with the SKSE source code, then define 253 | * and export these functions: 254 | * 255 | * bool SKSEPlugin_Query(const SKSEInterface * skse, PluginInfo * info) 256 | * 257 | * This primary purposes of this function are to fill out the PluginInfo 258 | * structure, and to perform basic version checks based on the info in the 259 | * SKSEInterface structure. Return false if your plugin is incompatible with 260 | * the version of SKSE or the runtime passed in, otherwise return true. In 261 | * either case, fill out the PluginInfo structure. 262 | * 263 | * Do not do anything other than fill out the PluginInfo structure and return 264 | * true/false in this callback. 265 | * 266 | * If the plugin is being loaded in the context of the editor, isEditor will be 267 | * non-zero, editorVersion will contain the current editor version, and 268 | * runtimeVersion will be zero. In this case you can probably just return 269 | * true, however if you have multiple DLLs implementing the same behavior, for 270 | * example one for each version of ther runtime, only one of them should return 271 | * true. 272 | * 273 | * The PluginInfo fields should be filled out as follows: 274 | * - infoVersion should be set to PluginInfo::kInfoVersion 275 | * - name should be a pointer to a null-terminated string uniquely identifying 276 | * your plugin, it will be used in the plugin querying API 277 | * - version is only used by the plugin query API, and will be returned to 278 | * scripts requesting the current version of your plugin 279 | * 280 | * bool SKSEPlugin_Load(const SKSEInterface * skse) 281 | * 282 | * In this function, use the interfaces above to register your commands, patch 283 | * memory, generally do whatever you need to for integration with the runtime. 284 | * 285 | * At this time, or at any point forward you can call the QueryInterface 286 | * callback to retrieve an interface structure for the base services provided 287 | * by the SKSE core. 288 | * 289 | * You may optionally return false from this function to unload your plugin, 290 | * but make sure that you DO NOT register any commands if you do. 291 | * 292 | * Note that all structure versions are backwards-compatible, so you only need 293 | * to check against the latest version that you need. New fields will be only 294 | * added to the end, and all old fields will remain compatible with their 295 | * previous implementations. 296 | * 297 | ******************************************************************************/ 298 | -------------------------------------------------------------------------------- /xbyak/xbyak_util.h: -------------------------------------------------------------------------------- 1 | #ifndef XBYAK_XBYAK_UTIL_H_ 2 | #define XBYAK_XBYAK_UTIL_H_ 3 | 4 | /** 5 | utility class and functions for Xbyak 6 | Xbyak::util::Clock ; rdtsc timer 7 | Xbyak::util::Cpu ; detect CPU 8 | @note this header is UNDER CONSTRUCTION! 9 | */ 10 | #include "xbyak.h" 11 | 12 | #ifdef _MSC_VER 13 | #if (_MSC_VER < 1400) && defined(XBYAK32) 14 | static inline __declspec(naked) void __cpuid(int[4], int) 15 | { 16 | __asm { 17 | push ebx 18 | push esi 19 | mov eax, dword ptr [esp + 4 * 2 + 8] // eaxIn 20 | cpuid 21 | mov esi, dword ptr [esp + 4 * 2 + 4] // data 22 | mov dword ptr [esi], eax 23 | mov dword ptr [esi + 4], ebx 24 | mov dword ptr [esi + 8], ecx 25 | mov dword ptr [esi + 12], edx 26 | pop esi 27 | pop ebx 28 | ret 29 | } 30 | } 31 | #else 32 | #include // for __cpuid 33 | #endif 34 | #else 35 | #ifndef __GNUC_PREREQ 36 | #define __GNUC_PREREQ(major, minor) ((((__GNUC__) << 16) + (__GNUC_MINOR__)) >= (((major) << 16) + (minor))) 37 | #endif 38 | #if __GNUC_PREREQ(4, 3) && !defined(__APPLE__) 39 | #include 40 | #else 41 | #if defined(__APPLE__) && defined(XBYAK32) // avoid err : can't find a register in class `BREG' while reloading `asm' 42 | #define __cpuid(eaxIn, a, b, c, d) __asm__ __volatile__("pushl %%ebx\ncpuid\nmovl %%ebp, %%esi\npopl %%ebx" : "=a"(a), "=S"(b), "=c"(c), "=d"(d) : "0"(eaxIn)) 43 | #define __cpuid_count(eaxIn, ecxIn, a, b, c, d) __asm__ __volatile__("pushl %%ebx\ncpuid\nmovl %%ebp, %%esi\npopl %%ebx" : "=a"(a), "=S"(b), "=c"(c), "=d"(d) : "0"(eaxIn), "2"(ecxIn)) 44 | #else 45 | #define __cpuid(eaxIn, a, b, c, d) __asm__ __volatile__("cpuid\n" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(eaxIn)) 46 | #define __cpuid_count(eaxIn, ecxIn, a, b, c, d) __asm__ __volatile__("cpuid\n" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(eaxIn), "2"(ecxIn)) 47 | #endif 48 | #endif 49 | #endif 50 | 51 | #ifdef _MSC_VER 52 | extern "C" unsigned __int64 __xgetbv(int); 53 | #endif 54 | 55 | namespace Xbyak { namespace util { 56 | 57 | /** 58 | CPU detection class 59 | */ 60 | class Cpu { 61 | uint64 type_; 62 | unsigned int get32bitAsBE(const char *x) const 63 | { 64 | return x[0] | (x[1] << 8) | (x[2] << 16) | (x[3] << 24); 65 | } 66 | unsigned int mask(int n) const 67 | { 68 | return (1U << n) - 1; 69 | } 70 | void setFamily() 71 | { 72 | unsigned int data[4]; 73 | getCpuid(1, data); 74 | stepping = data[0] & mask(4); 75 | model = (data[0] >> 4) & mask(4); 76 | family = (data[0] >> 8) & mask(4); 77 | // type = (data[0] >> 12) & mask(2); 78 | extModel = (data[0] >> 16) & mask(4); 79 | extFamily = (data[0] >> 20) & mask(8); 80 | if (family == 0x0f) { 81 | displayFamily = family + extFamily; 82 | } else { 83 | displayFamily = family; 84 | } 85 | if (family == 6 || family == 0x0f) { 86 | displayModel = (extModel << 4) + model; 87 | } else { 88 | displayModel = model; 89 | } 90 | } 91 | public: 92 | int model; 93 | int family; 94 | int stepping; 95 | int extModel; 96 | int extFamily; 97 | int displayFamily; // family + extFamily 98 | int displayModel; // model + extModel 99 | static inline void getCpuid(unsigned int eaxIn, unsigned int data[4]) 100 | { 101 | #ifdef _MSC_VER 102 | __cpuid(reinterpret_cast(data), eaxIn); 103 | #else 104 | __cpuid(eaxIn, data[0], data[1], data[2], data[3]); 105 | #endif 106 | } 107 | static inline void getCpuidEx(unsigned int eaxIn, unsigned int ecxIn, unsigned int data[4]) 108 | { 109 | #ifdef _MSC_VER 110 | __cpuidex(reinterpret_cast(data), eaxIn, ecxIn); 111 | #else 112 | __cpuid_count(eaxIn, ecxIn, data[0], data[1], data[2], data[3]); 113 | #endif 114 | } 115 | static inline uint64 getXfeature() 116 | { 117 | #ifdef _MSC_VER 118 | return __xgetbv(0); 119 | #else 120 | unsigned int eax, edx; 121 | // xgetvb is not support on gcc 4.2 122 | // __asm__ volatile("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0)); 123 | __asm__ volatile(".byte 0x0f, 0x01, 0xd0" : "=a"(eax), "=d"(edx) : "c"(0)); 124 | return ((uint64)edx << 32) | eax; 125 | #endif 126 | } 127 | typedef uint64 Type; 128 | static const Type NONE = 0; 129 | static const Type tMMX = 1 << 0; 130 | static const Type tMMX2 = 1 << 1; 131 | static const Type tCMOV = 1 << 2; 132 | static const Type tSSE = 1 << 3; 133 | static const Type tSSE2 = 1 << 4; 134 | static const Type tSSE3 = 1 << 5; 135 | static const Type tSSSE3 = 1 << 6; 136 | static const Type tSSE41 = 1 << 7; 137 | static const Type tSSE42 = 1 << 8; 138 | static const Type tPOPCNT = 1 << 9; 139 | static const Type tAESNI = 1 << 10; 140 | static const Type tSSE5 = 1 << 11; 141 | static const Type tOSXSAVE = 1 << 12; 142 | static const Type tPCLMULQDQ = 1 << 13; 143 | static const Type tAVX = 1 << 14; 144 | static const Type tFMA = 1 << 15; 145 | 146 | static const Type t3DN = 1 << 16; 147 | static const Type tE3DN = 1 << 17; 148 | static const Type tSSE4a = 1 << 18; 149 | static const Type tRDTSCP = 1 << 19; 150 | static const Type tAVX2 = 1 << 20; 151 | static const Type tBMI1 = 1 << 21; // andn, bextr, blsi, blsmsk, blsr, tzcnt 152 | static const Type tBMI2 = 1 << 22; // bzhi, mulx, pdep, pext, rorx, sarx, shlx, shrx 153 | static const Type tLZCNT = 1 << 23; 154 | 155 | static const Type tINTEL = 1 << 24; 156 | static const Type tAMD = 1 << 25; 157 | 158 | static const Type tENHANCED_REP = 1 << 26; // enhanced rep movsb/stosb 159 | static const Type tRDRAND = 1 << 27; 160 | static const Type tADX = 1 << 28; // adcx, adox 161 | static const Type tRDSEED = 1 << 29; // rdseed 162 | static const Type tSMAP = 1 << 30; // stac 163 | static const Type tHLE = uint64(1) << 31; // xacquire, xrelease, xtest 164 | static const Type tRTM = uint64(1) << 32; // xbegin, xend, xabort 165 | static const Type tF16C = uint64(1) << 33; // vcvtph2ps, vcvtps2ph 166 | static const Type tMOVBE = uint64(1) << 34; // mobve 167 | 168 | Cpu() 169 | : type_(NONE) 170 | { 171 | unsigned int data[4]; 172 | getCpuid(0, data); 173 | const unsigned int maxNum = data[0]; 174 | static const char intel[] = "ntel"; 175 | static const char amd[] = "cAMD"; 176 | if (data[2] == get32bitAsBE(amd)) { 177 | type_ |= tAMD; 178 | getCpuid(0x80000001, data); 179 | if (data[3] & (1U << 31)) type_ |= t3DN; 180 | if (data[3] & (1U << 15)) type_ |= tCMOV; 181 | if (data[3] & (1U << 30)) type_ |= tE3DN; 182 | if (data[3] & (1U << 22)) type_ |= tMMX2; 183 | if (data[3] & (1U << 27)) type_ |= tRDTSCP; 184 | } 185 | if (data[2] == get32bitAsBE(intel)) { 186 | type_ |= tINTEL; 187 | getCpuid(0x80000001, data); 188 | if (data[3] & (1U << 27)) type_ |= tRDTSCP; 189 | if (data[2] & (1U << 5)) type_ |= tLZCNT; 190 | } 191 | getCpuid(1, data); 192 | if (data[2] & (1U << 0)) type_ |= tSSE3; 193 | if (data[2] & (1U << 9)) type_ |= tSSSE3; 194 | if (data[2] & (1U << 19)) type_ |= tSSE41; 195 | if (data[2] & (1U << 20)) type_ |= tSSE42; 196 | if (data[2] & (1U << 22)) type_ |= tMOVBE; 197 | if (data[2] & (1U << 23)) type_ |= tPOPCNT; 198 | if (data[2] & (1U << 25)) type_ |= tAESNI; 199 | if (data[2] & (1U << 1)) type_ |= tPCLMULQDQ; 200 | if (data[2] & (1U << 27)) type_ |= tOSXSAVE; 201 | if (data[2] & (1U << 30)) type_ |= tRDRAND; 202 | if (data[2] & (1U << 29)) type_ |= tF16C; 203 | 204 | if (data[3] & (1U << 15)) type_ |= tCMOV; 205 | if (data[3] & (1U << 23)) type_ |= tMMX; 206 | if (data[3] & (1U << 25)) type_ |= tMMX2 | tSSE; 207 | if (data[3] & (1U << 26)) type_ |= tSSE2; 208 | 209 | if (type_ & tOSXSAVE) { 210 | // check XFEATURE_ENABLED_MASK[2:1] = '11b' 211 | uint64 bv = getXfeature(); 212 | if ((bv & 6) == 6) { 213 | if (data[2] & (1U << 28)) type_ |= tAVX; 214 | if (data[2] & (1U << 12)) type_ |= tFMA; 215 | } 216 | } 217 | if (maxNum >= 7) { 218 | getCpuidEx(7, 0, data); 219 | if (type_ & tAVX && data[1] & 0x20) type_ |= tAVX2; 220 | if (data[1] & (1U << 3)) type_ |= tBMI1; 221 | if (data[1] & (1U << 8)) type_ |= tBMI2; 222 | if (data[1] & (1U << 9)) type_ |= tENHANCED_REP; 223 | if (data[1] & (1U << 18)) type_ |= tRDSEED; 224 | if (data[1] & (1U << 19)) type_ |= tADX; 225 | if (data[1] & (1U << 20)) type_ |= tSMAP; 226 | if (data[1] & (1U << 4)) type_ |= tHLE; 227 | if (data[1] & (1U << 11)) type_ |= tRTM; 228 | } 229 | setFamily(); 230 | } 231 | void putFamily() 232 | { 233 | printf("family=%d, model=%X, stepping=%d, extFamily=%d, extModel=%X\n", 234 | family, model, stepping, extFamily, extModel); 235 | printf("display:family=%X, model=%X\n", displayFamily, displayModel); 236 | } 237 | bool has(Type type) const 238 | { 239 | return (type & type_) != 0; 240 | } 241 | }; 242 | 243 | class Clock { 244 | public: 245 | static inline uint64 getRdtsc() 246 | { 247 | #ifdef _MSC_VER 248 | return __rdtsc(); 249 | #else 250 | unsigned int eax, edx; 251 | __asm__ volatile("rdtsc" : "=a"(eax), "=d"(edx)); 252 | return ((uint64)edx << 32) | eax; 253 | #endif 254 | } 255 | Clock() 256 | : clock_(0) 257 | , count_(0) 258 | { 259 | } 260 | void begin() 261 | { 262 | clock_ -= getRdtsc(); 263 | } 264 | void end() 265 | { 266 | clock_ += getRdtsc(); 267 | count_++; 268 | } 269 | int getCount() const { return count_; } 270 | uint64 getClock() const { return clock_; } 271 | void clear() { count_ = 0; clock_ = 0; } 272 | private: 273 | uint64 clock_; 274 | int count_; 275 | }; 276 | 277 | #ifdef XBYAK64 278 | const int UseRCX = 1 << 6; 279 | const int UseRDX = 1 << 7; 280 | 281 | class Pack { 282 | static const size_t maxTblNum = 10; 283 | const Xbyak::Reg64 *tbl_[maxTblNum]; 284 | size_t n_; 285 | public: 286 | Pack() : n_(0) {} 287 | Pack(const Xbyak::Reg64 *tbl, size_t n) { init(tbl, n); } 288 | Pack(const Pack& rhs) 289 | : n_(rhs.n_) 290 | { 291 | if (n_ > maxTblNum) throw Error(ERR_INTERNAL); 292 | for (size_t i = 0; i < n_; i++) tbl_[i] = rhs.tbl_[i]; 293 | } 294 | Pack(const Xbyak::Reg64& t0) 295 | { n_ = 1; tbl_[0] = &t0; } 296 | Pack(const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) 297 | { n_ = 2; tbl_[0] = &t0; tbl_[1] = &t1; } 298 | Pack(const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) 299 | { n_ = 3; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; } 300 | Pack(const Xbyak::Reg64& t3, const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) 301 | { n_ = 4; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; tbl_[3] = &t3; } 302 | Pack(const Xbyak::Reg64& t4, const Xbyak::Reg64& t3, const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) 303 | { n_ = 5; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; tbl_[3] = &t3; tbl_[4] = &t4; } 304 | Pack(const Xbyak::Reg64& t5, const Xbyak::Reg64& t4, const Xbyak::Reg64& t3, const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) 305 | { n_ = 6; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; tbl_[3] = &t3; tbl_[4] = &t4; tbl_[5] = &t5; } 306 | Pack(const Xbyak::Reg64& t6, const Xbyak::Reg64& t5, const Xbyak::Reg64& t4, const Xbyak::Reg64& t3, const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) 307 | { n_ = 7; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; tbl_[3] = &t3; tbl_[4] = &t4; tbl_[5] = &t5; tbl_[6] = &t6; } 308 | Pack(const Xbyak::Reg64& t7, const Xbyak::Reg64& t6, const Xbyak::Reg64& t5, const Xbyak::Reg64& t4, const Xbyak::Reg64& t3, const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) 309 | { n_ = 8; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; tbl_[3] = &t3; tbl_[4] = &t4; tbl_[5] = &t5; tbl_[6] = &t6; tbl_[7] = &t7; } 310 | Pack(const Xbyak::Reg64& t8, const Xbyak::Reg64& t7, const Xbyak::Reg64& t6, const Xbyak::Reg64& t5, const Xbyak::Reg64& t4, const Xbyak::Reg64& t3, const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) 311 | { n_ = 9; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; tbl_[3] = &t3; tbl_[4] = &t4; tbl_[5] = &t5; tbl_[6] = &t6; tbl_[7] = &t7; tbl_[8] = &t8; } 312 | Pack(const Xbyak::Reg64& t9, const Xbyak::Reg64& t8, const Xbyak::Reg64& t7, const Xbyak::Reg64& t6, const Xbyak::Reg64& t5, const Xbyak::Reg64& t4, const Xbyak::Reg64& t3, const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) 313 | { n_ = 10; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; tbl_[3] = &t3; tbl_[4] = &t4; tbl_[5] = &t5; tbl_[6] = &t6; tbl_[7] = &t7; tbl_[8] = &t8; tbl_[9] = &t9; } 314 | Pack& append(const Xbyak::Reg64& t) 315 | { 316 | if (n_ == 10) { 317 | fprintf(stderr, "ERR Pack::can't append\n"); 318 | throw Error(ERR_BAD_PARAMETER); 319 | } 320 | tbl_[n_++] = &t; 321 | return *this; 322 | } 323 | void init(const Xbyak::Reg64 *tbl, size_t n) 324 | { 325 | if (n > maxTblNum) { 326 | fprintf(stderr, "ERR Pack::init bad n=%d\n", (int)n); 327 | throw Error(ERR_BAD_PARAMETER); 328 | } 329 | n_ = n; 330 | for (size_t i = 0; i < n; i++) { 331 | tbl_[i] = &tbl[i]; 332 | } 333 | } 334 | const Xbyak::Reg64& operator[](size_t n) const 335 | { 336 | if (n >= n_) { 337 | fprintf(stderr, "ERR Pack bad n=%d\n", (int)n); 338 | throw Error(ERR_BAD_PARAMETER); 339 | } 340 | return *tbl_[n]; 341 | } 342 | size_t size() const { return n_; } 343 | /* 344 | get tbl[pos, pos + num) 345 | */ 346 | Pack sub(size_t pos, size_t num = size_t(-1)) const 347 | { 348 | if (num == size_t(-1)) num = n_ - pos; 349 | if (pos + num > n_) { 350 | fprintf(stderr, "ERR Pack::sub bad pos=%d, num=%d\n", (int)pos, (int)num); 351 | throw Error(ERR_BAD_PARAMETER); 352 | } 353 | Pack pack; 354 | pack.n_ = num; 355 | for (size_t i = 0; i < num; i++) { 356 | pack.tbl_[i] = tbl_[pos + i]; 357 | } 358 | return pack; 359 | } 360 | void put() const 361 | { 362 | for (size_t i = 0; i < n_; i++) { 363 | printf("%s ", tbl_[i]->toString()); 364 | } 365 | printf("\n"); 366 | } 367 | }; 368 | 369 | class StackFrame { 370 | #ifdef XBYAK64_WIN 371 | static const int noSaveNum = 6; 372 | static const int rcxPos = 0; 373 | static const int rdxPos = 1; 374 | #else 375 | static const int noSaveNum = 8; 376 | static const int rcxPos = 3; 377 | static const int rdxPos = 2; 378 | #endif 379 | Xbyak::CodeGenerator *code_; 380 | int pNum_; 381 | int tNum_; 382 | bool useRcx_; 383 | bool useRdx_; 384 | int saveNum_; 385 | int P_; 386 | bool makeEpilog_; 387 | Xbyak::Reg64 pTbl_[4]; 388 | Xbyak::Reg64 tTbl_[10]; 389 | Pack p_; 390 | Pack t_; 391 | StackFrame(const StackFrame&); 392 | void operator=(const StackFrame&); 393 | public: 394 | const Pack& p; 395 | const Pack& t; 396 | /* 397 | make stack frame 398 | @param sf [in] this 399 | @param pNum [in] num of function parameter(0 <= pNum <= 4) 400 | @param tNum [in] num of temporary register(0 <= tNum <= 10, with UseRCX, UseRDX) 401 | @param stackSizeByte [in] local stack size 402 | @param makeEpilog [in] automatically call close() if true 403 | 404 | you can use 405 | rax 406 | gp0, ..., gp(pNum - 1) 407 | gt0, ..., gt(tNum-1) 408 | rcx if tNum & UseRCX 409 | rdx if tNum & UseRDX 410 | rsp[0..stackSizeByte - 1] 411 | */ 412 | StackFrame(Xbyak::CodeGenerator *code, int pNum, int tNum = 0, int stackSizeByte = 0, bool makeEpilog = true) 413 | : code_(code) 414 | , pNum_(pNum) 415 | , tNum_(tNum & ~(UseRCX | UseRDX)) 416 | , useRcx_((tNum & UseRCX) != 0) 417 | , useRdx_((tNum & UseRDX) != 0) 418 | , saveNum_(0) 419 | , P_(0) 420 | , makeEpilog_(makeEpilog) 421 | , p(p_) 422 | , t(t_) 423 | { 424 | using namespace Xbyak; 425 | if (pNum < 0 || pNum > 4) throw Error(ERR_BAD_PNUM); 426 | const int allRegNum = pNum + tNum_ + (useRcx_ ? 1 : 0) + (useRdx_ ? 1 : 0); 427 | if (allRegNum < pNum || allRegNum > 14) throw Error(ERR_BAD_TNUM); 428 | const Reg64& _rsp = code->rsp; 429 | const AddressFrame& _ptr = code->ptr; 430 | saveNum_ = (std::max)(0, allRegNum - noSaveNum); 431 | const int *tbl = getOrderTbl() + noSaveNum; 432 | P_ = saveNum_ + (stackSizeByte + 7) / 8; 433 | if (P_ > 0 && (P_ & 1) == 0) P_++; // here (rsp % 16) == 8, then increment P_ for 16 byte alignment 434 | P_ *= 8; 435 | if (P_ > 0) code->sub(_rsp, P_); 436 | #ifdef XBYAK64_WIN 437 | for (int i = 0; i < (std::min)(saveNum_, 4); i++) { 438 | code->mov(_ptr [_rsp + P_ + (i + 1) * 8], Reg64(tbl[i])); 439 | } 440 | for (int i = 4; i < saveNum_; i++) { 441 | code->mov(_ptr [_rsp + P_ - 8 * (saveNum_ - i)], Reg64(tbl[i])); 442 | } 443 | #else 444 | for (int i = 0; i < saveNum_; i++) { 445 | code->mov(_ptr [_rsp + P_ - 8 * (saveNum_ - i)], Reg64(tbl[i])); 446 | } 447 | #endif 448 | int pos = 0; 449 | for (int i = 0; i < pNum; i++) { 450 | pTbl_[i] = Xbyak::Reg64(getRegIdx(pos)); 451 | } 452 | for (int i = 0; i < tNum_; i++) { 453 | tTbl_[i] = Xbyak::Reg64(getRegIdx(pos)); 454 | } 455 | if (useRcx_ && rcxPos < pNum) code_->mov(code_->r10, code_->rcx); 456 | if (useRdx_ && rdxPos < pNum) code_->mov(code_->r11, code_->rdx); 457 | p_.init(pTbl_, pNum); 458 | t_.init(tTbl_, tNum_); 459 | } 460 | /* 461 | make epilog manually 462 | @param callRet [in] call ret() if true 463 | */ 464 | void close(bool callRet = true) 465 | { 466 | using namespace Xbyak; 467 | const Reg64& _rsp = code_->rsp; 468 | const AddressFrame& _ptr = code_->ptr; 469 | const int *tbl = getOrderTbl() + noSaveNum; 470 | #ifdef XBYAK64_WIN 471 | for (int i = 0; i < (std::min)(saveNum_, 4); i++) { 472 | code_->mov(Reg64(tbl[i]), _ptr [_rsp + P_ + (i + 1) * 8]); 473 | } 474 | for (int i = 4; i < saveNum_; i++) { 475 | code_->mov(Reg64(tbl[i]), _ptr [_rsp + P_ - 8 * (saveNum_ - i)]); 476 | } 477 | #else 478 | for (int i = 0; i < saveNum_; i++) { 479 | code_->mov(Reg64(tbl[i]), _ptr [_rsp + P_ - 8 * (saveNum_ - i)]); 480 | } 481 | #endif 482 | if (P_ > 0) code_->add(_rsp, P_); 483 | 484 | if (callRet) code_->ret(); 485 | } 486 | ~StackFrame() 487 | { 488 | if (!makeEpilog_) return; 489 | try { 490 | close(); 491 | } catch (std::exception& e) { 492 | printf("ERR:StackFrame %s\n", e.what()); 493 | exit(1); 494 | } catch (...) { 495 | printf("ERR:StackFrame otherwise\n"); 496 | exit(1); 497 | } 498 | } 499 | private: 500 | const int *getOrderTbl() const 501 | { 502 | using namespace Xbyak; 503 | static const int tbl[] = { 504 | #ifdef XBYAK64_WIN 505 | Operand::RCX, Operand::RDX, Operand::R8, Operand::R9, Operand::R10, Operand::R11, Operand::RDI, Operand::RSI, 506 | #else 507 | Operand::RDI, Operand::RSI, Operand::RDX, Operand::RCX, Operand::R8, Operand::R9, Operand::R10, Operand::R11, 508 | #endif 509 | Operand::RBX, Operand::RBP, Operand::R12, Operand::R13, Operand::R14, Operand::R15 510 | }; 511 | return &tbl[0]; 512 | } 513 | int getRegIdx(int& pos) const 514 | { 515 | assert(pos < 14); 516 | using namespace Xbyak; 517 | const int *tbl = getOrderTbl(); 518 | int r = tbl[pos++]; 519 | if (useRcx_) { 520 | if (r == Operand::RCX) { return Operand::R10; } 521 | if (r == Operand::R10) { r = tbl[pos++]; } 522 | } 523 | if (useRdx_) { 524 | if (r == Operand::RDX) { return Operand::R11; } 525 | if (r == Operand::R11) { return tbl[pos++]; } 526 | } 527 | return r; 528 | } 529 | }; 530 | #endif 531 | 532 | } } // end of util 533 | #endif 534 | -------------------------------------------------------------------------------- /SkyrimUncapper/xbyak_util.h: -------------------------------------------------------------------------------- 1 | #ifndef XBYAK_XBYAK_UTIL_H_ 2 | #define XBYAK_XBYAK_UTIL_H_ 3 | 4 | /** 5 | utility class and functions for Xbyak 6 | Xbyak::util::Clock ; rdtsc timer 7 | Xbyak::util::Cpu ; detect CPU 8 | @note this header is UNDER CONSTRUCTION! 9 | */ 10 | #include "xbyak.h" 11 | 12 | #ifdef _MSC_VER 13 | #if (_MSC_VER < 1400) && defined(XBYAK32) 14 | static inline __declspec(naked) void __cpuid(int[4], int) 15 | { 16 | __asm { 17 | push ebx 18 | push esi 19 | mov eax, dword ptr [esp + 4 * 2 + 8] // eaxIn 20 | cpuid 21 | mov esi, dword ptr [esp + 4 * 2 + 4] // data 22 | mov dword ptr [esi], eax 23 | mov dword ptr [esi + 4], ebx 24 | mov dword ptr [esi + 8], ecx 25 | mov dword ptr [esi + 12], edx 26 | pop esi 27 | pop ebx 28 | ret 29 | } 30 | } 31 | #else 32 | #include // for __cpuid 33 | #endif 34 | #else 35 | #ifndef __GNUC_PREREQ 36 | #define __GNUC_PREREQ(major, minor) ((((__GNUC__) << 16) + (__GNUC_MINOR__)) >= (((major) << 16) + (minor))) 37 | #endif 38 | #if __GNUC_PREREQ(4, 3) && !defined(__APPLE__) 39 | #include 40 | #else 41 | #if defined(__APPLE__) && defined(XBYAK32) // avoid err : can't find a register in class `BREG' while reloading `asm' 42 | #define __cpuid(eaxIn, a, b, c, d) __asm__ __volatile__("pushl %%ebx\ncpuid\nmovl %%ebp, %%esi\npopl %%ebx" : "=a"(a), "=S"(b), "=c"(c), "=d"(d) : "0"(eaxIn)) 43 | #define __cpuid_count(eaxIn, ecxIn, a, b, c, d) __asm__ __volatile__("pushl %%ebx\ncpuid\nmovl %%ebp, %%esi\npopl %%ebx" : "=a"(a), "=S"(b), "=c"(c), "=d"(d) : "0"(eaxIn), "2"(ecxIn)) 44 | #else 45 | #define __cpuid(eaxIn, a, b, c, d) __asm__ __volatile__("cpuid\n" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(eaxIn)) 46 | #define __cpuid_count(eaxIn, ecxIn, a, b, c, d) __asm__ __volatile__("cpuid\n" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(eaxIn), "2"(ecxIn)) 47 | #endif 48 | #endif 49 | #endif 50 | 51 | #ifdef _MSC_VER 52 | extern "C" unsigned __int64 __xgetbv(int); 53 | #endif 54 | 55 | namespace Xbyak { namespace util { 56 | 57 | /** 58 | CPU detection class 59 | */ 60 | class Cpu { 61 | uint64 type_; 62 | unsigned int get32bitAsBE(const char *x) const 63 | { 64 | return x[0] | (x[1] << 8) | (x[2] << 16) | (x[3] << 24); 65 | } 66 | unsigned int mask(int n) const 67 | { 68 | return (1U << n) - 1; 69 | } 70 | void setFamily() 71 | { 72 | unsigned int data[4]; 73 | getCpuid(1, data); 74 | stepping = data[0] & mask(4); 75 | model = (data[0] >> 4) & mask(4); 76 | family = (data[0] >> 8) & mask(4); 77 | // type = (data[0] >> 12) & mask(2); 78 | extModel = (data[0] >> 16) & mask(4); 79 | extFamily = (data[0] >> 20) & mask(8); 80 | if (family == 0x0f) { 81 | displayFamily = family + extFamily; 82 | } else { 83 | displayFamily = family; 84 | } 85 | if (family == 6 || family == 0x0f) { 86 | displayModel = (extModel << 4) + model; 87 | } else { 88 | displayModel = model; 89 | } 90 | } 91 | public: 92 | int model; 93 | int family; 94 | int stepping; 95 | int extModel; 96 | int extFamily; 97 | int displayFamily; // family + extFamily 98 | int displayModel; // model + extModel 99 | static inline void getCpuid(unsigned int eaxIn, unsigned int data[4]) 100 | { 101 | #ifdef _MSC_VER 102 | __cpuid(reinterpret_cast(data), eaxIn); 103 | #else 104 | __cpuid(eaxIn, data[0], data[1], data[2], data[3]); 105 | #endif 106 | } 107 | static inline void getCpuidEx(unsigned int eaxIn, unsigned int ecxIn, unsigned int data[4]) 108 | { 109 | #ifdef _MSC_VER 110 | __cpuidex(reinterpret_cast(data), eaxIn, ecxIn); 111 | #else 112 | __cpuid_count(eaxIn, ecxIn, data[0], data[1], data[2], data[3]); 113 | #endif 114 | } 115 | static inline uint64 getXfeature() 116 | { 117 | #ifdef _MSC_VER 118 | return __xgetbv(0); 119 | #else 120 | unsigned int eax, edx; 121 | // xgetvb is not support on gcc 4.2 122 | // __asm__ volatile("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0)); 123 | __asm__ volatile(".byte 0x0f, 0x01, 0xd0" : "=a"(eax), "=d"(edx) : "c"(0)); 124 | return ((uint64)edx << 32) | eax; 125 | #endif 126 | } 127 | typedef uint64 Type; 128 | static const Type NONE = 0; 129 | static const Type tMMX = 1 << 0; 130 | static const Type tMMX2 = 1 << 1; 131 | static const Type tCMOV = 1 << 2; 132 | static const Type tSSE = 1 << 3; 133 | static const Type tSSE2 = 1 << 4; 134 | static const Type tSSE3 = 1 << 5; 135 | static const Type tSSSE3 = 1 << 6; 136 | static const Type tSSE41 = 1 << 7; 137 | static const Type tSSE42 = 1 << 8; 138 | static const Type tPOPCNT = 1 << 9; 139 | static const Type tAESNI = 1 << 10; 140 | static const Type tSSE5 = 1 << 11; 141 | static const Type tOSXSAVE = 1 << 12; 142 | static const Type tPCLMULQDQ = 1 << 13; 143 | static const Type tAVX = 1 << 14; 144 | static const Type tFMA = 1 << 15; 145 | 146 | static const Type t3DN = 1 << 16; 147 | static const Type tE3DN = 1 << 17; 148 | static const Type tSSE4a = 1 << 18; 149 | static const Type tRDTSCP = 1 << 19; 150 | static const Type tAVX2 = 1 << 20; 151 | static const Type tBMI1 = 1 << 21; // andn, bextr, blsi, blsmsk, blsr, tzcnt 152 | static const Type tBMI2 = 1 << 22; // bzhi, mulx, pdep, pext, rorx, sarx, shlx, shrx 153 | static const Type tLZCNT = 1 << 23; 154 | 155 | static const Type tINTEL = 1 << 24; 156 | static const Type tAMD = 1 << 25; 157 | 158 | static const Type tENHANCED_REP = 1 << 26; // enhanced rep movsb/stosb 159 | static const Type tRDRAND = 1 << 27; 160 | static const Type tADX = 1 << 28; // adcx, adox 161 | static const Type tRDSEED = 1 << 29; // rdseed 162 | static const Type tSMAP = 1 << 30; // stac 163 | static const Type tHLE = uint64(1) << 31; // xacquire, xrelease, xtest 164 | static const Type tRTM = uint64(1) << 32; // xbegin, xend, xabort 165 | static const Type tF16C = uint64(1) << 33; // vcvtph2ps, vcvtps2ph 166 | static const Type tMOVBE = uint64(1) << 34; // mobve 167 | 168 | Cpu() 169 | : type_(NONE) 170 | { 171 | unsigned int data[4]; 172 | getCpuid(0, data); 173 | const unsigned int maxNum = data[0]; 174 | static const char intel[] = "ntel"; 175 | static const char amd[] = "cAMD"; 176 | if (data[2] == get32bitAsBE(amd)) { 177 | type_ |= tAMD; 178 | getCpuid(0x80000001, data); 179 | if (data[3] & (1U << 31)) type_ |= t3DN; 180 | if (data[3] & (1U << 15)) type_ |= tCMOV; 181 | if (data[3] & (1U << 30)) type_ |= tE3DN; 182 | if (data[3] & (1U << 22)) type_ |= tMMX2; 183 | if (data[3] & (1U << 27)) type_ |= tRDTSCP; 184 | } 185 | if (data[2] == get32bitAsBE(intel)) { 186 | type_ |= tINTEL; 187 | getCpuid(0x80000001, data); 188 | if (data[3] & (1U << 27)) type_ |= tRDTSCP; 189 | if (data[2] & (1U << 5)) type_ |= tLZCNT; 190 | } 191 | getCpuid(1, data); 192 | if (data[2] & (1U << 0)) type_ |= tSSE3; 193 | if (data[2] & (1U << 9)) type_ |= tSSSE3; 194 | if (data[2] & (1U << 19)) type_ |= tSSE41; 195 | if (data[2] & (1U << 20)) type_ |= tSSE42; 196 | if (data[2] & (1U << 22)) type_ |= tMOVBE; 197 | if (data[2] & (1U << 23)) type_ |= tPOPCNT; 198 | if (data[2] & (1U << 25)) type_ |= tAESNI; 199 | if (data[2] & (1U << 1)) type_ |= tPCLMULQDQ; 200 | if (data[2] & (1U << 27)) type_ |= tOSXSAVE; 201 | if (data[2] & (1U << 30)) type_ |= tRDRAND; 202 | if (data[2] & (1U << 29)) type_ |= tF16C; 203 | 204 | if (data[3] & (1U << 15)) type_ |= tCMOV; 205 | if (data[3] & (1U << 23)) type_ |= tMMX; 206 | if (data[3] & (1U << 25)) type_ |= tMMX2 | tSSE; 207 | if (data[3] & (1U << 26)) type_ |= tSSE2; 208 | 209 | if (type_ & tOSXSAVE) { 210 | // check XFEATURE_ENABLED_MASK[2:1] = '11b' 211 | uint64 bv = getXfeature(); 212 | if ((bv & 6) == 6) { 213 | if (data[2] & (1U << 28)) type_ |= tAVX; 214 | if (data[2] & (1U << 12)) type_ |= tFMA; 215 | } 216 | } 217 | if (maxNum >= 7) { 218 | getCpuidEx(7, 0, data); 219 | if (type_ & tAVX && data[1] & 0x20) type_ |= tAVX2; 220 | if (data[1] & (1U << 3)) type_ |= tBMI1; 221 | if (data[1] & (1U << 8)) type_ |= tBMI2; 222 | if (data[1] & (1U << 9)) type_ |= tENHANCED_REP; 223 | if (data[1] & (1U << 18)) type_ |= tRDSEED; 224 | if (data[1] & (1U << 19)) type_ |= tADX; 225 | if (data[1] & (1U << 20)) type_ |= tSMAP; 226 | if (data[1] & (1U << 4)) type_ |= tHLE; 227 | if (data[1] & (1U << 11)) type_ |= tRTM; 228 | } 229 | setFamily(); 230 | } 231 | void putFamily() 232 | { 233 | printf("family=%d, model=%X, stepping=%d, extFamily=%d, extModel=%X\n", 234 | family, model, stepping, extFamily, extModel); 235 | printf("display:family=%X, model=%X\n", displayFamily, displayModel); 236 | } 237 | bool has(Type type) const 238 | { 239 | return (type & type_) != 0; 240 | } 241 | }; 242 | 243 | class Clock { 244 | public: 245 | static inline uint64 getRdtsc() 246 | { 247 | #ifdef _MSC_VER 248 | return __rdtsc(); 249 | #else 250 | unsigned int eax, edx; 251 | __asm__ volatile("rdtsc" : "=a"(eax), "=d"(edx)); 252 | return ((uint64)edx << 32) | eax; 253 | #endif 254 | } 255 | Clock() 256 | : clock_(0) 257 | , count_(0) 258 | { 259 | } 260 | void begin() 261 | { 262 | clock_ -= getRdtsc(); 263 | } 264 | void end() 265 | { 266 | clock_ += getRdtsc(); 267 | count_++; 268 | } 269 | int getCount() const { return count_; } 270 | uint64 getClock() const { return clock_; } 271 | void clear() { count_ = 0; clock_ = 0; } 272 | private: 273 | uint64 clock_; 274 | int count_; 275 | }; 276 | 277 | #ifdef XBYAK64 278 | const int UseRCX = 1 << 6; 279 | const int UseRDX = 1 << 7; 280 | 281 | class Pack { 282 | static const size_t maxTblNum = 10; 283 | const Xbyak::Reg64 *tbl_[maxTblNum]; 284 | size_t n_; 285 | public: 286 | Pack() : n_(0) {} 287 | Pack(const Xbyak::Reg64 *tbl, size_t n) { init(tbl, n); } 288 | Pack(const Pack& rhs) 289 | : n_(rhs.n_) 290 | { 291 | if (n_ > maxTblNum) throw Error(ERR_INTERNAL); 292 | for (size_t i = 0; i < n_; i++) tbl_[i] = rhs.tbl_[i]; 293 | } 294 | Pack(const Xbyak::Reg64& t0) 295 | { n_ = 1; tbl_[0] = &t0; } 296 | Pack(const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) 297 | { n_ = 2; tbl_[0] = &t0; tbl_[1] = &t1; } 298 | Pack(const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) 299 | { n_ = 3; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; } 300 | Pack(const Xbyak::Reg64& t3, const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) 301 | { n_ = 4; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; tbl_[3] = &t3; } 302 | Pack(const Xbyak::Reg64& t4, const Xbyak::Reg64& t3, const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) 303 | { n_ = 5; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; tbl_[3] = &t3; tbl_[4] = &t4; } 304 | Pack(const Xbyak::Reg64& t5, const Xbyak::Reg64& t4, const Xbyak::Reg64& t3, const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) 305 | { n_ = 6; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; tbl_[3] = &t3; tbl_[4] = &t4; tbl_[5] = &t5; } 306 | Pack(const Xbyak::Reg64& t6, const Xbyak::Reg64& t5, const Xbyak::Reg64& t4, const Xbyak::Reg64& t3, const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) 307 | { n_ = 7; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; tbl_[3] = &t3; tbl_[4] = &t4; tbl_[5] = &t5; tbl_[6] = &t6; } 308 | Pack(const Xbyak::Reg64& t7, const Xbyak::Reg64& t6, const Xbyak::Reg64& t5, const Xbyak::Reg64& t4, const Xbyak::Reg64& t3, const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) 309 | { n_ = 8; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; tbl_[3] = &t3; tbl_[4] = &t4; tbl_[5] = &t5; tbl_[6] = &t6; tbl_[7] = &t7; } 310 | Pack(const Xbyak::Reg64& t8, const Xbyak::Reg64& t7, const Xbyak::Reg64& t6, const Xbyak::Reg64& t5, const Xbyak::Reg64& t4, const Xbyak::Reg64& t3, const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) 311 | { n_ = 9; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; tbl_[3] = &t3; tbl_[4] = &t4; tbl_[5] = &t5; tbl_[6] = &t6; tbl_[7] = &t7; tbl_[8] = &t8; } 312 | Pack(const Xbyak::Reg64& t9, const Xbyak::Reg64& t8, const Xbyak::Reg64& t7, const Xbyak::Reg64& t6, const Xbyak::Reg64& t5, const Xbyak::Reg64& t4, const Xbyak::Reg64& t3, const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) 313 | { n_ = 10; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; tbl_[3] = &t3; tbl_[4] = &t4; tbl_[5] = &t5; tbl_[6] = &t6; tbl_[7] = &t7; tbl_[8] = &t8; tbl_[9] = &t9; } 314 | Pack& append(const Xbyak::Reg64& t) 315 | { 316 | if (n_ == 10) { 317 | fprintf(stderr, "ERR Pack::can't append\n"); 318 | throw Error(ERR_BAD_PARAMETER); 319 | } 320 | tbl_[n_++] = &t; 321 | return *this; 322 | } 323 | void init(const Xbyak::Reg64 *tbl, size_t n) 324 | { 325 | if (n > maxTblNum) { 326 | fprintf(stderr, "ERR Pack::init bad n=%d\n", (int)n); 327 | throw Error(ERR_BAD_PARAMETER); 328 | } 329 | n_ = n; 330 | for (size_t i = 0; i < n; i++) { 331 | tbl_[i] = &tbl[i]; 332 | } 333 | } 334 | const Xbyak::Reg64& operator[](size_t n) const 335 | { 336 | if (n >= n_) { 337 | fprintf(stderr, "ERR Pack bad n=%d\n", (int)n); 338 | throw Error(ERR_BAD_PARAMETER); 339 | } 340 | return *tbl_[n]; 341 | } 342 | size_t size() const { return n_; } 343 | /* 344 | get tbl[pos, pos + num) 345 | */ 346 | Pack sub(size_t pos, size_t num = size_t(-1)) const 347 | { 348 | if (num == size_t(-1)) num = n_ - pos; 349 | if (pos + num > n_) { 350 | fprintf(stderr, "ERR Pack::sub bad pos=%d, num=%d\n", (int)pos, (int)num); 351 | throw Error(ERR_BAD_PARAMETER); 352 | } 353 | Pack pack; 354 | pack.n_ = num; 355 | for (size_t i = 0; i < num; i++) { 356 | pack.tbl_[i] = tbl_[pos + i]; 357 | } 358 | return pack; 359 | } 360 | void put() const 361 | { 362 | for (size_t i = 0; i < n_; i++) { 363 | printf("%s ", tbl_[i]->toString()); 364 | } 365 | printf("\n"); 366 | } 367 | }; 368 | 369 | class StackFrame { 370 | #ifdef XBYAK64_WIN 371 | static const int noSaveNum = 6; 372 | static const int rcxPos = 0; 373 | static const int rdxPos = 1; 374 | #else 375 | static const int noSaveNum = 8; 376 | static const int rcxPos = 3; 377 | static const int rdxPos = 2; 378 | #endif 379 | Xbyak::CodeGenerator *code_; 380 | int pNum_; 381 | int tNum_; 382 | bool useRcx_; 383 | bool useRdx_; 384 | int saveNum_; 385 | int P_; 386 | bool makeEpilog_; 387 | Xbyak::Reg64 pTbl_[4]; 388 | Xbyak::Reg64 tTbl_[10]; 389 | Pack p_; 390 | Pack t_; 391 | StackFrame(const StackFrame&); 392 | void operator=(const StackFrame&); 393 | public: 394 | const Pack& p; 395 | const Pack& t; 396 | /* 397 | make stack frame 398 | @param sf [in] this 399 | @param pNum [in] num of function parameter(0 <= pNum <= 4) 400 | @param tNum [in] num of temporary register(0 <= tNum <= 10, with UseRCX, UseRDX) 401 | @param stackSizeByte [in] local stack size 402 | @param makeEpilog [in] automatically call close() if true 403 | 404 | you can use 405 | rax 406 | gp0, ..., gp(pNum - 1) 407 | gt0, ..., gt(tNum-1) 408 | rcx if tNum & UseRCX 409 | rdx if tNum & UseRDX 410 | rsp[0..stackSizeByte - 1] 411 | */ 412 | StackFrame(Xbyak::CodeGenerator *code, int pNum, int tNum = 0, int stackSizeByte = 0, bool makeEpilog = true) 413 | : code_(code) 414 | , pNum_(pNum) 415 | , tNum_(tNum & ~(UseRCX | UseRDX)) 416 | , useRcx_((tNum & UseRCX) != 0) 417 | , useRdx_((tNum & UseRDX) != 0) 418 | , saveNum_(0) 419 | , P_(0) 420 | , makeEpilog_(makeEpilog) 421 | , p(p_) 422 | , t(t_) 423 | { 424 | using namespace Xbyak; 425 | if (pNum < 0 || pNum > 4) throw Error(ERR_BAD_PNUM); 426 | const int allRegNum = pNum + tNum_ + (useRcx_ ? 1 : 0) + (useRdx_ ? 1 : 0); 427 | if (allRegNum < pNum || allRegNum > 14) throw Error(ERR_BAD_TNUM); 428 | const Reg64& _rsp = code->rsp; 429 | const AddressFrame& _ptr = code->ptr; 430 | saveNum_ = (std::max)(0, allRegNum - noSaveNum); 431 | const int *tbl = getOrderTbl() + noSaveNum; 432 | P_ = saveNum_ + (stackSizeByte + 7) / 8; 433 | if (P_ > 0 && (P_ & 1) == 0) P_++; // here (rsp % 16) == 8, then increment P_ for 16 byte alignment 434 | P_ *= 8; 435 | if (P_ > 0) code->sub(_rsp, P_); 436 | #ifdef XBYAK64_WIN 437 | for (int i = 0; i < (std::min)(saveNum_, 4); i++) { 438 | code->mov(_ptr [_rsp + P_ + (i + 1) * 8], Reg64(tbl[i])); 439 | } 440 | for (int i = 4; i < saveNum_; i++) { 441 | code->mov(_ptr [_rsp + P_ - 8 * (saveNum_ - i)], Reg64(tbl[i])); 442 | } 443 | #else 444 | for (int i = 0; i < saveNum_; i++) { 445 | code->mov(_ptr [_rsp + P_ - 8 * (saveNum_ - i)], Reg64(tbl[i])); 446 | } 447 | #endif 448 | int pos = 0; 449 | for (int i = 0; i < pNum; i++) { 450 | pTbl_[i] = Xbyak::Reg64(getRegIdx(pos)); 451 | } 452 | for (int i = 0; i < tNum_; i++) { 453 | tTbl_[i] = Xbyak::Reg64(getRegIdx(pos)); 454 | } 455 | if (useRcx_ && rcxPos < pNum) code_->mov(code_->r10, code_->rcx); 456 | if (useRdx_ && rdxPos < pNum) code_->mov(code_->r11, code_->rdx); 457 | p_.init(pTbl_, pNum); 458 | t_.init(tTbl_, tNum_); 459 | } 460 | /* 461 | make epilog manually 462 | @param callRet [in] call ret() if true 463 | */ 464 | void close(bool callRet = true) 465 | { 466 | using namespace Xbyak; 467 | const Reg64& _rsp = code_->rsp; 468 | const AddressFrame& _ptr = code_->ptr; 469 | const int *tbl = getOrderTbl() + noSaveNum; 470 | #ifdef XBYAK64_WIN 471 | for (int i = 0; i < (std::min)(saveNum_, 4); i++) { 472 | code_->mov(Reg64(tbl[i]), _ptr [_rsp + P_ + (i + 1) * 8]); 473 | } 474 | for (int i = 4; i < saveNum_; i++) { 475 | code_->mov(Reg64(tbl[i]), _ptr [_rsp + P_ - 8 * (saveNum_ - i)]); 476 | } 477 | #else 478 | for (int i = 0; i < saveNum_; i++) { 479 | code_->mov(Reg64(tbl[i]), _ptr [_rsp + P_ - 8 * (saveNum_ - i)]); 480 | } 481 | #endif 482 | if (P_ > 0) code_->add(_rsp, P_); 483 | 484 | if (callRet) code_->ret(); 485 | } 486 | ~StackFrame() 487 | { 488 | if (!makeEpilog_) return; 489 | try { 490 | close(); 491 | } catch (std::exception& e) { 492 | printf("ERR:StackFrame %s\n", e.what()); 493 | exit(1); 494 | } catch (...) { 495 | printf("ERR:StackFrame otherwise\n"); 496 | exit(1); 497 | } 498 | } 499 | private: 500 | const int *getOrderTbl() const 501 | { 502 | using namespace Xbyak; 503 | static const int tbl[] = { 504 | #ifdef XBYAK64_WIN 505 | Operand::RCX, Operand::RDX, Operand::R8, Operand::R9, Operand::R10, Operand::R11, Operand::RDI, Operand::RSI, 506 | #else 507 | Operand::RDI, Operand::RSI, Operand::RDX, Operand::RCX, Operand::R8, Operand::R9, Operand::R10, Operand::R11, 508 | #endif 509 | Operand::RBX, Operand::RBP, Operand::R12, Operand::R13, Operand::R14, Operand::R15 510 | }; 511 | return &tbl[0]; 512 | } 513 | int getRegIdx(int& pos) const 514 | { 515 | assert(pos < 14); 516 | using namespace Xbyak; 517 | const int *tbl = getOrderTbl(); 518 | int r = tbl[pos++]; 519 | if (useRcx_) { 520 | if (r == Operand::RCX) { return Operand::R10; } 521 | if (r == Operand::R10) { r = tbl[pos++]; } 522 | } 523 | if (useRdx_) { 524 | if (r == Operand::RDX) { return Operand::R11; } 525 | if (r == Operand::R11) { return tbl[pos++]; } 526 | } 527 | return r; 528 | } 529 | }; 530 | #endif 531 | 532 | } } // end of util 533 | #endif 534 | -------------------------------------------------------------------------------- /SkyrimUncapper/Hook_Skill.cpp: -------------------------------------------------------------------------------- 1 | #include "Hook_Skill.h" 2 | #include "Settings.h" 3 | #include "GameSettings.h" 4 | #include "skse64_common/Relocation.h" 5 | #include "skse64_common/BranchTrampoline.h" 6 | #include "skse64_common/SafeWrite.h" 7 | #include "xbyak.h" 8 | 9 | using LevelData = PlayerSkills::StatData::LevelData; 10 | 11 | RelocPtr g_thePlayer = 0x02F40458; 12 | 13 | RelocAddr kHook_ModifyPerkPool_Ent = 0x008C584F; 14 | RelocAddr kHook_ModifyPerkPool_Ret = 0x008C584F + 0x1C; //1C 15 | 16 | RelocAddr kHook_SkillCapPatch_Ent = 0x006E4B78; 17 | RelocAddr kHook_SkillCapPatch_Ret = 0x006E4B78 + 0x9; 18 | 19 | RelocAddr kHook_ExecuteLegendarySkill_Ent = 0x08C95FD;//get it when legendary skill by trace setbaseav. 20 | RelocAddr kHook_ExecuteLegendarySkill_Ret = 0x08C95FD + 0x6; 21 | 22 | RelocAddr kHook_CheckConditionForLegendarySkill_Ent = 0x08BF655; 23 | RelocAddr kHook_CheckConditionForLegendarySkill_Ret = 0x08BF655 + 0x13; 24 | 25 | RelocAddr kHook_HideLegendaryButton_Ent = 0x08C1456; 26 | RelocAddr kHook_HideLegendaryButton_Ret = 0x08C1456 + 0x1D; 27 | 28 | typedef void(*_ImproveSkillByTraining)(void* pPlayer, UInt32 skillID, UInt32 count); 29 | RelocAddr <_ImproveSkillByTraining> ImproveSkillByTraining_Original = 0x06A0EE0; 30 | 31 | typedef void(*_ImprovePlayerSkillPoints)(void*, UInt32, float, UInt64, UInt32, UInt8, bool); 32 | RelocAddr <_ImprovePlayerSkillPoints> ImprovePlayerSkillPoints = 0x06E4B30; 33 | _ImprovePlayerSkillPoints ImprovePlayerSkillPoints_Original = nullptr; 34 | 35 | typedef UInt64(*_ImproveAttributeWhenLevelUp)(void*, UInt8); 36 | RelocAddr <_ImproveAttributeWhenLevelUp> ImproveAttributeWhenLevelUp = 0x08925D0; 37 | _ImproveAttributeWhenLevelUp ImproveAttributeWhenLevelUp_Original = nullptr; 38 | 39 | typedef bool(*_GetSkillCoefficients)(UInt32, float*, float*, float*, float*); 40 | RelocAddr <_GetSkillCoefficients> GetSkillCoefficients = 0x03E37F0; 41 | 42 | typedef UInt16(*_GetLevel)(void* pPlayer); 43 | RelocAddr <_GetLevel> GetLevel = 0x05D4C40; 44 | 45 | typedef float(*_ImproveLevelExpBySkillLevel)(float skillLevel); 46 | RelocAddr <_ImproveLevelExpBySkillLevel> ImproveLevelExpBySkillLevel_Original = 0x06E5D90; 47 | 48 | typedef float(*_GetCurrentActorValue)(void*, UInt32); 49 | RelocAddr <_GetCurrentActorValue> GetCurrentActorValue = 0x061F6C0; 50 | _GetCurrentActorValue GetCurrentActorValue_Original = nullptr; 51 | 52 | typedef float(*_GetBaseActorValue)(void*, UInt32); 53 | RelocAddr <_GetBaseActorValue> GetBaseActorValue = 0x061F890; //GetBaseActorValue 54 | 55 | typedef float(*_CalculateChargePointsPerUse)(float basePoints, float enchantingLevel); 56 | RelocAddr <_CalculateChargePointsPerUse> CalculateChargePointsPerUse_Original = 0x03C0F10; 57 | 58 | class ActorValueOwner; 59 | typedef float(*_GetEffectiveSkillLevel)(ActorValueOwner *, UInt32 skillID); 60 | RelocAddr <_GetEffectiveSkillLevel> GetEffectiveSkillLevel = 0x03E5400; //V1.5.3 61 | 62 | //typedef float(*_Pow)(float, float); 63 | //RelocAddr <_Pow> Pow = 0x01312C52; 64 | 65 | 66 | //#include 67 | float CalculateSkillExpForLevel(UInt32 skillID, float skillLevel) 68 | { 69 | #ifdef _DEBUG 70 | _MESSAGE("function:%s, skillId:%d, skillLevel:%.2f", __FUNCTION__, skillID, skillLevel); 71 | #endif 72 | float result = 0.0f; 73 | float fSkillUseCurve = 1.95f;//0x01D88258; 74 | if ((*g_gameSettingCollection) != nullptr) 75 | { 76 | auto pSetting = (*g_gameSettingCollection)->Get("fSkillUseCurve"); 77 | fSkillUseCurve = (pSetting != nullptr) ? pSetting->data.f32 : 1.95f; 78 | } 79 | if(skillLevel < settings.settingsSkillCaps[skillID - 6]) 80 | { 81 | result = pow(skillLevel, fSkillUseCurve); 82 | float a = 1.0f, b = 0.0f, c = 1.0f, d = 0.0f; 83 | if (GetSkillCoefficients(skillID, &a, &b, &c, &d)) 84 | result = result * c + d; 85 | } 86 | return result; 87 | } 88 | 89 | float CalculateChargePointsPerUse_Hook(float basePoints, float enchantingLevel) 90 | { 91 | 92 | 93 | float fEnchantingCostExponent = 1.10f;// 0x01D8A058; //1.10 94 | float fEnchantingSkillCostBase = 0.005f; // 0x01D8A010; //1/200 = 0.005 95 | float fEnchantingSkillCostScale = 0.5f;// 0x01D8A040; //0.5 sqrt 96 | //RelocPtr unk0 = 0x014E8F78; //1.00f 97 | float fEnchantingSkillCostMult = 3.00f;// 0x01D8A028; //3.00 98 | 99 | if ((*g_gameSettingCollection) != nullptr) 100 | { 101 | Setting * pSetting = (*g_gameSettingCollection)->Get("fEnchantingCostExponent"); 102 | fEnchantingCostExponent = (pSetting != nullptr) ? pSetting->data.f32 : 1.10f; 103 | pSetting = (*g_gameSettingCollection)->Get("fEnchantingSkillCostBase"); 104 | fEnchantingSkillCostBase = (pSetting != nullptr) ? pSetting->data.f32 : 0.005f; 105 | pSetting = (*g_gameSettingCollection)->Get("fEnchantingSkillCostScale"); 106 | fEnchantingSkillCostScale = (pSetting != nullptr) ? pSetting->data.f32 : 0.5f; 107 | pSetting = (*g_gameSettingCollection)->Get("fEnchantingSkillCostMult"); 108 | fEnchantingSkillCostMult = (pSetting != nullptr) ? pSetting->data.f32 : 3.00f; 109 | } 110 | 111 | enchantingLevel = (enchantingLevel > 199.0f) ? 199.0f : enchantingLevel; 112 | float result = fEnchantingSkillCostMult * pow(basePoints, fEnchantingCostExponent) * (1.00f - pow((enchantingLevel * fEnchantingSkillCostBase), fEnchantingSkillCostScale)); 113 | #ifdef _DEBUG 114 | _MESSAGE("function:%s, basePoints:%.2f, enchantingLevel:%.2f, result:%.2f", __FUNCTION__, basePoints, enchantingLevel, result); 115 | #endif 116 | return result; 117 | //Charges Per Use = 3 * (base enchantment cost * magnitude / maximum magnitude)^1.1 * (1 - sqrt(skill/200)) 118 | } 119 | 120 | void ImproveSkillByTraining_Hook(void* pPlayer, UInt32 skillID, UInt32 count) 121 | { 122 | PlayerSkills* skillData = *reinterpret_cast(reinterpret_cast(pPlayer)+0x9B0); 123 | if (count < 1) 124 | count = 1; 125 | if ((skillID >= 6) && (skillID <= 23)) 126 | { 127 | LevelData* levelData = &(skillData->data->levelData[skillID - 6]); 128 | float skillProgression = 0.0f; 129 | if (levelData->pointsMax > 0.0f) 130 | { 131 | skillProgression = levelData->points / levelData->pointsMax; 132 | #ifdef _DEBUG 133 | _MESSAGE("player:%p, skill:%d, points:%.2f, maxPoints:%.2f, level:%.2f", pPlayer, skillID, levelData->points, levelData->pointsMax, levelData->level); 134 | #endif 135 | if (skillProgression >= 1.0f) 136 | skillProgression = 0.99f; 137 | } 138 | else 139 | skillProgression = 0.0f; 140 | for (UInt32 i = 0; i < count; ++i) 141 | { 142 | float skillLevel = GetBaseActorValue((char*)(*g_thePlayer) + 0xB0, skillID); 143 | float expRequired = CalculateSkillExpForLevel(skillID, skillLevel); 144 | #ifdef _DEBUG 145 | _MESSAGE("maxPoints:%.2f, expRequired:%.2f", levelData->pointsMax, expRequired); 146 | #endif 147 | if (levelData->pointsMax != expRequired) 148 | levelData->pointsMax = expRequired; 149 | if (levelData->points <= 0.0f) 150 | levelData->points = (levelData->pointsMax > 0.0f) ? 0.1f : 0.0f; 151 | if (levelData->points >= levelData->pointsMax) 152 | levelData->points = (levelData->pointsMax > 0.0f) ? (levelData->pointsMax - 0.1f) : 0.0f; 153 | float expNeeded = levelData->pointsMax - levelData->points; 154 | ImprovePlayerSkillPoints_Original(skillData, skillID, expNeeded, 0, 0, 0, (i < count - 1)); 155 | } 156 | levelData->points += levelData->pointsMax * skillProgression; 157 | } 158 | } 159 | 160 | void ImprovePlayerSkillPoints_Hook(PlayerSkills* skillData, UInt32 skillID, float exp, UInt64 unk1, UInt32 unk2, UInt8 unk3, bool unk4) 161 | { 162 | if ((skillID >= 6) && (skillID <= 23)) 163 | { 164 | #ifdef _DEBUG 165 | _MESSAGE("function: %s, skillID: %d, SkillExpGainMults: %.2f", __FUNCTION__, skillID, settings.settingsSkillExpGainMults[skillID - 6]); 166 | #endif 167 | UInt32 baseSkillLevel = static_cast(GetBaseActorValue((char*)(*g_thePlayer) + 0xB0, skillID)); 168 | float skillMult = settings.settingsSkillExpGainMultsWithSkills[skillID - 6].GetValue(baseSkillLevel); 169 | UInt16 level = GetLevel(*g_thePlayer); 170 | float levelMult = settings.settingsSkillExpGainMultsWithPCLevel[skillID - 6].GetValue(level); 171 | exp *= settings.settingsSkillExpGainMults[skillID - 6] * skillMult * levelMult; 172 | } 173 | 174 | #ifdef _DEBUG 175 | _MESSAGE("function: %s", __FUNCTION__); 176 | void* actorValue = static_cast(*g_thePlayer) + 0xB0; 177 | for (size_t i = 0; i <= 8; ++i) 178 | _MESSAGE("Index: %d, Function: %016I64X", i, *(*static_cast(actorValue)+i)); 179 | #endif 180 | 181 | ImprovePlayerSkillPoints_Original(skillData, skillID, exp, unk1, unk2, unk3, unk4); 182 | } 183 | 184 | float ImproveLevelExpBySkillLevel_Hook(float skillLevel, UInt32 skillID) 185 | { 186 | float baseMult = 1.0f, skillMult = 1.0f, levelMult = 1.0f; 187 | if ((skillID >= 6) && (skillID <= 23)) 188 | { 189 | baseMult = settings.settingsLevelSkillExpMults[skillID - 6]; 190 | UInt32 baseSkillLevel = static_cast(GetBaseActorValue((char*)(*g_thePlayer) + 0xB0, skillID)); 191 | skillMult = settings.settingsLevelSkillExpMultsWithSkills[skillID - 6].GetValue(baseSkillLevel); 192 | UInt16 level = GetLevel(*g_thePlayer); 193 | levelMult = settings.settingsLevelSkillExpMultsWithPCLevel[skillID - 6].GetValue(level); 194 | //PCLevel has some minor glitch.I need to calculate player's actual level. 195 | } 196 | float result = ImproveLevelExpBySkillLevel_Original(skillLevel) * baseMult * skillMult * levelMult; 197 | #ifdef _DEBUG 198 | _MESSAGE("function:%s, skillId:%d, skillLevel:%.2f, skillMult:%.2f, result:%.2f", __FUNCTION__, skillID, skillLevel, skillMult, result); 199 | #endif 200 | return result; 201 | } 202 | 203 | UInt64 ImproveAttributeWhenLevelUp_Hook(void* unk0, UInt8 unk1) 204 | { 205 | enum 206 | { 207 | kHealth = 0x18, 208 | kMagicka, 209 | kStamina 210 | }; 211 | //static RelocPtr iAVDhmsLevelUp = 0x01D7C200; 212 | //static RelocPtr fLevelUpCarryWeightMod = 0x01D882B8; 213 | 214 | if ((*g_gameSettingCollection) != nullptr) 215 | { 216 | Setting * iAVDhmsLevelUp = (*g_gameSettingCollection)->Get("iAVDhmsLevelUp"); 217 | Setting * fLevelUpCarryWeightMod = (*g_gameSettingCollection)->Get("fLevelUpCarryWeightMod"); 218 | if (iAVDhmsLevelUp && fLevelUpCarryWeightMod) 219 | { 220 | UInt32 choice = *reinterpret_cast(static_cast(unk0) + 0x18); 221 | UInt16 level = GetLevel(*g_thePlayer); 222 | 223 | switch (choice) 224 | { 225 | case kHealth: 226 | { 227 | iAVDhmsLevelUp->data.u32 = settings.settingsHealthAtLevelUp.GetValue(level); 228 | fLevelUpCarryWeightMod->data.f32 = settings.settingsCarryWeightAtHealthLevelUp.GetValue(level); 229 | break; 230 | } 231 | case kMagicka: 232 | { 233 | iAVDhmsLevelUp->data.u32 = settings.settingsMagickaAtLevelUp.GetValue(level); 234 | fLevelUpCarryWeightMod->data.f32 = settings.settingsCarryWeightAtMagickaLevelUp.GetValue(level); 235 | break; 236 | } 237 | case kStamina: 238 | { 239 | iAVDhmsLevelUp->data.u32 = settings.settingsStaminaAtLevelUp.GetValue(level); 240 | fLevelUpCarryWeightMod->data.f32 = settings.settingsCarryWeightAtStaminaLevelUp.GetValue(level); 241 | break; 242 | } 243 | default: 244 | { 245 | iAVDhmsLevelUp->data.u32 = 5; 246 | fLevelUpCarryWeightMod->data.f32 = 10.0f; 247 | } 248 | } 249 | } 250 | } 251 | 252 | #ifdef _DEBUG 253 | _MESSAGE("function: %s, attributePerLevel: %d, carryweightPerLevel: %.2f, level: %d, choice: %d", __FUNCTION__, *attributePerLevel, *carryweightPerLevel, level, choice); 254 | #endif 255 | 256 | return ImproveAttributeWhenLevelUp_Original(unk0, unk1); 257 | } 258 | 259 | void ModifyPerkPool_Hook(SInt8 count) 260 | { 261 | UInt8* points = *reinterpret_cast(g_thePlayer.GetPtr()) + 0xB01; 262 | if (count > 0) //AddPerkPoints 263 | { 264 | //static float mantissa = 0.0f; //This vlaue needs to be stored to cross save. 265 | UInt16 level = GetLevel(*g_thePlayer); 266 | float increment = settings.settingsPerksAtLevelUp.GetValue(level) + settings.settingsPerksAtLevelUp.GetDecimal(level); 267 | count = static_cast(increment); 268 | //mantissa = increment - count; 269 | #ifdef _DEBUG 270 | _MESSAGE("function: %s, count: %d, perkPoints: %d, level: %d", __FUNCTION__, count, *points, level); 271 | #endif 272 | UInt32 sum = count + *points; 273 | *points = (sum > 0xFF) ? 0xFF : static_cast(sum); 274 | } 275 | else //RemovePerkPoints 276 | { 277 | SInt32 sum = *points + count; 278 | *points = (sum < 0) ? 0 : static_cast(sum); 279 | } 280 | } 281 | 282 | float GetSkillCap_Hook(UInt32 skillID) 283 | { 284 | if ((skillID >= 6) && (skillID <= 23)) 285 | { 286 | #ifdef _DEBUG 287 | _MESSAGE("function: %s, skillID: %d, skillCap: %d", __FUNCTION__, skillID, settings.settingsSkillCaps[skillID - 6]); 288 | #endif 289 | return settings.settingsSkillCaps[skillID - 6]; 290 | } 291 | else 292 | { 293 | //static RelocPtr skillCap = 0x014E6B68; 294 | #ifdef _DEBUG 295 | _MESSAGE("function: %s, skillCap: %.2f", __FUNCTION__, *skillCap); 296 | #endif 297 | return 100.0f; 298 | } 299 | } 300 | 301 | float GetCurrentActorValue_Hook(void* avo, UInt32 skillID) //PC&NPC //61F6C0 302 | { 303 | float skillLevel = GetCurrentActorValue_Original(avo, skillID); 304 | if ((skillID >= 6) && (skillID <= 23)) 305 | { 306 | UInt32 skillFormulaCap = settings.settingsSkillFormulaCaps[skillID - 6]; 307 | skillLevel = (skillLevel > skillFormulaCap) ? skillFormulaCap : skillLevel; 308 | #ifdef _DEBUG 309 | //_MESSAGE("function: %s, skillID: %d, skillLevel:%.2f, skillFormulaCap: %d, this:%p", __FUNCTION__, skillID, skillLevel, settings.settingsSkillFormulaCaps[skillID - 6], avo); 310 | #endif 311 | } 312 | return skillLevel; 313 | } 314 | 315 | void LegendaryResetSkillLevel_Hook(float baseLevel, UInt32 skillID) 316 | { 317 | if ((*g_gameSettingCollection) != nullptr) 318 | { 319 | Setting * fLegendarySkillResetValue = (*g_gameSettingCollection)->Get("fLegendarySkillResetValue"); 320 | if (fLegendarySkillResetValue != nullptr) 321 | { 322 | static float originalSetting = fLegendarySkillResetValue->data.f32; 323 | if ((skillID >= 6) && (skillID <= 23)) 324 | { 325 | if (settings.settingsLegendarySkill.bLegendaryKeepSkillLevel) 326 | fLegendarySkillResetValue->data.f32 = baseLevel; 327 | else 328 | { 329 | UInt32 legendaryLevel = settings.settingsLegendarySkill.iSkillLevelAfterLegendary; 330 | if ((legendaryLevel && legendaryLevel > baseLevel) || (!legendaryLevel && originalSetting > baseLevel)) 331 | fLegendarySkillResetValue->data.f32 = baseLevel; 332 | else 333 | fLegendarySkillResetValue->data.f32 = (!legendaryLevel) ? originalSetting : legendaryLevel; 334 | } 335 | } 336 | else 337 | fLegendarySkillResetValue->data.f32 = originalSetting; 338 | } 339 | } 340 | } 341 | 342 | bool CheckConditionForLegendarySkill_Hook(void* pActorValueOwner, UInt32 skillID) 343 | { 344 | float skillLevel = GetBaseActorValue(*(char**)(g_thePlayer.GetPtr()) + 0xB0, skillID); 345 | return (skillLevel >= settings.settingsLegendarySkill.iSkillLevelEnableLegendary) ? true : false; 346 | } 347 | 348 | bool HideLegendaryButton_Hook(UInt32 skillID) 349 | { 350 | float skillLevel = GetBaseActorValue(*(char**)(g_thePlayer.GetPtr()) + 0xB0, skillID); 351 | if (skillLevel >= settings.settingsLegendarySkill.iSkillLevelEnableLegendary && !settings.settingsLegendarySkill.bHideLegendaryButton) 352 | return true; 353 | return false; 354 | } 355 | 356 | 357 | void Hook_Skill_Init() 358 | { 359 | 360 | } 361 | 362 | void InitRVA() 363 | { 364 | g_thePlayer = RVAScan(GET_RVA(g_thePlayer), "F6 C1 04 74 65 4C 8B B7 18 01 00 00 4D 85 F6 74 59 49 8B D6 48 8B 0D ? ? ? ? E8 ? ? ? ? 0F B6 F0 49 8D 4E 30 84 C0 74 16", 0x14, 3, 7); 365 | 366 | g_gameSettingCollection = RVAScan(GET_RVA(g_gameSettingCollection), "74 1A 8B 15 ? ? ? ? 48 8B C8 E8 ? ? ? ? 48 8D 05 ? ? ? ? 48 89 03 EB 02 33 DB 48 89 1D ? ? ? ? 48 83 C4 30", 0x1E, 3, 7); 367 | 368 | kHook_ModifyPerkPool_Ent = RVAScan(GET_RVA(kHook_ModifyPerkPool_Ent), "48 85 C0 74 33 66 0F 6E C3 0F 5B C0 F3 0F 58 40 34 F3 0F 11 40 34 48 83 C4 20 5B C3 48 8B 15 ? ? ? ? 0F B6 8A 01 0B 00 00 8B C1 03 C3 78 08 02 CB 88 8A 01 0B 00 00", 0x1C); 369 | kHook_ModifyPerkPool_Ret = kHook_ModifyPerkPool_Ent; 370 | kHook_ModifyPerkPool_Ret += 0x1C; 371 | 372 | kHook_SkillCapPatch_Ent = RVAScan(GET_RVA(kHook_SkillCapPatch_Ent), "48 81 C1 B0 00 00 00 41 0F 29 73 D8 45 0F 29 43 B8 48 8B 01 FF 50 18 F3 44 0F 10 ? ? ? ? 00 0F 28 F0 41 0F 2F F0 0F 83 74 02 00 00 48 8D 44 24 3C", 0x17); 373 | kHook_SkillCapPatch_Ret = kHook_SkillCapPatch_Ent; 374 | kHook_SkillCapPatch_Ret += 9; 375 | 376 | kHook_ExecuteLegendarySkill_Ent = RVAScan(GET_RVA(kHook_ExecuteLegendarySkill_Ent), "0F 82 85 00 00 00 48 8B 0D ? ? ? ? 48 81 C1 B0 00 00 00 48 8B 01 F3 0F 10 15 ? ? ? ? 8B 56 1C FF 50 20 48 8B 05 ? ? ? ? 8B 56 1C 48 8B 88 B0 09 00 00"); 377 | kHook_ExecuteLegendarySkill_Ret = kHook_ExecuteLegendarySkill_Ent; 378 | kHook_ExecuteLegendarySkill_Ret += 6; 379 | 380 | kHook_CheckConditionForLegendarySkill_Ent = RVAScan(GET_RVA(kHook_CheckConditionForLegendarySkill_Ent), "8B D0 48 8D 8F B0 00 00 00 FF 53 18 0F 2F 05 ? ? ? ? 0F 82 10 0A 00 00 45 33 FF 4C 89 7D 80 44 89 7D 88 45 33 C0 48 8B 15 ? ? ? ? 48 8D 4D 80"); 381 | kHook_CheckConditionForLegendarySkill_Ret = kHook_CheckConditionForLegendarySkill_Ent; 382 | kHook_CheckConditionForLegendarySkill_Ret += 0x13; 383 | 384 | kHook_HideLegendaryButton_Ent = RVAScan(GET_RVA(kHook_HideLegendaryButton_Ent), "48 8B 0D ? ? ? ? 48 81 C1 B0 00 00 00 48 8B 01 8B D6 FF 50 18 0F 2F 05 ? ? ? ? 72 64 48 8D 05 ? ? ? ? 48 89 85 C0 00 00 00 4C 89 64 24 20"); 385 | kHook_HideLegendaryButton_Ret = kHook_HideLegendaryButton_Ent; 386 | kHook_HideLegendaryButton_Ret += 0x1D; 387 | 388 | ImproveSkillByTraining_Original = RVAScan<_ImproveSkillByTraining>(GET_RVA(ImproveSkillByTraining_Original), "48 8B 89 B0 09 00 00 B8 01 00 00 00 44 3B C0 44 0F 42 C0 E9 ? ? ? ?"); 389 | 390 | ImprovePlayerSkillPoints = RVAScan<_ImprovePlayerSkillPoints>(GET_RVA(ImprovePlayerSkillPoints), "4C 8B DC 55 56 41 56 48 81 EC 60 01 00 00 8D 42 FA 41 0F 29 7B C8 49 8B E9 0F 28 FA 8B F2 4C 8B F1 83 F8 11 0F 87 B9 02 00 00"); 391 | 392 | ImproveAttributeWhenLevelUp = RVAScan<_ImproveAttributeWhenLevelUp>(GET_RVA(ImproveAttributeWhenLevelUp), "0F B6 DA 48 8B F9 48 8B 15 ? ? ? ? 48 81 C2 28 01 00 00 48 8B 0D ? ? ? ? E8 ? ? ? ? 84 C0 0F 84 BA 00 00 00 84 DB 0F 85 AA 00 00 00", -0x1E); 393 | 394 | GetSkillCoefficients = RVAScan<_GetSkillCoefficients>(GET_RVA(GetSkillCoefficients), "81 F9 A3 00 00 00 77 52 48 8B 05 ? ? ? ? 48 63 C9 4C 8B 54 C8 08 4D 85 D2 74 3E 49 8B 82 08 01 00 00 48 85 C0 74 32"); 395 | 396 | GetLevel = RVAScan<_GetLevel>(GET_RVA(GetLevel), "84 C0 74 24 48 8B CB E8 ? ? ? ? 0F B7 C8 48 8B D7 E8 ? ? ? ? 84 C0 74 0D B0 01 48 8B 5C 24 30 48 83 C4 20 5F C3", 7, 1, 5); 397 | 398 | ImproveLevelExpBySkillLevel_Original = RVAScan<_ImproveLevelExpBySkillLevel>(GET_RVA(ImproveLevelExpBySkillLevel_Original), "F3 0F 58 D3 0F 28 E0 0F 29 34 24 0F 57 F6 0F 28 CA F3 0F 58 CB F3 0F 59 CA F3 0F 59 CD F3 0F 2C C1 66 0F 6E C0 0F 5B C0 F3 0F 5C C8 0F 2F CE 73 04 F3 0F 5C C3", -0x17); 399 | 400 | GetCurrentActorValue = RVAScan<_GetCurrentActorValue>(GET_RVA(GetCurrentActorValue), "4C 8B 44 F8 08 41 8B 40 60 C1 E8 12 A8 01 74 38 48 8B 49 40 48 85 C9 74 2F 48 83 79 50 00 74 28 40 B5 01 0F 57 C0 F3 0F 11 44 24 78 4C 8D 44 24 78 8B D7", -0x2C); 401 | 402 | GetBaseActorValue = RVAScan<_GetBaseActorValue>(GET_RVA(GetBaseActorValue), "48 89 5C 24 18 48 89 74 24 20 57 48 83 EC 30 48 63 DA 4C 8D 44 24 40 48 8B F9 0F 57 C0 8B D3 F3 0F 11 44 24 40 48 81 C1 50 01 00 00 E8 ? ? ? ? 84 C0 0F 85 DE 00 00 00"); 403 | 404 | CalculateChargePointsPerUse_Original = RVAScan<_CalculateChargePointsPerUse>(GET_RVA(CalculateChargePointsPerUse_Original), "48 83 EC 48 0F 29 74 24 30 0F 29 7C 24 20 0F 28 F8 F3 0F 10 05 ? ? ? ? F3 0F 59 C1 F3 0F 10 0D ? ? ? ? E8 ? ? ? ? F3 0F 10 35 ? ? ? ? F3 0F 10 0D ? ? ? ?"); 405 | 406 | GetEffectiveSkillLevel = RVAScan<_GetEffectiveSkillLevel>(GET_RVA(GetEffectiveSkillLevel), "40 53 48 83 EC 20 48 8B 01 48 63 DA 8B D3 FF 50 08 48 8B 05 ? ? ? ? 48 8B 4C D8 08 8B 51 60 8B C2 C1 E8 04 A8 01 74 1E F3 0F 10 0D ? ? ? ? 0F 2F C1 73 44 0F 57 D2 0F 2F C2 73 3F"); 407 | } 408 | 409 | void Hook_Skill_Commit() 410 | { 411 | 412 | InitRVA(); 413 | //return; 414 | 415 | SafeWrite16(GetEffectiveSkillLevel.GetUIntPtr() + 0x34, 0x9090); 416 | 417 | g_branchTrampoline.Write6Branch(ImproveSkillByTraining_Original.GetUIntPtr(), (uintptr_t)ImproveSkillByTraining_Hook); 418 | 419 | g_branchTrampoline.Write6Branch(CalculateChargePointsPerUse_Original.GetUIntPtr(), (uintptr_t)CalculateChargePointsPerUse_Hook); 420 | 421 | { 422 | struct ImproveLevelExpBySkillLevel_Code : Xbyak::CodeGenerator 423 | { 424 | ImproveLevelExpBySkillLevel_Code(void * buf) : Xbyak::CodeGenerator(4096, buf) 425 | { 426 | Xbyak::Label retnLabel; 427 | 428 | push(rcx); 429 | push(rdx); 430 | mov(rdx, rsi); 431 | sub(rsp, 0x20); 432 | call((void *)&ImproveLevelExpBySkillLevel_Hook); 433 | add(rsp, 0x20); 434 | pop(rdx); 435 | pop(rcx); 436 | jmp(ptr[rip + retnLabel]); 437 | 438 | L(retnLabel); 439 | dq(ImprovePlayerSkillPoints.GetUIntPtr() + 0x230); 440 | } 441 | }; 442 | 443 | void * codeBuf = g_localTrampoline.StartAlloc(); 444 | ImproveLevelExpBySkillLevel_Code code(codeBuf); 445 | g_localTrampoline.EndAlloc(code.getCurr()); 446 | 447 | g_branchTrampoline.Write5Branch(ImprovePlayerSkillPoints.GetUIntPtr() + 0x22B, uintptr_t(code.getCode())); 448 | } 449 | 450 | { 451 | 452 | struct ModifyPerkPool_Code : Xbyak::CodeGenerator 453 | { 454 | ModifyPerkPool_Code(void * buf) : Xbyak::CodeGenerator(4096, buf) 455 | { 456 | Xbyak::Label retnLabel; 457 | 458 | mov(rcx, rbx); 459 | sub(rsp, 0x20); 460 | call((void *)&ModifyPerkPool_Hook); 461 | add(rsp, 0x20); 462 | jmp(ptr[rip + retnLabel]); 463 | 464 | L(retnLabel); 465 | dq(kHook_ModifyPerkPool_Ret.GetUIntPtr()); 466 | } 467 | }; 468 | 469 | void * codeBuf = g_localTrampoline.StartAlloc(); 470 | ModifyPerkPool_Code code(codeBuf); 471 | g_localTrampoline.EndAlloc(code.getCurr()); 472 | 473 | g_branchTrampoline.Write5Branch(kHook_ModifyPerkPool_Ent.GetUIntPtr(), uintptr_t(code.getCode())); 474 | } 475 | 476 | { 477 | 478 | struct SkillCapPatch_Code : Xbyak::CodeGenerator 479 | { 480 | SkillCapPatch_Code(void * buf) : Xbyak::CodeGenerator(4096, buf) 481 | { 482 | Xbyak::Label retnLabel; 483 | 484 | mov(rcx, rsi); 485 | sub(rsp, 0x30); 486 | movss(ptr[rsp + 0x28], xmm0); 487 | call((void *)&GetSkillCap_Hook); 488 | movss(xmm8, xmm0); 489 | movss(xmm0, ptr[rsp + 0x28]); 490 | add(rsp, 0x30); 491 | jmp(ptr[rip + retnLabel]); 492 | 493 | L(retnLabel); 494 | dq(kHook_SkillCapPatch_Ret.GetUIntPtr()); 495 | } 496 | }; 497 | 498 | void * codeBuf = g_localTrampoline.StartAlloc(); 499 | SkillCapPatch_Code code(codeBuf); 500 | g_localTrampoline.EndAlloc(code.getCurr()); 501 | 502 | g_branchTrampoline.Write5Branch(kHook_SkillCapPatch_Ent.GetUIntPtr(), uintptr_t(code.getCode())); 503 | } 504 | 505 | 506 | { 507 | struct ImprovePlayerSkillPoints_Code : Xbyak::CodeGenerator 508 | { 509 | ImprovePlayerSkillPoints_Code(void * buf) : Xbyak::CodeGenerator(4096, buf) 510 | { 511 | Xbyak::Label retnLabel; 512 | 513 | mov(r11, rsp); 514 | push(rbp); 515 | push(rsi); 516 | jmp(ptr[rip + retnLabel]); 517 | 518 | L(retnLabel); 519 | dq(ImprovePlayerSkillPoints.GetUIntPtr() + 0x5); 520 | } 521 | }; 522 | 523 | void * codeBuf = g_localTrampoline.StartAlloc(); 524 | ImprovePlayerSkillPoints_Code code(codeBuf); 525 | g_localTrampoline.EndAlloc(code.getCurr()); 526 | 527 | ImprovePlayerSkillPoints_Original = (_ImprovePlayerSkillPoints)codeBuf; 528 | 529 | g_branchTrampoline.Write5Branch(ImprovePlayerSkillPoints.GetUIntPtr(), (uintptr_t)ImprovePlayerSkillPoints_Hook); 530 | } 531 | 532 | { 533 | struct ImproveAttributeWhenLevelUp_Code : Xbyak::CodeGenerator 534 | { 535 | ImproveAttributeWhenLevelUp_Code(void * buf) : Xbyak::CodeGenerator(4096, buf) 536 | { 537 | Xbyak::Label retnLabel; 538 | 539 | push(rdi); 540 | sub(rsp, 0x30); 541 | 542 | jmp(ptr[rip + retnLabel]); 543 | 544 | L(retnLabel); 545 | dq(ImproveAttributeWhenLevelUp.GetUIntPtr() + 0x6); 546 | } 547 | }; 548 | 549 | void * codeBuf = g_localTrampoline.StartAlloc(); 550 | ImproveAttributeWhenLevelUp_Code code(codeBuf); 551 | g_localTrampoline.EndAlloc(code.getCurr()); 552 | 553 | ImproveAttributeWhenLevelUp_Original = (_ImproveAttributeWhenLevelUp)codeBuf; 554 | 555 | g_branchTrampoline.Write6Branch(ImproveAttributeWhenLevelUp.GetUIntPtr(), (uintptr_t)ImproveAttributeWhenLevelUp_Hook); 556 | 557 | SafeWrite8(ImproveAttributeWhenLevelUp.GetUIntPtr() + 0x9B, 0); 558 | } 559 | 560 | { 561 | struct GetCurrentActorValue_Code : Xbyak::CodeGenerator 562 | { 563 | GetCurrentActorValue_Code(void * buf) : Xbyak::CodeGenerator(4096, buf) 564 | { 565 | Xbyak::Label retnLabel; 566 | 567 | push(rbp); 568 | push(rsi); 569 | push(rdi); 570 | push(r14); 571 | 572 | jmp(ptr[rip + retnLabel]); 573 | 574 | L(retnLabel); 575 | dq(GetCurrentActorValue.GetUIntPtr() + 0x6); 576 | } 577 | }; 578 | 579 | void * codeBuf = g_localTrampoline.StartAlloc(); 580 | GetCurrentActorValue_Code code(codeBuf); 581 | g_localTrampoline.EndAlloc(code.getCurr()); 582 | 583 | GetCurrentActorValue_Original = (_GetCurrentActorValue)codeBuf; 584 | 585 | g_branchTrampoline.Write6Branch(GetCurrentActorValue.GetUIntPtr(), (uintptr_t)GetCurrentActorValue_Hook); 586 | 587 | } 588 | 589 | { 590 | struct ExecuteLegendarySkill_Code : Xbyak::CodeGenerator 591 | { 592 | ExecuteLegendarySkill_Code(void * buf) : Xbyak::CodeGenerator(4096, buf) 593 | { 594 | Xbyak::Label retnLabel; 595 | 596 | mov(edx, ptr[rsi + 0x1C]); 597 | call((void*)&LegendaryResetSkillLevel_Hook); 598 | 599 | jmp(ptr[rip + retnLabel]); 600 | 601 | L(retnLabel); 602 | dq(kHook_ExecuteLegendarySkill_Ret.GetUIntPtr()); 603 | } 604 | }; 605 | 606 | void * codeBuf = g_localTrampoline.StartAlloc(); 607 | ExecuteLegendarySkill_Code code(codeBuf); 608 | g_localTrampoline.EndAlloc(code.getCurr()); 609 | 610 | g_branchTrampoline.Write6Branch(kHook_ExecuteLegendarySkill_Ent.GetUIntPtr(), uintptr_t(code.getCode())); 611 | } 612 | 613 | { 614 | struct CheckConditionForLegendarySkill_Code : Xbyak::CodeGenerator 615 | { 616 | CheckConditionForLegendarySkill_Code(void * buf) : Xbyak::CodeGenerator(4096, buf) 617 | { 618 | Xbyak::Label retnLabel; 619 | 620 | mov(edx, eax); 621 | lea(rcx, ptr[rdi + 0xB0]); 622 | call((void*)&CheckConditionForLegendarySkill_Hook); 623 | cmp(al, 1); 624 | 625 | jmp(ptr[rip + retnLabel]); 626 | 627 | L(retnLabel); 628 | dq(kHook_CheckConditionForLegendarySkill_Ret.GetUIntPtr()); 629 | } 630 | }; 631 | 632 | void * codeBuf = g_localTrampoline.StartAlloc(); 633 | CheckConditionForLegendarySkill_Code code(codeBuf); 634 | g_localTrampoline.EndAlloc(code.getCurr()); 635 | 636 | g_branchTrampoline.Write6Branch(kHook_CheckConditionForLegendarySkill_Ent.GetUIntPtr(), uintptr_t(code.getCode())); 637 | } 638 | 639 | { 640 | struct HideLegendaryButton_Code : Xbyak::CodeGenerator 641 | { 642 | HideLegendaryButton_Code(void * buf) : Xbyak::CodeGenerator(4096, buf) 643 | { 644 | Xbyak::Label retnLabel; 645 | mov(ecx, esi); 646 | call((void*)&HideLegendaryButton_Hook); 647 | cmp(al, 1); 648 | 649 | jmp(ptr[rip + retnLabel]); 650 | 651 | L(retnLabel); 652 | dq(kHook_HideLegendaryButton_Ret.GetUIntPtr()); 653 | } 654 | }; 655 | 656 | void * codeBuf = g_localTrampoline.StartAlloc(); 657 | HideLegendaryButton_Code code(codeBuf); 658 | g_localTrampoline.EndAlloc(code.getCurr()); 659 | 660 | g_branchTrampoline.Write6Branch(kHook_HideLegendaryButton_Ent.GetUIntPtr(), uintptr_t(code.getCode())); 661 | 662 | } 663 | } 664 | 665 | 666 | --------------------------------------------------------------------------------