From 19b33364522b4a96a8828599bd3547603a278733 Mon Sep 17 00:00:00 2001 From: Qudix <17361645+Qudix@users.noreply.github.com> Date: Sat, 27 Jan 2024 16:01:47 -0600 Subject: [PATCH 1/3] Improve WinAPI --- include/SKSE/Impl/WinAPI.h | 978 ++++++++++++++++++++++++++++------ src/SKSE/Impl/WinAPI.cpp | 1023 ++++++++++++++++++++++++++++-------- 2 files changed, 1641 insertions(+), 360 deletions(-) diff --git a/include/SKSE/Impl/WinAPI.h b/include/SKSE/Impl/WinAPI.h index ab6b84cb8..3b65100aa 100644 --- a/include/SKSE/Impl/WinAPI.h +++ b/include/SKSE/Impl/WinAPI.h @@ -1,117 +1,543 @@ #pragma once +#ifdef _INC_WINAPIFAMILY +# error Windows API detected. Please move any Windows API includes after CommonLibSSE, or remove them. +#else + namespace SKSE::WinAPI { + // general constants + inline const auto INVALID_HANDLE_VALUE{ reinterpret_cast(static_cast(-1)) }; + inline constexpr auto MAX_PATH{ 260u }; + + // standard access + inline constexpr auto STANDARD_RIGHTS_REQUIRED{ 0x000F0000 }; + inline constexpr auto STANDARD_RIGHTS_ALL{ 0x001F0000 }; + + // code page identifiers inline constexpr auto CP_UTF8{ 65001u }; + + // pe image header + inline constexpr auto IMAGE_DOS_SIGNATURE{ 0x5A4Du }; + inline constexpr auto IMAGE_NT_SIGNATURE{ 0x00004550u }; + inline constexpr auto IMAGE_NT_OPTIONAL_HDR32_MAGIC{ 0x10Bu }; + inline constexpr auto IMAGE_NT_OPTIONAL_HDR64_MAGIC{ 0x20Bu }; + + // pe image directory entries + inline constexpr auto IMAGE_DIRECTORY_ENTRY_EXPORT{ 0u }; + inline constexpr auto IMAGE_DIRECTORY_ENTRY_IMPORT{ 1u }; + inline constexpr auto IMAGE_DIRECTORY_ENTRY_RESOURCE{ 2u }; + inline constexpr auto IMAGE_DIRECTORY_ENTRY_EXCEPTION{ 3u }; + inline constexpr auto IMAGE_DIRECTORY_ENTRY_SECURITY{ 4u }; + inline constexpr auto IMAGE_DIRECTORY_ENTRY_BASERELOC{ 5u }; + inline constexpr auto IMAGE_DIRECTORY_ENTRY_DEBUG{ 6u }; + inline constexpr auto IMAGE_DIRECTORY_ENTRY_ARCHITECTURE{ 7u }; + inline constexpr auto IMAGE_DIRECTORY_ENTRY_GLOBALPTR{ 8u }; + inline constexpr auto IMAGE_DIRECTORY_ENTRY_TLS{ 9u }; + inline constexpr auto IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG{ 10u }; + inline constexpr auto IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT{ 11u }; + inline constexpr auto IMAGE_DIRECTORY_ENTRY_IAT{ 12u }; + inline constexpr auto IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT{ 13u }; + inline constexpr auto IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR{ 14u }; + inline constexpr auto IMAGE_NUMBEROF_DIRECTORY_ENTRIES{ 16u }; + + // pe image ordinal + inline constexpr auto IMAGE_ORDINAL_FLAG32{ 0x80000000u }; + inline constexpr auto IMAGE_ORDINAL_FLAG64{ 0x8000000000000000ull }; + + // pe image section header characteristics + inline constexpr auto IMAGE_SCN_MEM_SHARED{ 0x10000000u }; + inline constexpr auto IMAGE_SCN_MEM_EXECUTE{ 0x20000000u }; + inline constexpr auto IMAGE_SCN_MEM_READ{ 0x40000000u }; + inline constexpr auto IMAGE_SCN_MEM_WRITE{ 0x80000000u }; + inline constexpr auto IMAGE_SIZEOF_SECTION_HEADER{ 40u }; + inline constexpr auto IMAGE_SIZEOF_SHORT_NAME{ 8u }; + + // memory allocation types + inline constexpr auto MEM_COMMIT{ 0x00001000u }; + inline constexpr auto MEM_RESERVE{ 0x00002000u }; + inline constexpr auto MEM_DECOMMIT{ 0x00004000u }; + inline constexpr auto MEM_RELEASE{ 0x00008000u }; + inline constexpr auto MEM_FREE{ 0x00010000u }; + inline constexpr auto MEM_RESET{ 0x00080000u }; + inline constexpr auto MEM_RESET_UNDO{ 0x01000000u }; + + // memory page protection attributes + inline constexpr auto PAGE_NOACCESS{ 0x00000001u }; + inline constexpr auto PAGE_READONLY{ 0x00000002u }; + inline constexpr auto PAGE_READWRITE{ 0x00000004u }; + inline constexpr auto PAGE_WRITECOPY{ 0x00000008u }; + inline constexpr auto PAGE_EXECUTE{ 0x00000010u }; + inline constexpr auto PAGE_EXECUTE_READ{ 0x00000020u }; + inline constexpr auto PAGE_EXECUTE_READWRITE{ 0x00000040u }; + + // memory section + inline constexpr auto SECTION_QUERY{ 0x00000001 }; + inline constexpr auto SECTION_MAP_WRITE{ 0x00000002 }; + inline constexpr auto SECTION_MAP_READ{ 0x00000004 }; + inline constexpr auto SECTION_MAP_EXECUTE{ 0x00000008 }; + inline constexpr auto SECTION_EXTEND_SIZE{ 0x00000010 }; + inline constexpr auto SECTION_MAP_EXECUTE_EXPLICIT{ 0x00000020 }; + inline constexpr auto SECTION_ALL_ACCESS{ + STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_WRITE | SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_EXTEND_SIZE + }; + + // file attributes inline constexpr auto FILE_ATTRIBUTE_READONLY{ 0x00000001u }; inline constexpr auto FILE_ATTRIBUTE_HIDDEN{ 0x00000002u }; inline constexpr auto FILE_ATTRIBUTE_SYSTEM{ 0x00000004u }; inline constexpr auto FILE_ATTRIBUTE_DIRECTORY{ 0x00000010u }; inline constexpr auto FILE_ATTRIBUTE_ARCHIVE{ 0x00000020u }; - inline constexpr auto IMAGE_SCN_MEM_EXECUTE{ 0x20000000u }; - inline constexpr auto IMAGE_SCN_MEM_WRITE{ 0x80000000u }; - inline const auto INVALID_HANDLE_VALUE{ reinterpret_cast(static_cast(-1)) }; - inline constexpr auto MAX_PATH{ 260u }; - inline constexpr auto MEM_RELEASE{ 0x00008000u }; - inline constexpr auto PAGE_EXECUTE_READWRITE{ 0x40u }; + + // file mapping flags + inline constexpr auto FILE_MAP_ALL_ACCESS{ SECTION_ALL_ACCESS }; + inline constexpr auto FILE_MAP_COPY{ 0x00000001u }; + inline constexpr auto FILE_MAP_WRITE{ 0x00000002u }; + inline constexpr auto FILE_MAP_READ{ 0x00000004u }; + inline constexpr auto FILE_MAP_EXECUTE{ 0x00000020u }; + inline constexpr auto FILE_MAP_LARGE_PAGES{ 0x20000000u }; + inline constexpr auto FILE_MAP_TARGETS_INVALID{ 0x40000000u }; + inline constexpr auto FILE_MAP_RESERVE{ 0x80000000u }; + + // file open mode flags + inline constexpr auto GENERIC_READ{ 0x80000000L }; + inline constexpr auto GENERIC_WRITE{ 0x40000000L }; + inline constexpr auto GENERIC_EXECUTE{ 0x20000000L }; + inline constexpr auto GENERIC_ALL{ 0x10000000L }; + + // known folder flags + inline constexpr auto KF_FLAG_DEFAULT{ 0x00000000u }; + inline constexpr auto KF_FLAG_FORCE_APP_DATA_REDIRECTION{ 0x00080000u }; + inline constexpr auto KF_FLAG_RETURN_FILTER_REDIRECTION_TARGET{ 0x00040000u }; + inline constexpr auto KF_FLAG_FORCE_PACKAGE_REDIRECTION{ 0x00020000u }; + inline constexpr auto KF_FLAG_NO_PACKAGE_REDIRECTION{ 0x00010000u }; + inline constexpr auto KF_FLAG_FORCE_APPCONTAINER_REDIRECTION{ 0x00020000u }; + inline constexpr auto KF_FLAG_NO_APPCONTAINER_REDIRECTION{ 0x00010000u }; + inline constexpr auto KF_FLAG_CREATE{ 0x00008000u }; + inline constexpr auto KF_FLAG_DONT_VERIFY{ 0x00004000u }; + inline constexpr auto KF_FLAG_DONT_UNEXPAND{ 0x00002000u }; + inline constexpr auto KF_FLAG_NO_ALIAS{ 0x00001000u }; + inline constexpr auto KF_FLAG_INIT{ 0x00000800u }; + inline constexpr auto KF_FLAG_DEFAULT_PATH{ 0x00000400u }; + inline constexpr auto KF_FLAG_NOT_PARENT_RELATIVE{ 0x00000200u }; + inline constexpr auto KF_FLAG_SIMPLE_IDLIST{ 0x00000100u }; + inline constexpr auto KF_FLAG_ALIAS_ONLY{ 0x80000000u }; + + // string normalization forms + inline constexpr auto NORM_FORM_OTHER{ 0x0 }; + inline constexpr auto NORM_FORM_C{ 0x1 }; + inline constexpr auto NORM_FORM_D{ 0x2 }; + inline constexpr auto NORM_FORM_KC{ 0x5 }; + inline constexpr auto NORM_FORM_KD{ 0x6 }; + + // locale map flags + inline constexpr auto LCMAP_LOWERCASE{ 0x00000100u }; + inline constexpr auto LCMAP_UPPERCASE{ 0x00000200u }; + inline constexpr auto LCMAP_TITLECASE{ 0x00000300u }; + inline constexpr auto LCMAP_SORTKEY{ 0x00000400u }; + inline constexpr auto LCMAP_BYTEREV{ 0x00000800u }; + inline constexpr auto LCMAP_HIRAGANA{ 0x00100000u }; + inline constexpr auto LCMAP_KATAKANA{ 0x00200000u }; + inline constexpr auto LCMAP_HALFWIDTH{ 0x00400000u }; + inline constexpr auto LCMAP_FULLWIDTH{ 0x00800000u }; + inline constexpr auto LCMAP_LINGUISTIC_CASING{ 0x01000000u }; + inline constexpr auto LCMAP_SIMPLIFIED_CHINESE{ 0x02000000u }; + inline constexpr auto LCMAP_TRADITIONAL_CHINESE{ 0x04000000u }; + + // locale names + inline constexpr auto LOCALE_NAME_USER_DEFAULT{ nullptr }; + inline constexpr auto LOCALE_NAME_INVARIANT{ L"" }; + inline constexpr auto LOCALE_NAME_SYSTEM_DEFAULT{ L"!x-sys-default-locale" }; + + // process creation flags + inline constexpr auto DEBUG_PROCESS{ 0x00000001u }; + inline constexpr auto DEBUG_ONLY_THIS_PROCESS{ 0x00000002u }; + inline constexpr auto CREATE_SUSPENDED{ 0x00000004u }; + inline constexpr auto DETACHED_PROCESS{ 0x00000008u }; + inline constexpr auto CREATE_NEW_CONSOLE{ 0x00000010u }; + inline constexpr auto NORMAL_PRIORITY_CLASS{ 0x00000020u }; + inline constexpr auto IDLE_PRIORITY_CLASS{ 0x00000040u }; + inline constexpr auto HIGH_PRIORITY_CLASS{ 0x00000080u }; + inline constexpr auto REALTIME_PRIORITY_CLASS{ 0x00000100u }; + inline constexpr auto CREATE_NEW_PROCESS_GROUP{ 0x00000200u }; + inline constexpr auto CREATE_UNICODE_ENVIRONMENT{ 0x00000400u }; + inline constexpr auto CREATE_FORCEDOS{ 0x00002000u }; + inline constexpr auto BELOW_NORMAL_PRIORITY_CLASS{ 0x00004000u }; + inline constexpr auto ABOVE_NORMAL_PRIORITY_CLASS{ 0x00008000u }; + inline constexpr auto INHERIT_PARENT_AFFINITY{ 0x00010000u }; + inline constexpr auto CREATE_PROTECTED_PROCESS{ 0x00040000u }; + inline constexpr auto EXTENDED_STARTUPINFO_PRESENT{ 0x00080000u }; + inline constexpr auto PROCESS_MODE_BACKGROUND_BEGIN{ 0x00100000u }; + inline constexpr auto PROCESS_MODE_BACKGROUND_END{ 0x00200000u }; + inline constexpr auto CREATE_SECURE_PROCESS{ 0x00400000 }; + inline constexpr auto CREATE_BREAKAWAY_FROM_JOB{ 0x01000000u }; + inline constexpr auto CREATE_PRESERVE_CODE_AUTHZ_LEVEL{ 0x02000000u }; + inline constexpr auto CREATE_DEFAULT_ERROR_MODE{ 0x04000000u }; + inline constexpr auto CREATE_NO_WINDOW{ 0x08000000u }; + + // symbol name undecoration flags + inline constexpr auto UNDNAME_NO_MS_KEYWORDS{ 0x00000002u }; + inline constexpr auto UNDNAME_NO_FUNCTION_RETURNS{ 0x00000004u }; + inline constexpr auto UNDNAME_NO_ALLOCATION_MODEL{ 0x00000008u }; + inline constexpr auto UNDNAME_NO_ALLOCATION_LANGUAGE{ 0x00000010u }; + inline constexpr auto UNDNAME_NO_THISTYPE{ 0x00000060u }; + inline constexpr auto UNDNAME_NO_ACCESS_SPECIFIERS{ 0x00000080u }; + inline constexpr auto UNDNAME_NO_THROW_SIGNATURES{ 0x00000100u }; + inline constexpr auto UNDNAME_NO_RETURN_UDT_MODEL{ 0x00000400u }; + inline constexpr auto UNDNAME_NAME_ONLY{ 0x00001000u }; + inline constexpr auto UNDNAME_NO_ARGUMENTS{ 0x00002000u }; + + using THREAD_START_ROUTINE = std::uint32_t(void* a_param); struct CRITICAL_SECTION { public: - // members - void* DebugInfo; // 00 - std::int32_t LockCount; // 08 - std::int32_t RecursionCount; // 0C - void* OwningThread; // 10 - void* LockSemaphore; // 18 - std::uint64_t* SpinCount; // 20 + void* debugInfo; // 00 + std::int32_t lockCount; // 08 + std::int32_t recursionCount; // 0C + void* owningThread; // 10 + void* lockSemaphore; // 18 + std::uint64_t* spinCount; // 20 }; static_assert(sizeof(CRITICAL_SECTION) == 0x28); - struct _FILETIME + struct FILETIME { - public: - // members - std::uint32_t dwLowDateTime; // 00 - std::uint32_t dwHighDateTime; // 04 + std::uint32_t lo; // 00 + std::uint32_t hi; // 04 }; - static_assert(sizeof(_FILETIME) == 0x8); - using FILETIME = _FILETIME; + static_assert(sizeof(FILETIME) == 0x8); - struct _GUID + struct GUID { - std::uint32_t Data1; - std::uint16_t Data2; - std::uint16_t Data3; - std::uint8_t Data4[8]; + std::uint32_t data1; + std::uint16_t data2; + std::uint16_t data3; + std::uint8_t data4[8]; }; - static_assert(sizeof(_GUID) == 0x10); - using GUID = _GUID; + static_assert(sizeof(GUID) == 0x10); + + // known folder ids + inline constexpr GUID FOLDERID_DOCUMENTS{ 0xFDD39AD0u, 0x238Fu, 0x46AFu, 0xADu, 0xB4u, 0x6Cu, 0x85u, 0x48u, 0x03u, 0x69u, 0xC7u }; + inline constexpr GUID FOLDERID_PICTURES{ 0x33E28130u, 0x4E1Eu, 0x4676u, 0x83u, 0x5Au, 0x98u, 0x39u, 0x5Cu, 0x3Bu, 0xC3u, 0xBBu }; + inline constexpr GUID FOLDERID_PROGRAMDATA{ 0x62AB5D82u, 0xFDC1u, 0x4DC3u, 0xA9u, 0xDDu, 0x07u, 0x0Du, 0x1Du, 0x49u, 0x5Du, 0x97u }; + + using HRESULT = std::uint32_t; struct HWND__; using HWND = HWND__*; - struct HINSTANCE__; - using HINSTANCE = HINSTANCE__*; + using WNDPROC = std::int32_t(__stdcall*)(HWND, std::uint32_t, std::uint64_t, std::int64_t); struct HICON__; using HICON = HICON__*; - using WNDPROC = std::int32_t(__stdcall*)(HWND, std::uint32_t, std::uint64_t, std::int64_t); + struct HINSTANCE__; + using HINSTANCE = HINSTANCE__*; + using HMODULE = HINSTANCE; + + struct HKEY__; + using HKEY = HKEY__*; + + inline auto HKEY_CLASSES_ROOT{ reinterpret_cast(0x80000000ull) }; + inline auto HKEY_CURRENT_USER{ reinterpret_cast(0x80000001ull) }; + inline auto HKEY_LOCAL_MACHINE{ reinterpret_cast(0x80000002ull) }; - struct _WIN32_FIND_DATAA + struct IMAGE_DATA_DIRECTORY { - public: - // members - std::uint32_t dwFileAttributes; - FILETIME ftCreationTime; - FILETIME ftLastAccessTime; - FILETIME ftLastWriteTime; - std::uint32_t nFileSizeHigh; - std::uint32_t nFileSizeLow; - std::uint32_t dwReserved0; - std::uint32_t dwReserved1; - char cFileName[MAX_PATH]; - char cAlternateFileName[14]; + std::uint32_t virtualAddress; + std::uint32_t size; }; - static_assert(sizeof(_WIN32_FIND_DATAA) == 0x140); - using WIN32_FIND_DATAA = _WIN32_FIND_DATAA; + static_assert(sizeof(IMAGE_DATA_DIRECTORY) == 0x8); - struct _WIN32_FIND_DATAW + struct IMAGE_DOS_HEADER { - public: - // members - std::uint32_t dwFileAttributes; - FILETIME ftCreationTime; - FILETIME ftLastAccessTime; - FILETIME ftLastWriteTime; - std::uint32_t nFileSizeHigh; - std::uint32_t nFileSizeLow; - std::uint32_t dwReserved0; - std::uint32_t dwReserved1; - wchar_t cFileName[MAX_PATH]; - wchar_t cAlternateFileName[14]; + std::uint16_t magic; + std::uint16_t cblp; + std::uint16_t cp; + std::uint16_t crlc; + std::uint16_t cparhdr; + std::uint16_t minalloc; + std::uint16_t maxalloc; + std::uint16_t ss; + std::uint16_t sp; + std::uint16_t csum; + std::uint16_t ip; + std::uint16_t cs; + std::uint16_t lfarlc; + std::uint16_t ovno; + std::uint16_t res[4]; + std::uint16_t oemid; + std::uint16_t oeminfo; + std::uint16_t res2[10]; + std::int32_t lfanew; + }; + static_assert(sizeof(IMAGE_DOS_HEADER) == 0x40); + + struct IMAGE_FILE_HEADER + { + std::uint16_t machine; + std::uint16_t sectionCount; + std::uint32_t timeDateStamp; + std::uint32_t symbolTablePtr; + std::uint32_t symbolCount; + std::uint16_t optionalHeaderSize; + std::uint16_t characteristics; + }; + static_assert(sizeof(IMAGE_FILE_HEADER) == 0x14); + + struct IMAGE_IMPORT_BY_NAME + { + std::uint16_t hint; + char name[1]; + }; + static_assert(sizeof(IMAGE_IMPORT_BY_NAME) == 0x4); + + struct IMAGE_IMPORT_DESCRIPTOR + { + union + { + std::uint32_t characteristics; + std::uint32_t firstThunkOriginal; + }; + + std::uint32_t timeDateStamp; + std::uint32_t forwarderChain; + std::uint32_t name; + std::uint32_t firstThunk; }; - static_assert(sizeof(_WIN32_FIND_DATAW) == 0x250); - using WIN32_FIND_DATAW = _WIN32_FIND_DATAW; + static_assert(sizeof(IMAGE_IMPORT_DESCRIPTOR) == 0x14); - struct tagRECT + struct IMAGE_OPTIONAL_HEADER64 + { + std::uint16_t magic; + std::uint8_t linkerVersionMajor; + std::uint8_t linkerVersionMinor; + std::uint32_t codeSize; + std::uint32_t initializedDataSize; + std::uint32_t uninitializedDataSize; + std::uint32_t entryPointAddress; + std::uint32_t codeBase; + std::uint64_t imageBase; + std::uint32_t sectionAlignment; + std::uint32_t fileAlignment; + std::uint16_t osVersionMajor; + std::uint16_t osVersionMinor; + std::uint16_t imageVersionMajor; + std::uint16_t imageVersionMinor; + std::uint16_t subsystemVersionMajor; + std::uint16_t subsystemVersionMinor; + std::uint32_t win32Version; + std::uint32_t imageSize; + std::uint32_t headersSize; + std::uint32_t checksum; + std::uint16_t subsystem; + std::uint16_t dllCharacteristics; + std::uint64_t stackReserveSize; + std::uint64_t stackCommitSize; + std::uint64_t heapReserveSize; + std::uint64_t heapCommitSize; + std::uint32_t loaderFlags; + std::uint32_t rvaAndSizesCount; + IMAGE_DATA_DIRECTORY dataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; + }; + static_assert(sizeof(IMAGE_OPTIONAL_HEADER64) == 0xF0); + + struct IMAGE_NT_HEADERS64 + { + std::uint32_t signature; + IMAGE_FILE_HEADER fileHeader; + IMAGE_OPTIONAL_HEADER64 optionalHeader; + }; + static_assert(sizeof(IMAGE_NT_HEADERS64) == 0x108); + + struct IMAGE_SECTION_HEADER + { + std::uint8_t name[IMAGE_SIZEOF_SHORT_NAME]; + union + { + std::uint32_t physicalAddress; + std::uint32_t virtualSize; + }; + std::uint32_t virtualAddress; + std::uint32_t rawDataSize; + std::uint32_t rawDataPtr; + std::uint32_t relocationsPtr; + std::uint32_t lineNumbersPtr; + std::uint16_t relocationsCount; + std::uint16_t lineNumbersCount; + std::uint32_t characteristics; + }; + static_assert(sizeof(IMAGE_SECTION_HEADER) == 0x28); + + struct IMAGE_THUNK_DATA64 + { + union + { + std::uint64_t forwarderString; // PBYTE + std::uint64_t function; // PDWORD + std::uint64_t ordinal; + std::uint64_t address; // PIMAGE_IMPORT_BY_NAME + }; + }; + static_assert(sizeof(IMAGE_THUNK_DATA64) == 0x8); + + struct MEMORY_BASIC_INFORMATION + { + void* baseAddress; + void* allocationBase; + std::uint32_t allocationProtect; + std::uint16_t partitionID; + std::size_t regionSize; + std::uint32_t state; + std::uint32_t protect; + std::uint32_t type; + }; + static_assert(sizeof(MEMORY_BASIC_INFORMATION) == 0x30); + + struct NLSVERSIONINFO; + + struct POINT + { + std::int32_t x; + std::int32_t y; + }; + + struct PROCESS_INFORMATION + { + void* process; + void* thread; + std::uint32_t processID; + std::uint32_t threadID; + }; + static_assert(sizeof(PROCESS_INFORMATION) == 0x18); + + struct RECT { std::int32_t left; std::int32_t top; std::int32_t right; std::int32_t bottom; }; - using RECT = tagRECT; - struct tagPOINT + struct SECURITY_ATTRIBUTES { - std::int32_t x; - std::int32_t y; + std::uint32_t length; + void* securityDescriptor; + bool inheritHandle; }; - using POINT = tagPOINT; + static_assert(sizeof(SECURITY_ATTRIBUTES) == 0x18); - using HRESULT = std::int32_t; + struct STARTUPINFOA + { + std::uint32_t size; + char* reserved0; + char* desktop; + char* title; + std::uint32_t x; + std::uint32_t y; + std::uint32_t xSize; + std::uint32_t ySize; + std::uint32_t xCountChars; + std::uint32_t yCountChars; + std::uint32_t fillAttribute; + std::uint32_t flags; + std::uint16_t showWindow; + std::uint16_t reserved1; + std::uint8_t* reserved2; + void* stdIn; + void* stdOut; + void* stdErr; + }; + static_assert(sizeof(STARTUPINFOA) == 0x68); + + struct STARTUPINFOW + { + std::uint32_t size; + wchar_t* reserved0; + wchar_t* desktop; + wchar_t* title; + std::uint32_t x; + std::uint32_t y; + std::uint32_t xSize; + std::uint32_t ySize; + std::uint32_t xCountChars; + std::uint32_t yCountChars; + std::uint32_t fillAttribute; + std::uint32_t flags; + std::uint16_t showWindow; + std::uint16_t reserved1; + std::uint8_t* reserved2; + void* stdIn; + void* stdOut; + void* stdErr; + }; + static_assert(sizeof(STARTUPINFOW) == 0x68); + + struct SYSTEM_INFO + { + union + { + std::uint32_t oemID; + struct + { + std::uint16_t processorArch; + std::uint16_t reserved; + }; + }; + std::uint32_t pageSize; + void* appAddressMin; + void* appAddressMax; + std::uintptr_t processorActiveMask; + std::uint32_t processorCount; + std::uint32_t processorType; + std::uint32_t allocationGranularity; + std::uint16_t processorLevel; + std::uint16_t processorRevision; + }; + static_assert(sizeof(SYSTEM_INFO) == 0x30); + + union ULARGE_INTEGER + { + struct + { + std::uint32_t lo; + std::uint32_t hi; + }; + std::uint64_t value; + }; + + struct WIN32_FIND_DATAA + { + // members + std::uint32_t fileAttributes; + FILETIME creationTime; + FILETIME lastAccessTime; + FILETIME lastWriteTime; + std::uint32_t fileSizeHi; + std::uint32_t fileSizeLo; + std::uint32_t reserved0; + std::uint32_t reserved1; + char fileName[MAX_PATH]; + char fileNameAlt[14]; + }; + static_assert(sizeof(WIN32_FIND_DATAA) == 0x140); + + struct WIN32_FIND_DATAW + { + // members + std::uint32_t fileAttributes; + FILETIME creationTime; + FILETIME lastAccessTime; + FILETIME lastWriteTime; + std::uint32_t fileSizeHi; + std::uint32_t fileSizeLo; + std::uint32_t reserved0; + std::uint32_t reserved1; + wchar_t fileName[MAX_PATH]; + wchar_t fileNameAlt[14]; + }; + static_assert(sizeof(WIN32_FIND_DATAW) == 0x250); enum VKEnum : std::uint32_t { @@ -344,23 +770,105 @@ namespace SKSE::WinAPI VK_RESERVED_FF = 0xFF }; - [[nodiscard]] bool FindClose(void* a_findFile) noexcept; + bool CloseHandle( + void* a_handle) noexcept; + + void CoTaskMemFree( + void* a_block) noexcept; + + void* CreateFileMapping( + void* a_file, + SECURITY_ATTRIBUTES* a_attributes, + std::uint32_t a_protect, + std::uint32_t a_maxSizeHigh, + std::uint32_t a_maxSizeLow, + const char* a_name) noexcept; + + void* CreateFileMapping( + void* a_file, + SECURITY_ATTRIBUTES* a_attributes, + std::uint32_t a_protect, + std::uint32_t a_maxSizeHigh, + std::uint32_t a_maxSizeLow, + const wchar_t* a_name) noexcept; + + bool CreateProcess( + const char* a_name, + char* a_cmd, + SECURITY_ATTRIBUTES* a_procAttr, + SECURITY_ATTRIBUTES* a_threadAttr, + bool a_inherit, + std::uint32_t a_flags, + void* a_env, + const char* a_curDir, + STARTUPINFOA* a_startInfo, + PROCESS_INFORMATION* a_procInfo) noexcept; + + bool CreateProcess( + const wchar_t* a_name, + wchar_t* a_cmd, + SECURITY_ATTRIBUTES* a_procAttr, + SECURITY_ATTRIBUTES* a_threadAttr, + bool a_inherit, + std::uint32_t a_flags, + void* a_env, + const wchar_t* a_curDir, + STARTUPINFOW* a_startInfo, + PROCESS_INFORMATION* a_procInfo) noexcept; + + void* CreateRemoteThread( + void* a_process, + SECURITY_ATTRIBUTES* a_threadAttr, + std::size_t a_stackSize, + THREAD_START_ROUTINE* a_startAddr, + void* a_param, + std::uint32_t a_flags, + std::uint32_t* a_threadID) noexcept; + + void* CreateThread( + SECURITY_ATTRIBUTES* a_threadAttr, + std::size_t a_stackSize, + THREAD_START_ROUTINE* a_startAddr, + void* a_param, + std::uint32_t a_flags, + std::uint32_t* a_threadID) noexcept; + + std::uint32_t ExpandEnvironmentStrings( + const char* a_src, + char* a_dst, + std::uint32_t a_dstLen) noexcept; + + std::uint32_t ExpandEnvironmentStrings( + const wchar_t* a_src, + wchar_t* a_dst, + std::uint32_t a_dstLen) noexcept; + + [[nodiscard]] bool FindClose( + void* a_file) noexcept; [[nodiscard]] void* FindFirstFile( - const char* a_fileName, - WIN32_FIND_DATAA* a_findFileData) noexcept; + const char* a_name, + WIN32_FIND_DATAA* a_data) noexcept; [[nodiscard]] void* FindFirstFile( - const wchar_t* a_fileName, - WIN32_FIND_DATAW* a_findFileData) noexcept; + const wchar_t* a_name, + WIN32_FIND_DATAW* a_data) noexcept; [[nodiscard]] bool FindNextFile( - void* a_findFile, - WIN32_FIND_DATAA* a_findFileData) noexcept; + void* a_file, + WIN32_FIND_DATAA* a_data) noexcept; [[nodiscard]] bool FindNextFile( - void* a_findFile, - WIN32_FIND_DATAW* a_findFileData) noexcept; + void* a_file, + WIN32_FIND_DATAW* a_data) noexcept; + + bool FlushInstructionCache( + void* a_process, + const void* a_baseAddr, + std::size_t a_size) noexcept; + + bool FreeLibrary( + HMODULE a_module) noexcept; [[nodiscard]] void* GetCurrentModule() noexcept; @@ -379,152 +887,327 @@ namespace SKSE::WinAPI std::uint32_t a_size) noexcept; [[nodiscard]] bool GetFileVersionInfo( - const char* a_filename, + const char* a_name, std::uint32_t a_handle, - std::uint32_t a_len, + std::uint32_t a_dataLen, void* a_data) noexcept; [[nodiscard]] bool GetFileVersionInfo( - const wchar_t* a_filename, + const wchar_t* a_name, std::uint32_t a_handle, - std::uint32_t a_len, + std::uint32_t a_dataLen, void* a_data) noexcept; [[nodiscard]] std::uint32_t GetFileVersionInfoSize( - const char* a_filename, + const char* a_name, std::uint32_t* a_handle) noexcept; [[nodiscard]] std::uint32_t GetFileVersionInfoSize( - const wchar_t* a_filename, + const wchar_t* a_name, std::uint32_t* a_handle) noexcept; - [[nodiscard]] int GetKeyNameText( - std::int32_t a_lParam, + [[nodiscard]] std::int32_t GetKeyNameText( + std::int32_t a_param, char* a_buffer, - int a_size) noexcept; + std::int32_t a_bufferLen) noexcept; - [[nodiscard]] int GetKeyNameText( - std::int32_t a_lParam, + [[nodiscard]] std::int32_t GetKeyNameText( + std::int32_t a_param, wchar_t* a_buffer, - int a_size) noexcept; + std::int32_t a_bufferLen) noexcept; + + [[nodiscard]] std::int16_t GetKeyState( + std::int32_t a_key) noexcept; - [[nodiscard]] std::int16_t GetKeyState(int nVirtKey) noexcept; + [[nodiscard]] std::uint32_t GetLastError() noexcept; [[nodiscard]] std::size_t GetMaxPath() noexcept; [[nodiscard]] std::uint32_t GetModuleFileName( void* a_module, - char* a_filename, - std::uint32_t a_size) noexcept; + char* a_name, + std::uint32_t a_nameLen) noexcept; [[nodiscard]] std::uint32_t GetModuleFileName( void* a_module, - wchar_t* a_filename, - std::uint32_t a_size) noexcept; + wchar_t* a_name, + std::uint32_t a_nameLen) noexcept; - [[nodiscard]] void* GetModuleHandle(const char* a_moduleName) noexcept; + [[nodiscard]] HMODULE GetModuleHandle( + const char* a_name) noexcept; - [[nodiscard]] void* GetModuleHandle(const wchar_t* a_moduleName) noexcept; + [[nodiscard]] HMODULE GetModuleHandle( + const wchar_t* a_name) noexcept; [[nodiscard]] std::uint32_t GetPrivateProfileString( - const char* a_appName, - const char* a_keyName, + const char* a_app, + const char* a_key, const char* a_default, - char* a_outString, - std::uint32_t a_size, - const char* a_fileName) noexcept; + char* a_out, + std::uint32_t a_outLen, + const char* a_name) noexcept; [[nodiscard]] std::uint32_t GetPrivateProfileString( - const wchar_t* a_appName, - const wchar_t* a_keyName, + const wchar_t* a_app, + const wchar_t* a_key, const wchar_t* a_default, - wchar_t* a_outString, - std::uint32_t a_size, - const wchar_t* a_fileName) noexcept; + wchar_t* a_out, + std::uint32_t a_outLen, + const wchar_t* a_name) noexcept; [[nodiscard]] void* GetProcAddress( void* a_module, - const char* a_procName) noexcept; + const char* a_name) noexcept; + + [[nodiscard]] std::string_view GetProcPath( + HMODULE a_handle) noexcept; + + void GetSystemInfo( + SYSTEM_INFO* a_info) noexcept; + + [[nodiscard]] bool IMAGE_SNAP_BY_ORDINAL64( + std::uint64_t a_ordinal) noexcept; + + [[nodiscard]] IMAGE_SECTION_HEADER* IMAGE_FIRST_SECTION( + const IMAGE_NT_HEADERS64* a_header) noexcept; [[nodiscard]] bool IsDebuggerPresent() noexcept; + std::int32_t LCMapStringEx( + const wchar_t* a_locale, + std::uint32_t a_flags, + const wchar_t* a_src, + std::int32_t a_srcLen, + wchar_t* a_dest, + std::int32_t a_destLen, + NLSVERSIONINFO* a_info, + void* a_reserved, + std::intptr_t a_sortHandle) noexcept; + + [[nodiscard]] HMODULE LoadLibrary( + const char* a_name) noexcept; + + [[nodiscard]] HMODULE LoadLibrary( + const wchar_t* a_name) noexcept; + + [[nodiscard]] void* MapViewOfFile( + void* a_object, + std::uint32_t a_desiredAccess, + std::uint32_t a_fileOffsetHigh, + std::uint32_t a_fileOffsetLow, + std::size_t a_numBytesToMap) noexcept; + + [[nodiscard]] void* MapViewOfFileEx( + void* a_object, + std::uint32_t a_desiredAccess, + std::uint32_t a_fileOffsetHigh, + std::uint32_t a_fileOffsetLow, + std::size_t a_numBytesToMap, + void* a_baseAddress) noexcept; + std::int32_t MessageBox( - void* a_wnd, - const char* a_text, - const char* a_caption, - unsigned int a_type) noexcept; + void* a_wnd, + const char* a_text, + const char* a_caption, + std::uint32_t a_type) noexcept; std::int32_t MessageBox( void* a_wnd, const wchar_t* a_text, const wchar_t* a_caption, - unsigned int a_type) noexcept; + std::uint32_t a_type) noexcept; - [[nodiscard]] int MultiByteToWideChar( - unsigned int a_codePage, + [[nodiscard]] std::int32_t MultiByteToWideChar( + std::uint32_t a_codePage, std::uint32_t a_flags, - const char* a_multiByteStr, - int a_multiByte, - wchar_t* a_wideCharStr, - int a_wideChar); + const char* a_str, + std::int32_t a_strLen, + wchar_t* a_wstr, + std::int32_t a_wstrLen); + + [[nodiscard]] std::int32_t NormalizeString( + std::int32_t a_normForm, + const wchar_t* a_src, + std::int32_t a_srcLen, + wchar_t* a_dest, + std::int32_t a_destLen); + + [[nodiscard]] void* OpenFileMapping( + std::uint32_t a_desiredAccess, + bool a_inheritHandle, + const char* a_name) noexcept; + + [[nodiscard]] void* OpenFileMapping( + std::uint32_t a_desiredAccess, + bool a_inheritHandle, + const wchar_t* a_name) noexcept; void OutputDebugString( - const char* a_outputString) noexcept; + const char* a_outStr) noexcept; void OutputDebugString( - const wchar_t* a_outputString) noexcept; + const wchar_t* a_outStr) noexcept; - [[nodiscard]] int ShowCursor(bool bShow) noexcept; + std::int32_t RegGetValue( + HKEY a_key, + const char* a_subKey, + const char* a_value, + std::uint32_t a_flags, + std::uint32_t* a_type, + void* a_data, + std::uint32_t* a_dataLen); - [[noreturn]] void TerminateProcess( - void* a_process, - unsigned int a_exitCode) noexcept; + std::int32_t RegGetValue( + HKEY a_key, + const wchar_t* a_subKey, + const wchar_t* a_value, + std::uint32_t a_flags, + std::uint32_t* a_type, + void* a_data, + std::uint32_t* a_dataLen); - [[nodiscard]] void* TlsGetValue(std::uint32_t a_tlsIndex) noexcept; + std::uint32_t ResumeThread( + void* a_handle) noexcept; + + bool SetEnvironmentVariable( + const char* a_name, + const char* a_value) noexcept; + + bool SetEnvironmentVariable( + const wchar_t* a_name, + const wchar_t* a_value) noexcept; + + [[nodiscard]] std::int32_t SHGetKnownFolderPath( + const GUID& a_id, + std::uint32_t a_flags, + void* a_token, + wchar_t** a_path) noexcept; + + [[nodiscard]] std::int32_t ShowCursor( + bool a_show) noexcept; + + void Sleep( + std::uint32_t a_milliseconds) noexcept; + + bool TerminateProcess( + void* a_process, + std::uint32_t a_exitCode) noexcept; + + [[nodiscard]] void* TlsGetValue( + std::uint32_t a_index) noexcept; bool TlsSetValue( - std::uint32_t a_tlsIndex, - void* a_tlsValue) noexcept; + std::uint32_t a_index, + void* a_value) noexcept; - bool VirtualFree( - void* a_address, - std::size_t a_size, - std::uint32_t a_freeType) noexcept; + [[nodiscard]] std::uint32_t UnDecorateSymbolName( + const char* a_name, + char* a_out, + std::uint32_t a_outLenMax, + std::uint32_t a_flags) noexcept; + + [[nodiscard]] std::uint32_t UnDecorateSymbolName( + const wchar_t* a_name, + wchar_t* a_out, + std::uint32_t a_outLenMax, + std::uint32_t a_flags) noexcept; - [[nodiscard]] bool VerQueryValue( - const void* a_block, - const char* a_subBlock, - void** a_buffer, - unsigned int* a_len) noexcept; + bool UnmapViewOfFile( + const void* a_baseAddress) noexcept; + + bool VerQueryValue( + const void* a_block, + const char* a_subBlock, + void** a_buffer, + std::uint32_t* a_bufferLen) noexcept; - [[nodiscard]] bool VerQueryValue( + bool VerQueryValue( const void* a_block, const wchar_t* a_subBlock, void** a_buffer, - unsigned int* a_len) noexcept; + std::uint32_t* a_bufferLen) noexcept; - [[nodiscard]] bool VirtualProtect( + void* VirtualAlloc( + void* a_address, + std::size_t a_size, + std::uint32_t a_type, + std::uint32_t a_protect) noexcept; + + void* VirtualAllocEx( + void* a_process, + void* a_address, + std::size_t a_size, + std::uint32_t a_type, + std::uint32_t a_protect) noexcept; + + bool VirtualFree( + void* a_address, + std::size_t a_size, + std::uint32_t a_type) noexcept; + + bool VirtualFreeEx( + void* a_process, + void* a_address, + std::size_t a_size, + std::uint32_t a_type) noexcept; + + bool VirtualProtect( + void* a_address, + std::size_t a_size, + std::uint32_t a_newProtect, + std::uint32_t* a_oldProtect) noexcept; + + bool VirtualProtectEx( + void* a_process, void* a_address, std::size_t a_size, std::uint32_t a_newProtect, std::uint32_t* a_oldProtect) noexcept; - [[nodiscard]] int WideCharToMultiByte( - unsigned int a_codePage, + [[nodiscard]] std::size_t VirtualQuery( + const void* a_address, + MEMORY_BASIC_INFORMATION* a_buffer, + std::size_t a_bufferLen) noexcept; + + [[nodiscard]] std::size_t VirtualQueryEx( + void* a_process, + const void* a_address, + MEMORY_BASIC_INFORMATION* a_buffer, + std::size_t a_bufferLen) noexcept; + + [[nodiscard]] std::uint32_t WaitForSingleObject( + void* a_handle, + std::uint32_t a_milliseconds) noexcept; + + [[nodiscard]] std::uint32_t WaitForSingleObjectEx( + void* a_handle, + std::uint32_t a_milliseconds, + bool a_alertable) noexcept; + + [[nodiscard]] std::int32_t WideCharToMultiByte( + std::uint32_t a_codePage, std::uint32_t a_flags, - const wchar_t* a_wideCharStr, - int a_wideChar, - char* a_multiByteStr, - int a_multiByte, - const char* a_defaultChar, - int* a_usedDefaultChar); + const wchar_t* a_wstr, + std::int32_t a_wstrLen, + char* a_str, + std::int32_t a_strLen, + const char* a_default, + std::int32_t* a_defaultLen); + + bool WriteProcessMemory( + void* a_process, + void* a_address, + const void* a_buffer, + std::size_t a_bufferLen, + std::size_t* a_bufferWritten) noexcept; } +#endif // _INC_WINAPIFAMILY + namespace RE::DirectX { struct XMFLOAT4X4 { - public: // members float m[4][4]; }; @@ -532,7 +1215,6 @@ namespace RE::DirectX struct DXGI_RATIONAL { - public: // members std::uint32_t numerator; // 00 std::uint32_t denominator; // 04 diff --git a/src/SKSE/Impl/WinAPI.cpp b/src/SKSE/Impl/WinAPI.cpp index 0a7fecb9a..3a28645eb 100644 --- a/src/SKSE/Impl/WinAPI.cpp +++ b/src/SKSE/Impl/WinAPI.cpp @@ -1,7 +1,17 @@ #include "SKSE/Impl/WinAPI.h" +#define WIN32_LEAN_AND_MEAN + +// clang-format off #include +#include +#include +#include +// clang-format on +#undef CreateFileMapping +#undef CreateProcess +#undef ExpandEnvironmentStrings #undef FindFirstFile #undef FindNextFile #undef GetEnvironmentVariable @@ -11,252 +21,493 @@ #undef GetModuleFileName #undef GetModuleHandle #undef GetPrivateProfileString +#undef IMAGE_FIRST_SECTION +#undef IMAGE_SNAP_BY_ORDINAL64 +#undef LoadLibrary #undef MessageBox +#undef OpenFileMapping #undef OutputDebugString +#undef RegGetValue +#undef RegQueryValueEx +#undef SetEnvironmentVariable #undef VerQueryValue extern "C" IMAGE_DOS_HEADER __ImageBase; namespace SKSE::WinAPI { - [[nodiscard]] bool FindClose(void* a_findFile) noexcept + bool CloseHandle( + void* a_handle) noexcept { return static_cast( - ::FindClose(static_cast<::HMODULE>(a_findFile))); + ::CloseHandle( + a_handle)); + } + + void CoTaskMemFree( + void* a_block) noexcept + { + ::CoTaskMemFree( + a_block); + } + + void* CreateFileMapping( + void* a_file, + SECURITY_ATTRIBUTES* a_attributes, + std::uint32_t a_protect, + std::uint32_t a_maxSizeHigh, + std::uint32_t a_maxSizeLow, + const char* a_name) noexcept + { + return CreateFileMappingA( + a_file, + reinterpret_cast(a_attributes), + a_protect, + a_maxSizeHigh, + a_maxSizeLow, + a_name); } - [[nodiscard]] void* FindFirstFile( - const char* a_fileName, - WIN32_FIND_DATAA* a_findFileData) noexcept + void* CreateFileMapping( + void* a_file, + SECURITY_ATTRIBUTES* a_attributes, + std::uint32_t a_protect, + std::uint32_t a_maxSizeHigh, + std::uint32_t a_maxSizeLow, + const wchar_t* a_name) noexcept { - return static_cast( - ::FindFirstFileA( - static_cast<::LPCSTR>(a_fileName), - reinterpret_cast<::LPWIN32_FIND_DATAA>(a_findFileData))); + return CreateFileMappingW( + a_file, + reinterpret_cast(a_attributes), + a_protect, + a_maxSizeHigh, + a_maxSizeLow, + a_name); } - [[nodiscard]] void* FindFirstFile( - const wchar_t* a_fileName, - WIN32_FIND_DATAW* a_findFileData) noexcept + bool CreateProcess( + const char* a_name, + char* a_cmd, + SECURITY_ATTRIBUTES* a_procAttr, + SECURITY_ATTRIBUTES* a_threadAttr, + bool a_inherit, + std::uint32_t a_flags, + void* a_env, + const char* a_curDir, + STARTUPINFOA* a_startInfo, + PROCESS_INFORMATION* a_procInfo) noexcept { - return static_cast( - ::FindFirstFileW( - static_cast<::LPCWSTR>(a_fileName), - reinterpret_cast<::LPWIN32_FIND_DATAW>(a_findFileData))); + return static_cast( + CreateProcessA( + a_name, + a_cmd, + reinterpret_cast(a_procAttr), + reinterpret_cast(a_threadAttr), + a_inherit, + a_flags, + a_env, + a_curDir, + reinterpret_cast(a_startInfo), + reinterpret_cast(a_procInfo))); } - [[nodiscard]] bool FindNextFile( - void* a_findFile, - WIN32_FIND_DATAA* a_findFileData) noexcept + bool CreateProcess( + const wchar_t* a_name, + wchar_t* a_cmd, + SECURITY_ATTRIBUTES* a_procAttr, + SECURITY_ATTRIBUTES* a_threadAttr, + bool a_inherit, + std::uint32_t a_flags, + void* a_env, + const wchar_t* a_curDir, + STARTUPINFOW* a_startInfo, + PROCESS_INFORMATION* a_procInfo) noexcept { return static_cast( - ::FindNextFileA( - static_cast<::HANDLE>(a_findFile), - reinterpret_cast<::LPWIN32_FIND_DATAA>(a_findFileData))); + CreateProcessW( + a_name, + a_cmd, + reinterpret_cast(a_procAttr), + reinterpret_cast(a_threadAttr), + a_inherit, + a_flags, + a_env, + a_curDir, + reinterpret_cast(a_startInfo), + reinterpret_cast(a_procInfo))); + } + + void* CreateRemoteThread( + void* a_process, + SECURITY_ATTRIBUTES* a_threadAttr, + std::size_t a_stackSize, + THREAD_START_ROUTINE* a_startAddr, + void* a_param, + std::uint32_t a_flags, + std::uint32_t* a_threadID) noexcept + { + return ::CreateRemoteThread( + a_process, + reinterpret_cast(a_threadAttr), + a_stackSize, + reinterpret_cast(a_startAddr), + a_param, + a_flags, + reinterpret_cast(a_threadID)); + } + + void* CreateThread( + SECURITY_ATTRIBUTES* a_threadAttr, + std::size_t a_stackSize, + THREAD_START_ROUTINE* a_startAddr, + void* a_param, + std::uint32_t a_flags, + std::uint32_t* a_threadID) noexcept + { + return ::CreateThread( + reinterpret_cast(a_threadAttr), + a_stackSize, + reinterpret_cast(a_startAddr), + a_param, + a_flags, + reinterpret_cast(a_threadID)); + } + + std::uint32_t ExpandEnvironmentStrings( + const char* a_src, + char* a_dst, + std::uint32_t a_dstLen) noexcept + { + return static_cast( + ExpandEnvironmentStringsA( + a_src, + a_dst, + a_dstLen)); } - [[nodiscard]] bool FindNextFile( - void* a_findFile, - WIN32_FIND_DATAW* a_findFileData) noexcept + std::uint32_t ExpandEnvironmentStrings( + const wchar_t* a_src, + wchar_t* a_dst, + std::uint32_t a_dstLen) noexcept + { + return static_cast( + ExpandEnvironmentStringsW( + a_src, + a_dst, + a_dstLen)); + } + + bool FindClose( + void* a_file) noexcept { return static_cast( - ::FindNextFileW( - static_cast<::HANDLE>(a_findFile), - reinterpret_cast<::LPWIN32_FIND_DATAW>(a_findFileData))); + ::FindClose( + a_file)); + } + + void* FindFirstFile( + const char* a_name, + WIN32_FIND_DATAA* a_data) noexcept + { + return FindFirstFileA( + a_name, + reinterpret_cast(a_data)); + } + + void* FindFirstFile( + const wchar_t* a_name, + WIN32_FIND_DATAW* a_data) noexcept + { + return FindFirstFileW( + a_name, + reinterpret_cast(a_data)); + } + + bool FindNextFile( + void* a_file, + WIN32_FIND_DATAA* a_data) noexcept + { + return static_cast( + FindNextFileA( + a_file, + reinterpret_cast(a_data))); + } + + bool FindNextFile( + void* a_file, + WIN32_FIND_DATAW* a_data) noexcept + { + return static_cast( + FindNextFileW( + a_file, + reinterpret_cast(a_data))); + } + + bool FlushInstructionCache( + void* a_process, + const void* a_baseAddr, + std::size_t a_size) noexcept + { + return static_cast( + ::FlushInstructionCache( + a_process, + a_baseAddr, + a_size)); + } + + bool FreeLibrary( + HMODULE a_module) noexcept + { + return static_cast( + ::FreeLibrary( + reinterpret_cast<::HMODULE>(a_module))); } void* GetCurrentModule() noexcept { - return static_cast( - std::addressof(__ImageBase)); + return std::addressof(__ImageBase); } void* GetCurrentProcess() noexcept { - return static_cast( - ::GetCurrentProcess()); + return ::GetCurrentProcess(); } std::uint32_t GetCurrentThreadID() noexcept { return static_cast( - ::GetCurrentThreadId()); + GetCurrentThreadId()); } - [[nodiscard]] std::uint32_t GetEnvironmentVariable( + std::uint32_t GetEnvironmentVariable( const char* a_name, char* a_buffer, - std::uint32_t a_size) noexcept + std::uint32_t a_bufferLen) noexcept { return static_cast( - ::GetEnvironmentVariableA( - static_cast<::LPCSTR>(a_name), - static_cast<::LPSTR>(a_buffer), - static_cast<::DWORD>(a_size))); + GetEnvironmentVariableA( + a_name, + a_buffer, + a_bufferLen)); } - [[nodiscard]] std::uint32_t GetEnvironmentVariable( + std::uint32_t GetEnvironmentVariable( const wchar_t* a_name, wchar_t* a_buffer, - std::uint32_t a_size) noexcept + std::uint32_t a_bufferLen) noexcept { return static_cast( - ::GetEnvironmentVariableW( - static_cast<::LPCWSTR>(a_name), - static_cast<::LPWSTR>(a_buffer), - static_cast<::DWORD>(a_size))); + GetEnvironmentVariableW( + a_name, + a_buffer, + a_bufferLen)); } bool GetFileVersionInfo( - const char* a_filename, + const char* a_name, std::uint32_t a_handle, - std::uint32_t a_len, + std::uint32_t a_dataLen, void* a_data) noexcept { return static_cast( - ::GetFileVersionInfoA( - static_cast<::LPCSTR>(a_filename), - static_cast<::DWORD>(a_handle), - static_cast<::DWORD>(a_len), - static_cast<::LPVOID>(a_data))); + GetFileVersionInfoA( + a_name, + a_handle, + a_dataLen, + a_data)); } bool GetFileVersionInfo( - const wchar_t* a_filename, + const wchar_t* a_name, std::uint32_t a_handle, - std::uint32_t a_len, + std::uint32_t a_dataLen, void* a_data) noexcept { return static_cast( - ::GetFileVersionInfoW( - static_cast<::LPCWSTR>(a_filename), - static_cast<::DWORD>(a_handle), - static_cast<::DWORD>(a_len), - static_cast<::LPVOID>(a_data))); + GetFileVersionInfoW( + a_name, + a_handle, + a_dataLen, + a_data)); } std::uint32_t GetFileVersionInfoSize( - const char* a_filename, + const char* a_name, std::uint32_t* a_handle) noexcept { return static_cast( - ::GetFileVersionInfoSizeA( - static_cast<::LPCSTR>(a_filename), - reinterpret_cast<::LPDWORD>(a_handle))); + GetFileVersionInfoSizeA( + a_name, + reinterpret_cast(a_handle))); } std::uint32_t GetFileVersionInfoSize( - const wchar_t* a_filename, + const wchar_t* a_name, std::uint32_t* a_handle) noexcept { return static_cast( - ::GetFileVersionInfoSizeW( - static_cast<::LPCWSTR>(a_filename), - reinterpret_cast<::LPDWORD>(a_handle))); + GetFileVersionInfoSizeW( + a_name, + reinterpret_cast(a_handle))); } - int GetKeyNameText(std::int32_t a_lParam, wchar_t* a_buffer, int a_size) noexcept + std::int32_t GetKeyNameText( + std::int32_t a_param, + wchar_t* a_buffer, + std::int32_t a_bufferLen) noexcept { - return ::GetKeyNameTextW(static_cast<::LONG>(a_lParam), static_cast<::LPWSTR>(a_buffer), a_size); + return GetKeyNameTextW( + a_param, + a_buffer, + a_bufferLen); } - int GetKeyNameText(std::int32_t a_lParam, char* a_buffer, int a_size) noexcept + std::int32_t GetKeyNameText( + std::int32_t a_param, + char* a_buffer, + std::int32_t a_bufferLen) noexcept { - return ::GetKeyNameTextA(static_cast<::LONG>(a_lParam), static_cast<::LPSTR>(a_buffer), a_size); + return GetKeyNameTextA( + a_param, + a_buffer, + a_bufferLen); } - std::int16_t GetKeyState(int nVirtKey) noexcept + std::int16_t GetKeyState( + std::int32_t a_key) noexcept { - return ::GetKeyState(nVirtKey); + return ::GetKeyState(a_key); + } + + std::uint32_t GetLastError() noexcept + { + return static_cast( + ::GetLastError()); } std::size_t GetMaxPath() noexcept { - return static_cast(MAX_PATH); + return MAX_PATH; } std::uint32_t GetModuleFileName( void* a_module, - char* a_filename, - std::uint32_t a_size) noexcept + char* a_name, + std::uint32_t a_nameLen) noexcept { return static_cast( - ::GetModuleFileNameA( + GetModuleFileNameA( static_cast<::HMODULE>(a_module), - static_cast<::LPSTR>(a_filename), - static_cast<::DWORD>(a_size))); + a_name, + a_nameLen)); } std::uint32_t GetModuleFileName( void* a_module, - wchar_t* a_filename, - std::uint32_t a_size) noexcept + wchar_t* a_name, + std::uint32_t a_nameLen) noexcept { return static_cast( - ::GetModuleFileNameW( + GetModuleFileNameW( static_cast<::HMODULE>(a_module), - static_cast<::LPWSTR>(a_filename), - static_cast<::DWORD>(a_size))); + a_name, + a_nameLen)); } - void* GetModuleHandle(const char* a_moduleName) noexcept + HMODULE GetModuleHandle( + const char* a_name) noexcept { - return static_cast( - ::GetModuleHandleA( - static_cast<::LPCSTR>(a_moduleName))); + return reinterpret_cast( + GetModuleHandleA( + a_name)); } - void* GetModuleHandle(const wchar_t* a_moduleName) noexcept + HMODULE GetModuleHandle( + const wchar_t* a_name) noexcept { - return static_cast( - ::GetModuleHandleW( - static_cast<::LPCWSTR>(a_moduleName))); + return reinterpret_cast( + GetModuleHandleW(a_name)); } std::uint32_t GetPrivateProfileString( - const char* a_appName, - const char* a_keyName, + const char* a_app, + const char* a_key, const char* a_default, - char* a_outString, - std::uint32_t a_size, - const char* a_fileName) noexcept + char* a_out, + std::uint32_t a_outLen, + const char* a_name) noexcept { return static_cast( - ::GetPrivateProfileStringA( - static_cast<::LPCSTR>(a_appName), - static_cast<::LPCSTR>(a_keyName), - static_cast<::LPCSTR>(a_default), - static_cast<::LPSTR>(a_outString), - static_cast<::DWORD>(a_size), - static_cast<::LPCSTR>(a_fileName))); + GetPrivateProfileStringA( + a_app, + a_key, + a_default, + a_out, + a_outLen, + a_name)); } std::uint32_t GetPrivateProfileString( - const wchar_t* a_appName, - const wchar_t* a_keyName, + const wchar_t* a_app, + const wchar_t* a_key, const wchar_t* a_default, - wchar_t* a_outString, - std::uint32_t a_size, - const wchar_t* a_fileName) noexcept + wchar_t* a_out, + std::uint32_t a_outLen, + const wchar_t* a_name) noexcept { return static_cast( - ::GetPrivateProfileStringW( - static_cast<::LPCWSTR>(a_appName), - static_cast<::LPCWSTR>(a_keyName), - static_cast<::LPCWSTR>(a_default), - static_cast<::LPWSTR>(a_outString), - static_cast<::DWORD>(a_size), - static_cast<::LPCWSTR>(a_fileName))); + GetPrivateProfileStringW( + a_app, + a_key, + a_default, + a_out, + a_outLen, + a_name)); } void* GetProcAddress( void* a_module, - const char* a_procName) noexcept + const char* a_name) noexcept { return reinterpret_cast( ::GetProcAddress( static_cast<::HMODULE>(a_module), - static_cast<::LPCSTR>(a_procName))); + a_name)); + } + + std::string_view GetProcPath( + HMODULE a_handle) noexcept + { + static std::string fileName(MAX_PATH + 1, ' '); + auto res = GetModuleFileName(a_handle, fileName.data(), MAX_PATH + 1); + if (res == 0) { + fileName = "[ProcessHost]"; + res = 13; + } + + return { fileName.c_str(), res }; + } + + void GetSystemInfo( + SYSTEM_INFO* a_info) noexcept + { + ::GetSystemInfo( + reinterpret_cast(a_info)); + } + + bool IMAGE_SNAP_BY_ORDINAL64( + std::uint64_t a_ordinal) noexcept + { + return (a_ordinal & IMAGE_ORDINAL_FLAG64) != 0; + } + + IMAGE_SECTION_HEADER* IMAGE_FIRST_SECTION( + const IMAGE_NT_HEADERS64* a_header) noexcept + { + constexpr auto opt = __builtin_offsetof(IMAGE_NT_HEADERS64, optionalHeader); + const auto optSize = a_header->fileHeader.optionalHeaderSize; + const auto section = reinterpret_cast(a_header) + opt + optSize; + return reinterpret_cast(section); } bool IsDebuggerPresent() noexcept @@ -265,134 +516,405 @@ namespace SKSE::WinAPI ::IsDebuggerPresent()); } + std::int32_t LCMapStringEx( + const wchar_t* a_locale, + std::uint32_t a_flags, + const wchar_t* a_src, + std::int32_t a_srcLen, + wchar_t* a_dest, + std::int32_t a_destLen, + NLSVERSIONINFO* a_info, + void* a_reserved, + std::intptr_t a_sortHandle) noexcept + { + return ::LCMapStringEx( + a_locale, + a_flags, + a_src, + a_srcLen, + a_dest, + a_destLen, + reinterpret_cast(a_info), + a_reserved, + a_sortHandle); + } + + HMODULE LoadLibrary( + const char* a_name) noexcept + { + return reinterpret_cast( + LoadLibraryA( + a_name)); + } + + HMODULE LoadLibrary( + const wchar_t* a_name) noexcept + { + return reinterpret_cast( + LoadLibraryW(a_name)); + } + + void* MapViewOfFile( + void* a_object, + std::uint32_t a_desiredAccess, + std::uint32_t a_fileOffsetHigh, + std::uint32_t a_fileOffsetLow, + std::size_t a_numBytesToMap) noexcept + { + return ::MapViewOfFile( + a_object, + a_desiredAccess, + a_fileOffsetHigh, + a_fileOffsetLow, + a_numBytesToMap); + } + + void* MapViewOfFileEx( + void* a_object, + std::uint32_t a_desiredAccess, + std::uint32_t a_fileOffsetHigh, + std::uint32_t a_fileOffsetLow, + std::size_t a_numBytesToMap, + void* a_baseAddress) noexcept + { + return ::MapViewOfFileEx( + a_object, + a_desiredAccess, + a_fileOffsetHigh, + a_fileOffsetLow, + a_numBytesToMap, + a_baseAddress); + } + std::int32_t MessageBox( - void* a_wnd, - const char* a_text, - const char* a_caption, - unsigned int a_type) noexcept + void* a_wnd, + const char* a_text, + const char* a_caption, + std::uint32_t a_type) noexcept { - return static_cast( - ::MessageBoxA( - static_cast<::HWND>(a_wnd), - static_cast<::LPCSTR>(a_text), - static_cast<::LPCSTR>(a_caption), - static_cast<::UINT>(a_type))); + return MessageBoxA( + static_cast<::HWND>(a_wnd), + a_text, + a_caption, + a_type); } std::int32_t MessageBox( void* a_wnd, const wchar_t* a_text, const wchar_t* a_caption, - unsigned int a_type) noexcept + std::uint32_t a_type) noexcept { - return static_cast( - ::MessageBoxW( - static_cast<::HWND>(a_wnd), - static_cast<::LPCWSTR>(a_text), - static_cast<::LPCWSTR>(a_caption), - static_cast<::UINT>(a_type))); + return MessageBoxW( + static_cast<::HWND>(a_wnd), + a_text, + a_caption, + a_type); } - int MultiByteToWideChar( - unsigned int a_codePage, + std::int32_t MultiByteToWideChar( + std::uint32_t a_codePage, std::uint32_t a_flags, - const char* a_multiByteStr, - int a_multiByte, - wchar_t* a_wideCharStr, - int a_wideChar) + const char* a_str, + std::int32_t a_strLen, + wchar_t* a_wstr, + std::int32_t a_wstrLen) { return ::MultiByteToWideChar( - static_cast<::UINT>(a_codePage), - static_cast<::DWORD>(a_flags), - static_cast<::LPCCH>(a_multiByteStr), - a_multiByte, - static_cast<::LPWSTR>(a_wideCharStr), - a_wideChar); + a_codePage, + a_flags, + a_str, + a_strLen, + a_wstr, + a_wstrLen); + } + + std::int32_t NormalizeString( + std::int32_t a_normForm, + const wchar_t* a_src, + std::int32_t a_srcLen, + wchar_t* a_dest, + std::int32_t a_destLen) + { + return ::NormalizeString( + static_cast(a_normForm), + a_src, + a_srcLen, + a_dest, + a_destLen); + } + + void* OpenFileMapping( + std::uint32_t a_desiredAccess, + bool a_inheritHandle, + const char* a_name) noexcept + { + return OpenFileMappingA( + a_desiredAccess, + a_inheritHandle, + a_name); + } + + void* OpenFileMapping( + std::uint32_t a_desiredAccess, + bool a_inheritHandle, + const wchar_t* a_name) noexcept + { + return OpenFileMappingW( + a_desiredAccess, + a_inheritHandle, + a_name); } void OutputDebugString( - const char* a_outputString) noexcept + const char* a_out) noexcept { - ::OutputDebugStringA( - static_cast<::LPCSTR>(a_outputString)); + OutputDebugStringA( + a_out); } void OutputDebugString( - const wchar_t* a_outputString) noexcept + const wchar_t* a_out) noexcept { - ::OutputDebugStringW( - static_cast<::LPCWSTR>(a_outputString)); + OutputDebugStringW( + a_out); } - int ShowCursor(bool bShow) noexcept + std::int32_t RegGetValue( + HKEY a_key, + const char* a_subKey, + const char* a_value, + std::uint32_t a_flags, + std::uint32_t* a_type, + void* a_data, + std::uint32_t* a_dataLen) { - return ::ShowCursor(static_cast<::BOOL>(bShow)); + return RegGetValueA( + reinterpret_cast<::HKEY>(a_key), + a_subKey, + a_value, + a_flags, + reinterpret_cast(a_type), + a_data, + reinterpret_cast(a_dataLen)); } - void TerminateProcess( - void* a_process, - unsigned int a_exitCode) noexcept + std::int32_t RegGetValue( + HKEY a_key, + const wchar_t* a_subKey, + const wchar_t* a_value, + std::uint32_t a_flags, + std::uint32_t* a_type, + void* a_data, + std::uint32_t* a_dataLen) { - ::TerminateProcess( - static_cast<::HANDLE>(a_process), - static_cast<::UINT>(a_exitCode)); + return RegGetValueW( + reinterpret_cast<::HKEY>(a_key), + a_subKey, + a_value, + a_flags, + reinterpret_cast(a_type), + a_data, + reinterpret_cast(a_dataLen)); } - void* TlsGetValue(std::uint32_t a_tlsIndex) noexcept + std::uint32_t ResumeThread( + void* a_handle) noexcept { - return static_cast( - ::TlsGetValue( - static_cast<::DWORD>(a_tlsIndex))); + return static_cast( + ::ResumeThread( + a_handle)); + } + + bool SetEnvironmentVariable( + const char* a_name, + const char* a_value) noexcept + { + return static_cast( + SetEnvironmentVariableA( + a_name, + a_value)); + } + + bool SetEnvironmentVariable( + const wchar_t* a_name, + const wchar_t* a_value) noexcept + { + return static_cast( + SetEnvironmentVariableW( + a_name, + a_value)); + } + + std::int32_t SHGetKnownFolderPath( + const GUID& a_id, + std::uint32_t a_flags, + void* a_token, + wchar_t** a_path) noexcept + { + return static_cast( + ::SHGetKnownFolderPath( + reinterpret_cast(a_id), + a_flags, + a_token, + a_path)); + } + + std::int32_t ShowCursor( + bool a_show) noexcept + { + return ::ShowCursor( + a_show); + } + + void Sleep( + std::uint32_t a_milliseconds) noexcept + { + ::Sleep(a_milliseconds); + } + + bool TerminateProcess( + void* a_process, + std::uint32_t a_exitCode) noexcept + { + return static_cast( + ::TerminateProcess( + a_process, + a_exitCode)); + } + + void* TlsGetValue( + std::uint32_t a_index) noexcept + { + return ::TlsGetValue( + a_index); } bool TlsSetValue( - std::uint32_t a_tlsIndex, - void* a_tlsValue) noexcept + std::uint32_t a_index, + void* a_value) noexcept { return static_cast( ::TlsSetValue( - static_cast<::DWORD>(a_tlsIndex), - static_cast<::LPVOID>(a_tlsValue))); + a_index, + a_value)); } - bool VirtualFree( - void* a_address, - std::size_t a_size, - std::uint32_t a_freeType) noexcept + std::uint32_t UnDecorateSymbolName( + const char* a_name, + char* a_out, + std::uint32_t a_outLenMax, + std::uint32_t a_flags) noexcept + { + return static_cast( + ::UnDecorateSymbolName( + a_name, + a_out, + a_outLenMax, + a_flags)); + } + + std::uint32_t UnDecorateSymbolName( + const wchar_t* a_name, + wchar_t* a_out, + std::uint32_t a_outLenMax, + std::uint32_t a_flags) noexcept + { + return static_cast( + UnDecorateSymbolNameW( + a_name, + a_out, + a_outLenMax, + a_flags)); + } + + bool UnmapViewOfFile( + const void* a_baseAddress) noexcept { return static_cast( - ::VirtualFree( - static_cast<::LPVOID>(a_address), - static_cast<::SIZE_T>(a_size), - static_cast<::DWORD>(a_freeType))); + ::UnmapViewOfFile( + a_baseAddress)); } bool VerQueryValue( - const void* a_block, - const char* a_subBlock, - void** a_buffer, - unsigned int* a_len) noexcept + const void* a_block, + const char* a_subBlock, + void** a_buffer, + std::uint32_t* a_bufferLen) noexcept { return static_cast( - ::VerQueryValueA( - static_cast<::LPCVOID>(a_block), - static_cast<::LPCSTR>(a_subBlock), - static_cast<::LPVOID*>(a_buffer), - static_cast<::PUINT>(a_len))); + VerQueryValueA(a_block, + a_subBlock, + a_buffer, + a_bufferLen)); } bool VerQueryValue( const void* a_block, const wchar_t* a_subBlock, void** a_buffer, - unsigned int* a_len) noexcept + std::uint32_t* a_bufferLen) noexcept { return static_cast( - ::VerQueryValueW( - static_cast<::LPCVOID>(a_block), - static_cast<::LPCWSTR>(a_subBlock), - static_cast<::LPVOID*>(a_buffer), - static_cast<::PUINT>(a_len))); + VerQueryValueW( + a_block, + a_subBlock, + a_buffer, + a_bufferLen)); + } + + void* VirtualAlloc( + void* a_address, + std::size_t a_size, + std::uint32_t a_type, + std::uint32_t a_protect) noexcept + { + return ::VirtualAlloc( + a_address, + a_size, + a_type, + a_protect); + } + + void* VirtualAllocEx( + void* a_process, + void* a_address, + std::size_t a_size, + std::uint32_t a_type, + std::uint32_t a_protect) noexcept + { + return ::VirtualAllocEx( + a_process, + a_address, + a_size, + a_type, + a_protect); + } + + bool VirtualFree( + void* a_address, + std::size_t a_size, + std::uint32_t a_type) noexcept + { + return static_cast( + ::VirtualFree( + a_address, + a_size, + a_type)); + } + + bool VirtualFreeEx( + void* a_process, + void* a_address, + std::size_t a_size, + std::uint32_t a_type) noexcept + { + return static_cast( + ::VirtualFreeEx( + a_process, + a_address, + a_size, + a_type)); } bool VirtualProtect( @@ -403,31 +925,108 @@ namespace SKSE::WinAPI { return static_cast( ::VirtualProtect( - static_cast<::LPVOID>(a_address), - static_cast<::SIZE_T>(a_size), - static_cast<::DWORD>(a_newProtect), - reinterpret_cast<::PDWORD>(a_oldProtect))); + a_address, + a_size, + a_newProtect, + reinterpret_cast(a_oldProtect))); + } + + bool VirtualProtectEx( + void* a_process, + void* a_address, + std::size_t a_size, + std::uint32_t a_newProtect, + std::uint32_t* a_oldProtect) noexcept + { + return static_cast( + ::VirtualProtectEx( + a_process, + a_address, + a_size, + a_newProtect, + reinterpret_cast(a_oldProtect))); + } + + std::size_t VirtualQuery( + const void* a_address, + MEMORY_BASIC_INFORMATION* a_buffer, + std::size_t a_bufferLen) noexcept + { + return ::VirtualQuery( + a_address, + reinterpret_cast(a_buffer), + a_bufferLen); + } + + std::size_t VirtualQueryEx( + void* a_process, + const void* a_address, + MEMORY_BASIC_INFORMATION* a_buffer, + std::size_t a_bufferLen) noexcept + { + return ::VirtualQueryEx( + a_process, + a_address, + reinterpret_cast(a_buffer), + a_bufferLen); } - int WideCharToMultiByte( - unsigned int a_codePage, + std::uint32_t WaitForSingleObject( + void* a_handle, + std::uint32_t a_milliseconds) noexcept + { + return static_cast( + ::WaitForSingleObject( + a_handle, + a_milliseconds)); + } + + std::uint32_t WaitForSingleObjectEx( + void* a_handle, + std::uint32_t a_milliseconds, + bool a_alertable) noexcept + { + return static_cast( + ::WaitForSingleObjectEx( + a_handle, + a_milliseconds, + a_alertable)); + } + + std::int32_t WideCharToMultiByte( + std::uint32_t a_codePage, std::uint32_t a_flags, - const wchar_t* a_wideCharStr, - int a_wideChar, - char* a_multiByteStr, - int a_multiByte, - const char* a_defaultChar, - int* a_usedDefaultChar) + const wchar_t* a_wstr, + std::int32_t a_wstrLen, + char* a_str, + std::int32_t a_strLen, + const char* a_default, + std::int32_t* a_defaultLen) { return ::WideCharToMultiByte( - static_cast<::UINT>(a_codePage), - static_cast<::DWORD>(a_flags), - static_cast<::LPCWCH>(a_wideCharStr), - a_wideChar, - static_cast<::LPSTR>(a_multiByteStr), - a_multiByte, - static_cast<::LPCCH>(a_defaultChar), - static_cast<::LPBOOL>(a_usedDefaultChar)); + a_codePage, + a_flags, + a_wstr, + a_wstrLen, + a_str, + a_strLen, + a_default, + a_defaultLen); } + bool WriteProcessMemory( + void* a_process, + void* a_address, + const void* a_buffer, + std::size_t a_bufferLen, + std::size_t* a_bufferWritten) noexcept + { + return static_cast( + ::WriteProcessMemory( + a_process, + a_address, + a_buffer, + a_bufferLen, + a_bufferWritten)); + } } From 89bbcd3fde7b0e6b6576e6311ffd43240300ce63 Mon Sep 17 00:00:00 2001 From: Qudix <17361645+Qudix@users.noreply.github.com> Date: Sat, 27 Jan 2024 16:02:20 -0600 Subject: [PATCH 2/3] Improve WinAPI+ --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a08923da..5bace4cb0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,6 +95,7 @@ target_link_libraries( binary_io::binary_io spdlog::spdlog Version.lib + Dbghelp.lib ) target_precompile_headers( From 55bdb18c4b7d8395a935b6f883362d7cd9eed0b3 Mon Sep 17 00:00:00 2001 From: Qudix <17361645+Qudix@users.noreply.github.com> Date: Sat, 27 Jan 2024 16:05:05 -0600 Subject: [PATCH 3/3] Separate Relocation.h --- cmake/sourcelist.cmake | 9 +- include/REL/ID.h | 374 ++++++++++++++++++ include/REL/Module.h | 151 +++++++ include/REL/Offset.h | 30 ++ include/REL/Pattern.h | 189 +++++++++ include/REL/REL.h | 8 + include/REL/Relocation.h | 823 --------------------------------------- include/REL/Version.h | 105 +++++ include/SKSE/Impl/PCH.h | 2 +- src/REL/ID.cpp | 87 +++++ src/REL/Module.cpp | 25 ++ src/REL/Relocation.cpp | 155 -------- 12 files changed, 978 insertions(+), 980 deletions(-) create mode 100644 include/REL/ID.h create mode 100644 include/REL/Module.h create mode 100644 include/REL/Offset.h create mode 100644 include/REL/Pattern.h create mode 100644 include/REL/REL.h create mode 100644 include/REL/Version.h create mode 100644 src/REL/ID.cpp create mode 100644 src/REL/Module.cpp delete mode 100644 src/REL/Relocation.cpp diff --git a/cmake/sourcelist.cmake b/cmake/sourcelist.cmake index 10ddee0a5..297ce72d1 100644 --- a/cmake/sourcelist.cmake +++ b/cmake/sourcelist.cmake @@ -1613,7 +1613,13 @@ set(SOURCES include/RE/W/WerewolfFeedEffect.h include/RE/Z/ZeroFunctionArguments.h include/RE/Z/ZeroOverheadHeap.h + include/REL/ID.h + include/REL/Module.h + include/REL/Offset.h + include/REL/Pattern.h + include/REL/REL.h include/REL/Relocation.h + include/REL/Version.h include/SKSE/API.h include/SKSE/Events.h include/SKSE/IAT.h @@ -1957,7 +1963,8 @@ set(SOURCES src/RE/V/Variable.cpp src/RE/V/VirtualMachine.cpp src/RE/Z/ZeroFunctionArguments.cpp - src/REL/Relocation.cpp + src/REL/ID.cpp + src/REL/Module.cpp src/SKSE/API.cpp src/SKSE/IAT.cpp src/SKSE/Impl/PCH.cpp diff --git a/include/REL/ID.h b/include/REL/ID.h new file mode 100644 index 000000000..09d5f25ee --- /dev/null +++ b/include/REL/ID.h @@ -0,0 +1,374 @@ +#pragma once + +#include "REL/Module.h" + +namespace REL +{ + namespace detail + { + class memory_map + { + public: + memory_map() noexcept = default; + memory_map(const memory_map&) = delete; + + memory_map(memory_map&& a_rhs) noexcept : + _mapping(a_rhs._mapping), + _view(a_rhs._view) + { + a_rhs._mapping = nullptr; + a_rhs._view = nullptr; + } + + ~memory_map() { close(); } + + memory_map& operator=(const memory_map&) = delete; + + memory_map& operator=(memory_map&& a_rhs) noexcept + { + if (this != std::addressof(a_rhs)) { + _mapping = a_rhs._mapping; + a_rhs._mapping = nullptr; + + _view = a_rhs._view; + a_rhs._view = nullptr; + } + return *this; + } + + [[nodiscard]] void* data() noexcept { return _view; } + + bool open(stl::zwstring a_name, std::size_t a_size); + bool create(stl::zwstring a_name, std::size_t a_size); + void close(); + + private: + void* _mapping{ nullptr }; + void* _view{ nullptr }; + }; + } + + class IDDatabase + { + private: + struct mapping_t + { + std::uint64_t id; + std::uint64_t offset; + }; + + public: + class Offset2ID + { + public: + using value_type = mapping_t; + using container_type = std::vector; + using size_type = typename container_type::size_type; + using const_iterator = typename container_type::const_iterator; + using const_reverse_iterator = typename container_type::const_reverse_iterator; + + template + explicit Offset2ID(ExecutionPolicy&& a_policy) // + requires(std::is_execution_policy_v>) + { + const std::span id2offset = IDDatabase::get()._id2offset; + _offset2id.reserve(id2offset.size()); + _offset2id.insert(_offset2id.begin(), id2offset.begin(), id2offset.end()); + std::sort(a_policy, _offset2id.begin(), _offset2id.end(), [](auto&& a_lhs, auto&& a_rhs) { + return a_lhs.offset < a_rhs.offset; + }); + } + + Offset2ID() : + Offset2ID(std::execution::sequenced_policy{}) + {} + + [[nodiscard]] std::uint64_t operator()(std::size_t a_offset) const + { + const mapping_t elem{ 0, a_offset }; + const auto it = std::lower_bound( + _offset2id.begin(), + _offset2id.end(), + elem, + [](auto&& a_lhs, auto&& a_rhs) { + return a_lhs.offset < a_rhs.offset; + }); + if (it == _offset2id.end()) { + stl::report_and_fail( + std::format( + "Failed to find the offset within the database: 0x{:08X}"sv, + a_offset)); + } + + return it->id; + } + + [[nodiscard]] const_iterator begin() const noexcept { return _offset2id.begin(); } + [[nodiscard]] const_iterator cbegin() const noexcept { return _offset2id.cbegin(); } + + [[nodiscard]] const_iterator end() const noexcept { return _offset2id.end(); } + [[nodiscard]] const_iterator cend() const noexcept { return _offset2id.cend(); } + + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { return _offset2id.rbegin(); } + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { return _offset2id.crbegin(); } + + [[nodiscard]] const_reverse_iterator rend() const noexcept { return _offset2id.rend(); } + [[nodiscard]] const_reverse_iterator crend() const noexcept { return _offset2id.crend(); } + + [[nodiscard]] size_type size() const noexcept { return _offset2id.size(); } + + private: + container_type _offset2id; + }; + + [[nodiscard]] static IDDatabase& get() + { + static IDDatabase singleton; + return singleton; + } + + [[nodiscard]] inline std::size_t id2offset(std::uint64_t a_id) const + { + mapping_t elem{ a_id, 0 }; + const auto it = std::lower_bound( + _id2offset.begin(), + _id2offset.end(), + elem, + [](auto&& a_lhs, auto&& a_rhs) { + return a_lhs.id < a_rhs.id; + }); + if (it == _id2offset.end()) { + stl::report_and_fail( + std::format( + "Failed to find the id within the address library: {}\n" + "This means this script extender plugin is incompatible with the address " + "library for this version of the game, and thus does not support it."sv, + a_id)); + } + + return static_cast(it->offset); + } + + private: + friend Offset2ID; + + class header_t + { + public: + void read(binary_io::file_istream& a_in) + { + const auto [format] = a_in.read(); +#ifdef SKYRIM_SUPPORT_AE + if (format != 2) { +#else + if (format != 1) { +#endif + stl::report_and_fail( + std::format( + "Unsupported address library format: {}\n" + "This means this script extender plugin is incompatible with the address " + "library available for this version of the game, and thus does not " + "support it."sv, + format)); + } + + const auto [major, minor, patch, revision] = + a_in.read(); + _version[0] = static_cast(major); + _version[1] = static_cast(minor); + _version[2] = static_cast(patch); + _version[3] = static_cast(revision); + + const auto [nameLen] = a_in.read(); + a_in.seek_relative(nameLen); + + a_in.read(_pointerSize, _addressCount); + } + + [[nodiscard]] std::size_t address_count() const noexcept { return static_cast(_addressCount); } + [[nodiscard]] std::uint64_t pointer_size() const noexcept { return static_cast(_pointerSize); } + [[nodiscard]] Version version() const noexcept { return _version; } + + private: + Version _version; + std::int32_t _pointerSize{ 0 }; + std::int32_t _addressCount{ 0 }; + }; + + IDDatabase() { load(); } + + IDDatabase(const IDDatabase&) = delete; + IDDatabase(IDDatabase&&) = delete; + + ~IDDatabase() = default; + + IDDatabase& operator=(const IDDatabase&) = delete; + IDDatabase& operator=(IDDatabase&&) = delete; + + void load() + { + const auto version = Module::get().version(); + const auto filename = + stl::utf8_to_utf16( + std::format( +#ifdef SKYRIM_SUPPORT_AE + "Data/SKSE/Plugins/versionlib-{}.bin"sv, +#else + "Data/SKSE/Plugins/version-{}.bin"sv, +#endif + version.string())) + .value_or(L""s); + load_file(filename, version); + } + + void load_file(stl::zwstring a_filename, Version a_version) + { + try { + binary_io::file_istream in(a_filename); + header_t header; + header.read(in); + if (header.version() != a_version) { + stl::report_and_fail("version mismatch"sv); + } + + auto mapname = L"CommonLibSSEOffsets-v2-"s; + mapname += a_version.wstring(); + const auto byteSize = static_cast(header.address_count()) * sizeof(mapping_t); + if (_mmap.open(mapname, byteSize)) { + _id2offset = { static_cast(_mmap.data()), header.address_count() }; + } else if (_mmap.create(mapname, byteSize)) { + _id2offset = { static_cast(_mmap.data()), header.address_count() }; + unpack_file(in, header); + std::sort( + _id2offset.begin(), + _id2offset.end(), + [](auto&& a_lhs, auto&& a_rhs) { + return a_lhs.id < a_rhs.id; + }); + } else { + stl::report_and_fail("failed to create shared mapping"sv); + } + } catch (const std::system_error&) { + stl::report_and_fail( + std::format( + "Failed to locate an appropriate address library with the path: {}\n" + "This means you are missing the address library for this specific version of " + "the game. Please continue to the mod page for address library to download " + "an appropriate version. If one is not available, then it is likely that " + "address library has not yet added support for this version of the game."sv, + stl::utf16_to_utf8(a_filename).value_or(""s))); + } + } + + void unpack_file(binary_io::file_istream& a_in, header_t a_header) + { + std::uint8_t type = 0; + std::uint64_t id = 0; + std::uint64_t offset = 0; + std::uint64_t prevID = 0; + std::uint64_t prevOffset = 0; + for (auto& mapping : _id2offset) { + a_in.read(type); + const auto lo = static_cast(type & 0xF); + const auto hi = static_cast(type >> 4); + + switch (lo) { + case 0: + a_in.read(id); + break; + case 1: + id = prevID + 1; + break; + case 2: + id = prevID + std::get<0>(a_in.read()); + break; + case 3: + id = prevID - std::get<0>(a_in.read()); + break; + case 4: + id = prevID + std::get<0>(a_in.read()); + break; + case 5: + id = prevID - std::get<0>(a_in.read()); + break; + case 6: + std::tie(id) = a_in.read(); + break; + case 7: + std::tie(id) = a_in.read(); + break; + default: + stl::report_and_fail("unhandled type"sv); + } + + const std::uint64_t tmp = (hi & 8) != 0 ? (prevOffset / a_header.pointer_size()) : prevOffset; + + switch (hi & 7) { + case 0: + a_in.read(offset); + break; + case 1: + offset = tmp + 1; + break; + case 2: + offset = tmp + std::get<0>(a_in.read()); + break; + case 3: + offset = tmp - std::get<0>(a_in.read()); + break; + case 4: + offset = tmp + std::get<0>(a_in.read()); + break; + case 5: + offset = tmp - std::get<0>(a_in.read()); + break; + case 6: + std::tie(offset) = a_in.read(); + break; + case 7: + std::tie(offset) = a_in.read(); + break; + default: + stl::report_and_fail("unhandled type"sv); + } + + if ((hi & 8) != 0) { + offset *= a_header.pointer_size(); + } + + mapping = { id, offset }; + + prevOffset = offset; + prevID = id; + } + } + + detail::memory_map _mmap; + std::span _id2offset; + }; + + class ID + { + public: + constexpr ID() noexcept = default; + + explicit constexpr ID(std::uint64_t a_id) noexcept : + _id(a_id) + {} + + constexpr ID& operator=(std::uint64_t a_id) noexcept + { + _id = a_id; + return *this; + } + + [[nodiscard]] std::uintptr_t address() const { return base() + offset(); } + [[nodiscard]] constexpr std::uint64_t id() const noexcept { return _id; } + [[nodiscard]] std::size_t offset() const { return IDDatabase::get().id2offset(_id); } + + private: + [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); } + + std::uint64_t _id{ 0 }; + }; +} diff --git a/include/REL/Module.h b/include/REL/Module.h new file mode 100644 index 000000000..07f465e40 --- /dev/null +++ b/include/REL/Module.h @@ -0,0 +1,151 @@ +#pragma once + +#include "REL/Version.h" + +namespace REL +{ + class Segment + { + public: + enum Name : std::size_t + { + textx, + idata, + rdata, + data, + pdata, + tls, + textw, + gfids, + total + }; + + Segment() noexcept = default; + + Segment(std::uintptr_t a_proxyBase, std::uintptr_t a_address, std::uintptr_t a_size) noexcept : + _proxyBase(a_proxyBase), + _address(a_address), + _size(a_size) + {} + + [[nodiscard]] std::uintptr_t address() const noexcept { return _address; } + [[nodiscard]] std::size_t offset() const noexcept { return address() - _proxyBase; } + [[nodiscard]] std::size_t size() const noexcept { return _size; } + + [[nodiscard]] void* pointer() const noexcept { return reinterpret_cast(address()); } + + template + [[nodiscard]] T* pointer() const noexcept + { + return static_cast(pointer()); + } + + private: + std::uintptr_t _proxyBase{ 0 }; + std::uintptr_t _address{ 0 }; + std::size_t _size{ 0 }; + }; + + class Module + { + public: + [[nodiscard]] static Module& get() + { + static Module singleton; + return singleton; + } + + [[nodiscard]] std::uintptr_t base() const noexcept { return _base; } + [[nodiscard]] stl::zwstring filename() const noexcept { return _filename; } + [[nodiscard]] Version version() const noexcept { return _version; } + + [[nodiscard]] Segment segment(Segment::Name a_segment) const noexcept { return _segments[a_segment]; } + + [[nodiscard]] void* pointer() const noexcept { return reinterpret_cast(base()); } + + template + [[nodiscard]] T* pointer() const noexcept + { + return static_cast(pointer()); + } + + private: + Module() + { + const auto getFilename = [&]() { + return WinAPI::GetEnvironmentVariable( + ENVIRONMENT.data(), + _filename.data(), + static_cast(_filename.size())); + }; + + _filename.resize(getFilename()); + if (const auto result = getFilename(); + result != _filename.size() - 1 || + result == 0) { + _filename = L"SkyrimSE.exe"sv; + } + + load(); + } + + Module(const Module&) = delete; + Module(Module&&) = delete; + + ~Module() noexcept = default; + + Module& operator=(const Module&) = delete; + Module& operator=(Module&&) = delete; + + void load() + { + auto handle = WinAPI::GetModuleHandle(_filename.c_str()); + if (handle == nullptr) { + stl::report_and_fail( + std::format( + "Failed to obtain module handle for: \"{0}\".\n" + "You have likely renamed the executable to something unexpected. " + "Renaming the executable back to \"{0}\" may resolve the issue."sv, + stl::utf16_to_utf8(_filename).value_or(""s))); + } + _base = reinterpret_cast(handle); + + load_version(); + load_segments(); + } + + void load_segments(); + + void load_version() + { + const auto version = get_file_version(_filename); + if (version) { + _version = *version; + } else { + stl::report_and_fail( + std::format( + "Failed to obtain file version info for: {}\n" + "Please contact the author of this script extender plugin for further assistance."sv, + stl::utf16_to_utf8(_filename).value_or(""s))); + } + } + + static constexpr std::array SEGMENTS{ + std::make_pair(".text"sv, WinAPI::IMAGE_SCN_MEM_EXECUTE), + std::make_pair(".idata"sv, 0u), + std::make_pair(".rdata"sv, 0u), + std::make_pair(".data"sv, 0u), + std::make_pair(".pdata"sv, 0u), + std::make_pair(".tls"sv, 0u), + std::make_pair(".text"sv, WinAPI::IMAGE_SCN_MEM_WRITE), + std::make_pair(".gfids"sv, 0u) + }; + + static constexpr auto ENVIRONMENT = L"SKSE_RUNTIME"sv; + + std::wstring _filename; + std::array _segments; + Version _version; + std::uintptr_t _base{ 0 }; + }; +} diff --git a/include/REL/Offset.h b/include/REL/Offset.h new file mode 100644 index 000000000..6ed58b82d --- /dev/null +++ b/include/REL/Offset.h @@ -0,0 +1,30 @@ +#pragma once + +#include "REL/Module.h" + +namespace REL +{ + class Offset + { + public: + constexpr Offset() noexcept = default; + + explicit constexpr Offset(std::size_t a_offset) noexcept : + _offset(a_offset) + {} + + constexpr Offset& operator=(std::size_t a_offset) noexcept + { + _offset = a_offset; + return *this; + } + + [[nodiscard]] std::uintptr_t address() const { return base() + offset(); } + [[nodiscard]] constexpr std::size_t offset() const noexcept { return _offset; } + + private: + [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); } + + std::size_t _offset{ 0 }; + }; +} diff --git a/include/REL/Pattern.h b/include/REL/Pattern.h new file mode 100644 index 000000000..a23631940 --- /dev/null +++ b/include/REL/Pattern.h @@ -0,0 +1,189 @@ +#pragma once + +#include "REL/Module.h" + +namespace REL +{ + namespace detail + { + namespace characters + { + [[nodiscard]] constexpr bool hexadecimal(char a_ch) noexcept + { + return ('0' <= a_ch && a_ch <= '9') || + ('A' <= a_ch && a_ch <= 'F') || + ('a' <= a_ch && a_ch <= 'f'); + } + + [[nodiscard]] constexpr bool space(char a_ch) noexcept + { + return a_ch == ' '; + } + + [[nodiscard]] constexpr bool wildcard(char a_ch) noexcept + { + return a_ch == '?'; + } + } + + namespace rules + { + namespace detail + { + [[nodiscard]] consteval std::byte hexacharacters_to_hexadecimal(char a_hi, char a_lo) noexcept + { + constexpr auto lut = []() noexcept { + std::array::max() + 1> a = {}; + + const auto iterate = [&](std::uint8_t a_iFirst, unsigned char a_cFirst, unsigned char a_cLast) noexcept { + for (; a_cFirst <= a_cLast; ++a_cFirst, ++a_iFirst) { + a[a_cFirst] = a_iFirst; + } + }; + + iterate(0, '0', '9'); + iterate(0xA, 'A', 'F'); + iterate(0xa, 'a', 'f'); + + return a; + }(); + + return static_cast( + lut[static_cast(a_hi)] * 0x10u + + lut[static_cast(a_lo)]); + } + } + + template + class Hexadecimal + { + public: + [[nodiscard]] static constexpr bool match(std::byte a_byte) noexcept + { + constexpr auto expected = detail::hexacharacters_to_hexadecimal(HI, LO); + return a_byte == expected; + } + }; + + static_assert(Hexadecimal<'5', '7'>::match(std::byte{ 0x57 })); + static_assert(Hexadecimal<'6', '5'>::match(std::byte{ 0x65 })); + static_assert(Hexadecimal<'B', 'D'>::match(std::byte{ 0xBD })); + static_assert(Hexadecimal<'1', 'C'>::match(std::byte{ 0x1C })); + static_assert(Hexadecimal<'F', '2'>::match(std::byte{ 0xF2 })); + static_assert(Hexadecimal<'9', 'f'>::match(std::byte{ 0x9f })); + + static_assert(!Hexadecimal<'D', '4'>::match(std::byte{ 0xF8 })); + static_assert(!Hexadecimal<'6', '7'>::match(std::byte{ 0xAA })); + static_assert(!Hexadecimal<'7', '8'>::match(std::byte{ 0xE3 })); + static_assert(!Hexadecimal<'6', 'E'>::match(std::byte{ 0x61 })); + + class Wildcard + { + public: + [[nodiscard]] static constexpr bool match(std::byte) noexcept + { + return true; + } + }; + + static_assert(Wildcard::match(std::byte{ 0xB9 })); + static_assert(Wildcard::match(std::byte{ 0x96 })); + static_assert(Wildcard::match(std::byte{ 0x35 })); + static_assert(Wildcard::match(std::byte{ 0xE4 })); + + template + void rule_for() noexcept; + + template + Hexadecimal rule_for() noexcept + requires(characters::hexadecimal(C1) && characters::hexadecimal(C2)); + + template + Wildcard rule_for() noexcept + requires(characters::wildcard(C1) && characters::wildcard(C2)); + } + + template + class PatternMatcher + { + public: + static_assert(sizeof...(Rules) >= 1, "must provide at least 1 rule for the pattern matcher"); + + [[nodiscard]] constexpr bool match(std::span a_bytes) const noexcept + { + std::size_t i = 0; + return (Rules::match(a_bytes[i++]) && ...); + } + + [[nodiscard]] bool match(std::uintptr_t a_address) const noexcept + { + return this->match(*reinterpret_cast(a_address)); + } + + void match_or_fail(std::uintptr_t a_address, std::source_location a_loc = std::source_location::current()) const noexcept + { + if (!this->match(a_address)) { + const auto version = Module::get().version(); + stl::report_and_fail( + std::format( + "A pattern has failed to match.\n" + "This means the plugin is incompatible with the current version of the game ({}.{}.{}). " + "Head to the mod page of this plugin to see if an update is available."sv, + version[0], + version[1], + version[2]), + a_loc); + } + } + }; + + void consteval_error(const char* a_error); + + template + [[nodiscard]] constexpr auto do_make_pattern() noexcept + { + if constexpr (S.length() == 0) { + return PatternMatcher(); + } else if constexpr (S.length() == 1) { + constexpr char c = S[0]; + if constexpr (characters::hexadecimal(c) || characters::wildcard(c)) { + consteval_error("the given pattern has an unpaired rule (rules are required to be written in pairs of 2)"); + } else { + consteval_error("the given pattern has trailing characters at the end (which is not allowed)"); + } + } else { + using rule_t = decltype(rules::rule_for()); + if constexpr (std::same_as) { + consteval_error("the given pattern failed to match any known rules"); + } else { + if constexpr (S.length() <= 3) { + return do_make_pattern(), Rules..., rule_t>(); + } else if constexpr (characters::space(S[2])) { + return do_make_pattern(), Rules..., rule_t>(); + } else { + consteval_error("a space character is required to split byte patterns"); + } + } + } + } + + template + [[nodiscard]] consteval auto make_byte_array(Bytes... a_bytes) noexcept + -> std::array + { + static_assert((std::integral && ...), "all bytes must be an integral type"); + return { static_cast(a_bytes)... }; + } + } + + template + [[nodiscard]] constexpr auto make_pattern() noexcept + { + return detail::do_make_pattern(); + } + + static_assert(make_pattern<"40 10 F2 ??">().match( + detail::make_byte_array(0x40, 0x10, 0xF2, 0x41))); + static_assert(make_pattern<"B8 D0 ?? ?? D4 6E">().match( + detail::make_byte_array(0xB8, 0xD0, 0x35, 0x2A, 0xD4, 0x6E))); +} diff --git a/include/REL/REL.h b/include/REL/REL.h new file mode 100644 index 000000000..0fab8ad1f --- /dev/null +++ b/include/REL/REL.h @@ -0,0 +1,8 @@ +#pragma once + +#include "REL/ID.h" +#include "REL/Module.h" +#include "REL/Offset.h" +#include "REL/Pattern.h" +#include "REL/Relocation.h" +#include "REL/Version.h" diff --git a/include/REL/Relocation.h b/include/REL/Relocation.h index b71423f5e..b8edfe732 100644 --- a/include/REL/Relocation.h +++ b/include/REL/Relocation.h @@ -60,47 +60,6 @@ namespace REL { namespace detail { - class memory_map - { - public: - memory_map() noexcept = default; - memory_map(const memory_map&) = delete; - - memory_map(memory_map&& a_rhs) noexcept : - _mapping(a_rhs._mapping), - _view(a_rhs._view) - { - a_rhs._mapping = nullptr; - a_rhs._view = nullptr; - } - - ~memory_map() { close(); } - - memory_map& operator=(const memory_map&) = delete; - - memory_map& operator=(memory_map&& a_rhs) noexcept - { - if (this != std::addressof(a_rhs)) { - _mapping = a_rhs._mapping; - a_rhs._mapping = nullptr; - - _view = a_rhs._view; - a_rhs._view = nullptr; - } - return *this; - } - - [[nodiscard]] void* data() noexcept { return _view; } - - bool open(stl::zwstring a_name, std::size_t a_size); - bool create(stl::zwstring a_name, std::size_t a_size); - void close(); - - private: - void* _mapping{ nullptr }; - void* _view{ nullptr }; - }; - template struct member_function_pod_type; @@ -282,605 +241,6 @@ namespace REL assert(success != 0); } - class Version - { - public: - using value_type = std::uint16_t; - using reference = value_type&; - using const_reference = const value_type&; - - constexpr Version() noexcept = default; - - explicit constexpr Version(std::array a_version) noexcept : - _impl(a_version) - {} - - constexpr Version(value_type a_v1, value_type a_v2 = 0, value_type a_v3 = 0, value_type a_v4 = 0) noexcept : - _impl{ a_v1, a_v2, a_v3, a_v4 } - {} - - [[nodiscard]] constexpr reference operator[](std::size_t a_idx) noexcept { return _impl[a_idx]; } - [[nodiscard]] constexpr const_reference operator[](std::size_t a_idx) const noexcept { return _impl[a_idx]; } - - [[nodiscard]] constexpr decltype(auto) begin() const noexcept { return _impl.begin(); } - [[nodiscard]] constexpr decltype(auto) cbegin() const noexcept { return _impl.cbegin(); } - [[nodiscard]] constexpr decltype(auto) end() const noexcept { return _impl.end(); } - [[nodiscard]] constexpr decltype(auto) cend() const noexcept { return _impl.cend(); } - - [[nodiscard]] std::strong_ordering constexpr compare(const Version& a_rhs) const noexcept - { - for (std::size_t i = 0; i < _impl.size(); ++i) { - if ((*this)[i] != a_rhs[i]) { - return (*this)[i] < a_rhs[i] ? std::strong_ordering::less : std::strong_ordering::greater; - } - } - return std::strong_ordering::equal; - } - - [[nodiscard]] constexpr std::uint32_t pack() const noexcept - { - return static_cast( - (_impl[0] & 0x0FF) << 24u | - (_impl[1] & 0x0FF) << 16u | - (_impl[2] & 0xFFF) << 4u | - (_impl[3] & 0x00F) << 0u); - } - - [[nodiscard]] std::string string() const - { - std::string result; - for (auto&& ver : _impl) { - result += std::to_string(ver); - result += '-'; - } - result.pop_back(); - return result; - } - - [[nodiscard]] std::wstring wstring() const - { - std::wstring result; - for (auto&& ver : _impl) { - result += std::to_wstring(ver); - result += L'-'; - } - result.pop_back(); - return result; - } - - private: - std::array _impl{ 0, 0, 0, 0 }; - }; - - [[nodiscard]] constexpr bool operator==(const Version& a_lhs, const Version& a_rhs) noexcept { return a_lhs.compare(a_rhs) == 0; } - [[nodiscard]] constexpr std::strong_ordering operator<=>(const Version& a_lhs, const Version& a_rhs) noexcept { return a_lhs.compare(a_rhs); } - - [[nodiscard]] inline std::optional get_file_version(stl::zwstring a_filename) - { - std::uint32_t dummy; - std::vector buf(WinAPI::GetFileVersionInfoSize(a_filename.data(), std::addressof(dummy))); - if (buf.empty()) { - return std::nullopt; - } - - if (!WinAPI::GetFileVersionInfo(a_filename.data(), 0, static_cast(buf.size()), buf.data())) { - return std::nullopt; - } - - void* verBuf{ nullptr }; - std::uint32_t verLen{ 0 }; - if (!WinAPI::VerQueryValue(buf.data(), L"\\StringFileInfo\\040904B0\\ProductVersion", std::addressof(verBuf), std::addressof(verLen))) { - return std::nullopt; - } - - Version version; - std::wistringstream ss( - std::wstring(static_cast(verBuf), verLen)); - std::wstring token; - for (std::size_t i = 0; i < 4 && std::getline(ss, token, L'.'); ++i) { - version[i] = static_cast(std::stoi(token)); - } - - return version; - } - - class Segment - { - public: - enum Name : std::size_t - { - textx, - idata, - rdata, - data, - pdata, - tls, - textw, - gfids, - total - }; - - Segment() noexcept = default; - - Segment(std::uintptr_t a_proxyBase, std::uintptr_t a_address, std::uintptr_t a_size) noexcept : - _proxyBase(a_proxyBase), - _address(a_address), - _size(a_size) - {} - - [[nodiscard]] std::uintptr_t address() const noexcept { return _address; } - [[nodiscard]] std::size_t offset() const noexcept { return address() - _proxyBase; } - [[nodiscard]] std::size_t size() const noexcept { return _size; } - - [[nodiscard]] void* pointer() const noexcept { return reinterpret_cast(address()); } - - template - [[nodiscard]] T* pointer() const noexcept - { - return static_cast(pointer()); - } - - private: - std::uintptr_t _proxyBase{ 0 }; - std::uintptr_t _address{ 0 }; - std::size_t _size{ 0 }; - }; - - class Module - { - public: - [[nodiscard]] static Module& get() - { - static Module singleton; - return singleton; - } - - [[nodiscard]] std::uintptr_t base() const noexcept { return _base; } - [[nodiscard]] stl::zwstring filename() const noexcept { return _filename; } - [[nodiscard]] Version version() const noexcept { return _version; } - - [[nodiscard]] Segment segment(Segment::Name a_segment) const noexcept { return _segments[a_segment]; } - - [[nodiscard]] void* pointer() const noexcept { return reinterpret_cast(base()); } - - template - [[nodiscard]] T* pointer() const noexcept - { - return static_cast(pointer()); - } - - private: - Module() - { - const auto getFilename = [&]() { - return WinAPI::GetEnvironmentVariable( - ENVIRONMENT.data(), - _filename.data(), - static_cast(_filename.size())); - }; - - _filename.resize(getFilename()); - if (const auto result = getFilename(); - result != _filename.size() - 1 || - result == 0) { - _filename = L"SkyrimSE.exe"sv; - } - - load(); - } - - Module(const Module&) = delete; - Module(Module&&) = delete; - - ~Module() noexcept = default; - - Module& operator=(const Module&) = delete; - Module& operator=(Module&&) = delete; - - void load() - { - auto handle = WinAPI::GetModuleHandle(_filename.c_str()); - if (handle == nullptr) { - stl::report_and_fail( - std::format( - "Failed to obtain module handle for: \"{0}\".\n" - "You have likely renamed the executable to something unexpected. " - "Renaming the executable back to \"{0}\" may resolve the issue."sv, - stl::utf16_to_utf8(_filename).value_or(""s))); - } - _base = reinterpret_cast(handle); - - load_version(); - load_segments(); - } - - void load_segments(); - - void load_version() - { - const auto version = get_file_version(_filename); - if (version) { - _version = *version; - } else { - stl::report_and_fail( - std::format( - "Failed to obtain file version info for: {}\n" - "Please contact the author of this script extender plugin for further assistance."sv, - stl::utf16_to_utf8(_filename).value_or(""s))); - } - } - - static constexpr std::array SEGMENTS{ - std::make_pair(".text"sv, WinAPI::IMAGE_SCN_MEM_EXECUTE), - std::make_pair(".idata"sv, static_cast(0)), - std::make_pair(".rdata"sv, static_cast(0)), - std::make_pair(".data"sv, static_cast(0)), - std::make_pair(".pdata"sv, static_cast(0)), - std::make_pair(".tls"sv, static_cast(0)), - std::make_pair(".text"sv, WinAPI::IMAGE_SCN_MEM_WRITE), - std::make_pair(".gfids"sv, static_cast(0)) - }; - - static constexpr auto ENVIRONMENT = L"SKSE_RUNTIME"sv; - - std::wstring _filename; - std::array _segments; - Version _version; - std::uintptr_t _base{ 0 }; - }; - - class IDDatabase - { - private: - struct mapping_t - { - std::uint64_t id; - std::uint64_t offset; - }; - - public: - class Offset2ID - { - public: - using value_type = mapping_t; - using container_type = std::vector; - using size_type = typename container_type::size_type; - using const_iterator = typename container_type::const_iterator; - using const_reverse_iterator = typename container_type::const_reverse_iterator; - - template - explicit Offset2ID(ExecutionPolicy&& a_policy) // - requires(std::is_execution_policy_v>) - { - const std::span id2offset = IDDatabase::get()._id2offset; - _offset2id.reserve(id2offset.size()); - _offset2id.insert(_offset2id.begin(), id2offset.begin(), id2offset.end()); - std::sort( - a_policy, - _offset2id.begin(), - _offset2id.end(), - [](auto&& a_lhs, auto&& a_rhs) { - return a_lhs.offset < a_rhs.offset; - }); - } - - Offset2ID() : - Offset2ID(std::execution::sequenced_policy{}) - {} - - [[nodiscard]] std::uint64_t operator()(std::size_t a_offset) const - { - const mapping_t elem{ 0, a_offset }; - const auto it = std::lower_bound( - _offset2id.begin(), - _offset2id.end(), - elem, - [](auto&& a_lhs, auto&& a_rhs) { - return a_lhs.offset < a_rhs.offset; - }); - if (it == _offset2id.end()) { - stl::report_and_fail( - std::format( - "Failed to find the offset within the database: 0x{:08X}"sv, - a_offset)); - } - - return it->id; - } - - [[nodiscard]] const_iterator begin() const noexcept { return _offset2id.begin(); } - [[nodiscard]] const_iterator cbegin() const noexcept { return _offset2id.cbegin(); } - - [[nodiscard]] const_iterator end() const noexcept { return _offset2id.end(); } - [[nodiscard]] const_iterator cend() const noexcept { return _offset2id.cend(); } - - [[nodiscard]] const_reverse_iterator rbegin() const noexcept { return _offset2id.rbegin(); } - [[nodiscard]] const_reverse_iterator crbegin() const noexcept { return _offset2id.crbegin(); } - - [[nodiscard]] const_reverse_iterator rend() const noexcept { return _offset2id.rend(); } - [[nodiscard]] const_reverse_iterator crend() const noexcept { return _offset2id.crend(); } - - [[nodiscard]] size_type size() const noexcept { return _offset2id.size(); } - - private: - container_type _offset2id; - }; - - [[nodiscard]] static IDDatabase& get() - { - static IDDatabase singleton; - return singleton; - } - - [[nodiscard]] inline std::size_t id2offset(std::uint64_t a_id) const - { - mapping_t elem{ a_id, 0 }; - const auto it = std::lower_bound( - _id2offset.begin(), - _id2offset.end(), - elem, - [](auto&& a_lhs, auto&& a_rhs) { - return a_lhs.id < a_rhs.id; - }); - if (it == _id2offset.end()) { - stl::report_and_fail( - std::format( - "Failed to find the id within the address library: {}\n" - "This means this script extender plugin is incompatible with the address " - "library for this version of the game, and thus does not support it."sv, - a_id)); - } - - return static_cast(it->offset); - } - - private: - friend Offset2ID; - - class header_t - { - public: - void read(binary_io::file_istream& a_in) - { - const auto [format] = a_in.read(); -#ifdef SKYRIM_SUPPORT_AE - if (format != 2) { -#else - if (format != 1) { -#endif - stl::report_and_fail( - std::format( - "Unsupported address library format: {}\n" - "This means this script extender plugin is incompatible with the address " - "library available for this version of the game, and thus does not " - "support it."sv, - format)); - } - - const auto [major, minor, patch, revision] = - a_in.read(); - _version[0] = static_cast(major); - _version[1] = static_cast(minor); - _version[2] = static_cast(patch); - _version[3] = static_cast(revision); - - const auto [nameLen] = a_in.read(); - a_in.seek_relative(nameLen); - - a_in.read(_pointerSize, _addressCount); - } - - [[nodiscard]] std::size_t address_count() const noexcept { return static_cast(_addressCount); } - [[nodiscard]] std::uint64_t pointer_size() const noexcept { return static_cast(_pointerSize); } - [[nodiscard]] Version version() const noexcept { return _version; } - - private: - Version _version; - std::int32_t _pointerSize{ 0 }; - std::int32_t _addressCount{ 0 }; - }; - - IDDatabase() { load(); } - - IDDatabase(const IDDatabase&) = delete; - IDDatabase(IDDatabase&&) = delete; - - ~IDDatabase() = default; - - IDDatabase& operator=(const IDDatabase&) = delete; - IDDatabase& operator=(IDDatabase&&) = delete; - - void load() - { - const auto version = Module::get().version(); - const auto filename = - stl::utf8_to_utf16( - std::format( -#ifdef SKYRIM_SUPPORT_AE - "Data/SKSE/Plugins/versionlib-{}.bin"sv, -#else - "Data/SKSE/Plugins/version-{}.bin"sv, -#endif - version.string())) - .value_or(L""s); - load_file(filename, version); - } - - void load_file(stl::zwstring a_filename, Version a_version) - { - try { - binary_io::file_istream in(a_filename); - header_t header; - header.read(in); - if (header.version() != a_version) { - stl::report_and_fail("version mismatch"sv); - } - - auto mapname = L"CommonLibSSEOffsets-v2-"s; - mapname += a_version.wstring(); - const auto byteSize = static_cast(header.address_count()) * sizeof(mapping_t); - if (_mmap.open(mapname, byteSize)) { - _id2offset = { static_cast(_mmap.data()), header.address_count() }; - } else if (_mmap.create(mapname, byteSize)) { - _id2offset = { static_cast(_mmap.data()), header.address_count() }; - unpack_file(in, header); - std::sort( - _id2offset.begin(), - _id2offset.end(), - [](auto&& a_lhs, auto&& a_rhs) { - return a_lhs.id < a_rhs.id; - }); - } else { - stl::report_and_fail("failed to create shared mapping"sv); - } - } catch (const std::system_error&) { - stl::report_and_fail( - std::format( - "Failed to locate an appropriate address library with the path: {}\n" - "This means you are missing the address library for this specific version of " - "the game. Please continue to the mod page for address library to download " - "an appropriate version. If one is not available, then it is likely that " - "address library has not yet added support for this version of the game."sv, - stl::utf16_to_utf8(a_filename).value_or(""s))); - } - } - - void unpack_file(binary_io::file_istream& a_in, header_t a_header) - { - std::uint8_t type = 0; - std::uint64_t id = 0; - std::uint64_t offset = 0; - std::uint64_t prevID = 0; - std::uint64_t prevOffset = 0; - for (auto& mapping : _id2offset) { - a_in.read(type); - const auto lo = static_cast(type & 0xF); - const auto hi = static_cast(type >> 4); - - switch (lo) { - case 0: - a_in.read(id); - break; - case 1: - id = prevID + 1; - break; - case 2: - id = prevID + std::get<0>(a_in.read()); - break; - case 3: - id = prevID - std::get<0>(a_in.read()); - break; - case 4: - id = prevID + std::get<0>(a_in.read()); - break; - case 5: - id = prevID - std::get<0>(a_in.read()); - break; - case 6: - std::tie(id) = a_in.read(); - break; - case 7: - std::tie(id) = a_in.read(); - break; - default: - stl::report_and_fail("unhandled type"sv); - } - - const std::uint64_t tmp = (hi & 8) != 0 ? (prevOffset / a_header.pointer_size()) : prevOffset; - - switch (hi & 7) { - case 0: - a_in.read(offset); - break; - case 1: - offset = tmp + 1; - break; - case 2: - offset = tmp + std::get<0>(a_in.read()); - break; - case 3: - offset = tmp - std::get<0>(a_in.read()); - break; - case 4: - offset = tmp + std::get<0>(a_in.read()); - break; - case 5: - offset = tmp - std::get<0>(a_in.read()); - break; - case 6: - std::tie(offset) = a_in.read(); - break; - case 7: - std::tie(offset) = a_in.read(); - break; - default: - stl::report_and_fail("unhandled type"sv); - } - - if ((hi & 8) != 0) { - offset *= a_header.pointer_size(); - } - - mapping = { id, offset }; - - prevOffset = offset; - prevID = id; - } - } - - detail::memory_map _mmap; - std::span _id2offset; - }; - - class Offset - { - public: - constexpr Offset() noexcept = default; - - explicit constexpr Offset(std::size_t a_offset) noexcept : - _offset(a_offset) - {} - - constexpr Offset& operator=(std::size_t a_offset) noexcept - { - _offset = a_offset; - return *this; - } - - [[nodiscard]] std::uintptr_t address() const { return base() + offset(); } - [[nodiscard]] constexpr std::size_t offset() const noexcept { return _offset; } - - private: - [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); } - - std::size_t _offset{ 0 }; - }; - - class ID - { - public: - constexpr ID() noexcept = default; - - explicit constexpr ID(std::uint64_t a_id) noexcept : - _id(a_id) - {} - - constexpr ID& operator=(std::uint64_t a_id) noexcept - { - _id = a_id; - return *this; - } - - [[nodiscard]] std::uintptr_t address() const { return base() + offset(); } - [[nodiscard]] constexpr std::uint64_t id() const noexcept { return _id; } - [[nodiscard]] std::size_t offset() const { return IDDatabase::get().id2offset(_id); } - - private: - [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); } - - std::uint64_t _id{ 0 }; - }; - template class Relocation { @@ -983,189 +343,6 @@ namespace REL std::uintptr_t _impl{ 0 }; }; - - namespace detail - { - namespace characters - { - [[nodiscard]] constexpr bool hexadecimal(char a_ch) noexcept - { - return ('0' <= a_ch && a_ch <= '9') || - ('A' <= a_ch && a_ch <= 'F') || - ('a' <= a_ch && a_ch <= 'f'); - } - - [[nodiscard]] constexpr bool space(char a_ch) noexcept - { - return a_ch == ' '; - } - - [[nodiscard]] constexpr bool wildcard(char a_ch) noexcept - { - return a_ch == '?'; - } - } - - namespace rules - { - namespace detail - { - [[nodiscard]] consteval std::byte hexacharacters_to_hexadecimal(char a_hi, char a_lo) noexcept - { - constexpr auto lut = []() noexcept { - std::array::max() + 1> a = {}; - - const auto iterate = [&](std::uint8_t a_iFirst, unsigned char a_cFirst, unsigned char a_cLast) noexcept { - for (; a_cFirst <= a_cLast; ++a_cFirst, ++a_iFirst) { - a[a_cFirst] = a_iFirst; - } - }; - - iterate(0, '0', '9'); - iterate(0xA, 'A', 'F'); - iterate(0xa, 'a', 'f'); - - return a; - }(); - - return static_cast( - lut[static_cast(a_hi)] * 0x10u + - lut[static_cast(a_lo)]); - } - } - - template - class Hexadecimal - { - public: - [[nodiscard]] static constexpr bool match(std::byte a_byte) noexcept - { - constexpr auto expected = detail::hexacharacters_to_hexadecimal(HI, LO); - return a_byte == expected; - } - }; - - static_assert(Hexadecimal<'5', '7'>::match(std::byte{ 0x57 })); - static_assert(Hexadecimal<'6', '5'>::match(std::byte{ 0x65 })); - static_assert(Hexadecimal<'B', 'D'>::match(std::byte{ 0xBD })); - static_assert(Hexadecimal<'1', 'C'>::match(std::byte{ 0x1C })); - static_assert(Hexadecimal<'F', '2'>::match(std::byte{ 0xF2 })); - static_assert(Hexadecimal<'9', 'f'>::match(std::byte{ 0x9f })); - - static_assert(!Hexadecimal<'D', '4'>::match(std::byte{ 0xF8 })); - static_assert(!Hexadecimal<'6', '7'>::match(std::byte{ 0xAA })); - static_assert(!Hexadecimal<'7', '8'>::match(std::byte{ 0xE3 })); - static_assert(!Hexadecimal<'6', 'E'>::match(std::byte{ 0x61 })); - - class Wildcard - { - public: - [[nodiscard]] static constexpr bool match(std::byte) noexcept - { - return true; - } - }; - - static_assert(Wildcard::match(std::byte{ 0xB9 })); - static_assert(Wildcard::match(std::byte{ 0x96 })); - static_assert(Wildcard::match(std::byte{ 0x35 })); - static_assert(Wildcard::match(std::byte{ 0xE4 })); - - template - void rule_for() noexcept; - - template - Hexadecimal rule_for() noexcept - requires(characters::hexadecimal(C1) && characters::hexadecimal(C2)); - - template - Wildcard rule_for() noexcept - requires(characters::wildcard(C1) && characters::wildcard(C2)); - } - - template - class PatternMatcher - { - public: - static_assert(sizeof...(Rules) >= 1, "must provide at least 1 rule for the pattern matcher"); - - [[nodiscard]] constexpr bool match(std::span a_bytes) const noexcept - { - std::size_t i = 0; - return (Rules::match(a_bytes[i++]) && ...); - } - - [[nodiscard]] bool match(std::uintptr_t a_address) const noexcept - { - return this->match(*reinterpret_cast(a_address)); - } - - void match_or_fail(std::uintptr_t a_address, std::source_location a_loc = std::source_location::current()) const noexcept - { - if (!this->match(a_address)) { - const auto version = Module::get().version(); - stl::report_and_fail( - std::format( - "A pattern has failed to match.\n" - "This means the plugin is incompatible with the current version of the game ({}.{}.{}). " - "Head to the mod page of this plugin to see if an update is available."sv, - version[0], - version[1], - version[2]), - a_loc); - } - } - }; - - void consteval_error(const char* a_error); - - template - [[nodiscard]] constexpr auto do_make_pattern() noexcept - { - if constexpr (S.length() == 0) { - return PatternMatcher(); - } else if constexpr (S.length() == 1) { - constexpr char c = S[0]; - if constexpr (characters::hexadecimal(c) || characters::wildcard(c)) { - consteval_error("the given pattern has an unpaired rule (rules are required to be written in pairs of 2)"); - } else { - consteval_error("the given pattern has trailing characters at the end (which is not allowed)"); - } - } else { - using rule_t = decltype(rules::rule_for()); - if constexpr (std::same_as) { - consteval_error("the given pattern failed to match any known rules"); - } else { - if constexpr (S.length() <= 3) { - return do_make_pattern(), Rules..., rule_t>(); - } else if constexpr (characters::space(S[2])) { - return do_make_pattern(), Rules..., rule_t>(); - } else { - consteval_error("a space character is required to split byte patterns"); - } - } - } - } - - template - [[nodiscard]] consteval auto make_byte_array(Bytes... a_bytes) noexcept - -> std::array - { - static_assert((std::integral && ...), "all bytes must be an integral type"); - return { static_cast(a_bytes)... }; - } - } - - template - [[nodiscard]] constexpr auto make_pattern() noexcept - { - return detail::do_make_pattern(); - } - - static_assert(make_pattern<"40 10 F2 ??">().match( - detail::make_byte_array(0x40, 0x10, 0xF2, 0x41))); - static_assert(make_pattern<"B8 D0 ?? ?? D4 6E">().match( - detail::make_byte_array(0xB8, 0xD0, 0x35, 0x2A, 0xD4, 0x6E))); } #undef REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE diff --git a/include/REL/Version.h b/include/REL/Version.h new file mode 100644 index 000000000..04b3ffca8 --- /dev/null +++ b/include/REL/Version.h @@ -0,0 +1,105 @@ +#pragma once + +namespace REL +{ + class Version + { + public: + using value_type = std::uint16_t; + using reference = value_type&; + using const_reference = const value_type&; + + constexpr Version() noexcept = default; + + explicit constexpr Version(std::array a_version) noexcept : + _impl(a_version) + {} + + constexpr Version(value_type a_v1, value_type a_v2 = 0, value_type a_v3 = 0, value_type a_v4 = 0) noexcept : + _impl{ a_v1, a_v2, a_v3, a_v4 } + {} + + [[nodiscard]] constexpr reference operator[](std::size_t a_idx) noexcept { return _impl[a_idx]; } + [[nodiscard]] constexpr const_reference operator[](std::size_t a_idx) const noexcept { return _impl[a_idx]; } + + [[nodiscard]] constexpr decltype(auto) begin() const noexcept { return _impl.begin(); } + [[nodiscard]] constexpr decltype(auto) cbegin() const noexcept { return _impl.cbegin(); } + [[nodiscard]] constexpr decltype(auto) end() const noexcept { return _impl.end(); } + [[nodiscard]] constexpr decltype(auto) cend() const noexcept { return _impl.cend(); } + + [[nodiscard]] std::strong_ordering constexpr compare(const Version& a_rhs) const noexcept + { + for (std::size_t i = 0; i < _impl.size(); ++i) { + if ((*this)[i] != a_rhs[i]) { + return (*this)[i] < a_rhs[i] ? std::strong_ordering::less : std::strong_ordering::greater; + } + } + return std::strong_ordering::equal; + } + + [[nodiscard]] constexpr std::uint32_t pack() const noexcept + { + return static_cast( + (_impl[0] & 0x0FF) << 24u | + (_impl[1] & 0x0FF) << 16u | + (_impl[2] & 0xFFF) << 4u | + (_impl[3] & 0x00F) << 0u); + } + + [[nodiscard]] std::string string() const + { + std::string result; + for (auto&& ver : _impl) { + result += std::to_string(ver); + result += '-'; + } + result.pop_back(); + return result; + } + + [[nodiscard]] std::wstring wstring() const + { + std::wstring result; + for (auto&& ver : _impl) { + result += std::to_wstring(ver); + result += L'-'; + } + result.pop_back(); + return result; + } + + private: + std::array _impl{ 0, 0, 0, 0 }; + }; + + [[nodiscard]] constexpr bool operator==(const Version& a_lhs, const Version& a_rhs) noexcept { return a_lhs.compare(a_rhs) == 0; } + [[nodiscard]] constexpr std::strong_ordering operator<=>(const Version& a_lhs, const Version& a_rhs) noexcept { return a_lhs.compare(a_rhs); } + + [[nodiscard]] inline std::optional get_file_version(stl::zwstring a_filename) + { + std::uint32_t dummy; + std::vector buf(WinAPI::GetFileVersionInfoSize(a_filename.data(), std::addressof(dummy))); + if (buf.empty()) { + return std::nullopt; + } + + if (!WinAPI::GetFileVersionInfo(a_filename.data(), 0, static_cast(buf.size()), buf.data())) { + return std::nullopt; + } + + void* verBuf{ nullptr }; + std::uint32_t verLen{ 0 }; + if (!WinAPI::VerQueryValue(buf.data(), L"\\StringFileInfo\\040904B0\\ProductVersion", std::addressof(verBuf), std::addressof(verLen))) { + return std::nullopt; + } + + Version version; + std::wistringstream ss(std::wstring(static_cast(verBuf), verLen)); + std::wstring token; + for (std::size_t i = 0; i < 4 && std::getline(ss, token, L'.'); ++i) { + version[i] = static_cast(std::stoi(token)); + } + + return version; + } +} diff --git a/include/SKSE/Impl/PCH.h b/include/SKSE/Impl/PCH.h index 0a4b8051d..e76a73124 100644 --- a/include/SKSE/Impl/PCH.h +++ b/include/SKSE/Impl/PCH.h @@ -713,7 +713,7 @@ namespace REL # define RELOCATION_ID(SE, AE) REL::ID(SE) #endif -#include "REL/Relocation.h" +#include "REL/REL.h" #include "RE/Offsets.h" #include "RE/Offsets_NiRTTI.h" diff --git a/src/REL/ID.cpp b/src/REL/ID.cpp new file mode 100644 index 000000000..86926e12a --- /dev/null +++ b/src/REL/ID.cpp @@ -0,0 +1,87 @@ +#include "REL/ID.h" + +namespace REL +{ + namespace detail + { + bool memory_map::open(stl::zwstring a_name, std::size_t a_size) + { + close(); + + WinAPI::ULARGE_INTEGER bytes; + bytes.value = a_size; + + _mapping = WinAPI::OpenFileMapping( + WinAPI::FILE_MAP_READ | WinAPI::FILE_MAP_WRITE, + false, + a_name.data()); + if (!_mapping) { + close(); + return false; + } + + _view = WinAPI::MapViewOfFile( + _mapping, + WinAPI::FILE_MAP_READ | WinAPI::FILE_MAP_WRITE, + 0, + 0, + bytes.value); + if (!_view) { + close(); + return false; + } + + return true; + } + + bool memory_map::create(stl::zwstring a_name, std::size_t a_size) + { + close(); + + WinAPI::ULARGE_INTEGER bytes; + bytes.value = a_size; + + _mapping = WinAPI::OpenFileMapping( + WinAPI::FILE_MAP_READ | WinAPI::FILE_MAP_WRITE, + false, + a_name.data()); + if (!_mapping) { + _mapping = WinAPI::CreateFileMapping( + WinAPI::INVALID_HANDLE_VALUE, + nullptr, + WinAPI::PAGE_READWRITE, + bytes.hi, + bytes.lo, + a_name.data()); + if (!_mapping) { + return false; + } + } + + _view = WinAPI::MapViewOfFile( + _mapping, + WinAPI::FILE_MAP_READ | WinAPI::FILE_MAP_WRITE, + 0, + 0, + bytes.value); + if (!_view) { + return false; + } + + return true; + } + + void memory_map::close() + { + if (_view) { + WinAPI::UnmapViewOfFile(static_cast(_view)); + _view = nullptr; + } + + if (_mapping) { + WinAPI::CloseHandle(_mapping); + _mapping = nullptr; + } + } + } +} diff --git a/src/REL/Module.cpp b/src/REL/Module.cpp new file mode 100644 index 000000000..1e70aea3e --- /dev/null +++ b/src/REL/Module.cpp @@ -0,0 +1,25 @@ +#include "REL/Module.h" + +namespace REL +{ + void Module::load_segments() + { + const auto dosHeader = reinterpret_cast(_base); + const auto ntHeader = stl::adjust_pointer(dosHeader, dosHeader->lfanew); + const auto sections = WinAPI::IMAGE_FIRST_SECTION(ntHeader); + const auto size = std::min(ntHeader->fileHeader.sectionCount, _segments.size()); + for (std::size_t i = 0; i < size; ++i) { + const auto& section = sections[i]; + const auto it = std::find_if(SEGMENTS.begin(), SEGMENTS.end(), [&](auto&& a_elem) { + constexpr auto size = std::extent_v; + const auto len = std::min(a_elem.first.size(), size); + return std::memcmp(a_elem.first.data(), section.name, len) == 0 && + (section.characteristics & a_elem.second) == a_elem.second; + }); + if (it != SEGMENTS.end()) { + const auto idx = static_cast(std::distance(SEGMENTS.begin(), it)); + _segments[idx] = Segment{ _base, _base + section.virtualAddress, section.virtualSize }; + } + } + } +} diff --git a/src/REL/Relocation.cpp b/src/REL/Relocation.cpp deleted file mode 100644 index 940ed6a2c..000000000 --- a/src/REL/Relocation.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include "REL/Relocation.h" - -#define WIN32_LEAN_AND_MEAN - -#define NOGDICAPMASKS -#define NOVIRTUALKEYCODES -//#define NOWINMESSAGES -#define NOWINSTYLES -#define NOSYSMETRICS -#define NOMENUS -#define NOICONS -#define NOKEYSTATES -#define NOSYSCOMMANDS -#define NORASTEROPS -#define NOSHOWWINDOW -#define OEMRESOURCE -#define NOATOM -#define NOCLIPBOARD -#define NOCOLOR -//#define NOCTLMGR -#define NODRAWTEXT -#define NOGDI -#define NOKERNEL -//#define NOUSER -#define NONLS -//#define NOMB -#define NOMEMMGR -#define NOMETAFILE -#define NOMINMAX -//#define NOMSG -#define NOOPENFILE -#define NOSCROLL -#define NOSERVICE -#define NOSOUND -#define NOTEXTMETRIC -#define NOWH -#define NOWINOFFSETS -#define NOCOMM -#define NOKANJI -#define NOHELP -#define NOPROFILER -#define NODEFERWINDOWPOS -#define NOMCX - -#include - -namespace REL -{ - namespace detail - { - bool memory_map::open(stl::zwstring a_name, std::size_t a_size) - { - close(); - - ::ULARGE_INTEGER bytes; - bytes.QuadPart = a_size; - - _mapping = ::OpenFileMappingW( - FILE_MAP_READ | FILE_MAP_WRITE, - false, - a_name.data()); - if (!_mapping) { - close(); - return false; - } - - _view = ::MapViewOfFile( - _mapping, - FILE_MAP_READ | FILE_MAP_WRITE, - 0, - 0, - bytes.QuadPart); - if (!_view) { - close(); - return false; - } - - return true; - } - - bool memory_map::create(stl::zwstring a_name, std::size_t a_size) - { - close(); - - ::ULARGE_INTEGER bytes; - bytes.QuadPart = a_size; - - _mapping = ::OpenFileMappingW( - FILE_MAP_READ | FILE_MAP_WRITE, - false, - a_name.data()); - if (!_mapping) { - _mapping = ::CreateFileMappingW( - INVALID_HANDLE_VALUE, - nullptr, - PAGE_READWRITE, - bytes.HighPart, - bytes.LowPart, - a_name.data()); - if (!_mapping) { - return false; - } - } - - _view = ::MapViewOfFile( - _mapping, - FILE_MAP_READ | FILE_MAP_WRITE, - 0, - 0, - bytes.QuadPart); - if (!_view) { - return false; - } - - return true; - } - - void memory_map::close() - { - if (_view) { - ::UnmapViewOfFile(static_cast(_view)); - _view = nullptr; - } - - if (_mapping) { - ::CloseHandle(_mapping); - _mapping = nullptr; - } - } - } - - void Module::load_segments() - { - auto dosHeader = reinterpret_cast(_base); - auto ntHeader = stl::adjust_pointer(dosHeader, dosHeader->e_lfanew); - const auto* sections = IMAGE_FIRST_SECTION(ntHeader); - const auto size = std::min(ntHeader->FileHeader.NumberOfSections, _segments.size()); - for (std::size_t i = 0; i < size; ++i) { - const auto& section = sections[i]; - const auto it = std::find_if( - SEGMENTS.begin(), - SEGMENTS.end(), - [&](auto&& a_elem) { - constexpr auto size = std::extent_v; - const auto len = std::min(a_elem.first.size(), size); - return std::memcmp(a_elem.first.data(), section.Name, len) == 0 && - (section.Characteristics & a_elem.second) == a_elem.second; - }); - if (it != SEGMENTS.end()) { - const auto idx = static_cast(std::distance(SEGMENTS.begin(), it)); - _segments[idx] = Segment{ _base, _base + section.VirtualAddress, section.Misc.VirtualSize }; - } - } - } -}