(x);
+
+ } else {
+ if constexpr (is_basic_string_v| || is_basic_string_view_v | ) {
+ using Tdd = Td::value_type;
+ if constexpr (std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v)
+ return unicode::convert(x);
+ else
+ return std::forward(x);
+
+ } else if constexpr (std::is_same_v| ) {
+ auto u8s = x.u8string();
+ return std::move(*reinterpret_cast(&u8s));
+
+ } else {
+ return std::forward(x);
+ }
+ }
+ }
+
+ /**
+ * @brief Prints log, formatted.
+ * @param level Log level.
+ * @param fmt C-string.
+ * @param arg1 First format parameter.
+ * @param args Second and further format parameters, if any.
+ */
+ template
+ void print(Level level, const char* fmt, Arg&& arg1, Args&&...args) {
+ print(level, std::vformat(fmt, std::make_format_args(to_format_arg(std::forward(arg1)), to_format_arg(std::forward(args))...)));
+ }
+
+ template void V(Args&&...args) { print(Level::Verbose, std::forward(args)...); }
+ template void D(Args&&...args) { print(Level::Debug, std::forward(args)...); }
+ template void I(Args&&...args) { print(Level::Info, std::forward(args)...); }
+ template void W(Args&&...args) { print(Level::Warning, std::forward(args)...); }
+ template void E(Args&&...args) { print(Level::Error, std::forward(args)...); }
+ template void F(Args&&...args) { print(Level::Fatal, std::forward(args)...); }
+}
diff --git a/Dalamud.Boot/pch.h b/Dalamud.Boot/pch.h
index 3c2bd6c4b..bbd4b3d73 100644
--- a/Dalamud.Boot/pch.h
+++ b/Dalamud.Boot/pch.h
@@ -8,7 +8,8 @@
#define PCH_H
// Exclude rarely-used stuff from Windows headers
-#define WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#define NOMINMAX
// Windows Header Files
#include
@@ -17,25 +18,52 @@
#include
#include
#include
+#include
+#include
+
+// MSVC Compiler Intrinsic
+#include
// C++ Standard Libraries
#include
#include
+#include
#include
#include
#include
+#include
+#include
+#include
+#include
#include
+#include
#include
+#include
+
+// https://www.akenotsuki.com/misc/srell/en/
+#include "../lib/srell3_009/single-header/srell.hpp"
+
+// https://github.com/TsudaKageyu/minhook
+#include "../lib/TsudaKageyu-minhook/include/MinHook.h"
+
+// https://github.com/Nomade040/nmd
+#include "../lib/Nomade040-nmd/nmd_assembly.h"
// https://github.com/dotnet/coreclr
-#include "..\lib\CoreCLR\CoreCLR.h"
-#include "..\lib\CoreCLR\boot.h"
+#include "../lib/CoreCLR/CoreCLR.h"
+#include "../lib/CoreCLR/boot.h"
+
+// https://github.com/nlohmann/json
+#include "../lib/nlohmann-json/json.hpp"
+
+#include "unicode.h"
// Commonly used macros
#define DllExport extern "C" __declspec(dllexport)
// Global variables
extern HMODULE g_hModule;
+extern HINSTANCE g_hGameInstance;
extern std::optional g_clr;
#endif //PCH_H
diff --git a/Dalamud.Boot/pch_nmd_assembly_impl.cpp b/Dalamud.Boot/pch_nmd_assembly_impl.cpp
new file mode 100644
index 000000000..6f3af675a
--- /dev/null
+++ b/Dalamud.Boot/pch_nmd_assembly_impl.cpp
@@ -0,0 +1,2 @@
+#define NMD_ASSEMBLY_IMPLEMENTATION
+#include "../lib/Nomade040-nmd/nmd_assembly.h"
diff --git a/Dalamud.Boot/rewrite_entrypoint.cpp b/Dalamud.Boot/rewrite_entrypoint.cpp
index c79960859..5a585da7a 100644
--- a/Dalamud.Boot/rewrite_entrypoint.cpp
+++ b/Dalamud.Boot/rewrite_entrypoint.cpp
@@ -1,6 +1,8 @@
#include "pch.h"
-DllExport DWORD WINAPI Initialize(LPVOID lpParam);
+#include "logging.h"
+
+DllExport DWORD WINAPI Initialize(LPVOID lpParam, HANDLE hMainThreadContinue);
struct RewrittenEntryPointParameters {
void* pAllocation;
@@ -231,28 +233,14 @@ void* get_mapped_image_base_address(HANDLE hProcess, const std::filesystem::path
return mbi.AllocationBase;
- } catch (const std::filesystem::filesystem_error& e) {
- printf("%s", e.what());
+ } catch (const std::exception& e) {
+ logging::W("Failed to check memory block 0x{:X}(len=0x{:X}): {}", mbi.BaseAddress, mbi.RegionSize, e.what());
continue;
}
}
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(wstr.size()), nullptr, 0, nullptr, nullptr), 0);
WideCharToMultiByte(codePage, 0, &wstr[0], static_cast(wstr.size()), &str[0], static_cast(str.size()), nullptr, nullptr);
@@ -358,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) {
@@ -379,23 +358,17 @@ DllExport void WINAPI RewrittenEntryPoint(RewrittenEntryPointParameters& params)
params.hMainThread = CreateThread(nullptr, 0, [](void* p) -> DWORD {
try {
std::string loadInfo;
+ auto& params = *reinterpret_cast(p);
{
- auto& params = *reinterpret_cast(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]);
+ Initialize(&loadInfo[0], params.hMainThreadContinue);
return 0;
} catch (const std::exception& e) {
MessageBoxA(nullptr, std::format("Failed to load Dalamud.\n\nError: {}", e.what()).c_str(), "Dalamud.Boot", MB_OK | MB_ICONERROR);
diff --git a/Dalamud.Boot/unicode.cpp b/Dalamud.Boot/unicode.cpp
new file mode 100644
index 000000000..b558d667a
--- /dev/null
+++ b/Dalamud.Boot/unicode.cpp
@@ -0,0 +1,245 @@
+#include "unicode.h"
+
+size_t unicode::decode(EncodingTag, char32_t& out, const char8_t* in, size_t nRemainingBytes, bool strict) {
+ if (nRemainingBytes == 0) {
+ out = 0;
+ return 0;
+ }
+
+ if (0 == (*in & 0x80)) {
+ out = *in;
+ return 1;
+ }
+
+ if (0xC0 == (*in & 0xE0)) {
+ if (nRemainingBytes < 2) goto invalid;
+ if (0x80 != (in[1] & 0xC0)) goto invalid;
+ out = (
+ ((static_cast(in[0]) & 0x1F) << 6) |
+ ((static_cast(in[1]) & 0x3F) << 0));
+ return 2;
+ }
+
+ if (0xE0 == (*in & 0xF0)) {
+ if (nRemainingBytes < 3) goto invalid;
+ if (0x80 != (in[1] & 0xC0)) goto invalid;
+ if (0x80 != (in[2] & 0xC0)) goto invalid;
+ out = static_cast(
+ ((static_cast(in[0]) & 0x0F) << 12) |
+ ((static_cast(in[1]) & 0x3F) << 6) |
+ ((static_cast(in[2]) & 0x3F) << 0));
+ return 3;
+ }
+
+ if (0xF0 == (*in & 0xF8)) {
+ if (nRemainingBytes < 4) goto invalid;
+ if (0x80 != (in[1] & 0xC0)) goto invalid;
+ if (0x80 != (in[2] & 0xC0)) goto invalid;
+ if (0x80 != (in[3] & 0xC0)) goto invalid;
+ out = (
+ ((static_cast(in[0]) & 0x07) << 18) |
+ ((static_cast(in[1]) & 0x3F) << 12) |
+ ((static_cast(in[2]) & 0x3F) << 6) |
+ ((static_cast(in[3]) & 0x3F) << 0));
+ return 4;
+ }
+
+ if (!strict) {
+ if (0xF8 == (*in & 0xFC)) {
+ if (nRemainingBytes < 5) goto invalid;
+ if (0x80 != (in[1] & 0xC0)) goto invalid;
+ if (0x80 != (in[2] & 0xC0)) goto invalid;
+ if (0x80 != (in[3] & 0xC0)) goto invalid;
+ if (0x80 != (in[4] & 0xC0)) goto invalid;
+ out = (
+ ((static_cast(in[0]) & 0x07) << 24) |
+ ((static_cast(in[1]) & 0x3F) << 18) |
+ ((static_cast(in[2]) & 0x3F) << 12) |
+ ((static_cast(in[3]) & 0x3F) << 6) |
+ ((static_cast(in[4]) & 0x3F) << 0));
+ return 4;
+ }
+
+ if (0xFC == (*in & 0xFE)) {
+ if (nRemainingBytes < 6) goto invalid;
+ if (0x80 != (in[1] & 0xC0)) goto invalid;
+ if (0x80 != (in[2] & 0xC0)) goto invalid;
+ if (0x80 != (in[3] & 0xC0)) goto invalid;
+ if (0x80 != (in[4] & 0xC0)) goto invalid;
+ if (0x80 != (in[5] & 0xC0)) goto invalid;
+ out = (
+ ((static_cast(in[0]) & 0x07) << 30) |
+ ((static_cast(in[1]) & 0x3F) << 24) |
+ ((static_cast(in[2]) & 0x3F) << 18) |
+ ((static_cast(in[3]) & 0x3F) << 12) |
+ ((static_cast(in[4]) & 0x3F) << 6) |
+ ((static_cast(in[5]) & 0x3F) << 0));
+ return 5;
+ }
+ }
+
+invalid:
+ out = UReplacement;
+ return 1;
+}
+
+size_t unicode::decode(EncodingTag, char32_t& out, const char16_t* in, size_t nRemainingBytes, bool strict) {
+ if (nRemainingBytes == 0) {
+ out = 0;
+ return 0;
+ }
+
+ if ((*in & 0xFC00) == 0xD800) {
+ if (nRemainingBytes < 2 || (in[1] & 0xFC00) != 0xDC00)
+ goto invalid;
+ out = 0x10000 + (
+ ((static_cast(in[0]) & 0x03FF) << 10) |
+ ((static_cast(in[1]) & 0x03FF) << 0)
+ );
+ return 2;
+ }
+
+ if (0xD800 <= *in && *in <= 0xDFFF && strict)
+ out = UReplacement;
+ else
+ out = *in;
+ return 1;
+
+invalid:
+ out = UReplacement;
+ return 1;
+}
+
+size_t unicode::decode(EncodingTag, char32_t& out, const char32_t* in, size_t nRemainingBytes, bool strict) {
+ if (nRemainingBytes == 0) {
+ out = 0;
+ return 0;
+ }
+
+ out = *in;
+ return 1;
+}
+
+size_t unicode::decode(EncodingTag, char32_t& out, const char* in, size_t nRemainingBytes, bool strict) {
+ return decode(EncodingTag(), out, reinterpret_cast(in), nRemainingBytes, strict);
+}
+
+size_t unicode::decode(EncodingTag, char32_t& out, const wchar_t* in, size_t nRemainingBytes, bool strict) {
+ return decode(EncodingTag(), out, reinterpret_cast(in), nRemainingBytes, strict);
+}
+
+size_t unicode::encode(EncodingTag | |