Fix nullptr problems (#878)

This commit is contained in:
kizer 2022-06-04 20:45:22 +09:00 committed by GitHub
parent 74966fc4ef
commit 71f3680388
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 161 additions and 84 deletions

View file

@ -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.");
else {
try {
logging::start_file_logging(logFilePath);
logging::start_file_logging(logFilePath, !bootconfig::is_show_console());
logging::print<logging::I>(L"Logging to file: {}", logFilePath);
} catch (const std::exception& e) {
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)
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...");
entrypoint_fn(lpParam, hMainThreadContinue);
logging::print<logging::I>("Done!");

View file

@ -37,14 +37,15 @@ static const auto LdrUnregisterDllNotification = utils::loaded_module(GetModuleH
hooks::getprocaddress_singleton_import_hook::getprocaddress_singleton_import_hook()
: 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() {
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());
if (!hModule)
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)
throw std::out_of_range("Could not find the specified function.");
fnOnOriginalAddressAvailable(pfn);
auto& target = m_targetFns[hModule][functionName];
if (target)
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() {
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) {
if (notiReason == LDR_DLL_NOTIFICATION_REASON_LOADED) {
@ -138,7 +141,7 @@ void hooks::getprocaddress_singleton_import_hook::hook_module(const utils::loade
if (!hook) {
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);
}
}
}

View file

@ -7,8 +7,23 @@
namespace hooks {
class base_untyped_hook {
std::string m_name;
public:
base_untyped_hook(std::string name) : m_name(name) {}
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>
@ -23,9 +38,10 @@ namespace hooks {
utils::thunk<TReturn(TArgs...)> m_thunk;
public:
base_hook(TFn* pfnOriginal)
: m_pfnOriginal(pfnOriginal)
, m_thunk(m_pfnOriginal) {
base_hook(std::string name, TFn* pfnOriginal)
: base_untyped_hook(name)
, m_pfnOriginal(pfnOriginal)
, m_thunk(std::move(name), m_pfnOriginal) {
}
virtual void set_detour(std::function<TFn> fn) {
@ -56,23 +72,34 @@ namespace hooks {
TFn** const m_ppfnImportTableItem;
public:
import_hook(TFn** ppfnImportTableItem)
: Base(*ppfnImportTableItem)
import_hook(std::string name, TFn** ppfnImportTableItem)
: Base(std::move(name), *ppfnImportTableItem)
, m_ppfnImportTableItem(ppfnImportTableItem) {
const utils::memory_tenderizer tenderizer(ppfnImportTableItem, sizeof * ppfnImportTableItem, PAGE_READWRITE);
*ppfnImportTableItem = Base::get_thunk();
}
import_hook(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::string name, const char* pcszDllName, const char* pcszFunctionName, int hintOrOrdinal)
: import_hook(std::move(name), utils::loaded_module::current_process().get_imported_function_pointer<TFn>(pcszDllName, pcszFunctionName, hintOrOrdinal)) {
}
~import_hook() override {
const utils::memory_tenderizer tenderizer(m_ppfnImportTableItem, sizeof * m_ppfnImportTableItem, PAGE_READWRITE);
*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>
@ -86,8 +113,8 @@ namespace hooks {
TFn* m_pfnMinHookBridge;
public:
direct_hook(TFn* pfnFunction)
: Base(pfnFunction) {
direct_hook(std::string name, TFn* pfnFunction)
: Base(std::move(name), pfnFunction) {
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)));
@ -106,22 +133,33 @@ namespace hooks {
class wndproc_hook : public 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:
wndproc_hook(HWND hwnd)
: Base(reinterpret_cast<WNDPROC>(GetWindowLongPtrW(hwnd, GWLP_WNDPROC)))
, s_hwnd(hwnd) {
wndproc_hook(std::string name, HWND hwnd)
: Base(std::move(name), reinterpret_cast<WNDPROC>(GetWindowLongPtrW(hwnd, GWLP_WNDPROC)))
, m_hwnd(hwnd) {
SetWindowLongPtrW(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(Base::get_thunk()));
}
~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 {
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 {
@ -129,8 +167,9 @@ namespace hooks {
void* const m_pfnOriginalImport;
public:
untyped_import_hook(void** ppfnImportTableItem, void* pThunk)
: m_pfnOriginalImport(*ppfnImportTableItem)
untyped_import_hook(std::string name, void** ppfnImportTableItem, void* pThunk)
: base_untyped_hook(std::move(name))
, m_pfnOriginalImport(*ppfnImportTableItem)
, m_ppfnImportTableItem(ppfnImportTableItem) {
const utils::memory_tenderizer tenderizer(ppfnImportTableItem, sizeof * ppfnImportTableItem, PAGE_READWRITE);
@ -165,7 +204,7 @@ namespace hooks {
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();
@ -187,11 +226,16 @@ namespace hooks {
std::shared_ptr<void> m_singleImportHook;
public:
global_import_hook(std::wstring dllName, std::string functionName)
: m_thunk(nullptr) {
global_import_hook(std::string name, std::wstring dllName, std::string functionName)
: 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_thunk.set_target(reinterpret_cast<TFn*>(m_singleImportHook.get()));
m_singleImportHook = getprocaddress_singleton_import_hook::get_instance()->set_handler(
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) {

View file

@ -7,6 +7,7 @@
#include "logging.h"
static bool s_bLoaded = false;
static bool s_bSkipLogFileWrite = false;
static std::shared_ptr<void> s_hLogFile;
void logging::print(Level level, const char* s) {
@ -45,13 +46,13 @@ void logging::print(Level level, const char* s) {
DWORD wr{};
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);
}
}
}
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)
return;
@ -76,6 +77,12 @@ void logging::start_file_logging(const std::filesystem::path& path) {
SetFilePointer(h, 0, 0, FILE_END);
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) {

View file

@ -63,7 +63,7 @@ namespace logging {
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);
};

View file

@ -34,6 +34,7 @@
#include <functional>
#include <iostream>
#include <ranges>
#include <set>
#include <span>
#include <string>
#include <mutex>

View file

@ -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");
}
/// @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 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);
@ -360,15 +346,6 @@ DllExport DWORD WINAPI RewriteRemoteEntryPoint(HANDLE hProcess, const wchar_t* p
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.
/// @param params Parameters set up from RewriteRemoteEntryPoint.
DllExport void WINAPI RewrittenEntryPoint(RewrittenEntryPointParameters& params) {
@ -383,21 +360,14 @@ DllExport void WINAPI RewrittenEntryPoint(RewrittenEntryPointParameters& params)
std::string loadInfo;
auto& params = *reinterpret_cast<RewrittenEntryPointParameters*>(p);
{
// Restore original entry point.
// Use WriteProcessMemory instead of memcpy to avoid having to fiddle with VirtualProtect.
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.
loadInfo = params.pLoadInfo;
// Let the game initialize.
SetEvent(params.hMainThreadContinue);
}
wait_for_game_window();
Initialize(&loadInfo[0], params.hMainThreadContinue);
return 0;
} catch (const std::exception& e) {

View file

@ -488,3 +488,24 @@ std::filesystem::path utils::get_module_path(HMODULE hModule) {
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);
}

View file

@ -137,12 +137,14 @@ namespace utils {
static constexpr uint64_t Placeholder = 0xCC90CC90CC90CC90ULL;
const std::shared_ptr<void> m_pThunk;
std::string m_name;
std::function<TFn> m_fnTarget;
public:
thunk(std::function<TFn> target)
thunk(std::string name, std::function<TFn> target)
: 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) {
@ -153,6 +155,10 @@ namespace utils {
return reinterpret_cast<TFn*>(m_pThunk.get());
}
const std::string& name() const {
return m_name;
}
private:
// mark it as virtual to prevent compiler from inlining
virtual TReturn detour(TArgs... args) {
@ -249,4 +255,10 @@ namespace utils {
bool is_running_on_linux();
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();
}

View file

@ -184,7 +184,7 @@ void xivfixes::prevent_devicechange_crashes(bool bApply) {
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 {
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_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 {
if (uMsg == WM_DEVICECHANGE && wParam == DBT_DEVNODES_CHANGED) {
if (!GetGetInputDeviceManager(hWnd)()) {
@ -239,7 +239,7 @@ void xivfixes::disable_game_openprocess_access_check(bool bApply) {
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 {
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) {
static const char* LogTag = "[xivfixes:redirect_openprocess]";
static std::shared_ptr<hooks::base_untyped_hook> s_hook;
static std::mutex s_silenceSetMtx;
static std::set<DWORD> s_silenceSet;
if (bApply) {
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) {
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 {
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))
return res;
@ -292,10 +295,11 @@ void xivfixes::redirect_openprocess(bool bApply) {
logging::print<logging::I>("{} Enable via import_hook", LogTag);
} 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 {
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))
return res;
@ -309,6 +313,12 @@ void xivfixes::redirect_openprocess(bool bApply) {
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 {
if (s_hook) {
logging::print<logging::I>("{} Disable OpenProcess", LogTag);

View file

@ -9,29 +9,31 @@ namespace Dalamud.Interface.GameFonts
/// </summary>
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>
/// Initializes a new instance of the <see cref="FdtReader"/> class.
/// </summary>
/// <param name="data">Content of a FDT file.</param>
public FdtReader(byte[] data)
{
unsafe
{
fixed (byte* ptr = data)
{
this.FileHeader = *(FdtHeader*)ptr;
this.FontHeader = *(FontTableHeader*)(ptr + this.FileHeader.FontTableHeaderOffset);
this.KerningHeader = *(KerningTableHeader*)(ptr + this.FileHeader.KerningTableHeaderOffset);
this.FileHeader = StructureFromByteArray<FdtHeader>(data, 0);
this.FontHeader = StructureFromByteArray<FontTableHeader>(data, this.FileHeader.FontTableHeaderOffset);
this.KerningHeader = StructureFromByteArray<KerningTableHeader>(data, this.FileHeader.KerningTableHeaderOffset);
var glyphs = (FontTableEntry*)(ptr + this.FileHeader.FontTableHeaderOffset + Marshal.SizeOf(this.FontHeader));
for (var i = 0; i < this.FontHeader.FontTableEntryCount; i++)
this.Glyphs.Add(glyphs[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)));
var kerns = (KerningTableEntry*)(ptr + this.FileHeader.KerningTableHeaderOffset + Marshal.SizeOf(this.KerningHeader));
for (var i = 0; i < this.FontHeader.KerningTableEntryCount; i++)
this.Distances.Add(kerns[i]);
}
}
for (int i = 0, i_ = Math.Min(this.FontHeader.KerningTableEntryCount, this.KerningHeader.Count); i < i_; i++)
this.Distances.Add(StructureFromByteArray<KerningTableEntry>(data, this.FileHeader.KerningTableHeaderOffset+ Marshal.SizeOf<KerningTableHeader>() + (Marshal.SizeOf<KerningTableEntry>() * i)));
}
/// <summary>