mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 10:17:22 +01:00
Fix nullptr problems (#878)
This commit is contained in:
parent
74966fc4ef
commit
71f3680388
11 changed files with 161 additions and 84 deletions
|
|
@ -16,7 +16,7 @@ DllExport DWORD WINAPI Initialize(LPVOID lpParam, HANDLE hMainThreadContinue) {
|
||||||
logging::print<logging::I>("No log file path given; not logging to file.");
|
logging::print<logging::I>("No log file path given; not logging to file.");
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
logging::start_file_logging(logFilePath);
|
logging::start_file_logging(logFilePath, !bootconfig::is_show_console());
|
||||||
logging::print<logging::I>(L"Logging to file: {}", logFilePath);
|
logging::print<logging::I>(L"Logging to file: {}", logFilePath);
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
logging::print<logging::E>(L"Couldn't open log file: {}", logFilePath);
|
logging::print<logging::E>(L"Couldn't open log file: {}", logFilePath);
|
||||||
|
|
@ -84,6 +84,13 @@ DllExport DWORD WINAPI Initialize(LPVOID lpParam, HANDLE hMainThreadContinue) {
|
||||||
if (bootconfig::wait_messagebox() & bootconfig::WaitMessageboxFlags::BeforeDalamudEntrypoint)
|
if (bootconfig::wait_messagebox() & bootconfig::WaitMessageboxFlags::BeforeDalamudEntrypoint)
|
||||||
MessageBoxW(nullptr, L"Press OK to continue", L"Dalamud Boot", MB_OK);
|
MessageBoxW(nullptr, L"Press OK to continue", L"Dalamud Boot", MB_OK);
|
||||||
|
|
||||||
|
if (hMainThreadContinue) {
|
||||||
|
// Let the game initialize.
|
||||||
|
SetEvent(hMainThreadContinue);
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::wait_for_game_window();
|
||||||
|
|
||||||
logging::print<logging::I>("Initializing Dalamud...");
|
logging::print<logging::I>("Initializing Dalamud...");
|
||||||
entrypoint_fn(lpParam, hMainThreadContinue);
|
entrypoint_fn(lpParam, hMainThreadContinue);
|
||||||
logging::print<logging::I>("Done!");
|
logging::print<logging::I>("Done!");
|
||||||
|
|
|
||||||
|
|
@ -37,14 +37,15 @@ static const auto LdrUnregisterDllNotification = utils::loaded_module(GetModuleH
|
||||||
|
|
||||||
hooks::getprocaddress_singleton_import_hook::getprocaddress_singleton_import_hook()
|
hooks::getprocaddress_singleton_import_hook::getprocaddress_singleton_import_hook()
|
||||||
: m_pfnGetProcAddress(GetProcAddress)
|
: m_pfnGetProcAddress(GetProcAddress)
|
||||||
, m_thunk([this](HMODULE hModule, LPCSTR lpProcName) { return get_proc_address_handler(hModule, lpProcName); }) {
|
, m_thunk("kernel32!GetProcAddress(Singleton Import Hook)",
|
||||||
|
[this](HMODULE hModule, LPCSTR lpProcName) { return get_proc_address_handler(hModule, lpProcName); }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
hooks::getprocaddress_singleton_import_hook::~getprocaddress_singleton_import_hook() {
|
hooks::getprocaddress_singleton_import_hook::~getprocaddress_singleton_import_hook() {
|
||||||
LdrUnregisterDllNotification(m_ldrDllNotificationCookie);
|
LdrUnregisterDllNotification(m_ldrDllNotificationCookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<void> hooks::getprocaddress_singleton_import_hook::set_handler(std::wstring dllName, std::string functionName, void* pfnDetour) {
|
std::shared_ptr<void> hooks::getprocaddress_singleton_import_hook::set_handler(std::wstring dllName, std::string functionName, void* pfnDetour, std::function<void(void*)> fnOnOriginalAddressAvailable) {
|
||||||
const auto hModule = GetModuleHandleW(dllName.c_str());
|
const auto hModule = GetModuleHandleW(dllName.c_str());
|
||||||
if (!hModule)
|
if (!hModule)
|
||||||
throw std::out_of_range("Specified DLL is not found.");
|
throw std::out_of_range("Specified DLL is not found.");
|
||||||
|
|
@ -53,6 +54,8 @@ std::shared_ptr<void> hooks::getprocaddress_singleton_import_hook::set_handler(s
|
||||||
if (!pfn)
|
if (!pfn)
|
||||||
throw std::out_of_range("Could not find the specified function.");
|
throw std::out_of_range("Could not find the specified function.");
|
||||||
|
|
||||||
|
fnOnOriginalAddressAvailable(pfn);
|
||||||
|
|
||||||
auto& target = m_targetFns[hModule][functionName];
|
auto& target = m_targetFns[hModule][functionName];
|
||||||
if (target)
|
if (target)
|
||||||
throw std::runtime_error("Specified function has already been hooked.");
|
throw std::runtime_error("Specified function has already been hooked.");
|
||||||
|
|
@ -95,7 +98,7 @@ std::shared_ptr<hooks::getprocaddress_singleton_import_hook> hooks::getprocaddre
|
||||||
}
|
}
|
||||||
|
|
||||||
void hooks::getprocaddress_singleton_import_hook::initialize() {
|
void hooks::getprocaddress_singleton_import_hook::initialize() {
|
||||||
m_getProcAddressHandler = set_handler(L"kernel32.dll", "GetProcAddress", m_thunk.get_thunk());
|
m_getProcAddressHandler = set_handler(L"kernel32.dll", "GetProcAddress", m_thunk.get_thunk(), [this](void*) {});
|
||||||
|
|
||||||
LdrRegisterDllNotification(0, [](ULONG notiReason, const LDR_DLL_NOTIFICATION_DATA* pData, void* context) {
|
LdrRegisterDllNotification(0, [](ULONG notiReason, const LDR_DLL_NOTIFICATION_DATA* pData, void* context) {
|
||||||
if (notiReason == LDR_DLL_NOTIFICATION_REASON_LOADED) {
|
if (notiReason == LDR_DLL_NOTIFICATION_REASON_LOADED) {
|
||||||
|
|
@ -138,7 +141,7 @@ void hooks::getprocaddress_singleton_import_hook::hook_module(const utils::loade
|
||||||
if (!hook) {
|
if (!hook) {
|
||||||
logging::print<logging::I>("{} Hooking {}!{} imported by {}", LogTag, dllName, targetFn, unicode::convert<std::string>(mod.path().wstring()));
|
logging::print<logging::I>("{} Hooking {}!{} imported by {}", LogTag, dllName, targetFn, unicode::convert<std::string>(mod.path().wstring()));
|
||||||
|
|
||||||
hook.emplace(static_cast<void**>(pGetProcAddressImport), pfnThunk);
|
hook.emplace(std::format("getprocaddress_singleton_import_hook::hook_module({}!{})", dllName, targetFn), static_cast<void**>(pGetProcAddressImport), pfnThunk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,23 @@
|
||||||
|
|
||||||
namespace hooks {
|
namespace hooks {
|
||||||
class base_untyped_hook {
|
class base_untyped_hook {
|
||||||
|
std::string m_name;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
base_untyped_hook(std::string name) : m_name(name) {}
|
||||||
|
|
||||||
virtual ~base_untyped_hook() = default;
|
virtual ~base_untyped_hook() = default;
|
||||||
|
|
||||||
|
virtual bool check_consistencies() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void assert_dominance() const {
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& name() const {
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename>
|
template<typename>
|
||||||
|
|
@ -23,9 +38,10 @@ namespace hooks {
|
||||||
utils::thunk<TReturn(TArgs...)> m_thunk;
|
utils::thunk<TReturn(TArgs...)> m_thunk;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
base_hook(TFn* pfnOriginal)
|
base_hook(std::string name, TFn* pfnOriginal)
|
||||||
: m_pfnOriginal(pfnOriginal)
|
: base_untyped_hook(name)
|
||||||
, m_thunk(m_pfnOriginal) {
|
, m_pfnOriginal(pfnOriginal)
|
||||||
|
, m_thunk(std::move(name), m_pfnOriginal) {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void set_detour(std::function<TFn> fn) {
|
virtual void set_detour(std::function<TFn> fn) {
|
||||||
|
|
@ -56,23 +72,34 @@ namespace hooks {
|
||||||
TFn** const m_ppfnImportTableItem;
|
TFn** const m_ppfnImportTableItem;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
import_hook(TFn** ppfnImportTableItem)
|
import_hook(std::string name, TFn** ppfnImportTableItem)
|
||||||
: Base(*ppfnImportTableItem)
|
: Base(std::move(name), *ppfnImportTableItem)
|
||||||
, m_ppfnImportTableItem(ppfnImportTableItem) {
|
, m_ppfnImportTableItem(ppfnImportTableItem) {
|
||||||
|
|
||||||
const utils::memory_tenderizer tenderizer(ppfnImportTableItem, sizeof * ppfnImportTableItem, PAGE_READWRITE);
|
const utils::memory_tenderizer tenderizer(ppfnImportTableItem, sizeof * ppfnImportTableItem, PAGE_READWRITE);
|
||||||
*ppfnImportTableItem = Base::get_thunk();
|
*ppfnImportTableItem = Base::get_thunk();
|
||||||
}
|
}
|
||||||
|
|
||||||
import_hook(const char* pcszDllName, const char* pcszFunctionName, int hintOrOrdinal)
|
import_hook(std::string name, const char* pcszDllName, const char* pcszFunctionName, int hintOrOrdinal)
|
||||||
: import_hook(utils::loaded_module::current_process().get_imported_function_pointer<TFn>(pcszDllName, pcszFunctionName, hintOrOrdinal)) {
|
: import_hook(std::move(name), utils::loaded_module::current_process().get_imported_function_pointer<TFn>(pcszDllName, pcszFunctionName, hintOrOrdinal)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
~import_hook() override {
|
~import_hook() override {
|
||||||
const utils::memory_tenderizer tenderizer(m_ppfnImportTableItem, sizeof * m_ppfnImportTableItem, PAGE_READWRITE);
|
const utils::memory_tenderizer tenderizer(m_ppfnImportTableItem, sizeof * m_ppfnImportTableItem, PAGE_READWRITE);
|
||||||
|
|
||||||
*m_ppfnImportTableItem = Base::get_original();
|
*m_ppfnImportTableItem = Base::get_original();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool check_consistencies() const override {
|
||||||
|
return *m_ppfnImportTableItem == Base::get_thunk();
|
||||||
|
}
|
||||||
|
|
||||||
|
void assert_dominance() const override {
|
||||||
|
if (check_consistencies())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const utils::memory_tenderizer tenderizer(m_ppfnImportTableItem, sizeof * m_ppfnImportTableItem, PAGE_READWRITE);
|
||||||
|
*m_ppfnImportTableItem = Base::get_thunk();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename>
|
template<typename>
|
||||||
|
|
@ -86,8 +113,8 @@ namespace hooks {
|
||||||
TFn* m_pfnMinHookBridge;
|
TFn* m_pfnMinHookBridge;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
direct_hook(TFn* pfnFunction)
|
direct_hook(std::string name, TFn* pfnFunction)
|
||||||
: Base(pfnFunction) {
|
: Base(std::move(name), pfnFunction) {
|
||||||
if (const auto mhStatus = MH_CreateHook(pfnFunction, Base::get_thunk(), reinterpret_cast<void**>(&m_pfnMinHookBridge)); mhStatus != MH_OK)
|
if (const auto mhStatus = MH_CreateHook(pfnFunction, Base::get_thunk(), reinterpret_cast<void**>(&m_pfnMinHookBridge)); mhStatus != MH_OK)
|
||||||
throw std::runtime_error(std::format("MH_CreateHook(0x{:X}, ...) failure: {}", reinterpret_cast<size_t>(pfnFunction), static_cast<int>(mhStatus)));
|
throw std::runtime_error(std::format("MH_CreateHook(0x{:X}, ...) failure: {}", reinterpret_cast<size_t>(pfnFunction), static_cast<int>(mhStatus)));
|
||||||
|
|
||||||
|
|
@ -106,22 +133,33 @@ namespace hooks {
|
||||||
class wndproc_hook : public base_hook<std::remove_pointer_t<WNDPROC>> {
|
class wndproc_hook : public base_hook<std::remove_pointer_t<WNDPROC>> {
|
||||||
using Base = base_hook<std::remove_pointer_t<WNDPROC>>;
|
using Base = base_hook<std::remove_pointer_t<WNDPROC>>;
|
||||||
|
|
||||||
const HWND s_hwnd;
|
const HWND m_hwnd;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
wndproc_hook(HWND hwnd)
|
wndproc_hook(std::string name, HWND hwnd)
|
||||||
: Base(reinterpret_cast<WNDPROC>(GetWindowLongPtrW(hwnd, GWLP_WNDPROC)))
|
: Base(std::move(name), reinterpret_cast<WNDPROC>(GetWindowLongPtrW(hwnd, GWLP_WNDPROC)))
|
||||||
, s_hwnd(hwnd) {
|
, m_hwnd(hwnd) {
|
||||||
SetWindowLongPtrW(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(Base::get_thunk()));
|
SetWindowLongPtrW(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(Base::get_thunk()));
|
||||||
}
|
}
|
||||||
|
|
||||||
~wndproc_hook() override {
|
~wndproc_hook() override {
|
||||||
SetWindowLongPtrW(s_hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(Base::get_original()));
|
SetWindowLongPtrW(m_hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(Base::get_original()));
|
||||||
}
|
}
|
||||||
|
|
||||||
LRESULT call_original(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) override {
|
LRESULT call_original(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) override {
|
||||||
return CallWindowProcW(Base::get_original(), hwnd, msg, wParam, lParam);
|
return CallWindowProcW(Base::get_original(), hwnd, msg, wParam, lParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool check_consistencies() const override {
|
||||||
|
return GetWindowLongPtrW(m_hwnd, GWLP_WNDPROC) == reinterpret_cast<LONG_PTR>(Base::get_thunk());
|
||||||
|
}
|
||||||
|
|
||||||
|
void assert_dominance() const override {
|
||||||
|
if (check_consistencies())
|
||||||
|
return;
|
||||||
|
|
||||||
|
SetWindowLongPtrW(m_hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(Base::get_thunk()));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class untyped_import_hook : public base_untyped_hook {
|
class untyped_import_hook : public base_untyped_hook {
|
||||||
|
|
@ -129,8 +167,9 @@ namespace hooks {
|
||||||
void* const m_pfnOriginalImport;
|
void* const m_pfnOriginalImport;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
untyped_import_hook(void** ppfnImportTableItem, void* pThunk)
|
untyped_import_hook(std::string name, void** ppfnImportTableItem, void* pThunk)
|
||||||
: m_pfnOriginalImport(*ppfnImportTableItem)
|
: base_untyped_hook(std::move(name))
|
||||||
|
, m_pfnOriginalImport(*ppfnImportTableItem)
|
||||||
, m_ppfnImportTableItem(ppfnImportTableItem) {
|
, m_ppfnImportTableItem(ppfnImportTableItem) {
|
||||||
|
|
||||||
const utils::memory_tenderizer tenderizer(ppfnImportTableItem, sizeof * ppfnImportTableItem, PAGE_READWRITE);
|
const utils::memory_tenderizer tenderizer(ppfnImportTableItem, sizeof * ppfnImportTableItem, PAGE_READWRITE);
|
||||||
|
|
@ -165,7 +204,7 @@ namespace hooks {
|
||||||
getprocaddress_singleton_import_hook();
|
getprocaddress_singleton_import_hook();
|
||||||
~getprocaddress_singleton_import_hook();
|
~getprocaddress_singleton_import_hook();
|
||||||
|
|
||||||
std::shared_ptr<void> set_handler(std::wstring dllName, std::string functionName, void* pfnDetour);
|
std::shared_ptr<void> set_handler(std::wstring dllName, std::string functionName, void* pfnDetour, std::function<void(void*)> fnOnOriginalAddressAvailable);
|
||||||
|
|
||||||
static std::shared_ptr<getprocaddress_singleton_import_hook> get_instance();
|
static std::shared_ptr<getprocaddress_singleton_import_hook> get_instance();
|
||||||
|
|
||||||
|
|
@ -187,11 +226,16 @@ namespace hooks {
|
||||||
std::shared_ptr<void> m_singleImportHook;
|
std::shared_ptr<void> m_singleImportHook;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
global_import_hook(std::wstring dllName, std::string functionName)
|
global_import_hook(std::string name, std::wstring dllName, std::string functionName)
|
||||||
: m_thunk(nullptr) {
|
: base_untyped_hook(name)
|
||||||
|
, m_thunk(std::move(name), nullptr) {
|
||||||
|
|
||||||
m_singleImportHook = getprocaddress_singleton_import_hook::get_instance()->set_handler(dllName, functionName, m_thunk.get_thunk());
|
m_singleImportHook = getprocaddress_singleton_import_hook::get_instance()->set_handler(
|
||||||
m_thunk.set_target(reinterpret_cast<TFn*>(m_singleImportHook.get()));
|
dllName,
|
||||||
|
functionName,
|
||||||
|
m_thunk.get_thunk(),
|
||||||
|
[this](void* p) { m_thunk.set_target(reinterpret_cast<TFn*>(p)); }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void set_detour(std::function<TFn> fn) {
|
virtual void set_detour(std::function<TFn> fn) {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
|
|
||||||
static bool s_bLoaded = false;
|
static bool s_bLoaded = false;
|
||||||
|
static bool s_bSkipLogFileWrite = false;
|
||||||
static std::shared_ptr<void> s_hLogFile;
|
static std::shared_ptr<void> s_hLogFile;
|
||||||
|
|
||||||
void logging::print(Level level, const char* s) {
|
void logging::print(Level level, const char* s) {
|
||||||
|
|
@ -45,13 +46,13 @@ void logging::print(Level level, const char* s) {
|
||||||
DWORD wr{};
|
DWORD wr{};
|
||||||
WriteFile(GetStdHandle(STD_ERROR_HANDLE), &estr[0], static_cast<DWORD>(estr.size()), &wr, nullptr);
|
WriteFile(GetStdHandle(STD_ERROR_HANDLE), &estr[0], static_cast<DWORD>(estr.size()), &wr, nullptr);
|
||||||
|
|
||||||
if (s_hLogFile) {
|
if (s_hLogFile && !s_bSkipLogFileWrite) {
|
||||||
WriteFile(s_hLogFile.get(), &estr[0], static_cast<DWORD>(estr.size()), &wr, nullptr);
|
WriteFile(s_hLogFile.get(), &estr[0], static_cast<DWORD>(estr.size()), &wr, nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void logging::start_file_logging(const std::filesystem::path& path) {
|
void logging::start_file_logging(const std::filesystem::path& path, bool redirect_stderrout) {
|
||||||
if (s_hLogFile)
|
if (s_hLogFile)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -76,6 +77,12 @@ void logging::start_file_logging(const std::filesystem::path& path) {
|
||||||
|
|
||||||
SetFilePointer(h, 0, 0, FILE_END);
|
SetFilePointer(h, 0, 0, FILE_END);
|
||||||
s_hLogFile = { h, &CloseHandle };
|
s_hLogFile = { h, &CloseHandle };
|
||||||
|
|
||||||
|
if (redirect_stderrout) {
|
||||||
|
SetStdHandle(STD_ERROR_HANDLE, h);
|
||||||
|
SetStdHandle(STD_OUTPUT_HANDLE, h);
|
||||||
|
s_bSkipLogFileWrite = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void logging::update_dll_load_status(bool loaded) {
|
void logging::update_dll_load_status(bool loaded) {
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ namespace logging {
|
||||||
print(level, std::format(pcszFormat, std::forward<Arg>(arg1), std::forward<Args>(args)...));
|
print(level, std::format(pcszFormat, std::forward<Arg>(arg1), std::forward<Args>(args)...));
|
||||||
}
|
}
|
||||||
|
|
||||||
void start_file_logging(const std::filesystem::path& path);
|
void start_file_logging(const std::filesystem::path& path, bool redirect_stderrout = false);
|
||||||
|
|
||||||
void update_dll_load_status(bool loaded);
|
void update_dll_load_status(bool loaded);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
#include <set>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
|
||||||
|
|
@ -241,20 +241,6 @@ void* get_mapped_image_base_address(HANDLE hProcess, const std::filesystem::path
|
||||||
throw std::runtime_error("corresponding base address not found");
|
throw std::runtime_error("corresponding base address not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Find the game main window.
|
|
||||||
/// @return Handle to the game main window, or nullptr if it doesn't exist (yet).
|
|
||||||
HWND try_find_game_window() {
|
|
||||||
HWND hwnd = nullptr;
|
|
||||||
while ((hwnd = FindWindowExW(nullptr, hwnd, L"FFXIVGAME", nullptr))) {
|
|
||||||
DWORD pid;
|
|
||||||
GetWindowThreadProcessId(hwnd, &pid);
|
|
||||||
|
|
||||||
if (pid == GetCurrentProcessId() && IsWindowVisible(hwnd))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return hwnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string from_utf16(const std::wstring& wstr, UINT codePage = CP_UTF8) {
|
std::string from_utf16(const std::wstring& wstr, UINT codePage = CP_UTF8) {
|
||||||
std::string str(WideCharToMultiByte(codePage, 0, &wstr[0], static_cast<int>(wstr.size()), nullptr, 0, nullptr, nullptr), 0);
|
std::string str(WideCharToMultiByte(codePage, 0, &wstr[0], static_cast<int>(wstr.size()), nullptr, 0, nullptr, nullptr), 0);
|
||||||
WideCharToMultiByte(codePage, 0, &wstr[0], static_cast<int>(wstr.size()), &str[0], static_cast<int>(str.size()), nullptr, nullptr);
|
WideCharToMultiByte(codePage, 0, &wstr[0], static_cast<int>(wstr.size()), &str[0], static_cast<int>(str.size()), nullptr, nullptr);
|
||||||
|
|
@ -360,15 +346,6 @@ DllExport DWORD WINAPI RewriteRemoteEntryPoint(HANDLE hProcess, const wchar_t* p
|
||||||
return RewriteRemoteEntryPointW(hProcess, pcwzPath, to_utf16(pcszLoadInfo).c_str());
|
return RewriteRemoteEntryPointW(hProcess, pcwzPath, to_utf16(pcszLoadInfo).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void wait_for_game_window() {
|
|
||||||
HWND game_window;
|
|
||||||
while (!(game_window = try_find_game_window())) {
|
|
||||||
WaitForInputIdle(GetCurrentProcess(), INFINITE);
|
|
||||||
Sleep(100);
|
|
||||||
};
|
|
||||||
SendMessageW(game_window, WM_NULL, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Entry point function "called" instead of game's original main entry point.
|
/// @brief Entry point function "called" instead of game's original main entry point.
|
||||||
/// @param params Parameters set up from RewriteRemoteEntryPoint.
|
/// @param params Parameters set up from RewriteRemoteEntryPoint.
|
||||||
DllExport void WINAPI RewrittenEntryPoint(RewrittenEntryPointParameters& params) {
|
DllExport void WINAPI RewrittenEntryPoint(RewrittenEntryPointParameters& params) {
|
||||||
|
|
@ -383,21 +360,14 @@ DllExport void WINAPI RewrittenEntryPoint(RewrittenEntryPointParameters& params)
|
||||||
std::string loadInfo;
|
std::string loadInfo;
|
||||||
auto& params = *reinterpret_cast<RewrittenEntryPointParameters*>(p);
|
auto& params = *reinterpret_cast<RewrittenEntryPointParameters*>(p);
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
// Restore original entry point.
|
// Restore original entry point.
|
||||||
// Use WriteProcessMemory instead of memcpy to avoid having to fiddle with VirtualProtect.
|
// Use WriteProcessMemory instead of memcpy to avoid having to fiddle with VirtualProtect.
|
||||||
write_process_memory_or_throw(GetCurrentProcess(), params.pEntrypoint, params.pEntrypointBytes, params.entrypointLength);
|
write_process_memory_or_throw(GetCurrentProcess(), params.pEntrypoint, params.pEntrypointBytes, params.entrypointLength);
|
||||||
|
|
||||||
// Make a copy of load info, as the whole params will be freed after this code block.
|
// Make a copy of load info, as the whole params will be freed after this code block.
|
||||||
loadInfo = params.pLoadInfo;
|
loadInfo = params.pLoadInfo;
|
||||||
|
|
||||||
// Let the game initialize.
|
|
||||||
SetEvent(params.hMainThreadContinue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wait_for_game_window();
|
|
||||||
|
|
||||||
Initialize(&loadInfo[0], params.hMainThreadContinue);
|
Initialize(&loadInfo[0], params.hMainThreadContinue);
|
||||||
return 0;
|
return 0;
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
|
|
|
||||||
|
|
@ -488,3 +488,24 @@ std::filesystem::path utils::get_module_path(HMODULE hModule) {
|
||||||
buf.resize(buf.size() * 2);
|
buf.resize(buf.size() * 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HWND utils::try_find_game_window() {
|
||||||
|
HWND hwnd = nullptr;
|
||||||
|
while ((hwnd = FindWindowExW(nullptr, hwnd, L"FFXIVGAME", nullptr))) {
|
||||||
|
DWORD pid;
|
||||||
|
GetWindowThreadProcessId(hwnd, &pid);
|
||||||
|
|
||||||
|
if (pid == GetCurrentProcessId() && IsWindowVisible(hwnd))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return hwnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void utils::wait_for_game_window() {
|
||||||
|
HWND game_window;
|
||||||
|
while (!(game_window = try_find_game_window())) {
|
||||||
|
WaitForInputIdle(GetCurrentProcess(), INFINITE);
|
||||||
|
Sleep(100);
|
||||||
|
};
|
||||||
|
SendMessageW(game_window, WM_NULL, 0, 0);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -137,12 +137,14 @@ namespace utils {
|
||||||
static constexpr uint64_t Placeholder = 0xCC90CC90CC90CC90ULL;
|
static constexpr uint64_t Placeholder = 0xCC90CC90CC90CC90ULL;
|
||||||
|
|
||||||
const std::shared_ptr<void> m_pThunk;
|
const std::shared_ptr<void> m_pThunk;
|
||||||
|
std::string m_name;
|
||||||
std::function<TFn> m_fnTarget;
|
std::function<TFn> m_fnTarget;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
thunk(std::function<TFn> target)
|
thunk(std::string name, std::function<TFn> target)
|
||||||
: m_pThunk(utils::create_thunk(&detour_static, this, Placeholder))
|
: m_pThunk(utils::create_thunk(&detour_static, this, Placeholder))
|
||||||
, m_fnTarget(std::move(target)) {
|
, m_fnTarget(std::move(target))
|
||||||
|
, m_name(name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_target(std::function<TFn> detour) {
|
void set_target(std::function<TFn> detour) {
|
||||||
|
|
@ -153,6 +155,10 @@ namespace utils {
|
||||||
return reinterpret_cast<TFn*>(m_pThunk.get());
|
return reinterpret_cast<TFn*>(m_pThunk.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string& name() const {
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// mark it as virtual to prevent compiler from inlining
|
// mark it as virtual to prevent compiler from inlining
|
||||||
virtual TReturn detour(TArgs... args) {
|
virtual TReturn detour(TArgs... args) {
|
||||||
|
|
@ -249,4 +255,10 @@ namespace utils {
|
||||||
bool is_running_on_linux();
|
bool is_running_on_linux();
|
||||||
|
|
||||||
std::filesystem::path get_module_path(HMODULE hModule);
|
std::filesystem::path get_module_path(HMODULE hModule);
|
||||||
|
|
||||||
|
/// @brief Find the game main window.
|
||||||
|
/// @return Handle to the game main window, or nullptr if it doesn't exist (yet).
|
||||||
|
HWND try_find_game_window();
|
||||||
|
|
||||||
|
void wait_for_game_window();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ void xivfixes::prevent_devicechange_crashes(bool bApply) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
s_hookCreateWindowExA.emplace("user32.dll", "CreateWindowExA", 0);
|
s_hookCreateWindowExA.emplace("user32.dll!CreateWindowExA (prevent_devicechange_crashes)", "user32.dll", "CreateWindowExA", 0);
|
||||||
s_hookCreateWindowExA->set_detour([](DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam)->HWND {
|
s_hookCreateWindowExA->set_detour([](DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam)->HWND {
|
||||||
const auto hWnd = s_hookCreateWindowExA->call_original(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
|
const auto hWnd = s_hookCreateWindowExA->call_original(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
|
||||||
|
|
||||||
|
|
@ -198,7 +198,7 @@ void xivfixes::prevent_devicechange_crashes(bool bApply) {
|
||||||
|
|
||||||
s_hookCreateWindowExA.reset();
|
s_hookCreateWindowExA.reset();
|
||||||
|
|
||||||
s_hookWndProc.emplace(hWnd);
|
s_hookWndProc.emplace("FFXIVGAME:WndProc (prevent_devicechange_crashes)", hWnd);
|
||||||
s_hookWndProc->set_detour([](HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -> LRESULT {
|
s_hookWndProc->set_detour([](HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -> LRESULT {
|
||||||
if (uMsg == WM_DEVICECHANGE && wParam == DBT_DEVNODES_CHANGED) {
|
if (uMsg == WM_DEVICECHANGE && wParam == DBT_DEVNODES_CHANGED) {
|
||||||
if (!GetGetInputDeviceManager(hWnd)()) {
|
if (!GetGetInputDeviceManager(hWnd)()) {
|
||||||
|
|
@ -239,7 +239,7 @@ void xivfixes::disable_game_openprocess_access_check(bool bApply) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
s_hook.emplace("kernel32.dll", "OpenProcess", 0);
|
s_hook.emplace("kernel32.dll!OpenProcess (import, disable_game_openprocess_access_check)", "kernel32.dll", "OpenProcess", 0);
|
||||||
s_hook->set_detour([](DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId)->HANDLE {
|
s_hook->set_detour([](DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId)->HANDLE {
|
||||||
logging::print<logging::I>("{} OpenProcess(0x{:08X}, {}, {}) was invoked by thread {}.", LogTag, dwDesiredAccess, bInheritHandle, dwProcessId, GetCurrentThreadId());
|
logging::print<logging::I>("{} OpenProcess(0x{:08X}, {}, {}) was invoked by thread {}.", LogTag, dwDesiredAccess, bInheritHandle, dwProcessId, GetCurrentThreadId());
|
||||||
|
|
||||||
|
|
@ -267,6 +267,8 @@ void xivfixes::disable_game_openprocess_access_check(bool bApply) {
|
||||||
void xivfixes::redirect_openprocess(bool bApply) {
|
void xivfixes::redirect_openprocess(bool bApply) {
|
||||||
static const char* LogTag = "[xivfixes:redirect_openprocess]";
|
static const char* LogTag = "[xivfixes:redirect_openprocess]";
|
||||||
static std::shared_ptr<hooks::base_untyped_hook> s_hook;
|
static std::shared_ptr<hooks::base_untyped_hook> s_hook;
|
||||||
|
static std::mutex s_silenceSetMtx;
|
||||||
|
static std::set<DWORD> s_silenceSet;
|
||||||
|
|
||||||
if (bApply) {
|
if (bApply) {
|
||||||
if (!bootconfig::gamefix_is_enabled(L"redirect_openprocess")) {
|
if (!bootconfig::gamefix_is_enabled(L"redirect_openprocess")) {
|
||||||
|
|
@ -275,10 +277,11 @@ void xivfixes::redirect_openprocess(bool bApply) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bootconfig::dotnet_openprocess_hook_mode() == bootconfig::ImportHooks) {
|
if (bootconfig::dotnet_openprocess_hook_mode() == bootconfig::ImportHooks) {
|
||||||
auto hook = std::make_shared<hooks::global_import_hook<decltype(OpenProcess)>>(L"kernel32.dll", "OpenProcess");
|
auto hook = std::make_shared<hooks::global_import_hook<decltype(OpenProcess)>>("kernel32.dll!OpenProcess (global import, redirect_openprocess)", L"kernel32.dll", "OpenProcess");
|
||||||
hook->set_detour([hook = hook.get()](DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId)->HANDLE {
|
hook->set_detour([hook = hook.get()](DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId)->HANDLE {
|
||||||
if (dwProcessId == GetCurrentProcessId()) {
|
if (dwProcessId == GetCurrentProcessId()) {
|
||||||
logging::print<logging::I>("{} OpenProcess(0x{:08X}, {}, {}) was invoked by thread {}. Redirecting to DuplicateHandle.", LogTag, dwDesiredAccess, bInheritHandle, dwProcessId, GetCurrentThreadId());
|
if (s_silenceSet.emplace(GetCurrentThreadId()).second)
|
||||||
|
logging::print<logging::I>("{} OpenProcess(0x{:08X}, {}, {}) was invoked by thread {}. Redirecting to DuplicateHandle.", LogTag, dwDesiredAccess, bInheritHandle, dwProcessId, GetCurrentThreadId());
|
||||||
|
|
||||||
if (HANDLE res; DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), &res, dwDesiredAccess, bInheritHandle, 0))
|
if (HANDLE res; DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), &res, dwDesiredAccess, bInheritHandle, 0))
|
||||||
return res;
|
return res;
|
||||||
|
|
@ -292,10 +295,11 @@ void xivfixes::redirect_openprocess(bool bApply) {
|
||||||
logging::print<logging::I>("{} Enable via import_hook", LogTag);
|
logging::print<logging::I>("{} Enable via import_hook", LogTag);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
auto hook = std::make_shared<hooks::direct_hook<decltype(OpenProcess)>>(OpenProcess);
|
auto hook = std::make_shared<hooks::direct_hook<decltype(OpenProcess)>>("kernel32.dll!OpenProcess (direct, redirect_openprocess)", OpenProcess);
|
||||||
hook->set_detour([hook = hook.get()](DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId)->HANDLE {
|
hook->set_detour([hook = hook.get()](DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId)->HANDLE {
|
||||||
if (dwProcessId == GetCurrentProcessId()) {
|
if (dwProcessId == GetCurrentProcessId()) {
|
||||||
logging::print<logging::I>("{} OpenProcess(0x{:08X}, {}, {}) was invoked by thread {}. Redirecting to DuplicateHandle.", LogTag, dwDesiredAccess, bInheritHandle, dwProcessId, GetCurrentThreadId());
|
if (s_silenceSet.emplace(GetCurrentThreadId()).second)
|
||||||
|
logging::print<logging::I>("{} OpenProcess(0x{:08X}, {}, {}) was invoked by thread {}. Redirecting to DuplicateHandle.", LogTag, dwDesiredAccess, bInheritHandle, dwProcessId, GetCurrentThreadId());
|
||||||
|
|
||||||
if (HANDLE res; DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), &res, dwDesiredAccess, bInheritHandle, 0))
|
if (HANDLE res; DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), &res, dwDesiredAccess, bInheritHandle, 0))
|
||||||
return res;
|
return res;
|
||||||
|
|
@ -309,6 +313,12 @@ void xivfixes::redirect_openprocess(bool bApply) {
|
||||||
logging::print<logging::I>("{} Enable via direct_hook", LogTag);
|
logging::print<logging::I>("{} Enable via direct_hook", LogTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//std::thread([]() {
|
||||||
|
// SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE);
|
||||||
|
// for (const auto to = GetTickCount64() + 3000; GetTickCount64() < to;)
|
||||||
|
// s_hook->assert_dominance();
|
||||||
|
//}).detach();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (s_hook) {
|
if (s_hook) {
|
||||||
logging::print<logging::I>("{} Disable OpenProcess", LogTag);
|
logging::print<logging::I>("{} Disable OpenProcess", LogTag);
|
||||||
|
|
|
||||||
|
|
@ -9,29 +9,31 @@ namespace Dalamud.Interface.GameFonts
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class FdtReader
|
public class FdtReader
|
||||||
{
|
{
|
||||||
|
private static unsafe T StructureFromByteArray<T> (byte[] data, int offset)
|
||||||
|
{
|
||||||
|
var len = Marshal.SizeOf<T>();
|
||||||
|
if (offset + len > data.Length)
|
||||||
|
throw new Exception("Data too short");
|
||||||
|
|
||||||
|
fixed (byte* ptr = data)
|
||||||
|
return Marshal.PtrToStructure<T>(new(ptr + offset));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="FdtReader"/> class.
|
/// Initializes a new instance of the <see cref="FdtReader"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">Content of a FDT file.</param>
|
/// <param name="data">Content of a FDT file.</param>
|
||||||
public FdtReader(byte[] data)
|
public FdtReader(byte[] data)
|
||||||
{
|
{
|
||||||
unsafe
|
this.FileHeader = StructureFromByteArray<FdtHeader>(data, 0);
|
||||||
{
|
this.FontHeader = StructureFromByteArray<FontTableHeader>(data, this.FileHeader.FontTableHeaderOffset);
|
||||||
fixed (byte* ptr = data)
|
this.KerningHeader = StructureFromByteArray<KerningTableHeader>(data, this.FileHeader.KerningTableHeaderOffset);
|
||||||
{
|
|
||||||
this.FileHeader = *(FdtHeader*)ptr;
|
|
||||||
this.FontHeader = *(FontTableHeader*)(ptr + this.FileHeader.FontTableHeaderOffset);
|
|
||||||
this.KerningHeader = *(KerningTableHeader*)(ptr + this.FileHeader.KerningTableHeaderOffset);
|
|
||||||
|
|
||||||
var glyphs = (FontTableEntry*)(ptr + this.FileHeader.FontTableHeaderOffset + Marshal.SizeOf(this.FontHeader));
|
for (var i = 0; i < this.FontHeader.FontTableEntryCount; i++)
|
||||||
for (var i = 0; i < this.FontHeader.FontTableEntryCount; i++)
|
this.Glyphs.Add(StructureFromByteArray<FontTableEntry>(data, this.FileHeader.FontTableHeaderOffset + Marshal.SizeOf<FontTableHeader>() + (Marshal.SizeOf<FontTableEntry>() * i)));
|
||||||
this.Glyphs.Add(glyphs[i]);
|
|
||||||
|
|
||||||
var kerns = (KerningTableEntry*)(ptr + this.FileHeader.KerningTableHeaderOffset + Marshal.SizeOf(this.KerningHeader));
|
for (int i = 0, i_ = Math.Min(this.FontHeader.KerningTableEntryCount, this.KerningHeader.Count); i < i_; i++)
|
||||||
for (var i = 0; i < this.FontHeader.KerningTableEntryCount; i++)
|
this.Distances.Add(StructureFromByteArray<KerningTableEntry>(data, this.FileHeader.KerningTableHeaderOffset+ Marshal.SizeOf<KerningTableHeader>() + (Marshal.SizeOf<KerningTableEntry>() * i)));
|
||||||
this.Distances.Add(kerns[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue