Files
eeee-uploaded-files/IDA/project/helpers.cpp
T
2026-03-08 16:19:24 -06:00

599 lines
21 KiB
C++

#include "helpers.h"
#include <algorithm>
#include <array>
#include <cctype>
#include <cstdarg>
#include <cstdio>
#include <cstring>
#include <cwctype>
#include "nlohmann/json.hpp"
RuntimeConfig g_Runtime;
namespace {
std::mutex g_il2cpp_mutex;
std::unordered_map<uintptr_t, void*> g_il2cpp_slots;
void* g_il2cpp_domain_cached = nullptr;
void* (*g_il2cpp_domain_get_fn)() = nullptr;
void* (*g_il2cpp_thread_attach_fn)(void*) = nullptr;
void* (*g_il2cpp_thread_current_fn)() = nullptr;
void (*g_il2cpp_thread_detach_fn)(void*) = nullptr;
thread_local void* g_tls_il2cpp_thread = nullptr;
thread_local bool g_tls_il2cpp_attached_by_us = false;
thread_local bool g_tls_game_thread_context = false;
std::mutex g_i18n_mutex;
bool g_i18n_initialized = false;
std::unordered_map<std::string, std::string> g_en;
std::unordered_map<std::string, std::string> g_zh_cn;
constexpr const char* kI18nBlob = R"json(
{
"NoClip": {"en":"NoClip", "zh_cn":"NoClip"},
"FreeCam": {"en":"FreeCam", "zh_cn":"????"},
"Free Camera": {"en":"Free Camera", "zh_cn":"????"},
"Fps Unlocker": {"en":"Fps Unlocker", "zh_cn":"????"},
"FPSModule": {"en":"FPSModule", "zh_cn":"FPS??"},
"SetDither": {"en":"SetDither", "zh_cn":"????"},
"Set Dither": {"en":"Set Dither", "zh_cn":"????"},
"PlayerProfile": {"en":"PlayerProfile", "zh_cn":"????"},
"Vacuum": {"en":"Vacuum", "zh_cn":"??"},
"SkillHack": {"en":"SkillHack", "zh_cn":"????"},
"Free Skill": {"en":"Free Skill", "zh_cn":"????"},
"ESPModule": {"en":"ESPModule", "zh_cn":"ESP??"},
"ESP Features": {"en":"ESP Features", "zh_cn":"ESP??"},
"Prevent Screenshot": {"en":"Prevent Screenshot", "zh_cn":"???"},
"Unity Explorer": {"en":"Unity Explorer", "zh_cn":"Unity???"},
"Unity Explorer Window": {"en":"Unity Explorer Window", "zh_cn":"Unity?????"},
"ON / OFF": {"en":"ON / OFF", "zh_cn":"? / ?"},
"Move Speed": {"en":"Move Speed", "zh_cn":"????"},
"Look Sensitivity": {"en":"Look Sensitivity", "zh_cn":"?????"},
"Roll Speed": {"en":"Roll Speed", "zh_cn":"????"},
"FOV Speed": {"en":"FOV Speed", "zh_cn":"????"},
"FOV": {"en":"FOV", "zh_cn":"??"},
"Fps Value": {"en":"Fps Value", "zh_cn":"???"},
"UID text": {"en":"UID text", "zh_cn":"UID??"},
"Name text": {"en":"Name text", "zh_cn":"????"},
"Name changer": {"en":"Name changer", "zh_cn":"??"},
"Monster": {"en":"Monster", "zh_cn":"??"},
"Show Monsters": {"en":"Show Monsters", "zh_cn":"????"},
"Show NPC": {"en":"Show NPC", "zh_cn":"??NPC"},
"Show Friendly": {"en":"Show Friendly", "zh_cn":"????"},
"Show Puzzles": {"en":"Show Puzzles", "zh_cn":"??????"},
"Show All": {"en":"Show All", "zh_cn":"????"},
"Show Distance": {"en":"Show Distance", "zh_cn":"????"},
"Show Name": {"en":"Show Name", "zh_cn":"????"},
"Show Type": {"en":"Show Type", "zh_cn":"????"},
"Theme": {"en":"Theme", "zh_cn":"??"},
"Toggle Console": {"en":"Toggle Console", "zh_cn":"?????"},
"About": {"en":"About", "zh_cn":"??"},
"Player": {"en":"Player", "zh_cn":"??"},
"ESP": {"en":"ESP", "zh_cn":"ESP"},
"World": {"en":"World", "zh_cn":"??"},
"Visual": {"en":"Visual", "zh_cn":"??"},
"Settings": {"en":"Settings", "zh_cn":"??"},
"Damagehack Toggle": {"en":"Damagehack Toggle", "zh_cn":"????"},
"Damage Multiplier": {"en":"Damage Multiplier", "zh_cn":"????"},
"Multiplier Value": {"en":"Multiplier Value", "zh_cn":"???"},
"Random Damage Type": {"en":"Random Damage Type", "zh_cn":"??????"},
"This is free, if you spend money, then you have been deceived": {
"en":"This is free, if you spend money, then you have been deceived",
"zh_cn":"?????,??????,?????"
}
}
)json";
ImVec4 ParseHexColor(const char* rrggbbaa) {
unsigned int rgba = 0u;
sscanf_s(rrggbbaa, "%x", &rgba);
const float r = static_cast<float>((rgba >> 24) & 0xFF) / 255.0f;
const float g = static_cast<float>((rgba >> 16) & 0xFF) / 255.0f;
const float b = static_cast<float>((rgba >> 8) & 0xFF) / 255.0f;
const float a = static_cast<float>(rgba & 0xFF) / 255.0f;
return ImVec4(r, g, b, a);
}
void ApplyThemePalette(const std::array<const char*, 11>& colors) {
ImGuiStyle& style = ImGui::GetStyle();
style.Colors[ImGuiCol_Text] = ParseHexColor(colors[0]);
style.Colors[ImGuiCol_WindowBg] = ParseHexColor(colors[1]);
style.Colors[ImGuiCol_ChildBg] = ParseHexColor(colors[2]);
style.Colors[ImGuiCol_PopupBg] = ParseHexColor(colors[3]);
style.Colors[ImGuiCol_Border] = ParseHexColor(colors[4]);
style.Colors[ImGuiCol_FrameBg] = ParseHexColor(colors[5]);
style.Colors[ImGuiCol_FrameBgHovered] = ParseHexColor(colors[6]);
style.Colors[ImGuiCol_FrameBgActive] = ParseHexColor(colors[7]);
style.Colors[ImGuiCol_Button] = ParseHexColor(colors[8]);
style.Colors[ImGuiCol_ButtonHovered] = ParseHexColor(colors[9]);
style.Colors[ImGuiCol_ButtonActive] = ParseHexColor(colors[10]);
}
uintptr_t FindGameAssemblyBaseNow() {
HMODULE h = GetModuleHandleW(L"GameAssembly.dll");
if (!h) {
h = GetModuleHandleW(L"gameassembly.dll");
}
if (h) {
return reinterpret_cast<uintptr_t>(h);
}
HMODULE modules[2048] = {};
DWORD needed = 0;
if (!K32EnumProcessModules(GetCurrentProcess(), modules, sizeof(modules), &needed)) {
return 0;
}
const size_t count = needed / sizeof(HMODULE);
for (size_t i = 0; i < count; ++i) {
wchar_t path[MAX_PATH] = {};
if (K32GetModuleFileNameExW(GetCurrentProcess(), modules[i], path, MAX_PATH) == 0) {
continue;
}
std::wstring name = path;
std::transform(name.begin(), name.end(), name.begin(), [](wchar_t c) {
return static_cast<wchar_t>(::towlower(c));
});
if (name.find(L"gameassembly.dll") != std::wstring::npos) {
return reinterpret_cast<uintptr_t>(modules[i]);
}
}
return 0;
}
void ResolveIl2CppThreadApisOnce() {
HMODULE ga = GetModuleHandleW(L"GameAssembly.dll");
if (!ga) {
ga = GetModuleHandleW(L"gameassembly.dll");
}
if (!ga) {
return;
}
g_il2cpp_domain_get_fn =
reinterpret_cast<void* (*)()>(GetProcAddress(ga, "il2cpp_domain_get"));
g_il2cpp_thread_attach_fn =
reinterpret_cast<void* (*)(void*)>(GetProcAddress(ga, "il2cpp_thread_attach"));
g_il2cpp_thread_current_fn =
reinterpret_cast<void* (*)()>(GetProcAddress(ga, "il2cpp_thread_current"));
g_il2cpp_thread_detach_fn =
reinterpret_cast<void (*)(void*)>(GetProcAddress(ga, "il2cpp_thread_detach"));
}
} // namespace
const std::array<Il2cppSlotDefinition, 71> kIl2cppSlotMap = {{
{0x1C48, 0x09DC0120, "GameObject.GetComponentByIndex"},
{0x1C50, 0x0018CEC0, "il2cpp_domain_get_assemblies_A"},
{0x1C58, 0x0B390700, "Component.GetComponent<Transform>"},
{0x1C60, 0x0B3A2D20, "Transform.get_localScale"},
{0x1C68, 0x0B3A2CB0, "Transform.get_rotation"},
{0x1C70, 0x0018AD80, "il2cpp_object_get_class"},
{0x1C78, 0x00007480, "il2cpp_class_get_method_from_name"},
{0x1C80, 0x0B3A1AE0, "Transform.get_localPosition"},
{0x1C88, 0x0B3A1A70, "Transform.get_position"},
{0x1C90, 0x0B3A51D0, "Transform.set_localPosition"},
{0x1C98, 0x0B398830, "GameObject.SetActive"},
{0x1CA0, 0x0B3911F0, "GameObject.get_transform"},
{0x1CA8, 0x0B3A59D0, "Transform.set_rotation"},
{0x1CB0, 0x0B391D90, "GameObject.get_activeInHierarchy"},
{0x1CB8, 0x0B3B2410, "GameObject.get_tag"},
{0x1CC0, 0x0B3B2120, "GameObject.get_name"},
{0x1CC8, 0x0B3B40B0, "GameObject.get_layer"},
{0x1CD0, 0x0B397980, "GameObject.get_scene"},
{0x1CD8, 0x02F3DA80, "Game_SkillBuff_A"},
{0x1CE0, 0x00F27E10, "GetEntityName"},
{0x1CE8, 0x0B329000, "Component.GetComponents"},
{0x1CF0, 0x0B329D80, "Component.GetComponentsInChildren"},
{0x1CF8, 0x03E9BB50, "Game_SkillSlot_A"},
{0x1D00, 0x0B383F60, "Object.get_name"},
{0x1D08, 0x0B33CAB0, "Component.GetType_A"},
{0x1D10, 0x0B33CB00, "Component.GetType_B"},
{0x1D18, 0x039D1AA0, "Game_SkillSlot_B"},
{0x1D20, 0x026DE9F0, "Game_Combat_HitA"},
{0x1D28, 0x0478E860, "GetEntityManager"},
{0x1D30, 0x025E2D40, "Game_Combat_HitB"},
{0x1D38, 0x087C2CD0, "Game_Movement_PhysA"},
{0x1D40, 0x03419AF0, "Game_Movement_PhysB"},
{0x1D48, 0x0B322FB0, "Scene.get_name / Application.set_targetFrameRate ref"},
{0x1D50, 0x0B396A20, "SceneManager.GetActiveScene"},
{0x1D58, 0x0B397700, "CinemachineBrain.get_ActiveVirtualCamera"},
{0x1D60, 0x03766810, "Game_EntityState_A"},
{0x1D68, 0x0B3A2A60, "Transform.set_localScale"},
{0x1D70, 0x0B326660, "Rigidbody.get_velocity"},
{0x1D78, 0x0B3266B0, "Rigidbody.set_velocity"},
{0x1D80, 0x0B383200, "Object.Destroy"},
{0x1D88, 0x0B3A1A10, "Transform.set_position"},
{0x1D90, 0x0B3A2340, "Game_EntityState_B"},
{0x1D98, 0x0B421800, "Input.GetAxis(Mouse X)"},
{0x1DA0, 0x0B3A26D0, "Game_PlayerState_A"},
{0x1DA8, 0x0B3A1FB0, "Game_PlayerState_B"},
{0x1DB0, 0x0B32B030, "Game_PlayerHealthStat"},
{0x1DB8, 0x0B391240, "GameObject.SetActive (second ref)"},
{0x1DC0, 0x0B390440, "Camera.get_main"},
{0x1DC8, 0x0B390F80, "GameObject.FindObjectOfType"},
{0x1DD0, 0x0B3A19A0, "Transform.get_position (second ref)"},
{0x1DD8, 0x03F85430, "Game_SkillAction_C"},
{0x1DE0, 0x0B3A1D30, "Transform.set_rotation (second ref)"},
{0x1DE8, 0x0B3A1C90, "Camera.get_fieldOfView"},
{0x1DF0, 0x00E92CF0, "Game_EntityState_C"},
{0x1E00, 0x03DC14C0, "Game_SkillAction_D"},
{0x1E08, 0x03E0D2C0, "Game_SkillAction_E"},
{0x1E10, 0x03D9DF30, "Game_SkillAction_F"},
{0x1E18, 0x00E9E9F0, "Game_SkillAction_G"},
{0x1E20, 0x03EA53C0, "Game_SkillAction_H"},
{0x1E28, 0x0ACA0EC0, "TMPro.set_text"},
{0x1E30, 0x0B391D40, "UnityUI.Text.set_text"},
{0x1E38, 0x036AA8F0, "Game_SkillAction_I"},
{0x1E40, 0x03EC31C0, "Game_SkillAction_J"},
{0x1E48, 0x037F8CD0, "Game_EntityActor_D"},
{0x1E50, 0x036EA640, "Game_SkillAction_K"},
{0x1E58, 0x03EC30E0, "Game_SkillAction_L"},
{0x1E60, 0x037F2A40, "Game_EntityActor_E"},
{0x1E68, 0x037FD7E0, "Game_EntityActor_F"},
{0x1E70, 0x00D94C10, "il2cpp_domain_get"},
{0x1E78, 0x00D94DA0, "il2cpp_assembly_get_image"},
{0x1E80, 0x00D93A60, "il2cpp_domain_get_assemblies_B"},
}};
void Log(const char* fmt, ...) {
char buffer[2048] = {};
va_list args;
va_start(args, fmt);
std::vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
OutputDebugStringA(buffer);
OutputDebugStringA("\n");
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
if (h != nullptr && h != INVALID_HANDLE_VALUE) {
DWORD written = 0;
WriteConsoleA(h, buffer, static_cast<DWORD>(std::strlen(buffer)), &written, nullptr);
WriteConsoleA(h, "\n", 1, &written, nullptr);
}
}
std::wstring Utf8ToWide(const std::string& value) {
if (value.empty()) {
return {};
}
const int size = MultiByteToWideChar(65001, 0, value.c_str(), -1, nullptr, 0);
if (size <= 0) {
return {};
}
std::wstring result(static_cast<size_t>(size), L'\0');
MultiByteToWideChar(65001, 0, value.c_str(), -1, result.data(), size);
if (!result.empty() && result.back() == L'\0') {
result.pop_back();
}
return result;
}
std::string WideToUtf8(const std::wstring& value) {
if (value.empty()) {
return {};
}
const int size = WideCharToMultiByte(65001, 0, value.c_str(), -1, nullptr, 0, nullptr, nullptr);
if (size <= 0) {
return {};
}
std::string result(static_cast<size_t>(size), '\0');
WideCharToMultiByte(65001, 0, value.c_str(), -1, result.data(), size, nullptr, nullptr);
if (!result.empty() && result.back() == '\0') {
result.pop_back();
}
return result;
}
uintptr_t GetGameAssemblyBase() {
if (g_Runtime.game_assembly_base != 0) {
return g_Runtime.game_assembly_base;
}
g_Runtime.game_assembly_base = FindGameAssemblyBaseNow();
return g_Runtime.game_assembly_base;
}
bool WaitForGameAssembly(uint32_t timeout_ms, uint32_t poll_ms) {
const ULONGLONG start = GetTickCount64();
while (g_Runtime.running.load(std::memory_order_relaxed) || timeout_ms == 0) {
const uintptr_t base = FindGameAssemblyBaseNow();
if (base != 0) {
g_Runtime.game_assembly_base = base;
return true;
}
if (timeout_ms != 0) {
const ULONGLONG elapsed = GetTickCount64() - start;
if (elapsed >= timeout_ms) {
break;
}
}
Sleep(poll_ms);
}
return false;
}
void ResolveGABypassSlots() {
std::scoped_lock lock(g_il2cpp_mutex);
g_il2cpp_slots.clear();
const uintptr_t base = GetGameAssemblyBase();
if (base == 0) {
Log("IL2CPP Domain Not Found!");
return;
}
for (const auto& slot : kIl2cppSlotMap) {
g_il2cpp_slots[slot.slot_offset] = reinterpret_cast<void*>(base + slot.gameassembly_offset);
}
Log("IL2CPP Domain Found: 0x%p", reinterpret_cast<void*>(base));
}
void* GetIl2cppSlot(uintptr_t slot_offset) {
std::scoped_lock lock(g_il2cpp_mutex);
const auto it = g_il2cpp_slots.find(slot_offset);
if (it == g_il2cpp_slots.end()) {
return nullptr;
}
return it->second;
}
bool EnsureIl2CppThreadAttached() {
if (!g_il2cpp_thread_attach_fn || !g_il2cpp_domain_get_fn) {
ResolveIl2CppThreadApisOnce();
}
if (!g_il2cpp_thread_attach_fn || !g_il2cpp_domain_get_fn) {
g_Runtime.il2cpp_thread_attached.store(false, std::memory_order_relaxed);
return false;
}
void* domain = g_il2cpp_domain_cached;
if (!domain) {
domain = g_il2cpp_domain_get_fn();
g_il2cpp_domain_cached = domain;
}
if (!domain) {
g_Runtime.il2cpp_thread_attached.store(false, std::memory_order_relaxed);
return false;
}
if (g_il2cpp_thread_current_fn) {
void* current = g_il2cpp_thread_current_fn();
if (current) {
g_tls_il2cpp_thread = current;
g_tls_il2cpp_attached_by_us = false;
g_Runtime.il2cpp_thread_attached.store(true, std::memory_order_relaxed);
return true;
}
} else if (g_tls_il2cpp_thread) {
g_Runtime.il2cpp_thread_attached.store(true, std::memory_order_relaxed);
return true;
}
void* attached = g_il2cpp_thread_attach_fn(domain);
if (!attached) {
g_Runtime.il2cpp_thread_attached.store(false, std::memory_order_relaxed);
return false;
}
g_tls_il2cpp_thread = attached;
g_tls_il2cpp_attached_by_us = true;
g_Runtime.il2cpp_thread_attached.store(true, std::memory_order_relaxed);
return true;
}
bool WaitForIl2CppThreadAttach(uint32_t timeout_ms, uint32_t poll_ms) {
const ULONGLONG start = GetTickCount64();
for (;;) {
if (EnsureIl2CppThreadAttached()) {
return true;
}
if (timeout_ms != 0) {
const ULONGLONG elapsed = GetTickCount64() - start;
if (elapsed >= timeout_ms) {
return false;
}
}
Sleep(poll_ms);
}
}
void DetachIl2CppThreadIfAttached() {
if (!g_tls_il2cpp_thread || !g_tls_il2cpp_attached_by_us) {
return;
}
if (!g_il2cpp_thread_detach_fn) {
ResolveIl2CppThreadApisOnce();
}
if (g_il2cpp_thread_detach_fn) {
g_il2cpp_thread_detach_fn(g_tls_il2cpp_thread);
}
g_tls_il2cpp_thread = nullptr;
g_tls_il2cpp_attached_by_us = false;
g_Runtime.il2cpp_thread_attached.store(false, std::memory_order_relaxed);
}
void SetGameThreadContext(bool enabled) {
g_tls_game_thread_context = enabled;
if (enabled) {
const DWORD tid = GetCurrentThreadId();
DWORD known = g_Runtime.game_thread_id.load(std::memory_order_relaxed);
if (known == 0) {
g_Runtime.game_thread_id.store(tid, std::memory_order_relaxed);
}
}
}
bool IsGameThreadContext() {
if (g_tls_game_thread_context) {
return true;
}
const DWORD known = g_Runtime.game_thread_id.load(std::memory_order_relaxed);
return known != 0 && known == GetCurrentThreadId();
}
bool IsWindowsBuildAtLeast(uint32_t build_number) {
using RtlGetVersionFn = LONG(WINAPI*)(PRTL_OSVERSIONINFOW);
static std::atomic<uint32_t> cached_build{0};
uint32_t value = cached_build.load(std::memory_order_acquire);
if (value == 0) {
HMODULE ntdll = GetModuleHandleW(L"ntdll.dll");
if (!ntdll) {
ntdll = LoadLibraryW(L"ntdll.dll");
}
if (!ntdll) {
return false;
}
auto* rtl_get_version = reinterpret_cast<RtlGetVersionFn>(
GetProcAddress(ntdll, "RtlGetVersion"));
if (!rtl_get_version) {
return false;
}
RTL_OSVERSIONINFOW info{};
info.dwOSVersionInfoSize = sizeof(info);
if (rtl_get_version(&info) == 0) {
value = info.dwBuildNumber;
cached_build.store(value, std::memory_order_release);
}
}
return value >= build_number;
}
void ToggleConsole(bool enabled) {
if (enabled) {
if (GetConsoleWindow() == nullptr) {
AllocConsole();
FILE* stream = nullptr;
freopen_s(&stream, "CONOUT$", "w", stdout);
freopen_s(&stream, "CONOUT$", "w", stderr);
SetConsoleOutputCP(65001);
SetConsoleTitleA("il2cpp Game Console");
}
} else {
if (GetConsoleWindow() != nullptr) {
ShowWindow(GetConsoleWindow(), SW_HIDE);
}
}
}
bool ScanLoadedModulesForInjectionMarkers() {
static const std::wstring kMarkerA = L"testyip";
static const std::wstring kMarkerB = L"gatmp.tmp";
static const std::wstring kMarkerC = L"ef-cheeto.dll";
HMODULE modules[2048] = {};
DWORD needed = 0;
if (!K32EnumProcessModules(GetCurrentProcess(), modules, sizeof(modules), &needed)) {
return false;
}
const size_t count = needed / sizeof(HMODULE);
for (size_t i = 0; i < count; ++i) {
wchar_t path[MAX_PATH] = {};
if (K32GetModuleFileNameExW(GetCurrentProcess(), modules[i], path, MAX_PATH) == 0) {
continue;
}
std::wstring lower = path;
std::transform(lower.begin(), lower.end(), lower.begin(), [](wchar_t c) {
return static_cast<wchar_t>(::towlower(c));
});
if (lower.find(kMarkerA) != std::wstring::npos ||
lower.find(kMarkerB) != std::wstring::npos ||
lower.find(kMarkerC) != std::wstring::npos) {
return true;
}
}
return false;
}
void InitializeI18N() {
std::scoped_lock lock(g_i18n_mutex);
if (g_i18n_initialized) {
return;
}
const nlohmann::json parsed = nlohmann::json::parse(kI18nBlob, nullptr, false);
if (!parsed.is_object()) {
g_i18n_initialized = true;
return;
}
for (auto it = parsed.begin(); it != parsed.end(); ++it) {
const std::string key = it.key();
const nlohmann::json& item = it.value();
if (!item.is_object()) {
continue;
}
g_en[key] = item.value("en", key);
g_zh_cn[key] = item.value("zh_cn", key);
}
g_i18n_initialized = true;
}
const char* Tr(const char* key) {
InitializeI18N();
std::scoped_lock lock(g_i18n_mutex);
const std::string k = key ? key : "";
const Language lang = g_Runtime.language.load(std::memory_order_relaxed);
const auto fallback_it = g_en.find(k);
if (lang == Language::ZhCN) {
const auto it = g_zh_cn.find(k);
if (it != g_zh_cn.end()) {
return it->second.c_str();
}
}
if (fallback_it != g_en.end()) {
return fallback_it->second.c_str();
}
return key ? key : "";
}
void SetLanguage(Language language) {
g_Runtime.language.store(language, std::memory_order_relaxed);
}
void ApplyThemeDefault() {
ImGui::StyleColorsDark();
}
void ApplyThemeEvangelionUnit01() {
static constexpr std::array<const char*, 11> kColors = {{
"FFFFFFFF", "7857A7FF", "19171CFF", "2A282EFF", "2A282EE6", "4DE6A5FF",
"00000000", "4A455240", "A38CCF80", "7857A780", "4A4552FF"}};
ApplyThemePalette(kColors);
}
void ApplyThemeEvangelionUnit02() {
static constexpr std::array<const char*, 11> kColors = {{
"FFFFFFFF", "D9293BFF", "1E1E1EFF", "3A3A3AFF", "3A3A3AE6", "F7B302FF",
"00000000", "4A455240", "F7465880", "D9293B80", "4A4552FF"}};
ApplyThemePalette(kColors);
}
void ApplyThemeMatrix() {
static constexpr std::array<const char*, 11> kColors = {{
"00FF41FF", "00AA2CFF", "000000FF", "0A0A0AFF", "0A0A0AE6", "004400FF",
"00000000", "00440040", "00881EFF", "00FF4180", "00FF4180"}};
ApplyThemePalette(kColors);
}
bool IsKeyPressedOnce(int vk) {
return (GetAsyncKeyState(vk) & 0x1) != 0;
}