mirror of
https://github.com/YimMenu/YimMenuV2.git
synced 2026-10-10 03:43:15 +08:00
Initial port
This commit is contained in:
@@ -19,7 +19,6 @@ AlwaysBreakTemplateDeclarations: 'Yes'
|
||||
BinPackArguments: 'false'
|
||||
BinPackParameters: 'true'
|
||||
BreakBeforeBinaryOperators: NonAssignment
|
||||
BreakBeforeBraces: Allman
|
||||
BreakBeforeTernaryOperators: 'false'
|
||||
BreakConstructorInitializers: AfterColon
|
||||
BreakInheritanceList: AfterColon
|
||||
|
||||
+9
-2
@@ -7,7 +7,6 @@ include(cmake/async-logger.cmake)
|
||||
include(cmake/imgui.cmake)
|
||||
include(cmake/json.cmake)
|
||||
include(cmake/minhook.cmake)
|
||||
include(cmake/gtav-classes.cmake)
|
||||
|
||||
# source
|
||||
set(SRC_DIR "${PROJECT_SOURCE_DIR}/src")
|
||||
@@ -21,13 +20,21 @@ set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 23)
|
||||
|
||||
target_precompile_headers(${PROJECT_NAME} PUBLIC "${SRC_DIR}/common.hpp")
|
||||
|
||||
add_compile_definitions(
|
||||
_CRT_SECURE_NO_WARNINGS
|
||||
WIN32_LEAN_AND_MEAN
|
||||
NOMINMAX
|
||||
IMGUI_DEFINE_MATH_OPERATORS
|
||||
)
|
||||
|
||||
message(STATUS "Setting up include directories.")
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||
"${SRC_DIR}"
|
||||
"${imgui_SOURCE_DIR}"
|
||||
"${minhook_SOURCE_DIR}/include"
|
||||
"${minhook_SOURCE_DIR}/src/hde"
|
||||
"${gtav_classes_SOURCE_DIR}"
|
||||
)
|
||||
|
||||
message(STATUS "Setting up linked libraries")
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE AsyncLogger imgui minhook nlohmann_json::nlohmann_json)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE AsyncLogger imgui minhook nlohmann_json::nlohmann_json dbghelp)
|
||||
+13
-14
@@ -1,18 +1,7 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "x64-Debug",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "Debug",
|
||||
"inheritEnvironments": [ "msvc_x64_x64" ],
|
||||
"buildRoot": "${projectDir}\\out\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\out\\install\\${name}",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": ""
|
||||
},
|
||||
{
|
||||
"name": "x64-Release",
|
||||
"name": "x64-Release-MSVC",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "RelWithDebInfo",
|
||||
"buildRoot": "${projectDir}\\out\\build\\${name}",
|
||||
@@ -20,8 +9,18 @@
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": "",
|
||||
"inheritEnvironments": [ "msvc_x64_x64" ],
|
||||
"variables": []
|
||||
"inheritEnvironments": [ "msvc_x64_x64" ]
|
||||
},
|
||||
{
|
||||
"name": "x64-Release-Clang",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "RelWithDebInfo",
|
||||
"buildRoot": "${projectDir}\\out\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\out\\install\\${name}",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": "",
|
||||
"inheritEnvironments": [ "clang_cl_x64_x64" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
# Template Hell Base
|
||||
# YimMenuV2
|
||||
|
||||
This base is honestly a joke and a learning opporunity for myself to learn some more stuff about C++20. I went through the basics of what makes a mod menu base and tried to template them to hell and back.
|
||||
Experimental menu for GTA 5: Enhanced
|
||||
|
||||
## Structure
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
include(FetchContent)
|
||||
|
||||
add_compile_definitions(CXX_FORMAT_SUPPORT)
|
||||
message(STATUS "Setting up AsyncLogger")
|
||||
FetchContent_Declare(
|
||||
AsyncLogger
|
||||
GIT_REPOSITORY https://github.com/Yimura/AsyncLogger.git
|
||||
GIT_TAG v0.0.6
|
||||
GIT_TAG 6fcfd90b3f4ca4dae09c4a96e9a506e6aea06472
|
||||
GIT_PROGRESS TRUE
|
||||
)
|
||||
FetchContent_MakeAvailable(AsyncLogger)
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
gtav_classes
|
||||
GIT_REPOSITORY https://github.com/maybegreat48/GTAV-Classes.git
|
||||
GIT_TAG fb7ec7fe0a8ac08823ab7a0a2e68c08b027fce2f
|
||||
GIT_PROGRESS TRUE
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
)
|
||||
message("GTAV-Classes")
|
||||
if(NOT gtav_classes_POPULATED)
|
||||
FetchContent_Populate(gtav_classes)
|
||||
endif()
|
||||
+3
-3
@@ -5,8 +5,8 @@ message(STATUS "Setting up ${LIB_NAME}")
|
||||
|
||||
FetchContent_Declare(
|
||||
${LIB_NAME}
|
||||
GIT_REPOSITORY https://github.com/Spyral-Org/imgui.git
|
||||
GIT_TAG 44d98bfd6d12caee43bea49fda71c8313c3e57e7
|
||||
GIT_REPOSITORY https://github.com/ocornut/imgui
|
||||
GIT_TAG 6e30c4210181aebb6137f6c10fdfc572ea1202f2
|
||||
GIT_PROGRESS TRUE
|
||||
)
|
||||
|
||||
@@ -19,7 +19,7 @@ if(NOT imgui_POPULATED)
|
||||
"${imgui_SOURCE_DIR}/*.cpp"
|
||||
"${imgui_SOURCE_DIR}/*.h"
|
||||
"${imgui_SOURCE_DIR}/backends/imgui_impl_win32.*"
|
||||
"${imgui_SOURCE_DIR}/backends/imgui_impl_dx11.*"
|
||||
"${imgui_SOURCE_DIR}/backends/imgui_impl_dx12.*"
|
||||
"${imgui_SOURCE_DIR}/misc/cpp/imgui_stdlib.*"
|
||||
)
|
||||
|
||||
|
||||
@@ -18,15 +18,20 @@
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using namespace al;
|
||||
#include "core/logger/LogHelper.hpp"
|
||||
|
||||
#undef Yield
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
extern std::atomic<bool> g_Running;
|
||||
extern HINSTANCE g_DllInstance;
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
#include "FiberPool.hpp"
|
||||
|
||||
#include "ScriptMgr.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
void FiberPool::InitImpl(int num_fibers)
|
||||
{
|
||||
for (std::size_t i = 0; i < num_fibers; ++i)
|
||||
{
|
||||
ScriptMgr::AddScript(std::make_unique<Script>(&ScriptEntry));
|
||||
}
|
||||
}
|
||||
|
||||
void FiberPool::DestroyImpl()
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
|
||||
while (!m_Jobs.empty())
|
||||
m_Jobs.pop();
|
||||
}
|
||||
|
||||
void FiberPool::PushImpl(std::function<void()> callback)
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
m_Jobs.push(std::move(callback));
|
||||
}
|
||||
|
||||
void FiberPool::Tick()
|
||||
{
|
||||
std::unique_lock lock(m_Mutex);
|
||||
|
||||
if (!m_Jobs.empty())
|
||||
{
|
||||
auto job = std::move(m_Jobs.top());
|
||||
m_Jobs.pop();
|
||||
lock.unlock();
|
||||
|
||||
std::invoke(std::move(job));
|
||||
}
|
||||
}
|
||||
|
||||
void FiberPool::ScriptEntry()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
FiberPool::GetInstance().Tick();
|
||||
ScriptMgr::Yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class FiberPool
|
||||
{
|
||||
FiberPool() = default;
|
||||
|
||||
public:
|
||||
FiberPool(const FiberPool&) = delete;
|
||||
FiberPool(FiberPool&&) noexcept = delete;
|
||||
FiberPool& operator=(const FiberPool&) = delete;
|
||||
FiberPool& operator=(FiberPool&&) noexcept = delete;
|
||||
virtual ~FiberPool() = default;
|
||||
|
||||
static void Init(int num_fibers)
|
||||
{
|
||||
GetInstance().InitImpl(num_fibers);
|
||||
}
|
||||
|
||||
static void Destroy()
|
||||
{
|
||||
GetInstance().DestroyImpl();
|
||||
}
|
||||
|
||||
static void Push(std::function<void()> callback)
|
||||
{
|
||||
GetInstance().PushImpl(callback);
|
||||
}
|
||||
|
||||
private:
|
||||
std::recursive_mutex m_Mutex{};
|
||||
std::stack<std::function<void()>> m_Jobs{};
|
||||
|
||||
void InitImpl(int num_fibers);
|
||||
void DestroyImpl();
|
||||
void PushImpl(std::function<void()> callback);
|
||||
void Tick();
|
||||
static void ScriptEntry();
|
||||
|
||||
static FiberPool& GetInstance()
|
||||
{
|
||||
static FiberPool i{};
|
||||
return i;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#include "PatternCache.hpp"
|
||||
|
||||
#include "core/filemgr/FileMgr.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
std::optional<int> PatternCache::GetCachedOffsetImpl(PatternHash hash)
|
||||
{
|
||||
if (auto it = m_Data.find(hash.GetHash()); it != m_Data.end())
|
||||
return it->second;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void PatternCache::UpdateCachedOffsetImpl(PatternHash hash, int offset)
|
||||
{
|
||||
m_Data[hash.GetHash()] = offset;
|
||||
}
|
||||
|
||||
void PatternCache::InitImpl()
|
||||
{
|
||||
auto file = FileMgr::GetProjectFile("./pattern_cache.bin");
|
||||
if (file.Exists())
|
||||
{
|
||||
std::ifstream stream(file.Path(), std::ios_base::binary);
|
||||
while (!stream.eof())
|
||||
{
|
||||
std::uint64_t hash;
|
||||
int offset;
|
||||
|
||||
stream.read(reinterpret_cast<char*>(&hash), sizeof(hash));
|
||||
stream.read(reinterpret_cast<char*>(&offset), sizeof(offset));
|
||||
|
||||
m_Data.emplace(hash, offset);
|
||||
}
|
||||
}
|
||||
|
||||
m_Initialized = true;
|
||||
}
|
||||
|
||||
void PatternCache::UpdateImpl()
|
||||
{
|
||||
auto file = FileMgr::GetProjectFile("./pattern_cache.bin");
|
||||
std::ofstream stream(file.Path(), std::ios_base::binary);
|
||||
|
||||
for (auto& [h, offset] : m_Data)
|
||||
{
|
||||
auto hash = h;
|
||||
stream.write(reinterpret_cast<char*>(&hash), sizeof(hash));
|
||||
stream.write(reinterpret_cast<char*>(&offset), sizeof(offset));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
#include "core/memory/PatternHash.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class PatternCache
|
||||
{
|
||||
bool m_Initialized;
|
||||
std::unordered_map<std::uint64_t, int> m_Data;
|
||||
|
||||
public:
|
||||
PatternCache() :
|
||||
m_Initialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
static void Init()
|
||||
{
|
||||
GetInstance().InitImpl();
|
||||
}
|
||||
|
||||
static void Update()
|
||||
{
|
||||
GetInstance().UpdateImpl();
|
||||
}
|
||||
|
||||
static std::optional<int> GetCachedOffset(PatternHash hash)
|
||||
{
|
||||
return GetInstance().GetCachedOffsetImpl(hash);
|
||||
}
|
||||
|
||||
static void UpdateCachedOffset(PatternHash hash, int offset)
|
||||
{
|
||||
GetInstance().UpdateCachedOffsetImpl(hash, offset);
|
||||
}
|
||||
|
||||
static bool IsInitialized()
|
||||
{
|
||||
return GetInstance().m_Initialized;
|
||||
}
|
||||
|
||||
private:
|
||||
static PatternCache& GetInstance()
|
||||
{
|
||||
static PatternCache Instance;
|
||||
return Instance;
|
||||
}
|
||||
|
||||
void InitImpl();
|
||||
void UpdateImpl();
|
||||
std::optional<int> GetCachedOffsetImpl(PatternHash hash);
|
||||
void UpdateCachedOffsetImpl(PatternHash hash, int offset);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
#include "ScriptMgr.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
Script::Script(std::function<void()> callback) :
|
||||
m_Callback(callback),
|
||||
m_Done(false),
|
||||
m_ChildFiber(0),
|
||||
m_MainFiber(0),
|
||||
m_WakeTime(std::nullopt)
|
||||
{
|
||||
m_ChildFiber = CreateFiber(
|
||||
0,
|
||||
[](void* param) {
|
||||
auto this_script = static_cast<Script*>(param);
|
||||
this_script->m_Callback();
|
||||
this_script->m_Done = true;
|
||||
SwitchToFiber(this_script->m_MainFiber);
|
||||
},
|
||||
this);
|
||||
}
|
||||
|
||||
Script::~Script()
|
||||
{
|
||||
if (m_ChildFiber)
|
||||
DeleteFiber(m_ChildFiber);
|
||||
}
|
||||
|
||||
void Script::Tick()
|
||||
{
|
||||
m_MainFiber = GetCurrentFiber();
|
||||
if ((!m_WakeTime.has_value() || m_WakeTime.value() <= std::chrono::high_resolution_clock::now()) && !m_Done)
|
||||
{
|
||||
SwitchToFiber(m_ChildFiber);
|
||||
}
|
||||
}
|
||||
|
||||
void Script::Yield(std::optional<std::chrono::high_resolution_clock::duration> time)
|
||||
{
|
||||
if (time.has_value())
|
||||
m_WakeTime = std::chrono::high_resolution_clock::now() + time.value();
|
||||
else
|
||||
m_WakeTime = std::nullopt;
|
||||
|
||||
SwitchToFiber(m_MainFiber);
|
||||
}
|
||||
|
||||
void ScriptMgr::InitImpl()
|
||||
{
|
||||
// none required
|
||||
}
|
||||
|
||||
void ScriptMgr::DestroyImpl()
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
m_Scripts.clear();
|
||||
}
|
||||
|
||||
void ScriptMgr::TickImpl()
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
static bool ensure_main_fiber = (ConvertThreadToFiber(nullptr), true);
|
||||
|
||||
for (const auto& script : m_Scripts)
|
||||
script->Tick();
|
||||
}
|
||||
|
||||
void ScriptMgr::YieldImpl(std::optional<std::chrono::high_resolution_clock::duration> time)
|
||||
{
|
||||
if (auto script = static_cast<Script*>(GetFiberData()))
|
||||
script->Yield(time);
|
||||
}
|
||||
|
||||
void ScriptMgr::AddScriptImpl(std::unique_ptr<Script> script)
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
m_Scripts.push_back(std::move(script));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class Script
|
||||
{
|
||||
public:
|
||||
std::function<void()> m_Callback;
|
||||
bool m_Done;
|
||||
HANDLE m_ChildFiber;
|
||||
HANDLE m_MainFiber;
|
||||
std::optional<std::chrono::high_resolution_clock::time_point> m_WakeTime;
|
||||
|
||||
public:
|
||||
explicit Script(std::function<void()> callback);
|
||||
~Script();
|
||||
|
||||
void Tick();
|
||||
void Yield(std::optional<std::chrono::high_resolution_clock::duration> time = std::nullopt);
|
||||
};
|
||||
|
||||
class ScriptMgr
|
||||
{
|
||||
private:
|
||||
ScriptMgr() {};
|
||||
|
||||
public:
|
||||
virtual ~ScriptMgr()
|
||||
{
|
||||
DestroyImpl();
|
||||
};
|
||||
|
||||
ScriptMgr(const ScriptMgr&) = delete;
|
||||
ScriptMgr(ScriptMgr&&) noexcept = delete;
|
||||
ScriptMgr& operator=(const ScriptMgr&) = delete;
|
||||
ScriptMgr& operator=(ScriptMgr&&) noexcept = delete;
|
||||
|
||||
static void Init()
|
||||
{
|
||||
GetInstance().InitImpl();
|
||||
}
|
||||
|
||||
static void Destroy()
|
||||
{
|
||||
GetInstance().DestroyImpl();
|
||||
}
|
||||
|
||||
static void Tick()
|
||||
{
|
||||
GetInstance().TickImpl();
|
||||
}
|
||||
|
||||
static void Yield(std::optional<std::chrono::high_resolution_clock::duration> time = std::nullopt)
|
||||
{
|
||||
GetInstance().YieldImpl(time);
|
||||
}
|
||||
|
||||
static void AddScript(std::unique_ptr<Script> script)
|
||||
{
|
||||
GetInstance().AddScriptImpl(std::move(script));
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex m_Mutex;
|
||||
std::vector<std::unique_ptr<Script>> m_Scripts;
|
||||
|
||||
void InitImpl();
|
||||
void DestroyImpl();
|
||||
void TickImpl();
|
||||
void YieldImpl(std::optional<std::chrono::high_resolution_clock::duration> time = std::nullopt);
|
||||
void AddScriptImpl(std::unique_ptr<Script> script);
|
||||
|
||||
static ScriptMgr& GetInstance()
|
||||
{
|
||||
static ScriptMgr i{};
|
||||
return i;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#include "BoolCommand.hpp"
|
||||
#include "core/backend/FiberPool.hpp"
|
||||
#include "Commands.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
BoolCommand::BoolCommand(std::string name, std::string label, std::string description, bool def_value) :
|
||||
Command(name, label, description, 0),
|
||||
m_State(def_value)
|
||||
{
|
||||
Commands::AddBoolCommand(this);
|
||||
}
|
||||
|
||||
void BoolCommand::OnCall()
|
||||
{
|
||||
SetState(!m_State);
|
||||
}
|
||||
|
||||
void BoolCommand::SaveState(nlohmann::json& value)
|
||||
{
|
||||
value = m_State;
|
||||
}
|
||||
|
||||
void BoolCommand::LoadState(nlohmann::json& value)
|
||||
{
|
||||
m_State = value;
|
||||
}
|
||||
|
||||
bool BoolCommand::GetState()
|
||||
{
|
||||
return m_State;
|
||||
}
|
||||
|
||||
void BoolCommand::SetState(bool state)
|
||||
{
|
||||
if (state && !m_State)
|
||||
FiberPool::Push([this] {
|
||||
OnEnable();
|
||||
});
|
||||
else if (!state && m_State)
|
||||
FiberPool::Push([this] {
|
||||
OnDisable();
|
||||
});
|
||||
|
||||
m_State = state;
|
||||
MarkDirty();
|
||||
}
|
||||
|
||||
void BoolCommand::Initialize()
|
||||
{
|
||||
OnEnable();
|
||||
}
|
||||
|
||||
void BoolCommand::Shutdown()
|
||||
{
|
||||
OnDisable();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include "Command.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class BoolCommand : public Command
|
||||
{
|
||||
protected:
|
||||
virtual void OnEnable() {};
|
||||
virtual void OnDisable() {};
|
||||
virtual void OnCall() override;
|
||||
virtual void SaveState(nlohmann::json& value) override;
|
||||
virtual void LoadState(nlohmann::json& value) override;
|
||||
|
||||
bool m_State = false;
|
||||
public:
|
||||
BoolCommand(std::string name, std::string label, std::string description, bool def_value = false);
|
||||
bool GetState();
|
||||
void SetState(bool state);
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
#include "ColorCommand.hpp"
|
||||
#include "core/backend/FiberPool.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
bool AreColorsEqual(ImVec4 col1, ImVec4 col2)
|
||||
{
|
||||
return col1.x == col2.x && col1.y == col2.y && col1.z == col2.z && col1.w == col2.w;
|
||||
}
|
||||
}
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
void ColorCommand::OnCall()
|
||||
{
|
||||
}
|
||||
|
||||
void ColorCommand::SaveState(nlohmann::json& value)
|
||||
{
|
||||
value = nlohmann::json::object();
|
||||
value["r"] = m_State.x;
|
||||
value["g"] = m_State.y;
|
||||
value["b"] = m_State.z;
|
||||
value["a"] = m_State.w;
|
||||
}
|
||||
|
||||
void ColorCommand::LoadState(nlohmann::json& value)
|
||||
{
|
||||
if (value.is_object())
|
||||
{
|
||||
m_State.x = value["r"];
|
||||
m_State.y = value["g"];
|
||||
m_State.z = value["b"];
|
||||
m_State.w = value["a"];
|
||||
}
|
||||
else if (value.is_array())
|
||||
{
|
||||
auto arr = value.get<std::array<float, 4>>();
|
||||
m_State.x = arr[0];
|
||||
m_State.y = arr[1];
|
||||
m_State.z = arr[2];
|
||||
m_State.w = arr[3];
|
||||
}
|
||||
}
|
||||
|
||||
ColorCommand::ColorCommand(std::string name, std::string label, std::string description, ImVec4 color) :
|
||||
Command(name, label, description, 0),
|
||||
m_State(color)
|
||||
{
|
||||
}
|
||||
|
||||
ImVec4 ColorCommand::GetState()
|
||||
{
|
||||
return m_State;
|
||||
}
|
||||
|
||||
void ColorCommand::SetState(ImVec4 state)
|
||||
{
|
||||
if (!AreColorsEqual(m_State, state))
|
||||
{
|
||||
FiberPool::Push([this] {
|
||||
OnChange();
|
||||
});
|
||||
m_State = state;
|
||||
MarkDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "Command.hpp"
|
||||
#include "imgui.h"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class ColorCommand : public Command
|
||||
{
|
||||
protected:
|
||||
virtual void OnChange(){};
|
||||
virtual void OnCall() override;
|
||||
virtual void SaveState(nlohmann::json& value) override;
|
||||
virtual void LoadState(nlohmann::json& value) override;
|
||||
|
||||
ImVec4 m_State = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
public:
|
||||
ColorCommand(std::string name, std::string label, std::string description, ImVec4 color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImVec4 GetState();
|
||||
void SetState(ImVec4 state);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#include "core/util/Joaat.hpp"
|
||||
#include "Command.hpp"
|
||||
#include "Commands.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
Command::Command(std::string name, std::string label, std::string description, int num_args) :
|
||||
m_Name(name),
|
||||
m_Label(label),
|
||||
m_Description(description),
|
||||
m_NumArgs(num_args),
|
||||
m_Hash(Joaat(name))
|
||||
{
|
||||
Commands::AddCommand(this);
|
||||
}
|
||||
|
||||
void Command::Call()
|
||||
{
|
||||
OnCall();
|
||||
}
|
||||
|
||||
void Command::MarkDirty()
|
||||
{
|
||||
Commands::MarkDirty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
#include "core/util/Joaat.hpp"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class Command
|
||||
{
|
||||
private:
|
||||
std::string m_Name;
|
||||
std::string m_Label;
|
||||
std::string m_Description;
|
||||
joaat_t m_Hash;
|
||||
|
||||
int m_NumArgs = 0; // TODO: currently unused
|
||||
|
||||
protected:
|
||||
virtual void OnCall() = 0;
|
||||
void MarkDirty();
|
||||
|
||||
public:
|
||||
Command(std::string name, std::string label, std::string description, int num_args = 0);
|
||||
void Call();
|
||||
|
||||
virtual void SaveState(nlohmann::json& value){};
|
||||
virtual void LoadState(nlohmann::json& value){};
|
||||
|
||||
const std::string& GetName()
|
||||
{
|
||||
return m_Name;
|
||||
}
|
||||
|
||||
const std::string& GetLabel()
|
||||
{
|
||||
return m_Label;
|
||||
}
|
||||
|
||||
const std::string& GetDescription()
|
||||
{
|
||||
return m_Description;
|
||||
}
|
||||
|
||||
joaat_t GetHash()
|
||||
{
|
||||
return m_Hash;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
#include "Commands.hpp"
|
||||
#include "Command.hpp"
|
||||
#include "LoopedCommand.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
Commands::Commands() :
|
||||
IStateSerializer("commands")
|
||||
{
|
||||
}
|
||||
|
||||
void Commands::RunScript()
|
||||
{
|
||||
Commands::EnableBoolCommands();
|
||||
while (g_Running)
|
||||
{
|
||||
Commands::RunLoopedCommands();
|
||||
}
|
||||
}
|
||||
|
||||
void Commands::AddCommandImpl(Command* command)
|
||||
{
|
||||
m_Commands.insert({command->GetHash(), command});
|
||||
}
|
||||
|
||||
void Commands::AddBoolCommandImpl(BoolCommand* command)
|
||||
{
|
||||
m_BoolCommands.push_back(command);
|
||||
}
|
||||
|
||||
void Commands::AddLoopedCommandImpl(LoopedCommand* command)
|
||||
{
|
||||
m_LoopedCommands.push_back(command);
|
||||
}
|
||||
|
||||
void Commands::EnableBoolCommandsImpl()
|
||||
{
|
||||
for (auto& command : m_BoolCommands)
|
||||
if (command->GetState())
|
||||
command->Initialize();
|
||||
}
|
||||
|
||||
void Commands::RunLoopedCommandsImpl()
|
||||
{
|
||||
for (auto& command : m_LoopedCommands)
|
||||
if (command->GetState())
|
||||
command->Tick();
|
||||
}
|
||||
|
||||
Command* Commands::GetCommandImpl(joaat_t hash)
|
||||
{
|
||||
if (auto it = m_Commands.find(hash); it != m_Commands.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Commands::SaveStateImpl(nlohmann::json& state)
|
||||
{
|
||||
for (auto& command : m_Commands)
|
||||
{
|
||||
if (!state.contains(command.second->GetName()))
|
||||
state[command.second->GetName()] = nlohmann::json::object();
|
||||
|
||||
command.second->SaveState(state[command.second->GetName()]);
|
||||
}
|
||||
}
|
||||
|
||||
void Commands::LoadStateImpl(nlohmann::json& state)
|
||||
{
|
||||
for (auto& command : m_Commands)
|
||||
{
|
||||
if (state.contains(command.second->GetName()))
|
||||
command.second->LoadState(state[command.second->GetName()]);
|
||||
}
|
||||
}
|
||||
|
||||
void Commands::ShutdownImpl()
|
||||
{
|
||||
for (auto& command : m_LoopedCommands)
|
||||
if (command->GetState())
|
||||
command->Shutdown();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
#include "core/util/Joaat.hpp"
|
||||
#include "core/settings/IStateSerializer.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class Command;
|
||||
class LoopedCommand;
|
||||
class BoolCommand;
|
||||
|
||||
class Commands :
|
||||
private IStateSerializer
|
||||
{
|
||||
private:
|
||||
std::unordered_map<joaat_t, Command*> m_Commands;
|
||||
std::vector<LoopedCommand*> m_LoopedCommands;
|
||||
std::vector<BoolCommand*> m_BoolCommands;
|
||||
Commands();
|
||||
|
||||
public:
|
||||
static void AddCommand(Command* command)
|
||||
{
|
||||
GetInstance().AddCommandImpl(command);
|
||||
}
|
||||
|
||||
static void AddBoolCommand(BoolCommand* command)
|
||||
{
|
||||
GetInstance().AddBoolCommandImpl(command);
|
||||
}
|
||||
|
||||
static void AddLoopedCommand(LoopedCommand* command)
|
||||
{
|
||||
GetInstance().AddLoopedCommandImpl(command);
|
||||
}
|
||||
|
||||
static void RunLoopedCommands()
|
||||
{
|
||||
GetInstance().RunLoopedCommandsImpl();
|
||||
}
|
||||
|
||||
static void EnableBoolCommands()
|
||||
{
|
||||
GetInstance().EnableBoolCommandsImpl();
|
||||
}
|
||||
|
||||
template<typename T = Command>
|
||||
static T* GetCommand(joaat_t hash)
|
||||
{
|
||||
return reinterpret_cast<T*>(GetInstance().GetCommandImpl(hash));
|
||||
}
|
||||
|
||||
static std::unordered_map<joaat_t, Command*>& GetCommands()
|
||||
{
|
||||
return GetInstance().m_Commands;
|
||||
};
|
||||
|
||||
static std::vector<LoopedCommand*> GetLoopedCommands()
|
||||
{
|
||||
return GetInstance().m_LoopedCommands;
|
||||
}
|
||||
|
||||
static void MarkDirty()
|
||||
{
|
||||
GetInstance().MarkStateDirty();
|
||||
}
|
||||
|
||||
static void Shutdown()
|
||||
{
|
||||
GetInstance().ShutdownImpl();
|
||||
}
|
||||
|
||||
static void RunScript();
|
||||
|
||||
private:
|
||||
void AddCommandImpl(Command* command);
|
||||
void AddBoolCommandImpl(BoolCommand* command);
|
||||
void AddLoopedCommandImpl(LoopedCommand* command);
|
||||
void EnableBoolCommandsImpl();
|
||||
void RunLoopedCommandsImpl();
|
||||
Command* GetCommandImpl(joaat_t hash);
|
||||
virtual void SaveStateImpl(nlohmann::json& state) override;
|
||||
virtual void LoadStateImpl(nlohmann::json& state) override;
|
||||
void ShutdownImpl();
|
||||
|
||||
static Commands& GetInstance()
|
||||
{
|
||||
static Commands instance{};
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#include "FloatCommand.hpp"
|
||||
#include "core/backend/FiberPool.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
void FloatCommand::OnCall()
|
||||
{
|
||||
}
|
||||
|
||||
void FloatCommand::SaveState(nlohmann::json& value)
|
||||
{
|
||||
value = m_State;
|
||||
}
|
||||
|
||||
void FloatCommand::LoadState(nlohmann::json& value)
|
||||
{
|
||||
m_State = value;
|
||||
}
|
||||
|
||||
FloatCommand::FloatCommand(std::string name, std::string label, std::string description, std::optional<float> min, std::optional<float> max, float def_val) :
|
||||
Command(name, label, description, 0),
|
||||
m_Min(min),
|
||||
m_Max(max),
|
||||
m_State(def_val)
|
||||
{
|
||||
}
|
||||
|
||||
float FloatCommand::GetState()
|
||||
{
|
||||
return m_State;
|
||||
}
|
||||
|
||||
void FloatCommand::SetState(float state)
|
||||
{
|
||||
FiberPool::Push([this] {
|
||||
OnChange();
|
||||
});
|
||||
m_State = state;
|
||||
MarkDirty();
|
||||
}
|
||||
|
||||
std::optional<float> FloatCommand::GetMinimum()
|
||||
{
|
||||
return m_Min;
|
||||
}
|
||||
|
||||
std::optional<float> FloatCommand::GetMaximum()
|
||||
{
|
||||
return m_Max;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include "Command.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class FloatCommand : public Command
|
||||
{
|
||||
protected:
|
||||
virtual void OnChange(){};
|
||||
virtual void OnCall() override;
|
||||
virtual void SaveState(nlohmann::json& value) override;
|
||||
virtual void LoadState(nlohmann::json& value) override;
|
||||
|
||||
float m_State = 0;
|
||||
std::optional<float> m_Min;
|
||||
std::optional<float> m_Max;
|
||||
|
||||
public:
|
||||
FloatCommand(std::string name, std::string label, std::string description, std::optional<float> min = std::nullopt, std::optional<float> max = std::nullopt, float def_val = 0.0f);
|
||||
float GetState();
|
||||
void SetState(float state);
|
||||
std::optional<float> GetMinimum();
|
||||
std::optional<float> GetMaximum();
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
#include "HotkeySystem.hpp"
|
||||
#include "core/backend/FiberPool.hpp"
|
||||
#include "core/backend/ScriptMgr.hpp"
|
||||
#include "Commands.hpp"
|
||||
#include "LoopedCommand.hpp"
|
||||
#include "core/util/Joaat.hpp"
|
||||
|
||||
// TODO: serialization isn't stable
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
HotkeySystem::HotkeySystem() :
|
||||
IStateSerializer("hotkeys")
|
||||
{
|
||||
}
|
||||
|
||||
void HotkeySystem::RegisterCommands()
|
||||
{
|
||||
auto& cmds = Commands::GetCommands();
|
||||
|
||||
for (auto& [hash, cmd] : cmds)
|
||||
{
|
||||
CommandLink link;
|
||||
m_CommandHotkeys.insert(std::make_pair(hash, link));
|
||||
}
|
||||
|
||||
LOG(INFO) << "Registered " << m_CommandHotkeys.size() << " commands";
|
||||
}
|
||||
|
||||
bool HotkeySystem::ListenAndApply(int& Hotkey, std::vector<int> Blacklist)
|
||||
{
|
||||
static auto IsKeyBlacklisted = [Blacklist](int Key) -> bool {
|
||||
for (auto Key_ : Blacklist)
|
||||
if (Key_ == Key)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// VK_OEM_CLEAR Is about the limit in terms of virtual key codes
|
||||
for (int i = 0; i < VK_OEM_CLEAR; i++)
|
||||
{
|
||||
if ((GetKeyState(i) & 0x8000) && i != 1 && !IsKeyBlacklisted(i))
|
||||
{
|
||||
Hotkey = i;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Will return the keycode if there are no labels
|
||||
std::string HotkeySystem::GetHotkeyLabel(int HotkeyModifier)
|
||||
{
|
||||
char KeyName[32];
|
||||
GetKeyNameTextA(MapVirtualKey(HotkeyModifier, MAPVK_VK_TO_VSC) << 16, KeyName, 32);
|
||||
|
||||
if (std::string(KeyName).empty())
|
||||
strcpy(KeyName, std::to_string(HotkeyModifier).data());
|
||||
|
||||
return KeyName;
|
||||
}
|
||||
|
||||
// Meant to be called in a loop
|
||||
void HotkeySystem::CreateHotkey(std::vector<int>& chain)
|
||||
{
|
||||
static auto is_key_unique = [this](int Key, std::vector<int> List) -> bool {
|
||||
for (auto& Key_ : List)
|
||||
if (GetHotkeyLabel(Key_) == GetHotkeyLabel(Key))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
int pressed_key = 0;
|
||||
ListenAndApply(pressed_key, chain);
|
||||
|
||||
if (pressed_key > 1)
|
||||
{
|
||||
if (is_key_unique(pressed_key, chain))
|
||||
{
|
||||
chain.push_back(pressed_key);
|
||||
}
|
||||
}
|
||||
|
||||
MarkStateDirty();
|
||||
}
|
||||
|
||||
void HotkeySystem::Update()
|
||||
{
|
||||
for (auto& [hash, link] : m_CommandHotkeys)
|
||||
{
|
||||
if (link.m_Chain.empty() || link.m_BeingModified)
|
||||
continue;
|
||||
|
||||
bool all_keys_pressed = true;
|
||||
|
||||
for (auto modifier : link.m_Chain)
|
||||
{
|
||||
if (!(GetAsyncKeyState(modifier) & 0x8000))
|
||||
{
|
||||
all_keys_pressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (all_keys_pressed && std::chrono::system_clock::now() - m_LastHotkeyTriggerTime > 100ms)
|
||||
{
|
||||
auto command = Commands::GetCommand(hash);
|
||||
if (command)
|
||||
{
|
||||
// TODO: this is the only way I can prevent chat from blocking the main loop while keeping everything else fast
|
||||
if (hash != "chathelper"_J)
|
||||
command->Call();
|
||||
else
|
||||
{
|
||||
FiberPool::Push([command] {
|
||||
command->Call();
|
||||
});
|
||||
}
|
||||
}
|
||||
m_LastHotkeyTriggerTime = std::chrono::system_clock::now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HotkeySystem::SaveStateImpl(nlohmann::json& state)
|
||||
{
|
||||
for (auto& hotkey : m_CommandHotkeys)
|
||||
{
|
||||
if (!hotkey.second.m_Chain.empty())
|
||||
{
|
||||
state[std::to_string(hotkey.first).data()] = hotkey.second.m_Chain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HotkeySystem::LoadStateImpl(nlohmann::json& state)
|
||||
{
|
||||
for (auto& [key, value] : state.items())
|
||||
{
|
||||
if (m_CommandHotkeys.contains(std::atoi(key.data())))
|
||||
m_CommandHotkeys[std::atoi(key.data())].m_Chain = value.get<std::vector<int>>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include "core/settings/IStateSerializer.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
struct CommandLink
|
||||
{
|
||||
public:
|
||||
std::vector<int> m_Chain{};
|
||||
bool m_BeingModified = false;
|
||||
|
||||
CommandLink(){};
|
||||
};
|
||||
|
||||
class HotkeySystem :
|
||||
private IStateSerializer
|
||||
{
|
||||
std::chrono::system_clock::time_point m_LastHotkeyTriggerTime;
|
||||
|
||||
public:
|
||||
HotkeySystem();
|
||||
|
||||
std::map<uint32_t, CommandLink> m_CommandHotkeys;
|
||||
void RegisterCommands();
|
||||
bool ListenAndApply(int& Hotkey, std::vector<int> blacklist = {0});
|
||||
std::string GetHotkeyLabel(int hotkey_modifiers);
|
||||
void CreateHotkey(std::vector<int>& Hotkey);
|
||||
|
||||
void Update();
|
||||
|
||||
virtual void SaveStateImpl(nlohmann::json& state) override;
|
||||
virtual void LoadStateImpl(nlohmann::json& state) override;
|
||||
};
|
||||
|
||||
inline HotkeySystem g_HotkeySystem;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#include "IntCommand.hpp"
|
||||
#include "core/backend/FiberPool.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
void IntCommand::OnCall()
|
||||
{
|
||||
}
|
||||
|
||||
void IntCommand::SaveState(nlohmann::json& value)
|
||||
{
|
||||
value = m_State;
|
||||
}
|
||||
|
||||
void IntCommand::LoadState(nlohmann::json& value)
|
||||
{
|
||||
m_State = value;
|
||||
}
|
||||
|
||||
IntCommand::IntCommand(std::string name, std::string label, std::string description, std::optional<int> min, std::optional<int> max, int def_val) :
|
||||
Command(name, label, description, 0),
|
||||
m_Min(min),
|
||||
m_Max(max),
|
||||
m_State(def_val)
|
||||
{
|
||||
}
|
||||
|
||||
int IntCommand::GetState()
|
||||
{
|
||||
return m_State;
|
||||
}
|
||||
|
||||
void IntCommand::SetState(int state)
|
||||
{
|
||||
FiberPool::Push([this] {
|
||||
OnChange();
|
||||
});
|
||||
m_State = state;
|
||||
MarkDirty();
|
||||
}
|
||||
|
||||
std::optional<int> IntCommand::GetMinimum()
|
||||
{
|
||||
return m_Min;
|
||||
}
|
||||
|
||||
std::optional<int> IntCommand::GetMaximum()
|
||||
{
|
||||
return m_Max;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include "Command.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class IntCommand : public Command
|
||||
{
|
||||
protected:
|
||||
virtual void OnChange(){};
|
||||
virtual void OnCall() override;
|
||||
virtual void SaveState(nlohmann::json& value) override;
|
||||
virtual void LoadState(nlohmann::json& value) override;
|
||||
|
||||
int m_State = 0;
|
||||
std::optional<int> m_Min;
|
||||
std::optional<int> m_Max;
|
||||
|
||||
public:
|
||||
IntCommand(std::string name, std::string label, std::string description, std::optional<int> min = std::nullopt, std::optional<int> max = std::nullopt, int def_val = 0);
|
||||
int GetState();
|
||||
void SetState(int state);
|
||||
std::optional<int> GetMinimum();
|
||||
std::optional<int> GetMaximum();
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#include "ListCommand.hpp"
|
||||
#include "core/backend/FiberPool.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
void ListCommand::OnCall()
|
||||
{
|
||||
}
|
||||
|
||||
void ListCommand::SaveState(nlohmann::json& value)
|
||||
{
|
||||
value = m_State;
|
||||
}
|
||||
|
||||
void ListCommand::LoadState(nlohmann::json& value)
|
||||
{
|
||||
m_State = value;
|
||||
}
|
||||
|
||||
ListCommand::ListCommand(std::string name, std::string label, std::string description, std::vector<std::pair<int, const char*>> list, int def_val) :
|
||||
Command(name, label, description, 0),
|
||||
m_List(list),
|
||||
m_State(def_val)
|
||||
{
|
||||
}
|
||||
|
||||
int ListCommand::GetState()
|
||||
{
|
||||
return m_State;
|
||||
}
|
||||
|
||||
void ListCommand::SetState(int state)
|
||||
{
|
||||
FiberPool::Push([this] {
|
||||
OnChange();
|
||||
});
|
||||
m_State = state;
|
||||
MarkDirty();
|
||||
}
|
||||
|
||||
std::vector<std::pair<int, const char*>>& ListCommand::GetList()
|
||||
{
|
||||
return m_List;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include "Command.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class ListCommand : public Command
|
||||
{
|
||||
protected:
|
||||
virtual void OnChange(){};
|
||||
virtual void OnCall() override;
|
||||
virtual void SaveState(nlohmann::json& value) override;
|
||||
virtual void LoadState(nlohmann::json& value) override;
|
||||
|
||||
int m_State = 0;
|
||||
std::vector<std::pair<int, const char*>> m_List; // not very efficient but keeps things sorted
|
||||
|
||||
public:
|
||||
ListCommand(std::string name, std::string label, std::string description, std::vector<std::pair<int, const char*>> list, int def_val = 0);
|
||||
int GetState();
|
||||
void SetState(int state);
|
||||
std::vector<std::pair<int, const char*>>& GetList();
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#include "LoopedCommand.hpp"
|
||||
#include "Commands.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
LoopedCommand::LoopedCommand(std::string name, std::string label, std::string description) :
|
||||
BoolCommand(name, label, description)
|
||||
{
|
||||
Commands::AddLoopedCommand(this);
|
||||
}
|
||||
|
||||
void LoopedCommand::Tick()
|
||||
{
|
||||
OnTick();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include "BoolCommand.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class LoopedCommand : public BoolCommand
|
||||
{
|
||||
protected:
|
||||
virtual void OnTick() = 0;
|
||||
|
||||
public:
|
||||
LoopedCommand(std::string name, std::string label, std::string description);
|
||||
void Tick();
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#include "StringCommand.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
void StringCommand::OnCall()
|
||||
{
|
||||
}
|
||||
|
||||
void StringCommand::SaveState(nlohmann::json& value)
|
||||
{
|
||||
value = m_StringValue;
|
||||
}
|
||||
|
||||
void StringCommand::LoadState(nlohmann::json& value)
|
||||
{
|
||||
m_StringValue = value.get<std::string>();
|
||||
}
|
||||
|
||||
StringCommand::StringCommand(std::string name, std::string label, std::string description, std::string defaultValue) :
|
||||
Command(name, label, description, 0),
|
||||
m_StringValue(defaultValue)
|
||||
{
|
||||
}
|
||||
|
||||
std::string StringCommand::GetString() const
|
||||
{
|
||||
return m_StringValue;
|
||||
}
|
||||
|
||||
void StringCommand::SetStringValue(const std::string& value)
|
||||
{
|
||||
m_StringValue = value;
|
||||
OnChange();
|
||||
MarkDirty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include "Command.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class StringCommand : public Command
|
||||
{
|
||||
protected:
|
||||
std::string m_StringValue;
|
||||
|
||||
virtual void OnChange()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void OnCall() override;
|
||||
virtual void SaveState(nlohmann::json& value) override;
|
||||
virtual void LoadState(nlohmann::json& value) override;
|
||||
|
||||
public:
|
||||
StringCommand(std::string name, std::string label, std::string description, std::string defaultValue = "");
|
||||
|
||||
std::string GetString() const;
|
||||
void SetStringValue(const std::string& value);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#include "Vector3Command.hpp"
|
||||
#include "core/backend/FiberPool.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
void Vector3Command::OnCall()
|
||||
{
|
||||
}
|
||||
|
||||
void Vector3Command::SaveState(nlohmann::json& value)
|
||||
{
|
||||
value = nlohmann::json::object();
|
||||
value["x"] = m_State.x;
|
||||
value["y"] = m_State.y;
|
||||
value["z"] = m_State.z;
|
||||
}
|
||||
|
||||
void Vector3Command::LoadState(nlohmann::json& value)
|
||||
{
|
||||
if (value.is_object())
|
||||
{
|
||||
m_State.x = value["x"];
|
||||
m_State.y = value["y"];
|
||||
m_State.z = value["z"];
|
||||
}
|
||||
}
|
||||
|
||||
Vector3Command::Vector3Command(std::string name, std::string label, std::string description, rage::fvector3 def_val) :
|
||||
Command(name, label, description, 0),
|
||||
m_State(def_val)
|
||||
{
|
||||
}
|
||||
|
||||
rage::fvector3 Vector3Command::GetState()
|
||||
{
|
||||
return m_State;
|
||||
}
|
||||
|
||||
void Vector3Command::SetState(const rage::fvector3& state)
|
||||
{
|
||||
FiberPool::Push([this] {
|
||||
OnChange();
|
||||
});
|
||||
m_State = state;
|
||||
MarkDirty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "types/rage/vector.hpp"
|
||||
#include "Command.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class Vector3Command : public Command
|
||||
{
|
||||
protected:
|
||||
virtual void OnChange(){};
|
||||
virtual void OnCall() override;
|
||||
virtual void SaveState(nlohmann::json& value) override;
|
||||
virtual void LoadState(nlohmann::json& value) override;
|
||||
|
||||
rage::fvector3 m_State{};
|
||||
|
||||
public:
|
||||
Vector3Command(std::string name, std::string label, std::string description, rage::fvector3 def_val = {});
|
||||
rage::fvector3 GetState();
|
||||
void SetState(const rage::fvector3& state);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
#include "Notifications.hpp"
|
||||
|
||||
#include "core/logger/LogHelper.hpp"
|
||||
#include "core/backend/FiberPool.hpp"
|
||||
#include "core/util/Joaat.hpp"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
|
||||
Notification Notifications::ShowImpl(std::string title, std::string message, NotificationType type, int duration, std::function<void()> context_function, std::string context_function_name)
|
||||
{
|
||||
if (title.empty() || message.empty())
|
||||
return {};
|
||||
|
||||
auto message_id = Joaat(title + message);
|
||||
|
||||
auto exists = std::find_if(m_Notifications.begin(), m_Notifications.end(), [&](auto& notification) {
|
||||
return notification.second.m_Identifier == message_id;
|
||||
});
|
||||
|
||||
if (exists != m_Notifications.end())
|
||||
{
|
||||
exists->second.m_CreatedOn = std::chrono::system_clock::now();
|
||||
return {};
|
||||
}
|
||||
|
||||
Notification notification{};
|
||||
notification.m_Title = title;
|
||||
notification.m_Message = message;
|
||||
notification.m_Type = type;
|
||||
notification.m_CreatedOn = std::chrono::system_clock::now();
|
||||
notification.m_Duration = duration;
|
||||
notification.m_Identifier = message_id;
|
||||
|
||||
if (context_function)
|
||||
{
|
||||
notification.m_ContextFunc = context_function;
|
||||
notification.m_ContextFuncName = context_function_name.empty() ? "Context Function" : context_function_name;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
auto result = m_Notifications.insert(std::make_pair(title + message, notification));
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
bool Notifications::EraseImpl(Notification notification)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
for (auto& [id, n] : m_Notifications)
|
||||
{
|
||||
if (n.m_Identifier == notification.m_Identifier)
|
||||
{
|
||||
n.m_Erasing = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void DrawNotification(Notification& notification, int position)
|
||||
{
|
||||
float y_pos = position * 100;
|
||||
float x_pos = 10;
|
||||
ImVec2 cardSize(m_CardSizeX, m_CardSizeY);
|
||||
|
||||
ImGui::SetNextWindowSize(cardSize, ImGuiCond_Always);
|
||||
ImGui::SetNextWindowPos(ImVec2(x_pos + notification.m_AnimationOffset, y_pos + 10), ImGuiCond_Always);
|
||||
|
||||
std::string windowTitle = std::to_string(position);
|
||||
ImGui::Begin(windowTitle.c_str(), nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoFocusOnAppearing);
|
||||
|
||||
auto timeElapsed = (float)std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - notification.m_CreatedOn).count();
|
||||
|
||||
auto depletionProgress = 1.0f - (timeElapsed / (float)notification.m_Duration);
|
||||
|
||||
ImGui::ProgressBar(depletionProgress, ImVec2(-1, 3.5f), "");
|
||||
|
||||
// TODO: Add icon for type instead of colored text
|
||||
if (notification.m_Type == NotificationType::Info)
|
||||
{
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::Text("%s", notification.m_Title.c_str());
|
||||
}
|
||||
else if (notification.m_Type == NotificationType::Success)
|
||||
{
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.0f, 1.0f, 0.0f, 1.0f));
|
||||
ImGui::Text("%s", notification.m_Title.c_str());
|
||||
}
|
||||
else if (notification.m_Type == NotificationType::Warning)
|
||||
{
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.5f, 0.0f, 1.0f));
|
||||
ImGui::Text("%s", notification.m_Title.c_str());
|
||||
}
|
||||
else if (notification.m_Type == NotificationType::Error)
|
||||
{
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.0f, 0.0f, 1.0f));
|
||||
ImGui::Text("%s", notification.m_Title.c_str());
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::TextWrapped("%s", notification.m_Message.c_str());
|
||||
|
||||
if (notification.m_ContextFunc)
|
||||
{
|
||||
ImGui::Spacing();
|
||||
if (ImGui::Selectable(notification.m_ContextFuncName.c_str()))
|
||||
FiberPool::Push([notification] {
|
||||
notification.m_ContextFunc();
|
||||
});
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void Notifications::DrawImpl()
|
||||
{
|
||||
std::vector<std::string> keys_to_erase;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
int position = 0;
|
||||
|
||||
for (auto& [id, notification] : m_Notifications)
|
||||
{
|
||||
DrawNotification(notification, position);
|
||||
|
||||
if (!notification.m_Erasing)
|
||||
{
|
||||
if (notification.m_AnimationOffset < 0)
|
||||
notification.m_AnimationOffset += m_CardAnimationSpeed;
|
||||
|
||||
//Need this to account for changes in card size (x dimension), custom increments might result in odd numbers
|
||||
if (notification.m_AnimationOffset > 0)
|
||||
notification.m_AnimationOffset = 0.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
notification.m_AnimationOffset -= m_CardAnimationSpeed;
|
||||
if (notification.m_AnimationOffset <= -m_CardSizeX)
|
||||
keys_to_erase.push_back(id);
|
||||
}
|
||||
|
||||
|
||||
if ((float)std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - notification.m_CreatedOn).count() >= notification.m_Duration)
|
||||
keys_to_erase.push_back(id);
|
||||
|
||||
position++;
|
||||
}
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
for (const auto& key : keys_to_erase)
|
||||
{
|
||||
m_Notifications.erase(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
static inline float m_CardSizeX = 350.f;
|
||||
static inline float m_CardSizeY = 100.f;
|
||||
static inline float m_CardAnimationSpeed = 50.f;
|
||||
|
||||
enum class NotificationType
|
||||
{
|
||||
Info,
|
||||
Success,
|
||||
Warning,
|
||||
Error
|
||||
};
|
||||
|
||||
struct Notification
|
||||
{
|
||||
NotificationType m_Type;
|
||||
std::string m_Title;
|
||||
std::string m_Message;
|
||||
std::chrono::time_point<std::chrono::system_clock> m_CreatedOn;
|
||||
int m_Duration;
|
||||
std::function<void()> m_ContextFunc;
|
||||
std::string m_ContextFuncName;
|
||||
float m_AnimationOffset = -m_CardSizeX;
|
||||
bool m_Erasing = false;
|
||||
std::uint32_t m_Identifier;
|
||||
};
|
||||
|
||||
class Notifications
|
||||
{
|
||||
private:
|
||||
std::unordered_map<std::string, Notification> m_Notifications = {};
|
||||
std::mutex m_mutex;
|
||||
|
||||
// duration is in milliseconds
|
||||
Notification ShowImpl(std::string title, std::string message, NotificationType type, int duration, std::function<void()> context_function, std::string context_function_name);
|
||||
void DrawImpl();
|
||||
bool EraseImpl(Notification notification);
|
||||
|
||||
static Notifications& GetInstance()
|
||||
{
|
||||
static Notifications instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
public:
|
||||
static Notification Show(std::string title, std::string message, NotificationType type = NotificationType::Info, int duration = 5000, std::function<void()> context_function = nullptr, std::string context_function_name = "")
|
||||
{
|
||||
return GetInstance().ShowImpl(title, message, type, duration, context_function, context_function_name);
|
||||
}
|
||||
|
||||
static void Draw()
|
||||
{
|
||||
GetInstance().DrawImpl();
|
||||
}
|
||||
|
||||
static bool Erase(Notification notification)
|
||||
{
|
||||
return GetInstance().EraseImpl(notification);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#include "Category.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
void Category::Draw()
|
||||
{
|
||||
for (auto& item : m_Items)
|
||||
item->Draw();
|
||||
}
|
||||
|
||||
int Category::GetLength()
|
||||
{
|
||||
if (m_Length.has_value())
|
||||
return m_Length.value();
|
||||
|
||||
m_Length = std::max(ImGui::CalcTextSize(m_Name.c_str()).x + 25.0f, 75.0f);
|
||||
return m_Length.value();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
#include "UIItem.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class Category
|
||||
{
|
||||
public:
|
||||
constexpr Category(std::string_view name) :
|
||||
m_Name(name)
|
||||
{
|
||||
}
|
||||
|
||||
void AddItem(std::shared_ptr<UIItem>&& item)
|
||||
{
|
||||
m_Items.push_back(std::move(item));
|
||||
}
|
||||
|
||||
void PrependItem(std::shared_ptr<UIItem>&& item)
|
||||
{
|
||||
m_Items.insert(m_Items.begin(), std::move(item));
|
||||
}
|
||||
|
||||
void Draw();
|
||||
int GetLength();
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<UIItem>> m_Items;
|
||||
std::optional<int> m_Length;
|
||||
|
||||
public:
|
||||
std::string m_Name;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "Submenu.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
void Submenu::SetActiveCategory(const std::shared_ptr<Category> category)
|
||||
{
|
||||
m_ActiveCategory = category;
|
||||
}
|
||||
|
||||
void Submenu::AddCategory(std::shared_ptr<Category>&& category)
|
||||
{
|
||||
if (!m_ActiveCategory)
|
||||
m_ActiveCategory = category;
|
||||
|
||||
m_Categories.push_back(std::move(category));
|
||||
}
|
||||
|
||||
void Submenu::DrawCategorySelectors()
|
||||
{
|
||||
for (auto& category : m_Categories)
|
||||
{
|
||||
if (category)
|
||||
{
|
||||
auto& style = ImGui::GetStyle();
|
||||
auto color = style.Colors[ImGuiCol_Button];
|
||||
color.w -= 0.5;
|
||||
|
||||
auto active = category == GetActiveCategory();
|
||||
|
||||
if (!active)
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, color);
|
||||
|
||||
if (ImGui::Button(category->m_Name.data(), ImVec2(category->GetLength(), 35)))
|
||||
{
|
||||
SetActiveCategory(category);
|
||||
}
|
||||
|
||||
if (!active)
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (m_Categories.back() != category)
|
||||
ImGui::SameLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Submenu::Draw()
|
||||
{
|
||||
if (m_ActiveCategory)
|
||||
{
|
||||
m_ActiveCategory->Draw();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
#include "Category.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class Submenu
|
||||
{
|
||||
public:
|
||||
constexpr Submenu(std::string name, std::string icon = "") :
|
||||
m_Name(name),
|
||||
m_Icon(icon)
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<Category> GetActiveCategory() const
|
||||
{
|
||||
return m_ActiveCategory;
|
||||
}
|
||||
|
||||
void AddCategory(std::shared_ptr<Category>&& category);
|
||||
void DrawCategorySelectors();
|
||||
void SetActiveCategory(const std::shared_ptr<Category> category);
|
||||
void Draw();
|
||||
|
||||
private:
|
||||
std::shared_ptr<Category> m_ActiveCategory;
|
||||
|
||||
public:
|
||||
std::vector<std::shared_ptr<Category>> m_Categories;
|
||||
std::string m_Name;
|
||||
std::string m_Icon; // currently unused
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "core/util/Joaat.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
// Preferably, all items should be saved in a global instance to make UI elements searchable
|
||||
class UIItem
|
||||
{
|
||||
public:
|
||||
virtual void Draw() = 0;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
#include "UIManager.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
void UIManager::AddSubmenuImpl(const std::shared_ptr<Submenu>&& submenu)
|
||||
{
|
||||
if (!m_ActiveSubmenu)
|
||||
m_ActiveSubmenu = submenu;
|
||||
|
||||
m_Submenus.push_back(std::move(submenu));
|
||||
}
|
||||
|
||||
void UIManager::SetActiveSubmenuImpl(const std::shared_ptr<Submenu> Submenu)
|
||||
{
|
||||
m_ActiveSubmenu = Submenu;
|
||||
}
|
||||
|
||||
void UIManager::DrawImpl()
|
||||
{
|
||||
auto pos = ImGui::GetCursorPos();
|
||||
|
||||
if (ImGui::BeginChild("##submenus", ImVec2(120, ImGui::GetContentRegionAvail().y - 20), true))
|
||||
{
|
||||
for (auto& submenu : m_Submenus)
|
||||
{
|
||||
if (ImGui::Selectable(submenu->m_Name.data(), (submenu == m_ActiveSubmenu)))
|
||||
{
|
||||
SetActiveSubmenu(submenu);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::Text("Terminus");
|
||||
|
||||
pos.y -= 28;
|
||||
ImGui::SetCursorPos(ImVec2(pos.x + 130, pos.y));
|
||||
|
||||
if (ImGui::BeginChild("##minisubmenus", ImVec2(0, 50), true, ImGuiWindowFlags_NoScrollbar))
|
||||
{
|
||||
if (m_ActiveSubmenu)
|
||||
m_ActiveSubmenu->DrawCategorySelectors();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::SetCursorPos(ImVec2(pos.x + 130, pos.y + 60));
|
||||
|
||||
if (ImGui::BeginChild("##options", ImVec2(0, 0), true))
|
||||
{
|
||||
if (m_OptionsFont)
|
||||
ImGui::PushFont(m_OptionsFont);
|
||||
|
||||
if (m_ActiveSubmenu)
|
||||
m_ActiveSubmenu->Draw();
|
||||
|
||||
if (m_OptionsFont)
|
||||
ImGui::PopFont();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
std::shared_ptr<Submenu> UIManager::GetActiveSubmenuImpl()
|
||||
{
|
||||
if (m_ActiveSubmenu)
|
||||
{
|
||||
return m_ActiveSubmenu;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<Category> UIManager::GetActiveCategoryImpl()
|
||||
{
|
||||
if (m_ActiveSubmenu)
|
||||
{
|
||||
return m_ActiveSubmenu->GetActiveCategory();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
#include "Category.hpp"
|
||||
#include "Submenu.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class UIManager
|
||||
{
|
||||
public:
|
||||
static void AddSubmenu(const std::shared_ptr<Submenu>&& submenu)
|
||||
{
|
||||
GetInstance().AddSubmenuImpl(std::move(submenu));
|
||||
}
|
||||
|
||||
static void SetActiveSubmenu(const std::shared_ptr<Submenu> submenu)
|
||||
{
|
||||
GetInstance().SetActiveSubmenuImpl(submenu);
|
||||
}
|
||||
|
||||
static void Draw()
|
||||
{
|
||||
GetInstance().DrawImpl();
|
||||
}
|
||||
|
||||
static std::shared_ptr<Submenu> GetActiveSubmenu()
|
||||
{
|
||||
return GetInstance().GetActiveSubmenuImpl();
|
||||
}
|
||||
|
||||
static std::shared_ptr<Category> GetActiveCategory()
|
||||
{
|
||||
return GetInstance().GetActiveCategoryImpl();
|
||||
}
|
||||
|
||||
static void SetOptionsFont(ImFont* font)
|
||||
{
|
||||
GetInstance().m_OptionsFont = font;
|
||||
}
|
||||
|
||||
private:
|
||||
static inline UIManager& GetInstance()
|
||||
{
|
||||
static UIManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void AddSubmenuImpl(const std::shared_ptr<Submenu>&& submenu);
|
||||
void SetActiveSubmenuImpl(const std::shared_ptr<Submenu> submenu);
|
||||
void DrawImpl();
|
||||
std::shared_ptr<Submenu> GetActiveSubmenuImpl();
|
||||
std::shared_ptr<Category> GetActiveCategoryImpl();
|
||||
|
||||
std::shared_ptr<Submenu> m_ActiveSubmenu;
|
||||
std::vector<std::shared_ptr<Submenu>> m_Submenus;
|
||||
ImFont* m_OptionsFont = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
#pragma once
|
||||
#include "imgui.h"
|
||||
|
||||
namespace ImGui
|
||||
{
|
||||
// https://easyrgb.com/en/convert.php
|
||||
struct Colors
|
||||
{
|
||||
// CSS colors
|
||||
inline static ImColor AliceBlue{0.94f, 0.97f, 1.0f, 1.0f}; // ARGB: #FFF0F8FF
|
||||
inline static ImColor AntiqueWhite{0.98f, 0.92f, 0.84f, 1.0f}; // ARGB: #FFFAEBD7
|
||||
inline static ImColor Aqua{0.0f, 1.0f, 1.0f, 1.0f}; // ARGB: #FF00FFFF
|
||||
inline static ImColor Aquamarine{0.50f, 1.0f, 0.83f, 1.0f}; // ARGB: #FF7FFFD4
|
||||
inline static ImColor Azure{0.94f, 1.0f, 1.0f, 1.0f}; // ARGB: #FFF0FFFF
|
||||
inline static ImColor Beige{0.96f, 0.96f, 0.86f, 1.0f}; // ARGB: #FFF5F5DC
|
||||
inline static ImColor Bisque{1.0f, 0.89f, 0.77f, 1.0f}; // ARGB: #FFFFE4C4
|
||||
inline static ImColor Black{0.0f, 0.0f, 0.0f, 1.0f}; // ARGB: #FF000000
|
||||
inline static ImColor BlanchedAlmond{1.0f, 0.92f, 0.80f, 1.0f}; // ARGB: #FFFFEBCD
|
||||
inline static ImColor Blue{0.0f, 0.0f, 1.0f, 1.0f}; // ARGB: #FF0000FF
|
||||
inline static ImColor BlueViolet{0.54f, 0.17f, 0.89f, 1.0f}; // ARGB: #FF8A2BE2
|
||||
inline static ImColor Brown{0.65f, 0.16f, 0.16f, 1.0f}; // ARGB: #FFA52A2A
|
||||
inline static ImColor BurlyWood{0.87f, 0.72f, 0.53f, 1.0f}; // ARGB: #FFDEB887
|
||||
inline static ImColor CadetBlue{0.37f, 0.62f, 0.63f, 1.0f}; // ARGB: #FF5F9EA0
|
||||
inline static ImColor Chartreuse{0.50f, 1.0f, 0.0f, 1.0f}; // ARGB: #FF7FFF00
|
||||
inline static ImColor Chocolate{0.82f, 0.41f, 0.12f, 1.0f}; // ARGB: #FFD2691E
|
||||
inline static ImColor Coral{1.0f, 0.50f, 0.31f, 1.0f}; // ARGB: #FFFF7F50
|
||||
inline static ImColor CornflowerBlue{0.39f, 0.58f, 0.93f, 1.0f}; // ARGB: #FF6495ED
|
||||
inline static ImColor Cornsilk{1.0f, 0.97f, 0.86f, 1.0f}; // ARGB: #FFFFF8DC
|
||||
inline static ImColor Crimson{0.86f, 0.08f, 0.24f, 1.0f}; // ARGB: #FFDC143C
|
||||
inline static ImColor Cyan{0.0f, 1.0f, 1.0f, 1.0f}; // ARGB: #FF00FFFF
|
||||
inline static ImColor DarkBlue{0.0f, 0.0f, 0.55f, 1.0f}; // ARGB: #FF00008B
|
||||
inline static ImColor DarkCyan{0.0f, 0.55f, 0.55f, 1.0f}; // ARGB: #FF008B8B
|
||||
inline static ImColor DarkGoldenrod{0.72f, 0.53f, 0.04f, 1.0f}; // ARGB: #FFB8860B
|
||||
inline static ImColor DarkGray{0.66f, 0.66f, 0.66f, 1.0f}; // ARGB: #FFA9A9A9
|
||||
inline static ImColor DarkGreen{0.0f, 0.39f, 0.0f, 1.0f}; // ARGB: #FF006400
|
||||
inline static ImColor DarkKhaki{0.74f, 0.72f, 0.42f, 1.0f}; // ARGB: #FFBDB76B
|
||||
inline static ImColor DarkMagenta{0.55f, 0.0f, 0.55f, 1.0f}; // ARGB: #FF8B008B
|
||||
inline static ImColor DarkOliveGreen{0.33f, 0.42f, 0.18f, 1.0f}; // ARGB: #FF556B2F
|
||||
inline static ImColor DarkOrange{1.0f, 0.55f, 0.0f, 1.0f}; // ARGB: #FFFF8C00
|
||||
inline static ImColor DarkOrchid{0.60f, 0.20f, 0.80f, 1.0f}; // ARGB: #FF9932CC
|
||||
inline static ImColor DarkRed{0.55f, 0.0f, 0.0f, 1.0f}; // ARGB: #FF8B0000
|
||||
inline static ImColor DarkSalmon{0.91f, 0.59f, 0.48f, 1.0f}; // ARGB: #FFE9967A
|
||||
inline static ImColor DarkSeaGreen{0.56f, 0.74f, 0.56f, 1.0f}; // ARGB: #FF8FBC8F
|
||||
inline static ImColor DarkSlateBlue{0.28f, 0.24f, 0.55f, 1.0f}; // ARGB: #FF483D8B
|
||||
inline static ImColor DarkSlateGray{0.18f, 0.31f, 0.31f, 1.0f}; // ARGB: #FF2F4F4F
|
||||
inline static ImColor DarkTurquoise{0.0f, 0.81f, 0.82f, 1.0f}; // ARGB: #FF00CED1
|
||||
inline static ImColor DarkViolet{0.58f, 0.0f, 0.83f, 1.0f}; // ARGB: #FF9400D3
|
||||
inline static ImColor DeepPink{1.0f, 0.08f, 0.58f, 1.0f}; // ARGB: #FFFF1493
|
||||
inline static ImColor DeepSkyBlue{0.0f, 0.75f, 1.0f, 1.0f}; // ARGB: #FF00BFFF
|
||||
inline static ImColor DimGray{0.41f, 0.41f, 0.41f, 1.0f}; // ARGB: #FF696969
|
||||
inline static ImColor DodgerBlue{0.12f, 0.56f, 1.0f, 1.0f}; // ARGB: #FF1E90FF
|
||||
inline static ImColor Firebrick{0.70f, 0.13f, 0.13f, 1.0f}; // ARGB: #FFB22222
|
||||
inline static ImColor FloralWhite{1.0f, 0.98f, 0.94f, 1.0f}; // ARGB: #FFFAF0E6
|
||||
inline static ImColor ForestGreen{0.13f, 0.55f, 0.13f, 1.0f}; // ARGB: #FF228B22
|
||||
inline static ImColor Fuchsia{1.0f, 0.0f, 1.0f, 1.0f}; // ARGB: #FFFF00FF
|
||||
inline static ImColor Gainsboro{0.86f, 0.86f, 0.86f, 1.0f}; // ARGB: #FFDCDCDC
|
||||
inline static ImColor GhostWhite{0.97f, 0.97f, 1.0f, 1.0f}; // ARGB: #FFF8F8FF
|
||||
inline static ImColor Gold{1.0f, 0.84f, 0.0f, 1.0f}; // ARGB: #FFFFD700
|
||||
inline static ImColor Goldenrod{0.85f, 0.65f, 0.13f, 1.0f}; // ARGB: #FFDAA520
|
||||
inline static ImColor Gray{0.50f, 0.50f, 0.50f, 1.0f}; // ARGB: #FF808080
|
||||
inline static ImColor Green{0.0f, 0.50f, 0.0f, 1.0f}; // ARGB: #FF008000
|
||||
inline static ImColor GreenYellow{0.68f, 1.0f, 0.18f, 1.0f}; // ARGB: #FFADFF2F
|
||||
inline static ImColor Honeydew{0.94f, 1.0f, 0.94f, 1.0f}; // ARGB: #FFF0FFF0
|
||||
inline static ImColor HotPink{1.0f, 0.41f, 0.71f, 1.0f}; // ARGB: #FFFF69B4
|
||||
inline static ImColor IndianRed{0.80f, 0.36f, 0.36f, 1.0f}; // ARGB: #FFCD5C5C
|
||||
inline static ImColor Indigo{0.29f, 0.0f, 0.51f, 1.0f}; // ARGB: #FF4B0082
|
||||
inline static ImColor Ivory{1.0f, 1.0f, 0.94f, 1.0f}; // ARGB: #FFFFFFF0
|
||||
inline static ImColor Khaki{0.94f, 0.90f, 0.55f, 1.0f}; // ARGB: #FFF0E68C
|
||||
inline static ImColor Lavender{0.90f, 0.90f, 0.98f, 1.0f}; // ARGB: #FFE6E6FA
|
||||
inline static ImColor LavenderBlush{1.0f, 0.94f, 0.96f, 1.0f}; // ARGB: #FFFFF0F5
|
||||
inline static ImColor LawnGreen{0.49f, 0.99f, 0.0f, 1.0f}; // ARGB: #FF7CFC00
|
||||
inline static ImColor LemonChiffon{1.0f, 0.98f, 0.80f, 1.0f}; // ARGB: #FFFFFACD
|
||||
inline static ImColor LightBlue{0.68f, 0.85f, 0.90f, 1.0f}; // ARGB: #FFADD8E6
|
||||
inline static ImColor LightCoral{0.94f, 0.50f, 0.50f, 1.0f}; // ARGB: #FFF08080
|
||||
inline static ImColor LightCyan{0.88f, 1.0f, 1.0f, 1.0f}; // ARGB: #FFE0FFFF
|
||||
inline static ImColor LightGoldenrodYellow{0.98f, 0.98f, 0.82f, 1.0f}; // ARGB: #FFFAFAD2
|
||||
inline static ImColor LightGray{0.83f, 0.83f, 0.83f, 1.0f}; // ARGB: #FFD3D3D3
|
||||
inline static ImColor LightGreen{0.56f, 0.93f, 0.56f, 1.0f}; // ARGB: #FF90EE90
|
||||
inline static ImColor LightPink{1.0f, 0.71f, 0.76f, 1.0f}; // ARGB: #FFFFB6C1
|
||||
inline static ImColor LightSalmon{1.0f, 0.63f, 0.48f, 1.0f}; // ARGB: #FFFFA07A
|
||||
inline static ImColor LightSeaGreen{0.13f, 0.70f, 0.67f, 1.0f}; // ARGB: #FF20B2AA
|
||||
inline static ImColor LightSkyBlue{0.53f, 0.81f, 0.98f, 1.0f}; // ARGB: #FF87CEFA
|
||||
inline static ImColor LightSlateGray{0.47f, 0.53f, 0.60f, 1.0f}; // ARGB: #FF778899
|
||||
inline static ImColor LightSteelBlue{0.69f, 0.77f, 0.87f, 1.0f}; // ARGB: #FFB0C4DE
|
||||
inline static ImColor LightYellow{1.0f, 1.0f, 0.88f, 1.0f}; // ARGB: #FFFFFFE0
|
||||
inline static ImColor Lime{0.0f, 1.0f, 0.0f, 1.0f}; // ARGB: #FF00FF00
|
||||
inline static ImColor LimeGreen{0.20f, 0.80f, 0.20f, 1.0f}; // ARGB: #FF32CD32
|
||||
inline static ImColor Linen{0.98f, 0.94f, 0.90f, 1.0f}; // ARGB: #FFFAF0E6
|
||||
inline static ImColor Magenta{1.0f, 0.0f, 1.0f, 1.0f}; // ARGB: #FFFF00FF
|
||||
inline static ImColor Maroon{0.50f, 0.0f, 0.0f, 1.0f}; // ARGB: #FF800000
|
||||
inline static ImColor MediumAquamarine{0.40f, 0.80f, 0.67f, 1.0f}; // ARGB: #FF66CDAA
|
||||
inline static ImColor MediumBlue{0.0f, 0.0f, 0.80f, 1.0f}; // ARGB: #FF0000CD
|
||||
inline static ImColor MediumOrchid{0.73f, 0.33f, 0.83f, 1.0f}; // ARGB: #FFBA55D3
|
||||
inline static ImColor MediumPurple{0.58f, 0.44f, 0.86f, 1.0f}; // ARGB: #FF9370DB
|
||||
inline static ImColor MediumSeaGreen{0.24f, 0.70f, 0.44f, 1.0f}; // ARGB: #FF3CB371
|
||||
inline static ImColor MediumSlateBlue{0.48f, 0.41f, 0.93f, 1.0f}; // ARGB: #FF7B68EE
|
||||
inline static ImColor MediumSpringGreen{0.0f, 0.98f, 0.60f, 1.0f}; // ARGB: #FF00FA9A
|
||||
inline static ImColor MediumTurquoise{0.28f, 0.82f, 0.80f, 1.0f}; // ARGB: #FF48D1CC
|
||||
inline static ImColor MediumVioletRed{0.78f, 0.08f, 0.52f, 1.0f}; // ARGB: #FFC71585
|
||||
inline static ImColor MidnightBlue{0.10f, 0.10f, 0.44f, 1.0f}; // ARGB: #FF191970
|
||||
inline static ImColor MintCream{0.96f, 1.0f, 0.98f, 1.0f}; // ARGB: #FFF5FFFA
|
||||
inline static ImColor MistyRose{1.0f, 0.89f, 0.88f, 1.0f}; // ARGB: #FFFFE4E1
|
||||
inline static ImColor Moccasin{1.0f, 0.89f, 0.71f, 1.0f}; // ARGB: #FFFFE4B5
|
||||
inline static ImColor NavajoWhite{1.0f, 0.87f, 0.68f, 1.0f}; // ARGB: #FFFFDEAD
|
||||
inline static ImColor Navy{0.0f, 0.0f, 0.50f, 1.0f}; // ARGB: #FF000080
|
||||
inline static ImColor OldLace{0.99f, 0.96f, 0.90f, 1.0f}; // ARGB: #FFFDF5E6
|
||||
inline static ImColor Olive{0.50f, 0.50f, 0.0f, 1.0f}; // ARGB: #FF808000
|
||||
inline static ImColor OliveDrab{0.42f, 0.56f, 0.14f, 1.0f}; // ARGB: #FF6B8E23
|
||||
inline static ImColor Orange{1.0f, 0.65f, 0.0f, 1.0f}; // ARGB: #FFFFA500
|
||||
inline static ImColor OrangeRed{1.0f, 0.27f, 0.0f, 1.0f}; // ARGB: #FFFF4500
|
||||
inline static ImColor Orchid{0.85f, 0.44f, 0.84f, 1.0f}; // ARGB: #FFDA70D6
|
||||
inline static ImColor PaleGoldenrod{0.93f, 0.91f, 0.67f, 1.0f}; // ARGB: #FFEEE8AA
|
||||
inline static ImColor PaleGreen{0.60f, 0.98f, 0.60f, 1.0f}; // ARGB: #FF98FB98
|
||||
inline static ImColor PaleTurquoise{0.69f, 0.93f, 0.93f, 1.0f}; // ARGB: #FFAFEEEE
|
||||
inline static ImColor PaleVioletRed{0.86f, 0.44f, 0.58f, 1.0f}; // ARGB: #FFDB7093
|
||||
inline static ImColor PapayaWhip{1.0f, 0.94f, 0.84f, 1.0f}; // ARGB: #FFFFEFD5
|
||||
inline static ImColor PeachPuff{1.0f, 0.85f, 0.73f, 1.0f}; // ARGB: #FFFFDAB9
|
||||
inline static ImColor Peru{0.80f, 0.52f, 0.25f, 1.0f}; // ARGB: #FFCD853F
|
||||
inline static ImColor Pink{1.0f, 0.75f, 0.80f, 1.0f}; // ARGB: #FFFFC0CB
|
||||
inline static ImColor Plum{0.87f, 0.63f, 0.87f, 1.0f}; // ARGB: #FFDDA0DD
|
||||
inline static ImColor PowderBlue{0.69f, 0.88f, 0.90f, 1.0f}; // ARGB: #FFB0E0E6
|
||||
inline static ImColor Purple{0.50f, 0.0f, 0.50f, 1.0f}; // ARGB: #FF800080
|
||||
inline static ImColor Red{1.0f, 0.0f, 0.0f, 1.0f}; // ARGB: #FFFF0000
|
||||
inline static ImColor RosyBrown{0.74f, 0.56f, 0.56f, 1.0f}; // ARGB: #FFBC8F8F
|
||||
inline static ImColor RoyalBlue{0.25f, 0.41f, 0.88f, 1.0f}; // ARGB: #FF4169E1
|
||||
inline static ImColor SaddleBrown{0.55f, 0.27f, 0.07f, 1.0f}; // ARGB: #FF8B4513
|
||||
inline static ImColor Salmon{0.98f, 0.50f, 0.45f, 1.0f}; // ARGB: #FFFA8072
|
||||
inline static ImColor SandyBrown{0.96f, 0.64f, 0.38f, 1.0f}; // ARGB: #FFF4A460
|
||||
inline static ImColor SeaGreen{0.18f, 0.55f, 0.34f, 1.0f}; // ARGB: #FF2E8B57
|
||||
inline static ImColor SeaShell{1.0f, 0.96f, 0.93f, 1.0f}; // ARGB: #FFFFF5EE
|
||||
inline static ImColor Sienna{0.63f, 0.32f, 0.18f, 1.0f}; // ARGB: #FFA0522D
|
||||
inline static ImColor Silver{0.75f, 0.75f, 0.75f, 1.0f}; // ARGB: #FFC0C0C0
|
||||
inline static ImColor SkyBlue{0.53f, 0.81f, 0.92f, 1.0f}; // ARGB: #FF87CEEB
|
||||
inline static ImColor SlateBlue{0.42f, 0.35f, 0.80f, 1.0f}; // ARGB: #FF6A5ACD
|
||||
inline static ImColor SlateGray{0.44f, 0.50f, 0.56f, 1.0f}; // ARGB: #FF708090
|
||||
inline static ImColor Snow{1.0f, 0.98f, 0.98f, 1.0f}; // ARGB: #FFFFFAFA
|
||||
inline static ImColor SpringGreen{0.0f, 1.0f, 0.50f, 1.0f}; // ARGB: #FF00FF7F
|
||||
inline static ImColor SteelBlue{0.27f, 0.51f, 0.71f, 1.0f}; // ARGB: #FF4682B4
|
||||
inline static ImColor Tan{0.82f, 0.71f, 0.55f, 1.0f}; // ARGB: #FFD2B48C
|
||||
inline static ImColor Teal{0.0f, 0.50f, 0.50f, 1.0f}; // ARGB: #FF008080
|
||||
inline static ImColor Thistle{0.85f, 0.75f, 0.85f, 1.0f}; // ARGB: #FFD8BFD8
|
||||
inline static ImColor Tomato{1.0f, 0.39f, 0.28f, 1.0f}; // ARGB: #FFFF6347
|
||||
inline static ImColor Transparent{1.0f, 1.0f, 1.0f, 0.0f}; // ARGB: #00FFFFFF
|
||||
inline static ImColor Turquoise{0.25f, 0.88f, 0.82f, 1.0f}; // ARGB: #FF40E0D0
|
||||
inline static ImColor Violet{0.93f, 0.51f, 0.93f, 1.0f}; // ARGB: #FFEE82EE
|
||||
inline static ImColor Wheat{0.96f, 0.87f, 0.70f, 1.0f}; // ARGB: #FFF5DEB3
|
||||
inline static ImColor White{1.0f, 1.0f, 1.0f, 1.0f}; // ARGB: #FFFFFFFF
|
||||
inline static ImColor WhiteSmoke{0.96f, 0.96f, 0.96f, 1.0f}; // ARGB: #FFF5F5F5
|
||||
inline static ImColor Yellow{1.0f, 1.0f, 0.0f, 1.0f}; // ARGB: #FFFFFF00
|
||||
inline static ImColor YellowGreen{0.60f, 0.80f, 0.20f, 1.0f}; // ARGB: #FF9ACD32
|
||||
|
||||
// others
|
||||
inline static ImColor Freemode{0.17647f, 0.43137f, 0.72549, 1.0f}; // ARGB: #FF2D6EB9
|
||||
inline static ImColor IngameBg{0.0f, 0.0f, 0.0f, 0.54901f}; // ARGB: #8C000000
|
||||
inline static ImColor DisabledText{0.0f, 0.0f, 0.0f, 0.8f}; // ARGB: #CC000000
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
// Helper: ImOffsetRect A set of offsets to apply to an ImRect.
|
||||
struct IMGUI_API ImOffsetRect
|
||||
{
|
||||
union
|
||||
{
|
||||
float Offsets[4];
|
||||
|
||||
struct
|
||||
{
|
||||
float Top;
|
||||
float Left;
|
||||
float Bottom;
|
||||
float Right;
|
||||
};
|
||||
};
|
||||
|
||||
constexpr ImOffsetRect() : Top(0.0f), Left(0.0f), Bottom(0.0f), Right(0.0f) {}
|
||||
constexpr ImOffsetRect(const ImVec2& topLeft, const ImVec2& bottomRight) : ImOffsetRect(topLeft.y, topLeft.x, bottomRight.y, bottomRight.x) {}
|
||||
constexpr ImOffsetRect(const ImVec4& v) : ImOffsetRect(v.x, v.y, v.z, v.w) {}
|
||||
constexpr ImOffsetRect(float top, float left, float bottom, float right) : Top(top), Left(left), Bottom(bottom), Right(right) {}
|
||||
constexpr ImOffsetRect(float all) : Top(all), Left(all), Bottom(all), Right(all) {}
|
||||
|
||||
ImVec2 GetSize() const { return ImVec2(Left + Right, Top + Bottom); }
|
||||
float GetWidth() const { return Left + Right; }
|
||||
float GetHeight() const { return Top + Bottom; }
|
||||
float GetAverage() const { return (Top + Left + Bottom + Right) / 4.0f; }
|
||||
ImOffsetRect MirrorHorizontally() const { return ImOffsetRect(Top, Right, Bottom, Left); }
|
||||
ImOffsetRect MirrorVertically() const { return ImOffsetRect(Bottom, Left, Top, Right); }
|
||||
ImOffsetRect Mirror() const { return ImOffsetRect(Bottom, Right, Top, Left); }
|
||||
};
|
||||
|
||||
// Helpers: ImOffsetRect operators
|
||||
IM_MSVC_RUNTIME_CHECKS_OFF
|
||||
static inline ImOffsetRect operator+(const ImOffsetRect& lhs, const ImOffsetRect& rhs) { return ImOffsetRect(lhs.Top + rhs.Top, lhs.Left + rhs.Left, lhs.Bottom + rhs.Bottom, lhs.Right + rhs.Right); }
|
||||
static inline ImOffsetRect operator-(const ImOffsetRect& lhs, const ImOffsetRect& rhs) { return ImOffsetRect(lhs.Top - rhs.Top, lhs.Left - rhs.Left, lhs.Bottom - rhs.Bottom, lhs.Right - rhs.Right); }
|
||||
static inline ImOffsetRect operator*(const ImOffsetRect& lhs, const ImOffsetRect& rhs) { return ImOffsetRect(lhs.Top * rhs.Top, lhs.Left * rhs.Left, lhs.Bottom * rhs.Bottom, lhs.Right * rhs.Right); }
|
||||
IM_MSVC_RUNTIME_CHECKS_RESTORE
|
||||
@@ -0,0 +1,115 @@
|
||||
#include "imgui_toggle.hpp"
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_toggle_math.hpp"
|
||||
#include "imgui_toggle_palette.hpp"
|
||||
#include "imgui_toggle_renderer.hpp"
|
||||
|
||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#endif // IMGUI_DEFINE_MATH_OPERATORS
|
||||
#include "imgui_internal.h"
|
||||
|
||||
using namespace ImGuiToggleConstants;
|
||||
using namespace ImGuiToggleMath;
|
||||
|
||||
namespace
|
||||
{
|
||||
bool ToggleInternal(const char* label, bool* value, const ImGuiToggleConfig& config);
|
||||
|
||||
// sets the given config structure's values to the
|
||||
// default ones used by the `Toggle()` overloads.
|
||||
inline void SetToAliasDefaults(ImGuiToggleConfig& config)
|
||||
{
|
||||
config.Flags = ImGuiToggleFlags_Default;
|
||||
config.AnimationDuration = AnimationDurationDisabled;
|
||||
config.FrameRounding = FrameRoundingDefault;
|
||||
config.KnobRounding = KnobRoundingDefault;
|
||||
}
|
||||
|
||||
// thread-local data for the `Toggle()` functions to easily call `ToggleInternal()`.
|
||||
static thread_local ImGuiToggleConfig _internalConfig;
|
||||
} // namespace
|
||||
|
||||
bool ImGui::Toggle(const char* label, bool* v, const ImVec2& size /*= ImVec2()*/)
|
||||
{
|
||||
::SetToAliasDefaults(::_internalConfig);
|
||||
return ::ToggleInternal(label, v, ::_internalConfig);
|
||||
}
|
||||
|
||||
bool ImGui::Toggle(const char* label, bool* v, ImGuiToggleFlags flags, const ImVec2& size /*= ImVec2()*/)
|
||||
{
|
||||
::SetToAliasDefaults(::_internalConfig);
|
||||
::_internalConfig.Flags = flags;
|
||||
::_internalConfig.Size = size;
|
||||
|
||||
// if the user is using any animation flags,
|
||||
// set the default animation duration.
|
||||
if ((flags & ImGuiToggleFlags_Animated) != 0)
|
||||
{
|
||||
_internalConfig.AnimationDuration = AnimationDurationDefault;
|
||||
}
|
||||
|
||||
return ::ToggleInternal(label, v, ::_internalConfig);
|
||||
}
|
||||
|
||||
bool ImGui::Toggle(const char* label, bool* v, ImGuiToggleFlags flags, float animation_duration, const ImVec2& size /*= ImVec2()*/)
|
||||
{
|
||||
// this overload implies the toggle should be animated.
|
||||
if (animation_duration > 0 && (flags & ImGuiToggleFlags_Animated) != 0)
|
||||
{
|
||||
// if the user didn't specify ImGuiToggleFlags_Animated, enable it.
|
||||
flags = flags | (ImGuiToggleFlags_Animated);
|
||||
}
|
||||
|
||||
::SetToAliasDefaults(::_internalConfig);
|
||||
::_internalConfig.Flags = flags;
|
||||
::_internalConfig.AnimationDuration = animation_duration;
|
||||
::_internalConfig.Size = size;
|
||||
|
||||
return ::ToggleInternal(label, v, ::_internalConfig);
|
||||
}
|
||||
|
||||
bool ImGui::Toggle(const char* label, bool* v, ImGuiToggleFlags flags, float frame_rounding, float knob_rounding, const ImVec2& size /*= ImVec2()*/)
|
||||
{
|
||||
::SetToAliasDefaults(::_internalConfig);
|
||||
::_internalConfig.Flags = flags;
|
||||
::_internalConfig.FrameRounding = frame_rounding;
|
||||
::_internalConfig.KnobRounding = knob_rounding;
|
||||
::_internalConfig.Size = size;
|
||||
|
||||
return ::ToggleInternal(label, v, ::_internalConfig);
|
||||
}
|
||||
|
||||
bool ImGui::Toggle(const char* label, bool* v, ImGuiToggleFlags flags, float animation_duration, float frame_rounding, float knob_rounding, const ImVec2& size /*= ImVec2()*/)
|
||||
{
|
||||
// this overload implies the toggle should be animated.
|
||||
if (animation_duration > 0 && (flags & ImGuiToggleFlags_Animated) != 0)
|
||||
{
|
||||
// if the user didn't specify ImGuiToggleFlags_Animated, enable it.
|
||||
flags = flags | (ImGuiToggleFlags_Animated);
|
||||
}
|
||||
|
||||
::_internalConfig.Flags = flags;
|
||||
::_internalConfig.AnimationDuration = animation_duration;
|
||||
::_internalConfig.FrameRounding = frame_rounding;
|
||||
::_internalConfig.KnobRounding = knob_rounding;
|
||||
::_internalConfig.Size = size;
|
||||
|
||||
return ::ToggleInternal(label, v, ::_internalConfig);
|
||||
}
|
||||
|
||||
bool ImGui::Toggle(const char* label, bool* v, const ImGuiToggleConfig& config)
|
||||
{
|
||||
return ::ToggleInternal(label, v, config);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
bool ToggleInternal(const char* label, bool* v, const ImGuiToggleConfig& config)
|
||||
{
|
||||
static thread_local ImGuiToggleRenderer renderer;
|
||||
renderer.SetConfig(label, v, config);
|
||||
return renderer.Render();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
#pragma once
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_offset_rect.hpp"
|
||||
|
||||
// Type forward declarations, definitions below in this file.
|
||||
struct ImGuiToggleConfig; // Configuration data to fully customize a toggle.
|
||||
struct ImGuiToggleStateConfig; // The data describing how to draw a toggle in a given state.
|
||||
struct ImGuiTogglePalette; // Color data to adjust how a toggle is drawn.
|
||||
|
||||
typedef int ImGuiToggleFlags; // -> enum ImGuiToggleFlags_ // Flags: for Toggle() modes
|
||||
typedef int ImGuiToggleA11yStyle; // -> enum ImGuiToggleA11yStyle_ // Describes how to draw A11y labels.
|
||||
|
||||
namespace ImGui
|
||||
{
|
||||
// Widgets: Toggle Switches
|
||||
// - Toggles behave similarly to ImGui::Checkbox()
|
||||
// - Sometimes called a toggle switch, see also: https://en.wikipedia.org/wiki/Toggle_switch_(widget)
|
||||
// - They represent two mutually exclusive states, with an optional animation on the UI when toggled.
|
||||
// Optional parameters:
|
||||
// - flags: Values from the ImGuiToggleFlags_ enumeration to set toggle modes.
|
||||
// - animation_duration: Animation duration. Amount of time in seconds the toggle should animate. (0,...] default: 1.0f (Overloads with this parameter imply ImGuiToggleFlags_Animated)
|
||||
// - frame_rounding: A scalar that controls how rounded the toggle frame is. 0 is square, 1 is round. (0, 1) default 1.0f
|
||||
// - knob_rounding: A scalar that controls how rounded the toggle knob is. 0 is square, 1 is round. (0, 1) default 1.0f
|
||||
// - size: A width and height to draw the toggle at. Defaults to `ImGui::GetFrameHeight()` and that height * Phi for the width.
|
||||
// Config structure:
|
||||
// - The overload taking a reference to an ImGuiToggleConfig structure allows for more complete control over the widget.
|
||||
IMGUI_API bool Toggle(const char* label, bool* v, const ImVec2& size = ImVec2());
|
||||
IMGUI_API bool Toggle(const char* label, bool* v, ImGuiToggleFlags flags, const ImVec2& size = ImVec2());
|
||||
IMGUI_API bool Toggle(const char* label, bool* v, ImGuiToggleFlags flags, float animation_duration, const ImVec2& size = ImVec2());
|
||||
IMGUI_API bool Toggle(const char* label, bool* v, ImGuiToggleFlags flags, float frame_rounding, float knob_rounding, const ImVec2& size = ImVec2());
|
||||
IMGUI_API bool Toggle(const char* label, bool* v, ImGuiToggleFlags flags, float animation_duration, float frame_rounding, float knob_rounding, const ImVec2& size = ImVec2());
|
||||
IMGUI_API bool Toggle(const char* label, bool* v, const ImGuiToggleConfig& config);
|
||||
|
||||
} // namespace ImGui
|
||||
|
||||
|
||||
// ImGuiToggleFlags: A set of flags that adjust behavior and display for ImGui::Toggle().
|
||||
enum ImGuiToggleFlags_
|
||||
{
|
||||
ImGuiToggleFlags_None = 0,
|
||||
ImGuiToggleFlags_Animated = 1 << 0, // The toggle's knob should be animated.
|
||||
// Bits 1-2 reserved.
|
||||
ImGuiToggleFlags_BorderedFrame = 1 << 3, // The toggle should have a border drawn on the frame.
|
||||
ImGuiToggleFlags_BorderedKnob = 1 << 4, // The toggle should have a border drawn on the knob.
|
||||
ImGuiToggleFlags_ShadowedFrame = 1 << 5, // The toggle should have a shadow drawn under the frame.
|
||||
ImGuiToggleFlags_ShadowedKnob = 1 << 6, // The toggle should have a shadow drawn under the knob.
|
||||
// Bit 7 reserved.
|
||||
ImGuiToggleFlags_A11y = 1 << 8, // The toggle should draw on and off glyphs to help indicate its state.
|
||||
ImGuiToggleFlags_Bordered = ImGuiToggleFlags_BorderedFrame | ImGuiToggleFlags_BorderedKnob, // Shorthand for bordered frame and knob.
|
||||
ImGuiToggleFlags_Shadowed = ImGuiToggleFlags_ShadowedFrame | ImGuiToggleFlags_ShadowedKnob, // Shorthand for shadowed frame and knob.
|
||||
ImGuiToggleFlags_Default = ImGuiToggleFlags_None, // The default flags used when no ImGuiToggleFlags_ are specified.
|
||||
};
|
||||
|
||||
// ImGuiToggleA11yStyle: Styles to draw A11y labels.
|
||||
enum ImGuiToggleA11yStyle_
|
||||
{
|
||||
ImGuiToggleA11yStyle_Label, // A11y glyphs draw as text labels.
|
||||
ImGuiToggleA11yStyle_Glyph, // A11y glyphs draw as power-icon style "I/O" glyphs.
|
||||
ImGuiToggleA11yStyle_Dot, // A11y glyphs draw as a small dot that can be colored separately from the frame.
|
||||
ImGuiToggleA11yStyle_Default = ImGuiToggleA11yStyle_Label, // Default: text labels.
|
||||
};
|
||||
|
||||
// ImGuiToggleConstants: A set of defaults and limits used by ImGuiToggleConfig
|
||||
namespace ImGuiToggleConstants
|
||||
{
|
||||
// The golden ratio.
|
||||
constexpr float Phi = 1.6180339887498948482045f;
|
||||
|
||||
// d = 2r
|
||||
constexpr float DiameterToRadiusRatio = 0.5f;
|
||||
|
||||
// Animation is disabled with a animation_duration of 0.
|
||||
constexpr float AnimationDurationDisabled = 0.0f;
|
||||
|
||||
// The default animation duration, in seconds. (0.1f: 100 ms.)
|
||||
constexpr float AnimationDurationDefault = 0.1f;
|
||||
|
||||
// The lowest allowable value for animation duration. (0.0f: Disabled animation.)
|
||||
constexpr float AnimationDurationMinimum = AnimationDurationDisabled;
|
||||
|
||||
// The default frame rounding value. (1.0f: Full rounding.)
|
||||
constexpr float FrameRoundingDefault = 1.0f;
|
||||
|
||||
// The minimum frame rounding value. (0.0f: Full rectangle.)
|
||||
constexpr float FrameRoundingMinimum = 0.0f;
|
||||
|
||||
// The maximum frame rounding value. (1.0f: Full rounding.)
|
||||
constexpr float FrameRoundingMaximum = 1.0f;
|
||||
|
||||
// The default knob rounding value. (1.0f: Full rounding.)
|
||||
constexpr float KnobRoundingDefault = 1.0f;
|
||||
|
||||
// The minimum knob rounding value. (0.0f: Full rectangle.)
|
||||
constexpr float KnobRoundingMinimum = 0.0f;
|
||||
|
||||
// The maximum knob rounding value. (1.0f: Full rounding.)
|
||||
constexpr float KnobRoundingMaximum = 1.0f;
|
||||
|
||||
// The default height to width ratio. (Phi: The golden ratio.)
|
||||
constexpr float WidthRatioDefault = Phi;
|
||||
|
||||
// The minimum allowable width ratio. (1.0f: Toggle width==height, not very useful but interesting.)
|
||||
constexpr float WidthRatioMinimum = 1.0f;
|
||||
|
||||
// The maximum allowable width ratio. (10.0f: It starts to get silly quickly.)
|
||||
constexpr float WidthRatioMaximum = 10.0f;
|
||||
|
||||
// The default amount of pixels the knob should be inset into the toggle frame. (1.5f in each direction: Pleasing to the eye.)
|
||||
constexpr ImOffsetRect KnobInsetDefault = 1.5f;
|
||||
|
||||
// The minimum amount of pixels the knob should be negatively inset (outset) from the toggle frame. (-100.0f: Big overgrown toggle.)
|
||||
constexpr float KnobInsetMinimum = -100.0f;
|
||||
|
||||
// The maximum amount of pixels the knob should be inset into the toggle frame. (100.0f: Toggle likely invisible!)
|
||||
constexpr float KnobInsetMaximum = 100.0f;
|
||||
|
||||
// The default thickness for borders drawn on the toggle frame and knob.
|
||||
constexpr float BorderThicknessDefault = 1.0f;
|
||||
|
||||
// The default thickness for shadows drawn under the toggle frame and knob.
|
||||
constexpr float ShadowThicknessDefault = 2.0f;
|
||||
|
||||
// The default a11y string used when the toggle is on.
|
||||
const char* const LabelA11yOnDefault = "1";
|
||||
|
||||
// The default a11y string used when the toggle is off.
|
||||
const char* const LabelA11yOffDefault = "0";
|
||||
}
|
||||
|
||||
struct ImGuiToggleStateConfig
|
||||
{
|
||||
// The thickness the border should be drawn on the frame when ImGuiToggleFlags_BorderedFrame is specified in `Flags`.
|
||||
float FrameBorderThickness = ImGuiToggleConstants::BorderThicknessDefault;
|
||||
|
||||
// The thickness the shadow should be drawn on the frame when ImGuiToggleFlags_ShadowedFrame is specified in `Flags`.
|
||||
float FrameShadowThickness = ImGuiToggleConstants::ShadowThicknessDefault;
|
||||
|
||||
// The thickness the border should be drawn on the frame when ImGuiToggleFlags_BorderedKnob is specified in `Flags`.
|
||||
float KnobBorderThickness = ImGuiToggleConstants::BorderThicknessDefault;
|
||||
|
||||
// The thickness the shadow should be drawn on the frame when ImGuiToggleFlags_ShadowedKnob is specified in `Flags`.
|
||||
float KnobShadowThickness = ImGuiToggleConstants::ShadowThicknessDefault;
|
||||
|
||||
// The label drawn on the toggle to show the toggle is in the when ImGuiToggleFlags_A11yLabels is specified in `Flags`.
|
||||
// If left null, default strings will be used.
|
||||
const char* Label = nullptr;
|
||||
|
||||
// The number of pixels the knob should be inset into the toggle frame.
|
||||
// With a round (circle) knob, an average of each offset will be used.
|
||||
// With a rectangular knob, each offset will be used in it's intended direction.
|
||||
ImOffsetRect KnobInset = ImGuiToggleConstants::KnobInsetDefault;
|
||||
|
||||
// A custom amount of pixels to offset the knob by. Positive x values will move the knob towards the inside, negative the outside.
|
||||
ImVec2 KnobOffset = ImVec2(0.0f, 0.0f);
|
||||
|
||||
// An optional custom palette to use for the colors to use when drawing the toggle. If left null, theme colors will be used.
|
||||
// If any of the values in the palette are zero, those specific colors will default to theme colors.
|
||||
const ImGuiTogglePalette* Palette = nullptr;
|
||||
};
|
||||
|
||||
// ImGuiToggleConfig: A collection of data used to customize the appearance and behavior of a toggle widget.
|
||||
struct ImGuiToggleConfig
|
||||
{
|
||||
// Flags that control the toggle's behavior and display.
|
||||
ImGuiToggleFlags Flags = ImGuiToggleFlags_Default;
|
||||
|
||||
// What style of A11y glyph to draw on the widget.
|
||||
ImGuiToggleFlags A11yStyle = ImGuiToggleA11yStyle_Default;
|
||||
|
||||
// The a duration in seconds that the toggle should animate for. If 0, animation will be disabled.
|
||||
float AnimationDuration = ImGuiToggleConstants::AnimationDurationDefault;
|
||||
|
||||
// A scalar that controls how rounded the toggle frame is. 0 is square, 1 is round. (0, 1) default 1.0f
|
||||
float FrameRounding = ImGuiToggleConstants::FrameRoundingDefault;
|
||||
|
||||
//A scalar that controls how rounded the toggle knob is. 0 is square, 1 is round. (0, 1) default 1.0f
|
||||
float KnobRounding = ImGuiToggleConstants::KnobRoundingDefault;
|
||||
|
||||
// A ratio that controls how wide the toggle is compared to it's height. If `Size.x` is non-zero, this value is ignored.
|
||||
float WidthRatio = ImGuiToggleConstants::WidthRatioDefault;
|
||||
|
||||
// A custom size to draw the toggle with. Defaults to (0, 0). If `Size.x` is zero, `WidthRatio` will control the toggle width.
|
||||
// If `Size.y` is zero, the toggle height will be set by `ImGui::GetFrameHeight()`.
|
||||
ImVec2 Size = ImVec2(0.0f, 0.0f);
|
||||
|
||||
// Specific configuration data to use when the knob is in the on state.
|
||||
ImGuiToggleStateConfig On;
|
||||
|
||||
// Specific configuration data to use when the knob is in the off state.
|
||||
ImGuiToggleStateConfig Off;
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#endif // IMGUI_DEFINE_MATH_OPERATORS
|
||||
#include "imgui_internal.h"
|
||||
|
||||
namespace ImGuiToggleMath
|
||||
{
|
||||
// lerp, but backwards!
|
||||
template<typename T> constexpr inline T ImInvLerp(T a, T b, float value) { return (T)((value - a) / (b - a)); }
|
||||
|
||||
// float comparison w/tolerance - can't constexper as ImFabs isn't constexpr.
|
||||
inline bool ImApproximately(float a, float b, float tolerance = 0.0001f) { return ImAbs(a - b) < tolerance; }
|
||||
|
||||
// helpers for checking if an ImVec4 is zero or not.
|
||||
constexpr inline bool IsZero(const ImVec4& v) { return v.w == 0 && v.x == 0 && v.y == 0 && v.z == 0; }
|
||||
constexpr inline bool IsNonZero(const ImVec4& v) { return v.w != 0 || v.x != 0 || v.y != 0 || v.z != 0; }
|
||||
} // namespace
|
||||
@@ -0,0 +1,84 @@
|
||||
#include "imgui_toggle_palette.hpp"
|
||||
#include "imgui_toggle_math.hpp"
|
||||
|
||||
#include "imgui.h"
|
||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#endif // IMGUI_DEFINE_MATH_OPERATORS
|
||||
#include "imgui_internal.h"
|
||||
|
||||
using namespace ImGuiToggleMath;
|
||||
|
||||
void ImGui::UnionPalette(ImGuiTogglePalette* target, const ImGuiTogglePalette* candidate, const ImVec4 colors[], bool v)
|
||||
{
|
||||
|
||||
target->Knob = colors[ImGuiCol_Text];
|
||||
target->KnobHover = colors[ImGuiCol_Text];
|
||||
target->Frame = colors[!v ? ImGuiCol_FrameBg : ImGuiCol_Button];
|
||||
target->FrameHover = colors[!v ? ImGuiCol_FrameBgHovered : ImGuiCol_ButtonHovered];
|
||||
target->FrameBorder = colors[ImGuiCol_Border];
|
||||
target->FrameShadow = colors[ImGuiCol_BorderShadow];
|
||||
target->KnobBorder = colors[ImGuiCol_Border];
|
||||
target->KnobShadow = colors[ImGuiCol_BorderShadow];
|
||||
target->A11yGlyph = colors[!v ? ImGuiCol_FrameBg : ImGuiCol_Text];
|
||||
|
||||
// if the user didn't provide a candidate, just provide the theme colored palette.
|
||||
if (candidate == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// if the user did provide a candidate, populate all non-zero colors
|
||||
#define GET_PALETTE_POPULATE_NONZERO(member) \
|
||||
do { \
|
||||
if (IsNonZero(candidate->member)) \
|
||||
{ \
|
||||
target->member = candidate->member; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
GET_PALETTE_POPULATE_NONZERO(Knob);
|
||||
GET_PALETTE_POPULATE_NONZERO(KnobHover);
|
||||
GET_PALETTE_POPULATE_NONZERO(Frame);
|
||||
GET_PALETTE_POPULATE_NONZERO(FrameHover);
|
||||
GET_PALETTE_POPULATE_NONZERO(FrameBorder);
|
||||
GET_PALETTE_POPULATE_NONZERO(FrameShadow);
|
||||
GET_PALETTE_POPULATE_NONZERO(KnobBorder);
|
||||
GET_PALETTE_POPULATE_NONZERO(KnobShadow);
|
||||
GET_PALETTE_POPULATE_NONZERO(A11yGlyph);
|
||||
|
||||
#undef GET_PALETTE_POPULATE_NONZERO
|
||||
}
|
||||
|
||||
void ImGui::BlendPalettes(ImGuiTogglePalette* result, const ImGuiTogglePalette& a, const ImGuiTogglePalette& b, float blend_amount)
|
||||
{
|
||||
// a quick out for if we are at either end of the blend.
|
||||
if (ImApproximately(blend_amount, 0.0f))
|
||||
{
|
||||
*result = a;
|
||||
return;
|
||||
}
|
||||
else if (ImApproximately(blend_amount, 1.0f))
|
||||
{
|
||||
*result = b;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
#define BLEND_PALETTES_LERP(member) \
|
||||
do { \
|
||||
result->member = ImLerp(a.member, b.member, blend_amount); \
|
||||
} while (0)
|
||||
|
||||
BLEND_PALETTES_LERP(Knob);
|
||||
BLEND_PALETTES_LERP(KnobHover);
|
||||
BLEND_PALETTES_LERP(Frame);
|
||||
BLEND_PALETTES_LERP(FrameHover);
|
||||
BLEND_PALETTES_LERP(FrameBorder);
|
||||
BLEND_PALETTES_LERP(FrameShadow);
|
||||
BLEND_PALETTES_LERP(KnobBorder);
|
||||
BLEND_PALETTES_LERP(KnobShadow);
|
||||
BLEND_PALETTES_LERP(A11yGlyph);
|
||||
|
||||
#undef BLEND_PALETTES_LERP
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
// ImGuiTogglePalette: A collection of colors used to customize the rendering of a toggle widget.
|
||||
// Leaving any ImVec4 as default (zero) will allow the theme color to be used for that member.
|
||||
struct ImGuiTogglePalette
|
||||
{
|
||||
// The default knob color.
|
||||
ImVec4 Knob;
|
||||
|
||||
// The default knob color, used when when the knob is hovered.
|
||||
ImVec4 KnobHover;
|
||||
|
||||
// The background color of the toggle frame.
|
||||
ImVec4 Frame;
|
||||
|
||||
// The background color of the toggle frame when the toggle is hovered.
|
||||
ImVec4 FrameHover;
|
||||
|
||||
// The background color of the toggle frame's border used when ImGuiToggleFlags_BorderedFrame is specified.
|
||||
ImVec4 FrameBorder;
|
||||
|
||||
// The shadow color of the toggle frame.
|
||||
ImVec4 FrameShadow;
|
||||
|
||||
// The background color of the toggle knob's border used when ImGuiToggleFlags_BorderedKnob is specified.
|
||||
ImVec4 KnobBorder;
|
||||
|
||||
// The shadow color of the toggle knob.
|
||||
ImVec4 KnobShadow;
|
||||
|
||||
// The color of the accessibility label or glyph.
|
||||
ImVec4 A11yGlyph;
|
||||
};
|
||||
|
||||
namespace ImGui
|
||||
{
|
||||
void UnionPalette(ImGuiTogglePalette* target, const ImGuiTogglePalette* candidate, const ImVec4 colors[], bool v);
|
||||
void BlendPalettes(ImGuiTogglePalette* result, const ImGuiTogglePalette& a, const ImGuiTogglePalette& b, float blend_amount);
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
#include "imgui_toggle_presets.hpp"
|
||||
#include "imgui_toggle_palette.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
// some color values shared between styles
|
||||
const ImVec4 White(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
const ImVec4 Black(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
const ImVec4 Green(0.24f, 0.52f, 0.15f, 1.0f);
|
||||
const ImVec4 GreenBorder(0.39f, 0.62f, 0.32f, 1.0f);
|
||||
const ImVec4 GreenHighlight(0.3f, 1.0f, 0.0f, 0.75f);
|
||||
const ImVec4 RedHighlight(1.0f, 0.3f, 0.0f, 0.75f);
|
||||
|
||||
} // namespace
|
||||
|
||||
ImGuiToggleConfig ImGuiTogglePresets::DefaultStyle()
|
||||
{
|
||||
return ImGuiToggleConfig();
|
||||
}
|
||||
|
||||
ImGuiToggleConfig ImGuiTogglePresets::RectangleStyle()
|
||||
{
|
||||
ImGuiToggleConfig config;
|
||||
config.Flags |= ImGuiToggleFlags_Animated;
|
||||
config.FrameRounding = 0.1f;
|
||||
config.KnobRounding = 0.3f;
|
||||
config.AnimationDuration = 0.5f;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
ImGuiToggleConfig ImGuiTogglePresets::GlowingStyle()
|
||||
{
|
||||
static ImGuiTogglePalette palette_on;
|
||||
palette_on.FrameShadow = ::GreenHighlight;
|
||||
palette_on.KnobShadow = ::GreenHighlight;
|
||||
|
||||
static ImGuiTogglePalette palette_off;
|
||||
palette_off.FrameShadow = ::RedHighlight;
|
||||
palette_off.KnobShadow = ::RedHighlight;
|
||||
|
||||
ImGuiToggleConfig config;
|
||||
config.Flags |= ImGuiToggleFlags_Animated | ImGuiToggleFlags_Shadowed;
|
||||
config.On.Palette = &palette_on;
|
||||
config.Off.Palette = &palette_off;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
ImGuiToggleConfig ImGuiTogglePresets::iOSStyle(const float size_scale /*= 1.0f*/, bool light_mode /*= false*/)
|
||||
{
|
||||
const ImVec4 frame_on(0.3f, 0.85f, 0.39f, 1.0f);
|
||||
const ImVec4 frame_on_hover(0.0f, 1.0f, 0.57f, 1.0f);
|
||||
const ImVec4 dark_mode_frame_off(0.22f, 0.22f, 0.24f, 1.0f);
|
||||
const ImVec4 light_mode_frame_off(0.91f, 0.91f, 0.92f, 1.0f);
|
||||
const ImVec4 dark_mode_frame_off_hover(0.4f, 0.4f, 0.4f, 1.0f);
|
||||
const ImVec4 light_mode_frame_off_hover(0.7f, 0.7f, 0.7f, 1.0f);
|
||||
const ImVec4 light_gray(0.89f, 0.89f, 0.89f, 1.0f);
|
||||
const ImVec4 a11y_glyph_on(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
const ImVec4 a11y_glyph_off(0.4f, 0.4f, 0.4f, 1.0f);
|
||||
|
||||
const float ios_width = 153 * size_scale;
|
||||
const float ios_height = 93 * size_scale;
|
||||
const float ios_frame_border_thickness = 0.0f * size_scale;
|
||||
const float ios_border_thickness = 0.0f * size_scale;
|
||||
const float ios_offset = 0.0f * size_scale;
|
||||
const float ios_inset = 6.0f * size_scale;
|
||||
|
||||
// setup 'on' colors
|
||||
static ImGuiTogglePalette ios_palette_on;
|
||||
ios_palette_on.Knob = ::White;
|
||||
ios_palette_on.Frame = frame_on;
|
||||
ios_palette_on.FrameHover = frame_on_hover;
|
||||
ios_palette_on.KnobBorder = light_gray;
|
||||
ios_palette_on.FrameBorder = light_gray;
|
||||
ios_palette_on.A11yGlyph = a11y_glyph_on;
|
||||
|
||||
// setup 'off' colors
|
||||
static ImGuiTogglePalette ios_palette_off;
|
||||
ios_palette_off.Knob = ::White;
|
||||
ios_palette_off.Frame = light_mode ? light_mode_frame_off : dark_mode_frame_off;
|
||||
ios_palette_off.FrameHover = light_mode ? light_mode_frame_off_hover : light_mode_frame_off_hover;
|
||||
ios_palette_off.KnobBorder = light_gray;
|
||||
ios_palette_off.FrameBorder = light_gray;
|
||||
ios_palette_off.A11yGlyph = a11y_glyph_off;
|
||||
|
||||
// setup config
|
||||
ImGuiToggleConfig config;
|
||||
config.Size = ImVec2(ios_width, ios_height);
|
||||
config.Flags |= ImGuiToggleFlags_A11y
|
||||
| ImGuiToggleFlags_Animated
|
||||
| (light_mode ? ImGuiToggleFlags_Bordered : 0);
|
||||
config.A11yStyle = ImGuiToggleA11yStyle_Glyph;
|
||||
|
||||
// setup 'on' config
|
||||
config.On.FrameBorderThickness = 0;
|
||||
config.On.KnobBorderThickness = ios_border_thickness;
|
||||
config.On.KnobOffset = ImVec2(ios_offset, 0);
|
||||
config.On.KnobInset = ios_inset;
|
||||
config.On.Palette = &ios_palette_on;
|
||||
|
||||
// setup 'off' config
|
||||
config.Off.FrameBorderThickness = ios_frame_border_thickness;
|
||||
config.Off.KnobBorderThickness = ios_border_thickness;
|
||||
config.Off.KnobOffset = ImVec2(ios_offset, 0);
|
||||
config.Off.KnobInset = ios_inset;
|
||||
config.Off.Palette = &ios_palette_off;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
ImGuiToggleConfig ImGuiTogglePresets::MaterialStyle(float size_scale /*= 1.0f*/)
|
||||
{
|
||||
const ImVec4 purple(0.4f, 0.08f, 0.97f, 1.0f);
|
||||
const ImVec4 purple_dim(0.78f, 0.65f, 0.99f, 1.0f);
|
||||
const ImVec4 purple_hover(0.53f, 0.08f, 1.0f, 1.0f);
|
||||
|
||||
const ImVec2 material_size(37 * size_scale, 16 * size_scale);
|
||||
const float material_inset = -2.5f * size_scale;
|
||||
|
||||
static ImGuiTogglePalette material_palette_on;
|
||||
material_palette_on.Frame = purple_dim;
|
||||
material_palette_on.FrameHover = purple_dim;
|
||||
material_palette_on.Knob = purple;
|
||||
material_palette_on.KnobHover = purple_hover;
|
||||
|
||||
// setup config
|
||||
ImGuiToggleConfig config;
|
||||
config.Flags |= ImGuiToggleFlags_Animated;
|
||||
config.Size = material_size;
|
||||
config.On.KnobInset = config.Off.KnobInset = material_inset;
|
||||
config.On.KnobOffset = config.Off.KnobOffset = ImVec2(-material_inset, 0);
|
||||
config.On.Palette = &material_palette_on;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
ImGuiToggleConfig ImGuiTogglePresets::MinecraftStyle(float size_scale /*= 1.0f*/)
|
||||
{
|
||||
const ImVec4 gray(0.35f, 0.35f, 0.35f, 1.0f);
|
||||
const ImVec4 dark_gray(0.12f, 0.12f, 0.12f, 1.0f);
|
||||
const ImVec4 frame_border_off(0.6f, 0.6f, 0.61f, 1.0f);
|
||||
const ImVec4 toggle_frame_off(0.55f, 0.55f, 0.56f, 1.0f);
|
||||
const ImVec4 gray_knob(0.82f, 0.82f, 0.83f, 1.0f);
|
||||
|
||||
const ImVec2 minecraft_knob_size(56.0f * size_scale, 40.0f * size_scale);
|
||||
const ImVec2 minecraft_size(104.0f * size_scale, 40.0f * size_scale); // 112x48
|
||||
const float minecraft_borders = 4.0f * size_scale;
|
||||
const ImVec2 minecraft_offset(0.0f * size_scale, -minecraft_borders * 2.0f);
|
||||
const ImOffsetRect minecraft_inset(
|
||||
0.0f * size_scale, // top
|
||||
-16.0f * size_scale, // left
|
||||
0.0f * size_scale, // bottom
|
||||
0.0f * size_scale // right
|
||||
);
|
||||
const float minecraft_rounding = 0.0f; // disable rounding
|
||||
const float minecraft_shadows = 4.0f * size_scale;
|
||||
|
||||
// set up the "on" palette.
|
||||
static ImGuiTogglePalette minecraft_palette_on;
|
||||
minecraft_palette_on.Frame = ::Green;
|
||||
minecraft_palette_on.FrameHover = ::Green;
|
||||
minecraft_palette_on.FrameBorder = ::GreenBorder;
|
||||
minecraft_palette_on.FrameShadow = ::Black;
|
||||
minecraft_palette_on.Knob = gray_knob;
|
||||
minecraft_palette_on.KnobHover = gray_knob;
|
||||
minecraft_palette_on.A11yGlyph = ::White;
|
||||
minecraft_palette_on.KnobBorder = ::White;
|
||||
minecraft_palette_on.KnobShadow = ::Black;
|
||||
|
||||
// start the "off" palette as a copy of the on
|
||||
static ImGuiTogglePalette minecraft_palette_off;
|
||||
minecraft_palette_off = minecraft_palette_on;
|
||||
|
||||
// make changes to the off palette
|
||||
minecraft_palette_off.Frame = toggle_frame_off;
|
||||
minecraft_palette_off.FrameHover = toggle_frame_off;
|
||||
minecraft_palette_off.FrameBorder = frame_border_off;
|
||||
|
||||
// setup config
|
||||
ImGuiToggleConfig config;
|
||||
config.Flags |= ImGuiToggleFlags_A11y | ImGuiToggleFlags_Bordered | ImGuiToggleFlags_Shadowed;
|
||||
config.Size = minecraft_size;
|
||||
config.FrameRounding = minecraft_rounding;
|
||||
config.KnobRounding = minecraft_rounding;
|
||||
config.A11yStyle = ImGuiToggleA11yStyle_Glyph;
|
||||
|
||||
// set up the "on" state configuration
|
||||
config.On.KnobInset = minecraft_inset;
|
||||
config.On.KnobOffset = minecraft_offset;
|
||||
config.On.FrameBorderThickness = minecraft_borders;
|
||||
config.On.FrameShadowThickness = minecraft_shadows;
|
||||
config.On.KnobBorderThickness = minecraft_borders;
|
||||
config.On.KnobShadowThickness = minecraft_shadows;
|
||||
config.On.Palette = &minecraft_palette_on;
|
||||
|
||||
// duplicate the "on" config to the "off", then make changes.
|
||||
config.Off = config.On;
|
||||
config.Off.KnobInset = minecraft_inset.MirrorHorizontally();
|
||||
config.Off.Palette = &minecraft_palette_off;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "imgui_toggle.hpp"
|
||||
|
||||
// ImGuiTogglePresets: A few canned configurations for various presets OOTB.
|
||||
namespace ImGuiTogglePresets
|
||||
{
|
||||
// The default, unmodified style.
|
||||
ImGuiToggleConfig DefaultStyle();
|
||||
|
||||
// A style similar to default, but with rectangular knob and frame.
|
||||
ImGuiToggleConfig RectangleStyle();
|
||||
|
||||
// A style that uses a shadow to appear to glow while it's on.
|
||||
ImGuiToggleConfig GlowingStyle();
|
||||
|
||||
// A style that emulates what a toggle on iOS looks like.
|
||||
ImGuiToggleConfig iOSStyle(float size_scale = 1.0f, bool light_mode = false);
|
||||
|
||||
// A style that emulates what a Material Design toggle looks like.
|
||||
ImGuiToggleConfig MaterialStyle(float size_scale = 1.0f);
|
||||
|
||||
// A style that emulates what a toggle close to one from Minecraft.
|
||||
ImGuiToggleConfig MinecraftStyle(float size_scale = 1.0f);
|
||||
}
|
||||
@@ -0,0 +1,519 @@
|
||||
#include "imgui_toggle_renderer.hpp"
|
||||
#include "imgui_toggle_palette.hpp"
|
||||
#include "imgui_toggle_math.hpp"
|
||||
|
||||
using namespace ImGuiToggleConstants;
|
||||
using namespace ImGuiToggleMath;
|
||||
|
||||
namespace
|
||||
{
|
||||
// a small helper to quickly check the mixed value flag.
|
||||
inline bool IsItemMixedValue()
|
||||
{
|
||||
return (GImGui->LastItemData.ItemFlags & ImGuiItemFlags_MixedValue) != 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ImGuiToggleRenderer::ImGuiToggleRenderer()
|
||||
{
|
||||
SetConfig(nullptr, nullptr, ImGuiToggleConfig());
|
||||
}
|
||||
|
||||
ImGuiToggleRenderer::ImGuiToggleRenderer(const char* label, bool* value, const ImGuiToggleConfig& user_config) : _style(nullptr), _label(label), _value(value)
|
||||
{
|
||||
SetConfig(label, value, user_config);
|
||||
}
|
||||
|
||||
void ImGuiToggleRenderer::SetConfig(const char* label, bool* value, const ImGuiToggleConfig& user_config)
|
||||
{
|
||||
// store mandatory settings
|
||||
_label = label;
|
||||
_value = value;
|
||||
|
||||
// copy our user's config and ensure it's valid.
|
||||
_config = user_config;
|
||||
ValidateConfig();
|
||||
}
|
||||
|
||||
bool ImGuiToggleRenderer::Render()
|
||||
{
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
|
||||
IM_ASSERT(window);
|
||||
IM_ASSERT(_label != nullptr);
|
||||
IM_ASSERT(_value != nullptr);
|
||||
|
||||
if (window->SkipItems)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// update igui context
|
||||
g = GImGui;
|
||||
_id = window->GetID(_label);
|
||||
_drawList = ImGui::GetWindowDrawList();
|
||||
_style = &ImGui::GetStyle();
|
||||
|
||||
// calculate the size of the toggle portion
|
||||
const float height = _config.Size.y > 0
|
||||
? _config.Size.y
|
||||
: ImGui::GetFrameHeight();
|
||||
const float width = _config.Size.x > 0
|
||||
? _config.Size.x
|
||||
: height * _config.WidthRatio;
|
||||
|
||||
// get the position of the widget and how large the label should be
|
||||
ImVec2 widget_position = window->DC.CursorPos;
|
||||
ImVec2 label_size = ImGui::CalcTextSize(_label, nullptr, true);
|
||||
|
||||
// if the knob is offset horizontally outside of the frame in the on state, we want to bump our label over.
|
||||
const float label_x_offset = ImMax(0.0f, -_config.On.KnobOffset.x / 2.0f);
|
||||
|
||||
// calculate bounding boxes for the toggle, and the whole widget including the label for interaction
|
||||
_boundingBox = ImRect(widget_position, widget_position + ImVec2(width, height));
|
||||
ImRect total_bounding_box = ImRect(widget_position,
|
||||
widget_position
|
||||
+ ImVec2(
|
||||
width + (label_size.x > 0.0f ? _style->ItemInnerSpacing.x + label_size.x : 0.0f) + label_x_offset,
|
||||
ImMax(height, label_size.y) + _style->FramePadding.y * 2.0f
|
||||
));
|
||||
|
||||
// handle the toggle input behavior
|
||||
bool pressed = ToggleBehavior(total_bounding_box);
|
||||
_isMixedValue = ::IsItemMixedValue();
|
||||
|
||||
// draw the toggle itself and the label
|
||||
DrawToggle();
|
||||
DrawLabel(label_x_offset);
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(_id, _label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*_value ? ImGuiItemStatusFlags_Checked : 0));
|
||||
return pressed;
|
||||
}
|
||||
|
||||
|
||||
void ImGuiToggleRenderer::ValidateConfig()
|
||||
{
|
||||
IM_ASSERT_USER_ERROR(_config.Size.x >= 0, "Size.x specified was negative.");
|
||||
IM_ASSERT_USER_ERROR(_config.Size.y >= 0, "Size.y specified was negative.");
|
||||
|
||||
// if no flags were specified, use defaults.
|
||||
if (_config.Flags == ImGuiToggleFlags_None)
|
||||
{
|
||||
_config.Flags = ImGuiToggleFlags_Default;
|
||||
}
|
||||
|
||||
// a zero or negative duration would prevent animation.
|
||||
_config.AnimationDuration = ImMax(_config.AnimationDuration, AnimationDurationMinimum);
|
||||
|
||||
// keep our size/scale and rounding numbers sane.
|
||||
_config.FrameRounding = ImClamp(_config.FrameRounding, FrameRoundingMinimum, FrameRoundingMaximum);
|
||||
_config.KnobRounding = ImClamp(_config.KnobRounding, KnobRoundingMinimum, KnobRoundingMaximum);
|
||||
_config.WidthRatio = ImClamp(_config.WidthRatio, WidthRatioMinimum, WidthRatioMaximum);
|
||||
|
||||
// Make sure our a11y labels have values.
|
||||
if (_config.On.Label == nullptr)
|
||||
{
|
||||
_config.On.Label = LabelA11yOnDefault;
|
||||
}
|
||||
|
||||
if (_config.Off.Label == nullptr)
|
||||
{
|
||||
_config.Off.Label = LabelA11yOffDefault;
|
||||
}
|
||||
}
|
||||
|
||||
bool ImGuiToggleRenderer::ToggleBehavior(const ImRect& interaction_bounding_box)
|
||||
{
|
||||
ImGui::ItemSize(interaction_bounding_box, _style->FramePadding.y);
|
||||
if (!ImGui::ItemAdd(interaction_bounding_box, _id))
|
||||
{
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(_id, _label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*_value ? ImGuiItemStatusFlags_Checked : 0));
|
||||
return false;
|
||||
}
|
||||
|
||||
// the meat and potatoes: the actual toggle button
|
||||
const ImGuiButtonFlags button_flags = ImGuiButtonFlags_PressedOnClick;
|
||||
bool hovered, held;
|
||||
bool pressed = ImGui::ButtonBehavior(interaction_bounding_box, _id, &hovered, &held, button_flags);
|
||||
if (pressed)
|
||||
{
|
||||
*_value = !(*_value);
|
||||
ImGui::MarkItemEdited(_id);
|
||||
}
|
||||
|
||||
return pressed;
|
||||
}
|
||||
|
||||
void ImGuiToggleRenderer::DrawToggle()
|
||||
{
|
||||
const float height = GetHeight();
|
||||
const float width = GetWidth();
|
||||
|
||||
// update imgui state
|
||||
_isHovered = g->HoveredId == _id;
|
||||
_isLastActive = g->LastActiveId == _id;
|
||||
_lastActiveTimer = g->LastActiveIdTimer;
|
||||
|
||||
// radius is by default half the diameter
|
||||
const float knob_radius = height * DiameterToRadiusRatio;
|
||||
|
||||
// update the toggle's animation timer, state, and palette.
|
||||
UpdateAnimationPercent();
|
||||
UpdateStateConfig();
|
||||
UpdatePalette();
|
||||
|
||||
// get colors modified by hover.
|
||||
const ImU32 color_frame = ImGui::GetColorU32((_isHovered || *_value) ? _palette.FrameHover : _palette.Frame);
|
||||
const ImU32 color_knob = ImGui::GetColorU32((_isHovered || *_value) ? _palette.KnobHover : _palette.Knob);
|
||||
|
||||
|
||||
// draw the background frame
|
||||
DrawFrame(color_frame);
|
||||
|
||||
// draw accessibility labels, if enabled.
|
||||
if (HasA11yGlyphs())
|
||||
{
|
||||
DrawA11yFrameOverlays(knob_radius);
|
||||
}
|
||||
|
||||
// draw the knob
|
||||
if (HasCircleKnob())
|
||||
{
|
||||
DrawCircleKnob(knob_radius, color_knob);
|
||||
}
|
||||
else if (HasRectangleKnob())
|
||||
{
|
||||
DrawRectangleKnob(knob_radius, color_knob);
|
||||
}
|
||||
else
|
||||
{
|
||||
// user didn't specify a knob mode, they get no knob.
|
||||
IM_ASSERT_USER_ERROR(false, "No toggle knob type to draw.");
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiToggleRenderer::DrawFrame(ImU32 color_frame)
|
||||
{
|
||||
const float height = GetHeight();
|
||||
const float frame_rounding = _config.FrameRounding >= 0
|
||||
? height * _config.FrameRounding
|
||||
: height * 0.5f;
|
||||
|
||||
// draw frame shadow, if enabled
|
||||
if (HasShadowedFrame())
|
||||
{
|
||||
const ImU32 color_frame_shadow = ImGui::GetColorU32(_palette.FrameShadow);
|
||||
DrawRectShadow(_boundingBox, color_frame_shadow, frame_rounding, _state.FrameShadowThickness);
|
||||
}
|
||||
|
||||
// draw frame background
|
||||
_drawList->AddRectFilled(_boundingBox.Min, _boundingBox.Max, color_frame, frame_rounding);
|
||||
|
||||
// draw frame border, if enabled
|
||||
if (HasBorderedFrame())
|
||||
{
|
||||
const ImU32 color_frame_border = ImGui::GetColorU32(_palette.FrameBorder);
|
||||
DrawRectBorder(_boundingBox, color_frame_border, frame_rounding, _state.FrameBorderThickness);
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiToggleRenderer::DrawA11yDot(const ImVec2& pos, ImU32 color)
|
||||
{
|
||||
ImGui::RenderBullet(_drawList, pos, color);
|
||||
}
|
||||
|
||||
void ImGuiToggleRenderer::DrawA11yGlyph(ImVec2 pos, ImU32 color, bool state, float radius, float thickness)
|
||||
{
|
||||
if (state)
|
||||
{
|
||||
// draw the I bar
|
||||
const float half_thickness = thickness * 0.5f;
|
||||
const ImVec2 offset(half_thickness, radius);
|
||||
_drawList->AddRectFilled(pos - offset, pos + offset, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
// draw the O ring
|
||||
const float o_adjustment = 1.0f;
|
||||
const float o_radius = radius - o_adjustment;
|
||||
const float o_thickness = thickness + o_adjustment;
|
||||
pos.x += o_adjustment;
|
||||
_drawList->AddCircle(pos, o_radius, color, 0, o_thickness);
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiToggleRenderer::DrawA11yLabel(ImVec2 pos, ImU32 color, const char* label)
|
||||
{
|
||||
// subtract out half the sizes of the text to center them
|
||||
const ImVec2 text_size = ImGui::CalcTextSize(label);
|
||||
pos.x -= (text_size.x * 0.5f);
|
||||
pos.y -= (text_size.y * 0.5f);
|
||||
|
||||
// draw the label.
|
||||
_drawList->AddText(pos, color, label);
|
||||
}
|
||||
|
||||
void ImGuiToggleRenderer::DrawA11yFrameOverlay(float knob_radius, bool state)
|
||||
{
|
||||
const float AnimationPercentOff = 0.0f;
|
||||
const float AnimationPercentOn = 1.0f;
|
||||
|
||||
// notice we swap the animation percents as compared to the labels/glyphs, as we want to draw the
|
||||
// a11y labels where the knob *isn't* when it's in a given state.
|
||||
ImVec2 pos = CalculateKnobCenter(knob_radius, state ? AnimationPercentOff : AnimationPercentOn);
|
||||
|
||||
// next, we want to adjust the position to move to a more pleasing spot in the toggle.
|
||||
// this is just some tinkering that got to a nice looking area based on the sizes,
|
||||
// but this is subject to change.
|
||||
const float diameter = ImMax(1.0f, GetHeight() / 3.0f);
|
||||
const float radius = diameter * 0.5f;
|
||||
const float thickness = ImCeil(radius * 0.2f);
|
||||
const ImVec2 adjustment = ImVec2(radius - thickness, 0.0f)
|
||||
* (state ? -1.0f : 1.0f); // if state is true, we want to subtract rather than add.
|
||||
|
||||
pos += adjustment;
|
||||
|
||||
const ImU32 color = state
|
||||
? ImGui::GetColorU32(_colorA11yGlyphOn)
|
||||
: ImGui::GetColorU32(_colorA11yGlyphOff);
|
||||
|
||||
switch (_config.A11yStyle)
|
||||
{
|
||||
case ImGuiToggleA11yStyle_Label:
|
||||
DrawA11yLabel(pos, color, state ? _config.On.Label : _config.Off.Label);
|
||||
break;
|
||||
case ImGuiToggleA11yStyle_Glyph:
|
||||
DrawA11yGlyph(pos, color, state, radius, thickness);
|
||||
break;
|
||||
case ImGuiToggleA11yStyle_Dot:
|
||||
DrawA11yDot(pos, color);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiToggleRenderer::DrawA11yFrameOverlays(float knob_radius)
|
||||
{
|
||||
DrawA11yFrameOverlay(knob_radius, true);
|
||||
DrawA11yFrameOverlay(knob_radius, false);
|
||||
}
|
||||
|
||||
void ImGuiToggleRenderer::DrawCircleKnob(float radius, ImU32 color_knob)
|
||||
{
|
||||
const float inset_size = ImMin(_state.KnobInset.GetAverage(), radius);
|
||||
IM_ASSERT_USER_ERROR(inset_size <= radius, "Inset size needs to be smaller or equal to the knob's radius for circular knobs.");
|
||||
|
||||
const ImVec2 knob_center = CalculateKnobCenter(radius, _animationPercent, _state.KnobOffset);
|
||||
const float knob_radius = radius - inset_size;
|
||||
|
||||
// draw knob shadow, if enabled
|
||||
if (HasShadowedKnob())
|
||||
{
|
||||
const ImU32 color_knob_shadow = ImGui::GetColorU32(_palette.KnobShadow);
|
||||
DrawCircleShadow(knob_center, knob_radius, color_knob_shadow, _state.KnobShadowThickness);
|
||||
}
|
||||
|
||||
// draw circle knob
|
||||
_drawList->AddCircleFilled(knob_center, knob_radius, color_knob);
|
||||
|
||||
// draw knob border, if enabled
|
||||
if (HasBorderedKnob())
|
||||
{
|
||||
const ImU32 color_knob_border = ImGui::GetColorU32(_palette.KnobBorder);
|
||||
DrawCircleBorder(knob_center, knob_radius, color_knob_border, _state.KnobBorderThickness);
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiToggleRenderer::DrawRectangleKnob(float radius, ImU32 color_knob)
|
||||
{
|
||||
const ImRect bounds = CalculateKnobBounds(radius, _animationPercent, _state.KnobOffset);
|
||||
|
||||
const float knob_diameter_total = bounds.GetHeight();
|
||||
const float knob_rounded_radius = (knob_diameter_total * 0.5f) * _config.KnobRounding;
|
||||
|
||||
// draw knob shadow, if enabled
|
||||
if (HasShadowedKnob())
|
||||
{
|
||||
const ImU32 color_knob_shadow = ImGui::GetColorU32(_palette.KnobShadow);
|
||||
DrawRectShadow(bounds, color_knob_shadow, _config.KnobRounding, _state.KnobShadowThickness);
|
||||
}
|
||||
|
||||
// draw rectangle/squircle knob
|
||||
_drawList->AddRectFilled(bounds.Min, bounds.Max, color_knob, knob_rounded_radius);
|
||||
|
||||
// draw knob border, if enabled
|
||||
if (HasBorderedKnob())
|
||||
{
|
||||
const ImU32 color_knob_border = ImGui::GetColorU32(_palette.KnobBorder);
|
||||
DrawRectBorder(bounds, color_knob_border, knob_rounded_radius, _state.KnobBorderThickness);
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiToggleRenderer::DrawLabel(float x_offset)
|
||||
{
|
||||
const ImVec2 label_size = ImGui::CalcTextSize(_label, nullptr, true);
|
||||
|
||||
const float half_height = GetHeight() * 0.5f;
|
||||
const float label_x = _boundingBox.Max.x + _style->ItemInnerSpacing.x + x_offset;
|
||||
const float label_y = _boundingBox.Min.y + half_height - (label_size.y * 0.5f);
|
||||
const ImVec2 label_pos = ImVec2(label_x, label_y);
|
||||
|
||||
if (g->LogEnabled)
|
||||
{
|
||||
ImGui::LogRenderedText(&label_pos, _isMixedValue ? "[~]" : *_value ? "[x]" : "[ ]");
|
||||
}
|
||||
|
||||
if (label_size.x > 0.0f)
|
||||
{
|
||||
ImGui::RenderText(label_pos, _label);
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiToggleRenderer::UpdateAnimationPercent()
|
||||
{
|
||||
// calculate the lerp percentage for animation,
|
||||
// but default to 1/0 for if we aren't animating at all,
|
||||
// or 0.5f if we have a mixed value. Also, trying to keep parity with
|
||||
// undocumented tristate/mixed/indeterminate checkbox (#2644)
|
||||
|
||||
float t = _isMixedValue
|
||||
? 0.5f
|
||||
: (*_value ? 1.0f : 0.0f);
|
||||
|
||||
if (IsAnimated() && _isLastActive)
|
||||
{
|
||||
const float t_anim = ImSaturate(ImInvLerp(0.0f, _config.AnimationDuration, _lastActiveTimer));
|
||||
t = *_value ? (t_anim) : (1.0f - t_anim);
|
||||
}
|
||||
|
||||
_animationPercent = t;
|
||||
}
|
||||
|
||||
void ImGuiToggleRenderer::UpdateStateConfig()
|
||||
{
|
||||
if (!IsAnimated())
|
||||
{
|
||||
_state = *_value ? _config.On : _config.Off;
|
||||
return;
|
||||
}
|
||||
|
||||
_state.FrameBorderThickness = ImLerp(_config.Off.FrameBorderThickness, _config.On.FrameBorderThickness, _animationPercent);
|
||||
_state.KnobBorderThickness = ImLerp(_config.Off.KnobBorderThickness, _config.On.KnobBorderThickness, _animationPercent);
|
||||
_state.KnobInset = ImLerp(_config.Off.KnobInset, _config.On.KnobInset, _animationPercent);
|
||||
_state.KnobOffset = ImLerp(_config.Off.KnobOffset, _config.On.KnobOffset, _animationPercent);
|
||||
}
|
||||
|
||||
void ImGuiToggleRenderer::UpdatePalette()
|
||||
{
|
||||
const ImGuiTogglePalette* on_candidate = _config.On.Palette;
|
||||
const ImGuiTogglePalette* off_candidate = _config.Off.Palette;
|
||||
|
||||
if (!IsAnimated())
|
||||
{
|
||||
ImGui::UnionPalette(
|
||||
&_palette,
|
||||
*_value ? on_candidate : off_candidate,
|
||||
_style->Colors,
|
||||
*_value);
|
||||
|
||||
// store specific colors that shouldn't blend.
|
||||
_colorA11yGlyphOff = _palette.A11yGlyph;
|
||||
_colorA11yGlyphOn = _palette.A11yGlyph;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ImGuiTogglePalette off_unioned;
|
||||
ImGuiTogglePalette on_unioned;
|
||||
ImGui::UnionPalette(&off_unioned, off_candidate, _style->Colors, false);
|
||||
ImGui::UnionPalette(&on_unioned, on_candidate, _style->Colors, true);
|
||||
|
||||
// otherwise, lets lerp them!
|
||||
ImGui::BlendPalettes(&_palette, off_unioned, on_unioned, _animationPercent);
|
||||
|
||||
// store specific colors that shouldn't blend.
|
||||
_colorA11yGlyphOff = off_unioned.A11yGlyph;
|
||||
_colorA11yGlyphOn = on_unioned.A11yGlyph;
|
||||
}
|
||||
|
||||
ImVec2 ImGuiToggleRenderer::CalculateKnobCenter(float radius, float animation_percent, const ImVec2& offset /*= ImVec2()*/) const
|
||||
{
|
||||
const ImVec2 pos = GetPosition();
|
||||
const float double_radius = radius * 2.0f;
|
||||
const float animation_percent_inverse = 1.0f - animation_percent;
|
||||
|
||||
const float knob_x = (pos.x + radius)
|
||||
+ animation_percent * (GetWidth() - double_radius - offset.x)
|
||||
+ (animation_percent_inverse * offset.x);
|
||||
const float knob_y = pos.y + radius + offset.y;
|
||||
return ImVec2(knob_x, knob_y);
|
||||
}
|
||||
|
||||
ImRect ImGuiToggleRenderer::CalculateKnobBounds(float radius, float animation_percent, const ImVec2& offset /*= ImVec2()*/) const
|
||||
{
|
||||
const ImVec2 position = GetPosition();
|
||||
const float double_radius = radius * 2.0f;
|
||||
const float animation_percent_inverse = 1.0f - animation_percent;
|
||||
|
||||
const float knob_left = (animation_percent * (GetWidth() - double_radius - offset.x))
|
||||
+ (animation_percent_inverse * offset.x)
|
||||
+ _state.KnobInset.Left;
|
||||
const float knob_top = _state.KnobInset.Top + _state.KnobOffset.y;
|
||||
const float knob_bottom = GetHeight() - _state.KnobInset.Bottom + _state.KnobOffset.y;
|
||||
const float knob_right = (knob_left - _state.KnobInset.Left) + double_radius - _state.KnobInset.Right;
|
||||
|
||||
// if our offsets in the x or y are close to 0,
|
||||
// we will just skip drawing the whole thing.
|
||||
if (ImApproximately(knob_left, knob_right) ||
|
||||
ImApproximately(knob_top, knob_bottom))
|
||||
{
|
||||
return ImRect();
|
||||
}
|
||||
|
||||
const ImVec2 knob_min = position + ImVec2(knob_left, knob_top);
|
||||
const ImVec2 knob_max = position + ImVec2(knob_right, knob_bottom);
|
||||
|
||||
return ImRect(knob_min, knob_max);
|
||||
}
|
||||
|
||||
void ImGuiToggleRenderer::DrawRectBorder(ImRect bounds, ImU32 color_border, float rounding, float thickness)
|
||||
{
|
||||
// the border should only grow "inside" the bounding box,
|
||||
// so we need to shrink the bounds used to prevent it from puffing out.
|
||||
const float half_thickness = thickness * 0.5f;
|
||||
bounds.Expand(-half_thickness);
|
||||
|
||||
_drawList->AddRect(bounds.Min, bounds.Max, color_border, rounding, ImDrawFlags_None, thickness);
|
||||
}
|
||||
|
||||
void ImGuiToggleRenderer::DrawCircleBorder(const ImVec2& center, float radius, ImU32 color_border, float thickness)
|
||||
{
|
||||
// the border should only grow "inside" the bounding box,
|
||||
// so we need to shrink the radius used to prevent it from puffing out.
|
||||
const float half_thickness = thickness * 0.5f;
|
||||
radius -= half_thickness;
|
||||
|
||||
_drawList->AddCircle(center, radius, color_border, 0, thickness);
|
||||
}
|
||||
|
||||
|
||||
void ImGuiToggleRenderer::DrawRectShadow(ImRect bounds, ImU32 color_shadow, float rounding, float thickness)
|
||||
{
|
||||
// the shadow should only grow "outside" the bounding box,
|
||||
// so we need to expand the bounds used to puff it out.
|
||||
const float half_thickness = thickness * 0.5f;
|
||||
bounds.Expand(half_thickness);
|
||||
|
||||
_drawList->AddRect(bounds.Min, bounds.Max, color_shadow, rounding, ImDrawFlags_None, thickness);
|
||||
}
|
||||
|
||||
void ImGuiToggleRenderer::DrawCircleShadow(const ImVec2& center, float radius, ImU32 color_border, float thickness)
|
||||
{
|
||||
// the shadow should only grow "outside" the bounding box,
|
||||
// so we need to expand the radius used to puff it out.
|
||||
const float half_thickness = thickness * 0.5f;
|
||||
radius += half_thickness;
|
||||
|
||||
_drawList->AddCircle(center, radius, color_border, 0, thickness);
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_toggle.hpp"
|
||||
#include "imgui_toggle_palette.hpp"
|
||||
|
||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#endif // IMGUI_DEFINE_MATH_OPERATORS
|
||||
#include "imgui_internal.h"
|
||||
|
||||
class ImGuiToggleRenderer
|
||||
{
|
||||
public:
|
||||
ImGuiToggleRenderer();
|
||||
ImGuiToggleRenderer(const char* label, bool* value, const ImGuiToggleConfig& user_config);
|
||||
void SetConfig(const char* label, bool* value, const ImGuiToggleConfig& user_config);
|
||||
bool Render();
|
||||
|
||||
private:
|
||||
// toggle state & context
|
||||
ImGuiToggleConfig _config;
|
||||
ImGuiToggleStateConfig _state;
|
||||
ImGuiTogglePalette _palette;
|
||||
|
||||
bool _isMixedValue;
|
||||
bool _isHovered;
|
||||
bool _isLastActive;
|
||||
float _lastActiveTimer;
|
||||
float _animationPercent;
|
||||
|
||||
// imgui specific context
|
||||
const ImGuiContext* g;
|
||||
const ImGuiStyle* _style;
|
||||
ImDrawList* _drawList;
|
||||
ImGuiID _id;
|
||||
|
||||
// raw ui value & label
|
||||
const char* _label;
|
||||
bool* _value;
|
||||
|
||||
// calculated values
|
||||
ImRect _boundingBox;
|
||||
ImVec4 _colorA11yGlyphOff;
|
||||
ImVec4 _colorA11yGlyphOn;
|
||||
|
||||
// inline accessors
|
||||
inline float GetWidth() const { return _boundingBox.GetWidth(); }
|
||||
inline float GetHeight() const { return _boundingBox.GetHeight(); }
|
||||
inline ImVec2 GetPosition() const { return _boundingBox.Min; }
|
||||
inline ImVec2 GetToggleSize() const { return _boundingBox.GetSize(); }
|
||||
inline bool IsAnimated() const { return (_config.Flags & ImGuiToggleFlags_Animated) != 0 && _config.AnimationDuration > 0; }
|
||||
inline bool HasBorderedFrame() const { return (_config.Flags & ImGuiToggleFlags_BorderedFrame) != 0 && _state.FrameBorderThickness > 0; }
|
||||
inline bool HasShadowedFrame() const { return (_config.Flags & ImGuiToggleFlags_ShadowedKnob) != 0 && _state.FrameShadowThickness > 0; }
|
||||
inline bool HasBorderedKnob() const { return (_config.Flags & ImGuiToggleFlags_BorderedKnob) != 0 && _state.KnobBorderThickness > 0; }
|
||||
inline bool HasShadowedKnob() const { return (_config.Flags & ImGuiToggleFlags_ShadowedKnob) != 0 && _state.KnobShadowThickness > 0; }
|
||||
inline bool HasA11yGlyphs() const { return (_config.Flags & ImGuiToggleFlags_A11y) != 0; }
|
||||
inline bool HasCircleKnob() const { return _config.KnobRounding >= 1.0f; }
|
||||
inline bool HasRectangleKnob() const { return _config.KnobRounding < 1.0f; }
|
||||
|
||||
// behavior
|
||||
void ValidateConfig();
|
||||
bool ToggleBehavior(const ImRect& interaction_bounding_box);
|
||||
|
||||
// drawing - general
|
||||
void DrawToggle();
|
||||
|
||||
// drawing - frame
|
||||
void DrawFrame(ImU32 color_frame);
|
||||
|
||||
// drawing a11y
|
||||
void DrawA11yDot(const ImVec2& pos, ImU32 color);
|
||||
void DrawA11yGlyph(ImVec2 pos, ImU32 color, bool state, float radius, float thickness);
|
||||
void DrawA11yLabel(ImVec2 pos, ImU32 color, const char* label);
|
||||
void DrawA11yFrameOverlay(float knob_radius, bool state);
|
||||
void DrawA11yFrameOverlays(float knob_radius);
|
||||
|
||||
// drawing - knob
|
||||
void DrawCircleKnob(float radius, ImU32 color_knob);
|
||||
void DrawRectangleKnob(float radius, ImU32 color_knob);
|
||||
|
||||
// drawing - label
|
||||
void DrawLabel(float x_offset);
|
||||
|
||||
// state updating
|
||||
void UpdateAnimationPercent();
|
||||
void UpdateStateConfig();
|
||||
void UpdatePalette();
|
||||
|
||||
// helpers
|
||||
ImVec2 CalculateKnobCenter(float radius, float animation_percent, const ImVec2& offset = ImVec2()) const;
|
||||
ImRect CalculateKnobBounds(float radius, float animation_percent, const ImVec2& offset = ImVec2()) const;
|
||||
void DrawRectBorder(ImRect bounds, ImU32 color_border, float rounding, float thickness);
|
||||
void DrawCircleBorder(const ImVec2& center, float radius, ImU32 color_border, float thickness);
|
||||
void DrawRectShadow(ImRect bounds, ImU32 color_shadow, float rounding, float thickness);
|
||||
void DrawCircleShadow(const ImVec2& center, float radius, ImU32 color_shadow, float thickness);
|
||||
};
|
||||
@@ -12,10 +12,9 @@ namespace YimMenu
|
||||
{
|
||||
BaseHook::Add<Window::WndProc>(new DetourHook("WndProc", Pointers.WndProc, Window::WndProc));
|
||||
|
||||
auto swapchain = new VMTHook<SwapChain::VMTSize>("SwapChain", *Pointers.SwapChain);
|
||||
swapchain->Hook(SwapChain::VMTPresentIdx, SwapChain::Present);
|
||||
swapchain->Hook(SwapChain::VMTResizeBuffersIdx, SwapChain::ResizeBuffers);
|
||||
BaseHook::Add<SwapChain::Present>(swapchain);
|
||||
auto swapchain_vft = *reinterpret_cast<void***>(*Pointers.SwapChain);
|
||||
BaseHook::Add<SwapChain::Present>(new DetourHook("Present", swapchain_vft[SwapChain::VMTPresentIdx], SwapChain::Present));
|
||||
BaseHook::Add<SwapChain::ResizeBuffers>(new DetourHook("ResizeBuffers", swapchain_vft[SwapChain::VMTResizeBuffersIdx], SwapChain::ResizeBuffers));
|
||||
}
|
||||
|
||||
Hooking::~Hooking()
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace YimMenu
|
||||
template<typename T>
|
||||
inline VMTHook<N>::VMTHook(const std::string_view name, T* vmtAddress) :
|
||||
BaseHook(name),
|
||||
m_Enabled(false),
|
||||
m_VMTAddress(reinterpret_cast<void***>(vmtAddress))
|
||||
{
|
||||
m_OriginalVMT = *m_VMTAddress;
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
#include "ExceptionHandler.hpp"
|
||||
|
||||
#include "StackTrace.hpp"
|
||||
|
||||
#include <hde64.h>
|
||||
#include <unordered_set>
|
||||
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
inline auto HashStackTrace(std::vector<uint64_t> stack_trace)
|
||||
{
|
||||
auto data = reinterpret_cast<const char*>(stack_trace.data());
|
||||
std::size_t size = stack_trace.size() * sizeof(uint64_t);
|
||||
|
||||
return std::hash<std::string_view>()({data, size});
|
||||
}
|
||||
|
||||
ExceptionHandler::ExceptionHandler()
|
||||
{
|
||||
LOG(INFO) << "ExceptionHandler initialized";
|
||||
m_OldErrorMode = SetErrorMode(0);
|
||||
m_Handler = SetUnhandledExceptionFilter(&VectoredExceptionHandler);
|
||||
}
|
||||
|
||||
ExceptionHandler::~ExceptionHandler()
|
||||
{
|
||||
SetErrorMode(m_OldErrorMode);
|
||||
SetUnhandledExceptionFilter(reinterpret_cast<decltype(&VectoredExceptionHandler)>(m_Handler));
|
||||
}
|
||||
|
||||
inline thread_local static StackTrace trace;
|
||||
LONG VectoredExceptionHandler(EXCEPTION_POINTERS* exception_info)
|
||||
{
|
||||
const auto exception_code = exception_info->ExceptionRecord->ExceptionCode;
|
||||
if (exception_code == EXCEPTION_BREAKPOINT || exception_code == DBG_PRINTEXCEPTION_C || exception_code == DBG_PRINTEXCEPTION_WIDE_C)
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
|
||||
static std::unordered_set<std::size_t> logged_exceptions;
|
||||
|
||||
trace.NewStackTrace(exception_info);
|
||||
const auto trace_hash = HashStackTrace(trace.GetFramePointers());
|
||||
if (const auto it = logged_exceptions.find(trace_hash); it == logged_exceptions.end())
|
||||
{
|
||||
LOG(FATAL) << trace;
|
||||
Logger::FlushQueue();
|
||||
|
||||
logged_exceptions.insert(trace_hash);
|
||||
}
|
||||
|
||||
if (exception_info->ExceptionRecord->ExceptionInformation[0] == EXCEPTION_EXECUTE_FAULT)
|
||||
{
|
||||
auto return_address_ptr = (uint64_t*)exception_info->ContextRecord->Rsp;
|
||||
if (IsBadReadPtr(reinterpret_cast<void*>(return_address_ptr), 8))
|
||||
{
|
||||
LOG(FATAL) << "Cannot resume execution, crashing (failed to find valid return address)";
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(FATAL) << "Force returning from function";
|
||||
exception_info->ContextRecord->Rip = *return_address_ptr;
|
||||
exception_info->ContextRecord->Rsp += 8;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
hde64s opcode{};
|
||||
hde64_disasm(reinterpret_cast<void*>(exception_info->ContextRecord->Rip), &opcode);
|
||||
if (opcode.flags & F_ERROR)
|
||||
{
|
||||
LOG(FATAL) << "Cannot resume execution, crashing (failed to decode insn)";
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
if (opcode.opcode == 0xFF && opcode.modrm_reg == 4) // JMP (FF /4)
|
||||
{
|
||||
auto return_address_ptr = (uint64_t*)exception_info->ContextRecord->Rsp;
|
||||
if (IsBadReadPtr(reinterpret_cast<void*>(return_address_ptr), 8))
|
||||
{
|
||||
LOG(FATAL) << "Cannot resume execution, crashing";
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
else
|
||||
{
|
||||
exception_info->ContextRecord->Rip = *return_address_ptr;
|
||||
exception_info->ContextRecord->Rsp += 8;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
exception_info->ContextRecord->Rip += opcode.len;
|
||||
|
||||
if (opcode.opcode == 0x8B && opcode.modrm_mod != 3)
|
||||
{
|
||||
uint8_t regId = opcode.rex_r | opcode.modrm_reg;
|
||||
switch (regId)
|
||||
{
|
||||
case 0: exception_info->ContextRecord->Rax = 0; break;
|
||||
case 1: exception_info->ContextRecord->Rcx = 0; break;
|
||||
case 2: exception_info->ContextRecord->Rdx = 0; break;
|
||||
case 3: exception_info->ContextRecord->Rbx = 0; break;
|
||||
case 4: exception_info->ContextRecord->Rsp = 0; break;
|
||||
case 5: exception_info->ContextRecord->Rbp = 0; break;
|
||||
case 6: exception_info->ContextRecord->Rsi = 0; break;
|
||||
case 7: exception_info->ContextRecord->Rdi = 0; break;
|
||||
case 8: exception_info->ContextRecord->R8 = 0; break;
|
||||
case 9: exception_info->ContextRecord->R9 = 0; break;
|
||||
case 10: exception_info->ContextRecord->R10 = 0; break;
|
||||
case 11: exception_info->ContextRecord->R11 = 0; break;
|
||||
case 12: exception_info->ContextRecord->R12 = 0; break;
|
||||
case 13: exception_info->ContextRecord->R13 = 0; break;
|
||||
case 14: exception_info->ContextRecord->R14 = 0; break;
|
||||
case 15: exception_info->ContextRecord->R15 = 0; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
}
|
||||
|
||||
static YimMenu::ExceptionHandler _ExceptionHandler{};
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class ExceptionHandler final
|
||||
{
|
||||
public:
|
||||
ExceptionHandler();
|
||||
virtual ~ExceptionHandler();
|
||||
|
||||
private:
|
||||
void* m_Handler;
|
||||
uint32_t m_OldErrorMode;
|
||||
};
|
||||
|
||||
extern LONG VectoredExceptionHandler(EXCEPTION_POINTERS* exception_info);
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
#include "StackTrace.hpp"
|
||||
|
||||
#include <DbgHelp.h>
|
||||
#include <winternl.h>
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
StackTrace::StackTrace() :
|
||||
m_FramePointers(32)
|
||||
{
|
||||
SymInitialize(GetCurrentProcess(), nullptr, true);
|
||||
}
|
||||
|
||||
StackTrace::~StackTrace()
|
||||
{
|
||||
SymCleanup(GetCurrentProcess());
|
||||
}
|
||||
|
||||
const std::vector<uint64_t>& StackTrace::GetFramePointers()
|
||||
{
|
||||
return m_FramePointers;
|
||||
}
|
||||
|
||||
void StackTrace::NewStackTrace(EXCEPTION_POINTERS* exception_info)
|
||||
{
|
||||
static std::mutex m;
|
||||
std::lock_guard lock(m);
|
||||
|
||||
m_ExceptionInfo = exception_info;
|
||||
|
||||
Clear();
|
||||
|
||||
m_Dump << ExceptionCodeToString(exception_info->ExceptionRecord->ExceptionCode) << '\n';
|
||||
|
||||
DumpModuleInfo();
|
||||
DumpRegisters();
|
||||
DumpStacktrace();
|
||||
DumpExceptionInfo();
|
||||
|
||||
m_Dump << "\n--------End of exception--------\n";
|
||||
}
|
||||
|
||||
std::string StackTrace::GetString() const
|
||||
{
|
||||
return m_Dump.str();
|
||||
}
|
||||
|
||||
void StackTrace::Clear()
|
||||
{
|
||||
m_Dump.str("");
|
||||
m_Dump.clear();
|
||||
}
|
||||
|
||||
// I'd prefer to make some sort of global instance that cache all modules once instead of doing this every time
|
||||
void StackTrace::DumpModuleInfo()
|
||||
{
|
||||
// modules cached already
|
||||
if (m_Modules.size())
|
||||
return;
|
||||
|
||||
m_Dump << "Dumping modules:\n";
|
||||
|
||||
const auto peb = reinterpret_cast<PPEB>(NtCurrentTeb()->ProcessEnvironmentBlock);
|
||||
if (!peb)
|
||||
return;
|
||||
|
||||
const auto ldr_data = peb->Ldr;
|
||||
if (!ldr_data)
|
||||
return;
|
||||
|
||||
const auto module_list = &ldr_data->InMemoryOrderModuleList;
|
||||
auto module_entry = module_list->Flink;
|
||||
for (; module_list != module_entry; module_entry = module_entry->Flink)
|
||||
{
|
||||
const auto table_entry = CONTAINING_RECORD(module_entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
|
||||
if (!table_entry)
|
||||
continue;
|
||||
|
||||
if (table_entry->FullDllName.Buffer)
|
||||
{
|
||||
auto mod_info = ModuleInfo(table_entry->FullDllName.Buffer, table_entry->DllBase);
|
||||
|
||||
m_Dump << mod_info.m_Name << " Base Address: " << HEX(mod_info.m_Base) << " Size: " << mod_info.m_Size << '\n';
|
||||
|
||||
m_Modules.emplace_back(std::move(mod_info));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StackTrace::DumpRegisters()
|
||||
{
|
||||
const auto context = m_ExceptionInfo->ContextRecord;
|
||||
|
||||
m_Dump << "Dumping registers:\n"
|
||||
<< "RAX: " << HEX(context->Rax) << '\n'
|
||||
<< "RCX: " << HEX(context->Rcx) << '\n'
|
||||
<< "RDX: " << HEX(context->Rdx) << '\n'
|
||||
<< "RBX: " << HEX(context->Rbx) << '\n'
|
||||
<< "RSI: " << HEX(context->Rsi) << '\n'
|
||||
<< "RDI: " << HEX(context->Rdi) << '\n'
|
||||
<< "RSP: " << HEX(context->Rsp) << '\n'
|
||||
<< "RBP: " << HEX(context->Rbp) << '\n'
|
||||
<< "R8: " << HEX(context->R8) << '\n'
|
||||
<< "R9: " << HEX(context->R9) << '\n'
|
||||
<< "R10: " << HEX(context->R10) << '\n'
|
||||
<< "R11: " << HEX(context->R11) << '\n'
|
||||
<< "R12: " << HEX(context->R12) << '\n'
|
||||
<< "R13: " << HEX(context->R13) << '\n'
|
||||
<< "R14: " << HEX(context->R14) << '\n'
|
||||
<< "R15: " << HEX(context->R15) << '\n';
|
||||
}
|
||||
|
||||
void StackTrace::DumpStacktrace()
|
||||
{
|
||||
m_Dump << "Dumping stacktrace:";
|
||||
GrabStacktrace();
|
||||
|
||||
// alloc once
|
||||
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
|
||||
auto symbol = reinterpret_cast<SYMBOL_INFO*>(buffer);
|
||||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
symbol->MaxNameLen = MAX_SYM_NAME;
|
||||
|
||||
DWORD64 displacement64;
|
||||
DWORD displacement;
|
||||
|
||||
|
||||
IMAGEHLP_LINE64 line;
|
||||
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||
|
||||
for (size_t i = 0; i < m_FramePointers.size() && m_FramePointers[i]; ++i)
|
||||
{
|
||||
const auto addr = m_FramePointers[i];
|
||||
const auto module_info = GetModuleByAddress(addr);
|
||||
|
||||
m_Dump << "\n[" << i << "]\t";
|
||||
if (SymFromAddr(GetCurrentProcess(), addr, &displacement64, symbol))
|
||||
{
|
||||
if (SymGetLineFromAddr64(GetCurrentProcess(), addr, &displacement, &line))
|
||||
{
|
||||
m_Dump << line.FileName << " L: " << line.LineNumber << ' ' << std::string_view(symbol->Name, symbol->NameLen);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (module_info)
|
||||
{
|
||||
m_Dump << module_info->m_Name << ' ' << std::string_view(symbol->Name, symbol->NameLen) << " ("
|
||||
<< module_info->m_Name << '+' << HEX(addr - module_info->m_Base) << ')';
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
m_Dump << HEX(addr) << ' ' << std::string_view(symbol->Name, symbol->NameLen);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (module_info)
|
||||
{
|
||||
m_Dump << module_info->m_Name << '+' << HEX(addr - module_info->m_Base) << ' ' << HEX(addr);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
m_Dump << HEX(addr);
|
||||
}
|
||||
}
|
||||
|
||||
void StackTrace::DumpExceptionInfo()
|
||||
{
|
||||
DWORD exception_code = m_ExceptionInfo->ExceptionRecord->ExceptionCode;
|
||||
|
||||
constexpr DWORD msvc_exception_code = 0xe06d7363;
|
||||
if (exception_code == msvc_exception_code)
|
||||
{
|
||||
m_Dump << '\n'
|
||||
<< reinterpret_cast<const std::exception*>(m_ExceptionInfo->ExceptionRecord->ExceptionInformation[1])->what() << '\n';
|
||||
}
|
||||
else if (exception_code == EXCEPTION_ACCESS_VIOLATION || exception_code == EXCEPTION_IN_PAGE_ERROR)
|
||||
{
|
||||
const auto flag = m_ExceptionInfo->ExceptionRecord->ExceptionInformation[0];
|
||||
const auto addr = m_ExceptionInfo->ExceptionRecord->ExceptionInformation[1];
|
||||
|
||||
switch (flag)
|
||||
{
|
||||
case EXCEPTION_READ_FAULT: m_Dump << '\n' << "Attempted to read from " << HEX(addr) << '\n'; break;
|
||||
case EXCEPTION_WRITE_FAULT: m_Dump << '\n' << "Attempted to write to " << HEX(addr) << '\n'; break;
|
||||
case EXCEPTION_EXECUTE_FAULT: m_Dump << '\n' << "Attempted to execute at " << HEX(addr) << '\n'; break;
|
||||
default: m_Dump << '\n' << "Inaccessible data at " << HEX(addr) << '\n';
|
||||
}
|
||||
|
||||
if (exception_code == EXCEPTION_IN_PAGE_ERROR)
|
||||
{
|
||||
m_Dump << "NTSTATUS code " << HEX(m_ExceptionInfo->ExceptionRecord->ExceptionInformation[2]) << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StackTrace::GrabStacktrace()
|
||||
{
|
||||
CONTEXT context = *m_ExceptionInfo->ContextRecord;
|
||||
|
||||
STACKFRAME64 frame{};
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
frame.AddrPC.Offset = context.Rip;
|
||||
frame.AddrFrame.Offset = context.Rbp;
|
||||
frame.AddrStack.Offset = context.Rsp;
|
||||
|
||||
for (size_t i = 0; i < m_FramePointers.size(); ++i)
|
||||
{
|
||||
if (!StackWalk64(IMAGE_FILE_MACHINE_AMD64, GetCurrentProcess(), GetCurrentThread(), &frame, &context, nullptr, SymFunctionTableAccess64, SymGetModuleBase64, nullptr))
|
||||
{
|
||||
break;
|
||||
}
|
||||
m_FramePointers[i] = frame.AddrPC.Offset;
|
||||
}
|
||||
}
|
||||
|
||||
const StackTrace::ModuleInfo* StackTrace::GetModuleByAddress(uint64_t addr) const
|
||||
{
|
||||
for (auto& mod_info : m_Modules)
|
||||
{
|
||||
if (mod_info.m_Base < addr && addr < mod_info.m_Base + mod_info.m_Size)
|
||||
{
|
||||
return &mod_info;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string StackTrace::ExceptionCodeToString(const DWORD code)
|
||||
{
|
||||
#define MAP_PAIR_STRINGIFY(x) \
|
||||
{ \
|
||||
x, #x \
|
||||
}
|
||||
static const std::map<DWORD, std::string> exceptions = {MAP_PAIR_STRINGIFY(EXCEPTION_ACCESS_VIOLATION), MAP_PAIR_STRINGIFY(EXCEPTION_ARRAY_BOUNDS_EXCEEDED), MAP_PAIR_STRINGIFY(EXCEPTION_DATATYPE_MISALIGNMENT), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DENORMAL_OPERAND), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DIVIDE_BY_ZERO), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INVALID_OPERATION), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_OVERFLOW), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_STACK_CHECK), MAP_PAIR_STRINGIFY(EXCEPTION_FLT_UNDERFLOW), MAP_PAIR_STRINGIFY(EXCEPTION_ILLEGAL_INSTRUCTION), MAP_PAIR_STRINGIFY(EXCEPTION_IN_PAGE_ERROR), MAP_PAIR_STRINGIFY(EXCEPTION_INT_DIVIDE_BY_ZERO), MAP_PAIR_STRINGIFY(EXCEPTION_INT_OVERFLOW), MAP_PAIR_STRINGIFY(EXCEPTION_INVALID_DISPOSITION), MAP_PAIR_STRINGIFY(EXCEPTION_NONCONTINUABLE_EXCEPTION), MAP_PAIR_STRINGIFY(EXCEPTION_PRIV_INSTRUCTION), MAP_PAIR_STRINGIFY(EXCEPTION_STACK_OVERFLOW), MAP_PAIR_STRINGIFY(EXCEPTION_BREAKPOINT), MAP_PAIR_STRINGIFY(EXCEPTION_SINGLE_STEP)};
|
||||
|
||||
if (const auto& it = exceptions.find(code); it != exceptions.end())
|
||||
return it->second;
|
||||
|
||||
return "UNKNOWN_EXCEPTION: CODE: " + std::to_string(code);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class StackTrace
|
||||
{
|
||||
public:
|
||||
StackTrace();
|
||||
virtual ~StackTrace();
|
||||
|
||||
const std::vector<uint64_t>& GetFramePointers();
|
||||
void NewStackTrace(EXCEPTION_POINTERS* exception_info);
|
||||
std::string GetString() const;
|
||||
void Clear();
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const StackTrace& st);
|
||||
friend std::ostream& operator<<(std::ostream& os, const StackTrace* st);
|
||||
|
||||
private:
|
||||
struct ModuleInfo
|
||||
{
|
||||
ModuleInfo(std::filesystem::path path, void* base) :
|
||||
m_Name(path.filename().string()),
|
||||
m_Base(reinterpret_cast<uintptr_t>(base))
|
||||
{
|
||||
const auto dos_header = reinterpret_cast<IMAGE_DOS_HEADER*>(base);
|
||||
const auto nt_header = reinterpret_cast<IMAGE_NT_HEADERS*>(m_Base + dos_header->e_lfanew);
|
||||
|
||||
m_Size = nt_header->OptionalHeader.SizeOfCode;
|
||||
}
|
||||
|
||||
std::string m_Name;
|
||||
uintptr_t m_Base;
|
||||
size_t m_Size;
|
||||
};
|
||||
|
||||
private:
|
||||
void DumpModuleInfo();
|
||||
void DumpRegisters();
|
||||
void DumpStacktrace();
|
||||
void DumpExceptionInfo();
|
||||
void GrabStacktrace();
|
||||
const ModuleInfo* GetModuleByAddress(uint64_t addr) const;
|
||||
|
||||
static std::string ExceptionCodeToString(const DWORD code);
|
||||
|
||||
private:
|
||||
EXCEPTION_POINTERS* m_ExceptionInfo;
|
||||
|
||||
std::stringstream m_Dump;
|
||||
std::vector<uint64_t> m_FramePointers;
|
||||
|
||||
inline static std::vector<ModuleInfo> m_Modules;
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const StackTrace& st)
|
||||
{
|
||||
os << st.GetString();
|
||||
return os;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const StackTrace* st)
|
||||
{
|
||||
os << st->GetString();
|
||||
return os;
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
#include "BytePatch.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
BytePatch::~BytePatch()
|
||||
{
|
||||
Restore();
|
||||
}
|
||||
|
||||
void BytePatch::Apply() const
|
||||
{
|
||||
std::copy_n(m_Patch.get(), m_Size, reinterpret_cast<byte*>(m_Address));
|
||||
}
|
||||
|
||||
void BytePatch::Restore() const
|
||||
{
|
||||
std::copy_n(m_Original.get(), m_Size, reinterpret_cast<byte*>(m_Address));
|
||||
}
|
||||
|
||||
void BytePatch::Remove() const
|
||||
{
|
||||
if (const auto it = std::find(m_Patches.begin(), m_Patches.end(), this); it != m_Patches.end())
|
||||
{
|
||||
m_Patches.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void BytePatch::RestoreAll()
|
||||
{
|
||||
m_Patches.clear();
|
||||
}
|
||||
|
||||
bool operator==(const std::unique_ptr<BytePatch>& a, const BytePatch* b)
|
||||
{
|
||||
return a->m_Address == b->m_Address;
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
template<typename T>
|
||||
concept SpanCompatibleType = requires(T a)
|
||||
{
|
||||
std::span{a};
|
||||
};
|
||||
|
||||
class BytePatch
|
||||
{
|
||||
private:
|
||||
byte* m_Address;
|
||||
std::unique_ptr<byte[]> m_Patch;
|
||||
std::unique_ptr<byte[]> m_Original;
|
||||
std::size_t m_Size;
|
||||
|
||||
protected:
|
||||
static inline std::vector<std::unique_ptr<BytePatch>> m_Patches;
|
||||
|
||||
public:
|
||||
virtual ~BytePatch();
|
||||
|
||||
void Apply() const;
|
||||
void Restore() const;
|
||||
void Remove() const;
|
||||
|
||||
static void RestoreAll();
|
||||
|
||||
template<typename TAddr>
|
||||
static const std::unique_ptr<BytePatch>& Make(TAddr addr, std::remove_pointer_t<std::remove_reference_t<TAddr>> value);
|
||||
|
||||
template<typename TAddr, typename T>
|
||||
requires SpanCompatibleType<T>
|
||||
static const std::unique_ptr<BytePatch>& Make(TAddr addr, T spanCompatible);
|
||||
|
||||
private:
|
||||
template<typename TAddr>
|
||||
BytePatch(TAddr addr, std::remove_pointer_t<std::remove_reference_t<TAddr>> value);
|
||||
|
||||
template<typename TAddr, typename T, std::size_t N>
|
||||
BytePatch(TAddr addr, std::span<T, N> span);
|
||||
|
||||
friend bool operator==(const std::unique_ptr<BytePatch>& a, const BytePatch* b);
|
||||
|
||||
};
|
||||
|
||||
template<typename TAddr>
|
||||
inline const std::unique_ptr<BytePatch>& BytePatch::Make(TAddr addr, std::remove_pointer_t<std::remove_reference_t<TAddr>> value)
|
||||
{
|
||||
return m_Patches.emplace_back(std::unique_ptr<BytePatch>(new BytePatch(addr, value)));
|
||||
}
|
||||
|
||||
template<typename TAddr, typename T>
|
||||
requires SpanCompatibleType<T>
|
||||
inline const std::unique_ptr<BytePatch>& BytePatch::Make(TAddr addr, T spanCompatible)
|
||||
{
|
||||
return m_Patches.emplace_back(std::unique_ptr<BytePatch>(new BytePatch(addr, std::span{spanCompatible})));
|
||||
}
|
||||
|
||||
template<typename TAddr>
|
||||
inline BytePatch::BytePatch(TAddr addr, std::remove_pointer_t<std::remove_reference_t<TAddr>> value) :
|
||||
m_Address(addr)
|
||||
{
|
||||
m_Size = sizeof(std::remove_pointer_t<std::remove_reference_t<TAddr>>);
|
||||
|
||||
m_Original = std::make_unique<byte[]>(m_Size);
|
||||
std::copy_n(m_Address, m_Size, m_Original.get());
|
||||
|
||||
m_Patch = std::make_unique<byte[]>(m_Size);
|
||||
std::copy_n(&value, m_Size, m_Patch.get());
|
||||
}
|
||||
|
||||
template<typename TAddr, typename T, std::size_t N>
|
||||
inline BytePatch::BytePatch(TAddr addr, std::span<T, N> span) :
|
||||
m_Address((byte*)addr)
|
||||
{
|
||||
m_Size = span.size();
|
||||
|
||||
m_Original = std::make_unique<byte[]>(m_Size);
|
||||
std::copy_n(m_Address, m_Size, m_Original.get());
|
||||
|
||||
m_Patch = std::make_unique<byte[]>(m_Size);
|
||||
std::copy(span.begin(), span.end(), m_Patch.get());
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,8 @@ namespace YimMenu
|
||||
*/
|
||||
template<typename T = void*>
|
||||
T GetExport(const std::string_view symbolName) const;
|
||||
template<typename T = void*>
|
||||
T GetExport(int ordinal) const;
|
||||
/**
|
||||
* @brief Gets the address of the import function
|
||||
*
|
||||
@@ -85,4 +87,28 @@ namespace YimMenu
|
||||
LOG(FATAL) << "Cannot find export: " << symbolName;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T Module::GetExport(int ordinal) const
|
||||
{
|
||||
const auto ntHeader = GetNtHeader();
|
||||
if (!ntHeader)
|
||||
return nullptr;
|
||||
|
||||
const auto imageDataDirectory = ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||
const auto exportDirectory = m_Base.Add(imageDataDirectory.VirtualAddress).As<IMAGE_EXPORT_DIRECTORY*>();
|
||||
const auto ordinalOffsets = m_Base.Add(exportDirectory->AddressOfNameOrdinals).As<uint16_t*>();
|
||||
const auto funcOffsets = m_Base.Add(exportDirectory->AddressOfFunctions).As<DWORD*>();
|
||||
|
||||
for (std::size_t i = 0; i < exportDirectory->NumberOfFunctions; i++)
|
||||
{
|
||||
if (ordinalOffsets[i] != ordinal)
|
||||
continue;
|
||||
|
||||
return m_Base.Add(funcOffsets[i]).As<T>();
|
||||
}
|
||||
|
||||
LOG(FATAL) << "Cannot find export: " << ordinal;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "ModuleMgr.hpp"
|
||||
|
||||
#include "util/Joaat.hpp"
|
||||
#include "core/util/Joaat.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
|
||||
+38
-16
@@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
#include "util/StrToHex.hpp"
|
||||
#include "PatternHash.hpp"
|
||||
#include "core/util/StrToHex.hpp"
|
||||
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
@@ -10,6 +12,7 @@ namespace YimMenu
|
||||
{
|
||||
char m_Signature[N]{};
|
||||
std::size_t m_SignatureByteLength;
|
||||
PatternHash m_Hash;
|
||||
|
||||
constexpr Signature(char const (&signature)[N]) :
|
||||
m_SignatureByteLength(0)
|
||||
@@ -18,9 +21,12 @@ namespace YimMenu
|
||||
|
||||
for (std::size_t i = 0; i < N; i++)
|
||||
{
|
||||
m_Hash = m_Hash.Update(m_Signature[i]);
|
||||
if (m_Signature[i] == ' ' || m_Signature[i] == '\0')
|
||||
m_SignatureByteLength++;
|
||||
}
|
||||
|
||||
m_Hash = m_Hash.Update(m_SignatureByteLength);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,6 +56,11 @@ namespace YimMenu
|
||||
{
|
||||
return m_SignatureByteLength;
|
||||
}
|
||||
|
||||
constexpr PatternHash Hash() const
|
||||
{
|
||||
return m_Hash;
|
||||
}
|
||||
};
|
||||
|
||||
class IPattern
|
||||
@@ -60,6 +71,7 @@ namespace YimMenu
|
||||
|
||||
virtual const std::string_view Name() const = 0;
|
||||
virtual constexpr std::span<const std::optional<std::uint8_t>> Signature() const = 0;
|
||||
virtual const PatternHash Hash() const = 0;
|
||||
};
|
||||
|
||||
template<Signature S>
|
||||
@@ -68,6 +80,7 @@ namespace YimMenu
|
||||
private:
|
||||
const std::string_view m_Name;
|
||||
std::array<std::optional<std::uint8_t>, S.ByteLength()> m_Signature;
|
||||
PatternHash m_Hash;
|
||||
|
||||
public:
|
||||
constexpr Pattern(const std::string_view name);
|
||||
@@ -80,8 +93,10 @@ namespace YimMenu
|
||||
{
|
||||
return m_Signature;
|
||||
}
|
||||
|
||||
friend std::ostream& operator<< <>(std::ostream& os, const Pattern<S>& signature);
|
||||
inline virtual const PatternHash Hash() const override
|
||||
{
|
||||
return m_Hash;
|
||||
}
|
||||
};
|
||||
|
||||
template<Signature S>
|
||||
@@ -89,6 +104,8 @@ namespace YimMenu
|
||||
IPattern(),
|
||||
m_Name(name)
|
||||
{
|
||||
m_Hash = S.Hash();
|
||||
|
||||
for (size_t i = 0, pos = 0; i < S.Length(); i++)
|
||||
{
|
||||
const auto c = S.Get()[i];
|
||||
@@ -112,20 +129,25 @@ namespace YimMenu
|
||||
}
|
||||
}
|
||||
|
||||
template<Signature S>
|
||||
inline std::ostream& operator<<(std::ostream& os, const Pattern<S>& pattern)
|
||||
class SimplePattern
|
||||
{
|
||||
os << pattern.Name() << ": { ";
|
||||
for (const auto byte : pattern.Signature())
|
||||
public:
|
||||
SimplePattern(std::string_view ida_sig);
|
||||
inline SimplePattern(const char* ida_sig) :
|
||||
SimplePattern(std::string_view(ida_sig))
|
||||
{
|
||||
if (byte == std::nullopt)
|
||||
{
|
||||
os << "?? ";
|
||||
continue;
|
||||
}
|
||||
os << std::hex << std::uppercase << DWORD64(byte.value()) << std::nouppercase << std::dec << ' ';
|
||||
}
|
||||
os << "}";
|
||||
return os;
|
||||
}
|
||||
|
||||
inline SimplePattern(SimplePattern&& other) :
|
||||
m_Bytes(other.m_Bytes)
|
||||
{
|
||||
}
|
||||
|
||||
inline SimplePattern(const SimplePattern& other) :
|
||||
m_Bytes(other.m_Bytes)
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<std::optional<uint8_t>> m_Bytes;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
// TODO: use an actual hash function
|
||||
namespace YimMenu
|
||||
{
|
||||
class PatternHash
|
||||
{
|
||||
public:
|
||||
std::uint64_t m_Hash;
|
||||
|
||||
constexpr PatternHash() :
|
||||
m_Hash(0xD84A917A64D4D016ULL)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr PatternHash(std::uint64_t hash) :
|
||||
m_Hash(hash)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr PatternHash(const PatternHash& other) :
|
||||
m_Hash(other.m_Hash)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr PatternHash(PatternHash&& other) :
|
||||
m_Hash(other.m_Hash)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr void operator=(const PatternHash& other)
|
||||
{
|
||||
m_Hash = other.m_Hash;
|
||||
}
|
||||
|
||||
constexpr PatternHash Update(char data) const
|
||||
{
|
||||
auto hash = m_Hash;
|
||||
hash += (static_cast<std::uint64_t>(data));
|
||||
hash += hash << 10;
|
||||
hash ^= (hash >> 6);
|
||||
return PatternHash(hash);
|
||||
}
|
||||
|
||||
constexpr PatternHash Update(int data) const
|
||||
{
|
||||
auto hash = m_Hash;
|
||||
hash ^= (data >> 8);
|
||||
hash += hash << 10;
|
||||
hash ^= (hash >> 7);
|
||||
return PatternHash(hash);
|
||||
}
|
||||
|
||||
constexpr PatternHash Update(std::uint64_t data) const
|
||||
{
|
||||
auto hash = m_Hash;
|
||||
hash ^= (data * 0xA5C38736C426FCB8ULL);
|
||||
hash += hash << 15;
|
||||
hash ^= (hash >> 5);
|
||||
return PatternHash(hash);
|
||||
}
|
||||
|
||||
constexpr std::uint64_t GetHash() const
|
||||
{
|
||||
return m_Hash;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "PatternScanner.hpp"
|
||||
|
||||
#include "Module.hpp"
|
||||
#include "core/backend/PatternCache.hpp"
|
||||
|
||||
#include <future>
|
||||
|
||||
@@ -42,6 +43,17 @@ namespace YimMenu
|
||||
{
|
||||
const auto signature = pattern->Signature();
|
||||
|
||||
if (PatternCache::IsInitialized())
|
||||
{
|
||||
auto offset = PatternCache::GetCachedOffset(pattern->Hash().Update(m_Module->Size()));
|
||||
if (offset.has_value())
|
||||
{
|
||||
LOG(INFO) << "Using cached pattern [" << pattern->Name() << "] : [" << HEX(m_Module->Base() + offset.value()) << "]";
|
||||
std::invoke(func, m_Module->Base() + offset.value());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto i = m_Module->Base(); i < m_Module->End(); ++i)
|
||||
{
|
||||
if (signature.size() + i > m_Module->End())
|
||||
@@ -63,6 +75,11 @@ namespace YimMenu
|
||||
|
||||
std::invoke(func, i);
|
||||
|
||||
if (PatternCache::IsInitialized())
|
||||
{
|
||||
PatternCache::UpdateCachedOffset(pattern->Hash().Update(m_Module->Size()), i - m_Module->Base());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+267
-30
@@ -1,11 +1,16 @@
|
||||
#include "Renderer.hpp"
|
||||
|
||||
#include "core/memory/ModuleMgr.hpp"
|
||||
#include "core/memory/PatternScanner.hpp"
|
||||
#include "game/frontend/GUI.hpp"
|
||||
#include "game/frontend/Menu.hpp"
|
||||
#include "game/pointers/Pointers.hpp"
|
||||
|
||||
#include <backends/imgui_impl_dx11.h>
|
||||
#include <backends/imgui_impl_dx12.h>
|
||||
#include <backends/imgui_impl_win32.h>
|
||||
#include <imgui.h>
|
||||
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
Renderer::Renderer()
|
||||
@@ -18,15 +23,16 @@ namespace YimMenu
|
||||
|
||||
void Renderer::DestroyImpl()
|
||||
{
|
||||
if (m_SwapChain && m_Device && m_DeviceContext)
|
||||
{
|
||||
ImGui_ImplDX11_Shutdown();
|
||||
ImGui_ImplWin32_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
ImGui_ImplWin32_Shutdown();
|
||||
|
||||
|
||||
WaitForLastFrame();
|
||||
ImGui_ImplDX12_Shutdown();
|
||||
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
|
||||
bool Renderer::InitImpl()
|
||||
bool Renderer::InitDX12()
|
||||
{
|
||||
if (!Pointers.SwapChain)
|
||||
{
|
||||
@@ -35,35 +41,161 @@ namespace YimMenu
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_SwapChain = *Pointers.SwapChain; !m_SwapChain)
|
||||
if (!Pointers.CommandQueue)
|
||||
{
|
||||
LOG(WARNING) << "CommandQueue pointer is invalid!";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//This is required. In order to stop ComPtr from releasing the original pointer, we create a new ComPtr with the ptr as the intializer. (The '=' operator uses swap which releases the object passed into it)
|
||||
if (m_GameSwapChain = ComPtr<IDXGISwapChain1>(*Pointers.SwapChain); !m_GameSwapChain.Get())
|
||||
{
|
||||
LOG(WARNING) << "Dereferenced SwapChain pointer is invalid!";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (const auto result = m_SwapChain->GetDevice(__uuidof(ID3D11Device), reinterpret_cast<void**>(&m_Device)); result < 0)
|
||||
if (m_CommandQueue = ComPtr<ID3D12CommandQueue>(*Pointers.CommandQueue); !m_CommandQueue.Get())
|
||||
{
|
||||
LOG(WARNING) << "Dereferenced CommandQueue pointer is invalid!";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_GameSwapChain.As(&m_SwapChain); //We need GetCurrentBackBufferIndex from IDXGISwapChain3
|
||||
|
||||
if (const auto result = m_SwapChain->GetDevice(__uuidof(ID3D12Device), reinterpret_cast<void**>(m_Device.GetAddressOf())); result < 0)
|
||||
{
|
||||
LOG(WARNING) << "Failed to get D3D Device with result: [" << result << "]";
|
||||
|
||||
return false;
|
||||
}
|
||||
m_Device->GetImmediateContext(&m_DeviceContext);
|
||||
|
||||
ImGui::CreateContext();
|
||||
ImGui::StyleColorsDark();
|
||||
if (const auto result = m_SwapChain->GetDesc(&m_SwapChainDesc); result < 0)
|
||||
{
|
||||
LOG(WARNING) << "Failed to get SwapChain Description with result: [" << result << "]";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (const auto result = m_Device->CreateFence(0, D3D12_FENCE_FLAG_NONE, __uuidof(ID3D12Fence), (void**)m_Fence.GetAddressOf()); result < 0)
|
||||
{
|
||||
LOG(WARNING) << "Failed to create Fence with result: [" << result << "]";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (const auto result = m_FenceEvent = CreateEventA(nullptr, FALSE, FALSE, nullptr); !result)
|
||||
{
|
||||
LOG(WARNING) << "Failed to create Fence Event!";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
m_FrameContext.resize(m_SwapChainDesc.BufferCount);
|
||||
|
||||
D3D12_DESCRIPTOR_HEAP_DESC DescriptorDesc{D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_SwapChainDesc.BufferCount, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE};
|
||||
if (const auto result =
|
||||
m_Device->CreateDescriptorHeap(&DescriptorDesc, __uuidof(ID3D12DescriptorHeap), (void**)m_DescriptorHeap.GetAddressOf());
|
||||
result < 0)
|
||||
{
|
||||
LOG(WARNING) << "Failed to create Descriptor Heap with result: [" << result << "]";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (const auto result = m_Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT,
|
||||
__uuidof(ID3D12CommandAllocator),
|
||||
(void**)m_CommandAllocator.GetAddressOf());
|
||||
result < 0)
|
||||
{
|
||||
LOG(WARNING) << "Failed to create Command Allocator with result: [" << result << "]";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i{}; i < m_SwapChainDesc.BufferCount; ++i)
|
||||
{
|
||||
m_FrameContext[i].CommandAllocator = m_CommandAllocator.Get();
|
||||
}
|
||||
|
||||
if (const auto result = m_Device->CreateCommandList(0,
|
||||
D3D12_COMMAND_LIST_TYPE_DIRECT,
|
||||
m_CommandAllocator.Get(),
|
||||
NULL,
|
||||
__uuidof(ID3D12GraphicsCommandList),
|
||||
(void**)m_CommandList.GetAddressOf());
|
||||
result < 0)
|
||||
{
|
||||
LOG(WARNING) << "Failed to create Command List with result: [" << result << "]";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (const auto result = m_CommandList->Close(); result < 0)
|
||||
{
|
||||
LOG(WARNING) << "Failed to finalize the creation of Command List with result: [" << result << "]";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D12_DESCRIPTOR_HEAP_DESC DescriptorBackbufferDesc{D3D12_DESCRIPTOR_HEAP_TYPE_RTV, m_SwapChainDesc.BufferCount, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1};
|
||||
if (const auto result = m_Device->CreateDescriptorHeap(&DescriptorBackbufferDesc,
|
||||
__uuidof(ID3D12DescriptorHeap),
|
||||
(void**)m_BackbufferDescriptorHeap.GetAddressOf());
|
||||
result < 0)
|
||||
{
|
||||
LOG(WARNING) << "Failed to create Backbuffer Descriptor Heap with result: [" << result << "]";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto RTVDescriptorSize{m_Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV)};
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE RTVHandle{m_BackbufferDescriptorHeap->GetCPUDescriptorHandleForHeapStart()};
|
||||
LOG(INFO) << m_SwapChainDesc.BufferCount;
|
||||
for (size_t i = 0; i < m_SwapChainDesc.BufferCount; ++i)
|
||||
{
|
||||
ComPtr<ID3D12Resource> BackBuffer{};
|
||||
m_FrameContext[i].Descriptor = RTVHandle;
|
||||
m_SwapChain->GetBuffer(i, __uuidof(ID3D12Resource), (void**)BackBuffer.GetAddressOf());
|
||||
m_Device->CreateRenderTargetView(BackBuffer.Get(), nullptr, RTVHandle);
|
||||
m_FrameContext[i].Resource = BackBuffer.Get();
|
||||
RTVHandle.ptr += RTVDescriptorSize;
|
||||
}
|
||||
|
||||
// never returns false, useless to check return
|
||||
ImGui_ImplDX11_Init(m_Device, m_DeviceContext);
|
||||
ImGui_ImplWin32_Init(Pointers.Hwnd);
|
||||
ImGui::CreateContext(&GetInstance().m_FontAtlas);
|
||||
ImGui_ImplWin32_Init(*Pointers.Hwnd);
|
||||
ImGui_ImplDX12_Init(m_Device.Get(),
|
||||
m_SwapChainDesc.BufferCount,
|
||||
DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||
m_DescriptorHeap.Get(),
|
||||
m_DescriptorHeap->GetCPUDescriptorHandleForHeapStart(),
|
||||
m_DescriptorHeap->GetGPUDescriptorHandleForHeapStart());
|
||||
|
||||
LOG(INFO) << "Renderer has finished initializing.";
|
||||
ImGui::StyleColorsDark();
|
||||
|
||||
LOG(INFO) << "DirectX 12 renderer has finished initializing.";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Renderer::AddDXCallbackImpl(DXCallback&& callback, std::uint32_t priority)
|
||||
bool Renderer::InitImpl()
|
||||
{
|
||||
return m_DXCallbacks.insert({priority, callback}).second;
|
||||
while (!*Pointers.Hwnd || !*Pointers.ScreenResX)
|
||||
{
|
||||
std::this_thread::sleep_for(1s);
|
||||
}
|
||||
|
||||
LOG(INFO) << "Using DX12, clear shader cache if you're having issues.";
|
||||
LOG(INFO) << "Waiting...";
|
||||
// std::this_thread::sleep_for(5s); //Early injection could result in errors.
|
||||
return InitDX12();
|
||||
}
|
||||
|
||||
bool Renderer::AddRendererCallBackImpl(RendererCallBack&& callback, std::uint32_t priority)
|
||||
{
|
||||
return m_RendererCallBacks.insert({priority, callback}).second;
|
||||
}
|
||||
|
||||
void Renderer::AddWindowProcedureCallbackImpl(WindowProcedureCallback&& callback)
|
||||
@@ -71,12 +203,12 @@ namespace YimMenu
|
||||
return m_WindowProcedureCallbacks.push_back(callback);
|
||||
}
|
||||
|
||||
void Renderer::OnPresentImpl()
|
||||
void Renderer::DX12OnPresentImpl()
|
||||
{
|
||||
Renderer::NewFrame();
|
||||
for (const auto& callback : m_DXCallbacks | std::views::values)
|
||||
Renderer::DX12NewFrame();
|
||||
for (const auto& callback : m_RendererCallBacks | std::views::values)
|
||||
callback();
|
||||
Renderer::EndFrame();
|
||||
Renderer::DX12EndFrame();
|
||||
}
|
||||
|
||||
LRESULT Renderer::WndProcImpl(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
@@ -87,26 +219,131 @@ namespace YimMenu
|
||||
return ImGui_ImplWin32_WndProcHandler(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
void Renderer::PreResize()
|
||||
void Renderer::ResizeImpl(float scale)
|
||||
{
|
||||
ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||
DX12PreResize();
|
||||
|
||||
if (scale != 1.0f)
|
||||
ImGui::GetStyle().ScaleAllSizes(scale);
|
||||
ImGui::GetStyle().MouseCursorScale = 1.0f;
|
||||
ImGui::GetIO().FontGlobalScale = scale;
|
||||
DX12PostResize();
|
||||
}
|
||||
|
||||
void Renderer::PostResize()
|
||||
void Renderer::WaitForLastFrame()
|
||||
{
|
||||
ImGui_ImplDX11_CreateDeviceObjects();
|
||||
FrameContext FrameCtx = GetInstance().m_FrameContext[GetInstance().m_FrameIndex % GetInstance().m_SwapChainDesc.BufferCount];
|
||||
|
||||
UINT64 FenceValue = FrameCtx.FenceValue;
|
||||
|
||||
if (FenceValue == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FrameCtx.FenceValue = 0;
|
||||
|
||||
if (GetInstance().m_Fence->GetCompletedValue() >= FenceValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GetInstance().m_Fence->SetEventOnCompletion(FenceValue, GetInstance().m_FenceEvent);
|
||||
|
||||
WaitForSingleObject(GetInstance().m_FenceEvent, INFINITE);
|
||||
}
|
||||
|
||||
void Renderer::NewFrame()
|
||||
void Renderer::WaitForNextFrame()
|
||||
{
|
||||
ImGui_ImplDX11_NewFrame();
|
||||
UINT NextFrameIndex = GetInstance().m_FrameIndex + 1;
|
||||
GetInstance().m_FrameIndex = NextFrameIndex;
|
||||
|
||||
HANDLE WaitableObjects[] = {GetInstance().m_SwapchainWaitableObject, nullptr};
|
||||
DWORD NumWaitableObjets = 1;
|
||||
|
||||
FrameContext FrameCtx = GetInstance().m_FrameContext[NextFrameIndex % GetInstance().m_SwapChainDesc.BufferCount];
|
||||
UINT64 FenceValue = FrameCtx.FenceValue;
|
||||
if (FenceValue != 0) // means no fence was signaled
|
||||
{
|
||||
FrameCtx.FenceValue = 0;
|
||||
GetInstance().m_Fence->SetEventOnCompletion(FenceValue, GetInstance().m_FenceEvent);
|
||||
WaitableObjects[1] = GetInstance().m_FenceEvent;
|
||||
NumWaitableObjets = 2;
|
||||
}
|
||||
|
||||
WaitForMultipleObjects(NumWaitableObjets, WaitableObjects, TRUE, INFINITE);
|
||||
}
|
||||
|
||||
void Renderer::DX12PreResize()
|
||||
{
|
||||
SetResizing(true);
|
||||
|
||||
WaitForLastFrame();
|
||||
|
||||
ImGui_ImplDX12_InvalidateDeviceObjects();
|
||||
|
||||
for (size_t i{}; i != GetInstance().m_SwapChainDesc.BufferCount; ++i)
|
||||
{
|
||||
REL(GetInstance().m_FrameContext[i].Resource);
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::DX12PostResize()
|
||||
{
|
||||
//Recreate our pointers and ImGui's
|
||||
ImGui_ImplDX12_CreateDeviceObjects();
|
||||
const auto RTVDescriptorSize{GetInstance().m_Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV)};
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE RTVHandle{GetInstance().m_BackbufferDescriptorHeap->GetCPUDescriptorHandleForHeapStart()};
|
||||
for (size_t i{}; i != GetInstance().m_SwapChainDesc.BufferCount; ++i)
|
||||
{
|
||||
ComPtr<ID3D12Resource> BackBuffer{};
|
||||
GetInstance().m_FrameContext[i].Descriptor = RTVHandle;
|
||||
GetInstance().m_SwapChain->GetBuffer(i, __uuidof(ID3D12Resource), (void**)BackBuffer.GetAddressOf());
|
||||
GetInstance().m_Device->CreateRenderTargetView(BackBuffer.Get(), nullptr, RTVHandle);
|
||||
GetInstance().m_FrameContext[i].Resource = BackBuffer.Get();
|
||||
RTVHandle.ptr += RTVDescriptorSize;
|
||||
}
|
||||
|
||||
SetResizing(false);
|
||||
}
|
||||
|
||||
void Renderer::DX12NewFrame()
|
||||
{
|
||||
ImGui_ImplDX12_NewFrame();
|
||||
ImGui_ImplWin32_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
}
|
||||
|
||||
void Renderer::EndFrame()
|
||||
void Renderer::DX12EndFrame()
|
||||
{
|
||||
WaitForNextFrame();
|
||||
|
||||
FrameContext& CurrentFrameContext{GetInstance().m_FrameContext[GetInstance().m_SwapChain->GetCurrentBackBufferIndex()]};
|
||||
CurrentFrameContext.CommandAllocator->Reset();
|
||||
|
||||
D3D12_RESOURCE_BARRIER Barrier{D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
|
||||
D3D12_RESOURCE_BARRIER_FLAG_NONE,
|
||||
{{CurrentFrameContext.Resource, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET}}};
|
||||
GetInstance().m_CommandList->Reset(CurrentFrameContext.CommandAllocator, nullptr);
|
||||
GetInstance().m_CommandList->ResourceBarrier(1, &Barrier);
|
||||
GetInstance().m_CommandList->OMSetRenderTargets(1, &CurrentFrameContext.Descriptor, FALSE, nullptr);
|
||||
GetInstance().m_CommandList->SetDescriptorHeaps(1, GetInstance().m_DescriptorHeap.GetAddressOf());
|
||||
|
||||
ImGui::Render();
|
||||
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
|
||||
|
||||
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), GetInstance().m_CommandList.Get());
|
||||
|
||||
Barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
Barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
|
||||
GetInstance().m_CommandList->ResourceBarrier(1, &Barrier);
|
||||
GetInstance().m_CommandList->Close();
|
||||
|
||||
ID3D12CommandList* CommandLists[]{GetInstance().m_CommandList.Get()};
|
||||
GetInstance().m_CommandQueue->ExecuteCommandLists(_countof(CommandLists), CommandLists);
|
||||
|
||||
UINT64 FenceValue = GetInstance().m_FenceLastSignaledValue + 1;
|
||||
GetInstance().m_CommandQueue->Signal(GetInstance().m_Fence.Get(), FenceValue);
|
||||
GetInstance().m_FenceLastSignaledValue = FenceValue;
|
||||
CurrentFrameContext.FenceValue = FenceValue;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,37 @@
|
||||
#pragma once
|
||||
#include "util/Joaat.hpp"
|
||||
#include "core/util/Joaat.hpp"
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <backends/imgui_impl_dx12.h>
|
||||
#include <comdef.h>
|
||||
#include <d3d12.h>
|
||||
#include <dxgi1_4.h>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <windows.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
|
||||
#define REL(o) \
|
||||
o->Release(); \
|
||||
if (o) \
|
||||
{ \
|
||||
o = nullptr; \
|
||||
}
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
using DXCallback = std::function<void()>;
|
||||
using namespace Microsoft::WRL;
|
||||
using RendererCallBack = std::function<void()>;
|
||||
using WindowProcedureCallback = std::function<void(HWND, UINT, WPARAM, LPARAM)>;
|
||||
|
||||
struct FrameContext
|
||||
{
|
||||
ID3D12CommandAllocator* CommandAllocator;
|
||||
ID3D12Resource* Resource;
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE Descriptor;
|
||||
UINT64 FenceValue;
|
||||
};
|
||||
|
||||
class Renderer final
|
||||
{
|
||||
private:
|
||||
@@ -41,9 +62,9 @@ namespace YimMenu
|
||||
* @return true Successfully added callback.
|
||||
* @return false Duplicate render priority was given.
|
||||
*/
|
||||
static bool AddDXCallback(DXCallback&& callback, std::uint32_t priority)
|
||||
static bool AddRendererCallBack(RendererCallBack&& callback, std::uint32_t priority)
|
||||
{
|
||||
return GetInstance().AddDXCallbackImpl(std::move(callback), priority);
|
||||
return GetInstance().AddRendererCallBackImpl(std::move(callback), priority);
|
||||
}
|
||||
/**
|
||||
* @brief Add a callback function to handle Windows WindowProcedure
|
||||
@@ -55,30 +76,54 @@ namespace YimMenu
|
||||
GetInstance().AddWindowProcedureCallbackImpl(std::move(callback));
|
||||
}
|
||||
|
||||
static void OnPresent()
|
||||
static void DX12OnPresent()
|
||||
{
|
||||
GetInstance().OnPresentImpl();
|
||||
GetInstance().DX12OnPresentImpl();
|
||||
}
|
||||
|
||||
static LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
{
|
||||
return GetInstance().WndProcImpl(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
static void PreResize();
|
||||
static void PostResize();
|
||||
static void WaitForLastFrame();
|
||||
static void WaitForNextFrame();
|
||||
|
||||
static void DX12PreResize();
|
||||
static void DX12PostResize();
|
||||
|
||||
static bool IsResizing()
|
||||
{
|
||||
return GetInstance().m_Resizing;
|
||||
}
|
||||
|
||||
static void SetResizing(const bool& status)
|
||||
{
|
||||
GetInstance().m_Resizing = status;
|
||||
}
|
||||
|
||||
static void Resize(float scale)
|
||||
{
|
||||
GetInstance().ResizeImpl(scale);
|
||||
}
|
||||
|
||||
private:
|
||||
static void NewFrame();
|
||||
static void EndFrame();
|
||||
static void DX12NewFrame();
|
||||
static void DX12EndFrame();
|
||||
|
||||
private:
|
||||
void DestroyImpl();
|
||||
bool InitImpl();
|
||||
|
||||
bool AddDXCallbackImpl(DXCallback&& callback, std::uint32_t priority);
|
||||
bool InitDX12();
|
||||
|
||||
void ResizeImpl(float scale);
|
||||
|
||||
bool AddRendererCallBackImpl(RendererCallBack&& callback, std::uint32_t priority);
|
||||
void AddWindowProcedureCallbackImpl(WindowProcedureCallback&& callback);
|
||||
|
||||
void OnPresentImpl();
|
||||
void DX12OnPresentImpl();
|
||||
|
||||
LRESULT WndProcImpl(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
||||
|
||||
static Renderer& GetInstance()
|
||||
@@ -89,11 +134,33 @@ namespace YimMenu
|
||||
}
|
||||
|
||||
private:
|
||||
ID3D11Device* m_Device;
|
||||
ID3D11DeviceContext* m_DeviceContext;
|
||||
IDXGISwapChain* m_SwapChain;
|
||||
bool m_Resizing;
|
||||
|
||||
std::map<joaat_t, DXCallback> m_DXCallbacks;
|
||||
// Shared
|
||||
ImFontAtlas m_FontAtlas;
|
||||
|
||||
//DX12
|
||||
std::vector<FrameContext> m_FrameContext;
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC m_SwapChainDesc;
|
||||
ComPtr<IDXGISwapChain1> m_GameSwapChain;
|
||||
ComPtr<IDXGISwapChain3> m_SwapChain;
|
||||
ComPtr<ID3D12Device> m_Device;
|
||||
ComPtr<ID3D12CommandQueue> m_CommandQueue;
|
||||
ComPtr<ID3D12CommandAllocator> m_CommandAllocator;
|
||||
ComPtr<ID3D12GraphicsCommandList> m_CommandList;
|
||||
ComPtr<ID3D12DescriptorHeap> m_BackbufferDescriptorHeap;
|
||||
ComPtr<ID3D12DescriptorHeap> m_DescriptorHeap;
|
||||
|
||||
ComPtr<ID3D12Fence> m_Fence;
|
||||
HANDLE m_FenceEvent;
|
||||
UINT64 m_FenceLastSignaledValue;
|
||||
HANDLE m_SwapchainWaitableObject;
|
||||
UINT64 m_FrameIndex;
|
||||
|
||||
private:
|
||||
//Other
|
||||
std::map<joaat_t, RendererCallBack> m_RendererCallBacks;
|
||||
std::vector<WindowProcedureCallback> m_WindowProcedureCallbacks;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "IStateSerializer.hpp"
|
||||
|
||||
#include "Settings.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
IStateSerializer::IStateSerializer(const std::string& name) :
|
||||
m_SerComponentName(name),
|
||||
m_IsDirty(false)
|
||||
{
|
||||
Settings::AddComponent(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class IStateSerializer
|
||||
{
|
||||
std::string m_SerComponentName;
|
||||
bool m_IsDirty;
|
||||
|
||||
public:
|
||||
IStateSerializer(const std::string& name);
|
||||
virtual void SaveStateImpl(nlohmann::json& state) = 0;
|
||||
virtual void LoadStateImpl(nlohmann::json& state) = 0;
|
||||
|
||||
inline void SaveState(nlohmann::json& state)
|
||||
{
|
||||
SaveStateImpl(state);
|
||||
m_IsDirty = false;
|
||||
}
|
||||
|
||||
inline void LoadState(nlohmann::json& state)
|
||||
{
|
||||
LoadStateImpl(state);
|
||||
m_IsDirty = false;
|
||||
}
|
||||
|
||||
inline bool IsStateDirty()
|
||||
{
|
||||
return m_IsDirty;
|
||||
}
|
||||
|
||||
inline void MarkStateDirty()
|
||||
{
|
||||
m_IsDirty = true;
|
||||
}
|
||||
|
||||
inline const std::string& GetSerializerComponentName()
|
||||
{
|
||||
return m_SerComponentName;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
#include "Settings.hpp"
|
||||
|
||||
#include "IStateSerializer.hpp"
|
||||
#include "Settings.hpp"
|
||||
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
Settings::Settings() :
|
||||
m_SettingsFile(),
|
||||
m_StateSerializers(),
|
||||
m_InitialLoadDone(false)
|
||||
{
|
||||
}
|
||||
|
||||
void Settings::InitializeImpl(File settingsFile)
|
||||
{
|
||||
m_SettingsFile = settingsFile;
|
||||
|
||||
if (!settingsFile.Exists())
|
||||
{
|
||||
Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
std::ifstream file(m_SettingsFile);
|
||||
|
||||
try
|
||||
{
|
||||
file >> m_Json;
|
||||
file.close();
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
LOG(WARNING) << "Detected corrupt settings, resetting settings...";
|
||||
Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& serializer : m_StateSerializers)
|
||||
LoadComponentImpl(serializer);
|
||||
|
||||
LOG(VERBOSE) << "All settings loaded";
|
||||
m_InitialLoadDone = true;
|
||||
}
|
||||
|
||||
void Settings::TickImpl()
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
while (!m_LateLoaders.empty())
|
||||
{
|
||||
if (auto component = std::move(m_LateLoaders.front()))
|
||||
{
|
||||
LoadComponent(component);
|
||||
}
|
||||
|
||||
m_LateLoaders.pop();
|
||||
}
|
||||
|
||||
if (m_InitialLoadDone && ShouldSave())
|
||||
{
|
||||
for (auto& serializer : m_StateSerializers)
|
||||
if (serializer->IsStateDirty())
|
||||
SaveComponentImpl(serializer);
|
||||
|
||||
std::ofstream file(m_SettingsFile, std::ios::out | std::ios::trunc);
|
||||
file << m_Json.dump(4);
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
void Settings::AddComponentImpl(IStateSerializer* serializer)
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
m_StateSerializers.push_back(serializer);
|
||||
if (m_InitialLoadDone)
|
||||
m_LateLoaders.push(serializer);
|
||||
}
|
||||
|
||||
void Settings::LoadComponentImpl(IStateSerializer* serializer)
|
||||
{
|
||||
LOG(VERBOSE) << "Loading component: " << serializer->GetSerializerComponentName();
|
||||
|
||||
if (!m_Json.contains(serializer->GetSerializerComponentName())
|
||||
|| !m_Json[serializer->GetSerializerComponentName()].is_object())
|
||||
m_Json[serializer->GetSerializerComponentName()] = nlohmann::json::object();
|
||||
|
||||
serializer->LoadState(m_Json[serializer->GetSerializerComponentName()]);
|
||||
}
|
||||
|
||||
void Settings::SaveComponentImpl(IStateSerializer* serializer)
|
||||
{
|
||||
//LOG(VERBOSE) << "Saving component: " << serializer->GetSerializerComponentName();
|
||||
serializer->SaveState(m_Json[serializer->GetSerializerComponentName()]);
|
||||
}
|
||||
|
||||
void Settings::Reset()
|
||||
{
|
||||
std::ofstream file(m_SettingsFile, std::ios::out | std::ios::trunc);
|
||||
file << "{}" << std::endl;
|
||||
file.close();
|
||||
m_Json = "{}";
|
||||
m_InitialLoadDone = true;
|
||||
}
|
||||
|
||||
bool Settings::ShouldSave()
|
||||
{
|
||||
for (auto& serializer : m_StateSerializers)
|
||||
if (serializer->IsStateDirty())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
#include "core/filemgr/File.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class IStateSerializer;
|
||||
|
||||
class Settings
|
||||
{
|
||||
private:
|
||||
std::filesystem::path m_SettingsFile;
|
||||
std::vector<IStateSerializer*> m_StateSerializers;
|
||||
std::queue<IStateSerializer*> m_LateLoaders;
|
||||
bool m_InitialLoadDone;
|
||||
nlohmann::json m_Json;
|
||||
std::mutex m_Mutex;
|
||||
|
||||
public:
|
||||
Settings();
|
||||
|
||||
static void Initialize(File settingsFile)
|
||||
{
|
||||
GetInstance().InitializeImpl(settingsFile);
|
||||
}
|
||||
|
||||
static void Tick()
|
||||
{
|
||||
GetInstance().TickImpl();
|
||||
}
|
||||
|
||||
static void AddComponent(IStateSerializer* serializer)
|
||||
{
|
||||
GetInstance().AddComponentImpl(serializer);
|
||||
}
|
||||
|
||||
// TODO: this is broken
|
||||
static void LoadComponent(IStateSerializer* serializer)
|
||||
{
|
||||
GetInstance().LoadComponentImpl(serializer);
|
||||
}
|
||||
|
||||
static bool InitialLoadDone()
|
||||
{
|
||||
return GetInstance().m_InitialLoadDone;
|
||||
}
|
||||
|
||||
static std::string GetFilePath()
|
||||
{
|
||||
return GetInstance().m_SettingsFile.string();
|
||||
}
|
||||
|
||||
private:
|
||||
static Settings& GetInstance()
|
||||
{
|
||||
static Settings Instance;
|
||||
return Instance;
|
||||
}
|
||||
|
||||
void InitializeImpl(File settingsFile);
|
||||
void TickImpl();
|
||||
void AddComponentImpl(IStateSerializer* serializer);
|
||||
void LoadComponentImpl(IStateSerializer* serializer);
|
||||
void SaveComponentImpl(IStateSerializer* serializer);
|
||||
void Reset();
|
||||
bool ShouldSave();
|
||||
};
|
||||
}
|
||||
@@ -11,7 +11,21 @@ namespace YimMenu
|
||||
return c >= 'A' && c <= 'Z' ? c | 1 << 5 : c;
|
||||
}
|
||||
|
||||
extern constexpr joaat_t Joaat(const std::string_view str);
|
||||
constexpr joaat_t Joaat(const std::string_view str)
|
||||
{
|
||||
joaat_t hash = 0;
|
||||
for (auto c : str)
|
||||
{
|
||||
hash += ToLower(c);
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
}
|
||||
hash += (hash << 3);
|
||||
hash ^= (hash >> 11);
|
||||
hash += (hash << 15);
|
||||
return hash;
|
||||
}
|
||||
|
||||
inline consteval joaat_t operator""_J(const char* s, std::size_t n)
|
||||
{
|
||||
joaat_t result = 0;
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
#include <chrono>
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class RateLimiter
|
||||
{
|
||||
uint32_t m_AttemptsAllowedInTimePeriod;
|
||||
std::chrono::milliseconds m_TimePeriod;
|
||||
std::chrono::system_clock::time_point m_LastEventTime{};
|
||||
uint32_t m_NumAttemptsAllowed = 0;
|
||||
|
||||
public:
|
||||
RateLimiter(std::chrono::milliseconds period, uint32_t numAllowedAttempts) :
|
||||
m_AttemptsAllowedInTimePeriod(numAllowedAttempts),
|
||||
m_TimePeriod(period)
|
||||
{
|
||||
}
|
||||
|
||||
// Returns true if the rate limit has been exceeded
|
||||
bool Process()
|
||||
{
|
||||
if (std::chrono::system_clock::now() - m_LastEventTime < m_TimePeriod)
|
||||
{
|
||||
if (++m_NumAttemptsAllowed > m_AttemptsAllowedInTimePeriod)
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_LastEventTime = std::chrono::system_clock::now();
|
||||
m_NumAttemptsAllowed = 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the rate limit was exceeded by the last process() call. Use this to prevent the player from being flooded with notifications
|
||||
bool ExceededLastProcess()
|
||||
{
|
||||
return (m_NumAttemptsAllowed - 1) == m_AttemptsAllowedInTimePeriod;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
#include "ChatDisplay.hpp"
|
||||
|
||||
#include "Menu.hpp"
|
||||
#include "core/logger/LogHelper.hpp"
|
||||
#include "core/backend/FiberPool.hpp"
|
||||
#include "game/pointers/Pointers.hpp"
|
||||
#include "core/frontend/widgets/imgui_colors.h"
|
||||
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
void ChatDisplay::ShowImpl(std::string sender, std::string message, ImColor color)
|
||||
{
|
||||
if (sender.empty() || message.empty())
|
||||
return;
|
||||
|
||||
Message notification{};
|
||||
notification.m_Sender = sender;
|
||||
notification.m_Message = message;
|
||||
notification.m_Color = color;
|
||||
|
||||
static const bool isBigScreen = *Pointers.ScreenResX > 1600 && *Pointers.ScreenResY > 900;
|
||||
static const int maxMessages = isBigScreen ? 17 : 7;
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
|
||||
if (m_Messages.size() >= maxMessages)
|
||||
{
|
||||
m_Messages.erase(m_Messages.begin());
|
||||
}
|
||||
|
||||
m_Messages.push_back(notification);
|
||||
}
|
||||
|
||||
void ChatDisplay::DrawImpl()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
int position = 0;
|
||||
|
||||
static const float y_pos = position * 100 + 200;
|
||||
static const float x_pos = *Pointers.ScreenResX - 470;
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(*Pointers.ScreenResX - x_pos - 10, *Pointers.ScreenResY - y_pos), ImGuiCond_Always);
|
||||
ImGui::SetNextWindowPos(ImVec2(x_pos, y_pos), ImGuiCond_Always);
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
ImGui::PushFont(Menu::Font::g_ChatFont);
|
||||
|
||||
ImGui::Begin("##chatwin", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoInputs);
|
||||
|
||||
for (auto& message : m_Messages)
|
||||
{
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(ImGui::Colors::Blue));
|
||||
ImGui::TextWrapped("%s:", message.m_Sender.data());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine();
|
||||
ImGui::TextWrapped("%s", message.m_Message.data());
|
||||
}
|
||||
|
||||
ImGui::PopFont();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void ChatDisplay::ClearImpl()
|
||||
{
|
||||
m_Messages.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include "game/pointers/Pointers.hpp" // game import in core
|
||||
|
||||
#include <mutex>
|
||||
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
struct Message
|
||||
{
|
||||
std::string m_Sender;
|
||||
std::string m_Message;
|
||||
ImColor m_Color;
|
||||
};
|
||||
|
||||
class ChatDisplay
|
||||
{
|
||||
private:
|
||||
std::vector<Message> m_Messages = {};
|
||||
std::mutex m_Mutex;
|
||||
|
||||
void ShowImpl(std::string sender, std::string message, ImColor color);
|
||||
void DrawImpl();
|
||||
void ClearImpl();
|
||||
|
||||
static ChatDisplay& GetInstance()
|
||||
{
|
||||
static ChatDisplay instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
public:
|
||||
static void Show(std::string sender, std::string message, ImColor color)
|
||||
{
|
||||
return GetInstance().ShowImpl(sender, message, color);
|
||||
}
|
||||
|
||||
static void Draw()
|
||||
{
|
||||
GetInstance().DrawImpl();
|
||||
}
|
||||
|
||||
static void Clear()
|
||||
{
|
||||
GetInstance().ClearImpl();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
+41
-16
@@ -1,17 +1,39 @@
|
||||
#include "GUI.hpp"
|
||||
|
||||
#include "menu/Menu.hpp"
|
||||
#include "Menu.hpp"
|
||||
#include "Overlay.hpp"
|
||||
#include "core/renderer/Renderer.hpp"
|
||||
#include "core/frontend/Notifications.hpp"
|
||||
#include "game/frontend/ChatDisplay.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
GUI::GUI() :
|
||||
m_IsOpen(false)
|
||||
m_IsOpen(false)
|
||||
{
|
||||
Renderer::AddDXCallback(Menu::Main, -1);
|
||||
Menu::SetupFonts();
|
||||
Menu::SetupStyle();
|
||||
Menu::Init();
|
||||
|
||||
Renderer::AddWindowProcedureCallback([this](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||
GUI::WndProc(hwnd, msg, wparam, lparam);
|
||||
});
|
||||
|
||||
Renderer::AddRendererCallBack(
|
||||
[&] {
|
||||
Notifications::Draw();
|
||||
},
|
||||
-2);
|
||||
|
||||
Renderer::AddRendererCallBack(
|
||||
[&] {
|
||||
ChatDisplay::Draw();
|
||||
},
|
||||
-5);
|
||||
Renderer::AddRendererCallBack(
|
||||
[&] {
|
||||
Overlay::Draw();
|
||||
},
|
||||
-6);
|
||||
}
|
||||
|
||||
GUI::~GUI()
|
||||
@@ -20,24 +42,27 @@ namespace YimMenu
|
||||
|
||||
void GUI::ToggleMouse()
|
||||
{
|
||||
auto& io = ImGui::GetIO();
|
||||
if (GUI::IsOpen())
|
||||
{
|
||||
io.MouseDrawCursor = true;
|
||||
io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse;
|
||||
}
|
||||
else
|
||||
{
|
||||
io.MouseDrawCursor = false;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NoMouse;
|
||||
}
|
||||
auto& io = ImGui::GetIO();
|
||||
io.MouseDrawCursor = GUI::IsOpen();
|
||||
GUI::IsOpen() ? io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse : io.ConfigFlags |= ImGuiConfigFlags_NoMouse;
|
||||
}
|
||||
|
||||
void GUI::WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
||||
{
|
||||
if (msg == WM_KEYUP && wparam == VK_INSERT)
|
||||
{
|
||||
m_IsOpen = !m_IsOpen;
|
||||
LOG(INFO) << "fired";
|
||||
// Persist and restore the cursor position between menu instances
|
||||
static POINT CursorCoords{};
|
||||
if (m_IsOpen)
|
||||
{
|
||||
GetCursorPos(&CursorCoords);
|
||||
}
|
||||
else if (CursorCoords.x + CursorCoords.y)
|
||||
{
|
||||
SetCursorPos(CursorCoords.x, CursorCoords.y);
|
||||
}
|
||||
Toggle();
|
||||
ToggleMouse();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace YimMenu
|
||||
|
||||
static void Init()
|
||||
{
|
||||
GetInstance();
|
||||
GetInstance().m_IsOpen = false;
|
||||
}
|
||||
|
||||
static bool IsOpen()
|
||||
@@ -26,6 +26,16 @@ namespace YimMenu
|
||||
return GetInstance().m_IsOpen;
|
||||
}
|
||||
|
||||
static void Toggle()
|
||||
{
|
||||
GetInstance().m_IsOpen ^= true;
|
||||
}
|
||||
|
||||
static bool IsUsingKeyboard()
|
||||
{
|
||||
return ImGui::GetIO().WantTextInput;
|
||||
}
|
||||
|
||||
private:
|
||||
void WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
||||
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
#include "Menu.hpp"
|
||||
|
||||
#include "core/commands/Commands.hpp"
|
||||
#include "core/frontend/manager/UIManager.hpp"
|
||||
#include "core/renderer/Renderer.hpp"
|
||||
#include "core/backend/FiberPool.hpp"
|
||||
#include "core/backend/ScriptMgr.hpp"
|
||||
#include "game/frontend/fonts/Fonts.hpp"
|
||||
#include "game/pointers/Pointers.hpp"
|
||||
#include "submenus/Self.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
void Menu::Init()
|
||||
{
|
||||
// Arguably the only place this file should be edited at for more menus
|
||||
UIManager::AddSubmenu(std::make_shared<Submenus::Self>());
|
||||
|
||||
Renderer::AddRendererCallBack(
|
||||
[&] {
|
||||
if (!GUI::IsOpen())
|
||||
return;
|
||||
|
||||
ImGui::PushFont(Menu::Font::g_DefaultFont);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImU32(ImColor(15, 15, 15)));
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2((*Pointers.ScreenResX / 2.5), (*Pointers.ScreenResY / 2.5)), ImGuiCond_Once);
|
||||
if (ImGui::Begin("Terminus", nullptr, ImGuiWindowFlags_NoDecoration))
|
||||
{
|
||||
//ImGui::BeginDisabled(*Pointers.IsSessionStarted);
|
||||
if (ImGui::Button("Unload", ImVec2(120, 0)))
|
||||
{
|
||||
if (true)
|
||||
{
|
||||
FiberPool::Push([] {
|
||||
Commands::Shutdown();
|
||||
g_Running = false;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
g_Running = false;
|
||||
}
|
||||
}
|
||||
//ImGui::EndDisabled();
|
||||
|
||||
UIManager::Draw();
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopFont();
|
||||
},
|
||||
-1);
|
||||
}
|
||||
|
||||
void Menu::SetupStyle()
|
||||
{
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
style.Colors[ImGuiCol_Text] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
|
||||
style.Colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
|
||||
|
||||
style.Colors[ImGuiCol_WindowBg] = ImVec4(0.10f, 0.10f, 0.12f, 1.00f);
|
||||
style.Colors[ImGuiCol_ChildBg] = ImVec4(0.12f, 0.12f, 0.14f, 1.00f);
|
||||
style.Colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.10f, 0.95f);
|
||||
|
||||
style.Colors[ImGuiCol_Border] = ImVec4(0.28f, 0.28f, 0.28f, 0.25f);
|
||||
style.Colors[ImGuiCol_FrameBg] = ImVec4(0.18f, 0.18f, 0.20f, 1.00f);
|
||||
style.Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.35f, 0.40f, 0.50f, 0.75f);
|
||||
style.Colors[ImGuiCol_FrameBgActive] = ImVec4(0.45f, 0.50f, 0.60f, 0.85f);
|
||||
|
||||
style.Colors[ImGuiCol_Button] = ImVec4(0.40f, 0.25f, 0.50f, 1.00f);
|
||||
style.Colors[ImGuiCol_ButtonHovered] = ImVec4(0.50f, 0.35f, 0.60f, 1.00f);
|
||||
style.Colors[ImGuiCol_ButtonActive] = ImVec4(0.60f, 0.45f, 0.75f, 1.00f);
|
||||
|
||||
style.Colors[ImGuiCol_TitleBg] = ImVec4(0.08f, 0.08f, 0.10f, 1.00f);
|
||||
style.Colors[ImGuiCol_TitleBgActive] = ImVec4(0.10f, 0.12f, 0.14f, 1.00f);
|
||||
style.Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.05f, 0.05f, 0.06f, 1.00f);
|
||||
|
||||
style.Colors[ImGuiCol_Tab] = ImVec4(0.25f, 0.20f, 0.45f, 0.85f);
|
||||
style.Colors[ImGuiCol_TabHovered] = ImVec4(0.35f, 0.30f, 0.65f, 0.85f);
|
||||
style.Colors[ImGuiCol_TabActive] = ImVec4(0.40f, 0.35f, 0.75f, 1.00f);
|
||||
|
||||
style.Colors[ImGuiCol_Header] = ImVec4(0.20f, 0.25f, 0.35f, 1.00f);
|
||||
style.Colors[ImGuiCol_HeaderHovered] = ImVec4(0.35f, 0.40f, 0.50f, 1.00f);
|
||||
style.Colors[ImGuiCol_HeaderActive] = ImVec4(0.45f, 0.50f, 0.60f, 1.00f);
|
||||
|
||||
style.Colors[ImGuiCol_PlotLines] = ImVec4(0.80f, 0.80f, 0.00f, 0.00f);
|
||||
style.Colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.85f, 0.00f, 1.00f);
|
||||
style.Colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.20f, 1.00f);
|
||||
style.Colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.80f, 0.40f, 1.00f);
|
||||
|
||||
style.GrabRounding = style.FrameRounding = style.ChildRounding = style.WindowRounding = 8.0f;
|
||||
}
|
||||
|
||||
void Menu::SetupFonts()
|
||||
{
|
||||
auto& IO = ImGui::GetIO();
|
||||
ImFontConfig FontCfg{};
|
||||
FontCfg.FontDataOwnedByAtlas = false;
|
||||
|
||||
Menu::Font::g_DefaultFont = IO.Fonts->AddFontFromMemoryTTF(const_cast<std::uint8_t*>(Fonts::MainFont), sizeof(Fonts::MainFont), Menu::Font::g_DefaultFontSize, &FontCfg);
|
||||
Menu::Font::g_OptionsFont = IO.Fonts->AddFontFromMemoryTTF(const_cast<std::uint8_t*>(Fonts::MainFont), sizeof(Fonts::MainFont), Menu::Font::g_OptionsFontSize, &FontCfg);
|
||||
Menu::Font::g_ChildTitleFont = IO.Fonts->AddFontFromMemoryTTF(const_cast<std::uint8_t*>(Fonts::MainFont), sizeof(Fonts::MainFont), Menu::Font::g_ChildTitleFontSize, &FontCfg);
|
||||
Menu::Font::g_ChatFont = IO.Fonts->AddFontFromMemoryTTF(const_cast<std::uint8_t*>(Fonts::MainFont), sizeof(Fonts::MainFont), Menu::Font::g_ChatFontSize, &FontCfg);
|
||||
Menu::Font::g_OverlayFont = IO.Fonts->AddFontFromMemoryTTF(const_cast<std::uint8_t*>(Fonts::MainFont), sizeof(Fonts::MainFont), Menu::Font::g_OverlayFontSize, &FontCfg);
|
||||
UIManager::SetOptionsFont(Menu::Font::g_OptionsFont);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "game/frontend/GUI.hpp"
|
||||
|
||||
namespace YimMenu::Menu
|
||||
{
|
||||
extern void Init();
|
||||
extern void SetupStyle();
|
||||
extern void SetupFonts();
|
||||
|
||||
namespace Font
|
||||
{
|
||||
inline ImFont* g_DefaultFont = nullptr;
|
||||
inline float g_DefaultFontSize = 19.0f;
|
||||
|
||||
inline ImFont* g_OptionsFont = nullptr;
|
||||
inline float g_OptionsFontSize = 17.0f;
|
||||
|
||||
inline ImFont* g_ChildTitleFont = nullptr;
|
||||
inline float g_ChildTitleFontSize = 14.5f;
|
||||
|
||||
inline ImFont* g_ChatFont = nullptr;
|
||||
inline float g_ChatFontSize = 24.0f;
|
||||
|
||||
inline ImFont* g_OverlayFont = nullptr;
|
||||
inline float g_OverlayFontSize = 30.0f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#include "Overlay.hpp"
|
||||
#include "Menu.hpp"
|
||||
#include "core/commands/BoolCommand.hpp"
|
||||
#include "game/pointers/Pointers.hpp"
|
||||
#include "game/gta/invoker/Invoker.hpp"
|
||||
|
||||
namespace YimMenu::Features
|
||||
{
|
||||
BoolCommand _OverlayEnabled("overlay", "Overlay Enabled", "Show an info overlay at the top left corner of the screen");
|
||||
BoolCommand _OverlayShowFPS("overlayfps", "Overlay Show FPS", "Show frame rate in the info overlay");
|
||||
}
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
void Overlay::Draw()
|
||||
{
|
||||
#if 0
|
||||
if (!Features::_OverlayEnabled.GetState() || !NativeInvoker::AreHandlersCached())
|
||||
return;
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(*Pointers.ScreenResX - 10.0f, *Pointers.ScreenResY - 10.0f), ImGuiCond_Always);
|
||||
ImGui::SetNextWindowPos(ImVec2(10.0f, 10.0f), ImGuiCond_Always);
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
ImGui::PushFont(Menu::Font::g_OverlayFont);
|
||||
|
||||
ImGui::Begin("##overlay", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoInputs);
|
||||
|
||||
if (Features::_OverlayShowFPS.GetState())
|
||||
ImGui::Text("FPS: %d", (int)(1 / MISC::GET_SYSTEM_TIME_STEP()));
|
||||
|
||||
ImGui::PopFont();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::End();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class Overlay
|
||||
{
|
||||
public:
|
||||
static void Draw();
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace YimMenu::Fonts
|
||||
{
|
||||
extern const uint8_t MainFont[78948];
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,55 @@
|
||||
#include "Items.hpp"
|
||||
#include "core/commands/Commands.hpp"
|
||||
#include "core/commands/Command.hpp"
|
||||
#include "core/commands/LoopedCommand.hpp"
|
||||
#include "core/frontend/widgets/toggle/imgui_toggle.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
BoolCommandItem::BoolCommandItem(joaat_t id, std::optional<std::string> label_override) :
|
||||
m_Command(Commands::GetCommand<BoolCommand>(id)),
|
||||
m_LabelOverride(label_override)
|
||||
{
|
||||
}
|
||||
|
||||
void BoolCommandItem::Draw()
|
||||
{
|
||||
if (!m_Command)
|
||||
{
|
||||
ImGui::Text("Unknown!");
|
||||
return;
|
||||
}
|
||||
|
||||
bool enabled = m_Command->GetState();
|
||||
if (ImGui::Toggle(m_LabelOverride.has_value() ? m_LabelOverride.value().data() : m_Command->GetLabel().data(), &enabled))
|
||||
m_Command->SetState(enabled);
|
||||
|
||||
// TODO: refactor this
|
||||
|
||||
auto windowLabel = std::format("{} Hotkey", m_Command->GetLabel());
|
||||
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
ImGui::SetTooltip("%s", m_Command->GetDescription().data());
|
||||
if (GetAsyncKeyState(VK_OEM_3) & 0x8000)
|
||||
ImGui::OpenPopup(std::format("{} Hotkey", m_Command->GetLabel()).data());
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(500, 120));
|
||||
if (ImGui::BeginPopupModal(windowLabel.data(), nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar))
|
||||
{
|
||||
ImGui::BulletText("Hover over the command name to change its hotkey");
|
||||
ImGui::BulletText("Press any registered key to remove");
|
||||
ImGui::Separator();
|
||||
|
||||
HotkeySetter(m_Command->GetHash()).Draw();
|
||||
|
||||
|
||||
ImGui::Spacing();
|
||||
if (ImGui::Button("Close") || ((!ImGui::IsWindowHovered() && !ImGui::IsAnyItemHovered()) && ImGui::IsMouseClicked(ImGuiMouseButton_Left)))
|
||||
ImGui::CloseCurrentPopup();
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#include "Items.hpp"
|
||||
#include "core/backend/FiberPool.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
Button::Button(const std::string_view& name, std::function<void()> onClick, const ImVec2 size, const std::string_view& information) :
|
||||
m_Name(name),
|
||||
m_Information(information),
|
||||
m_OnClick(onClick),
|
||||
m_Size(size)
|
||||
{
|
||||
}
|
||||
|
||||
void Button::Draw()
|
||||
{
|
||||
if (ImGui::Button(m_Name.data(), m_Size))
|
||||
{
|
||||
if (m_OnClick != nullptr)
|
||||
{
|
||||
FiberPool::Push([&] {
|
||||
m_OnClick();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "Items.hpp"
|
||||
#include "core/commands/ColorCommand.hpp"
|
||||
#include "core/commands/Command.hpp"
|
||||
#include "core/commands/Commands.hpp"
|
||||
#include "core/frontend/widgets/toggle/imgui_toggle.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
ColorCommandItem::ColorCommandItem(joaat_t id, std::optional<std::string> labelOverride) :
|
||||
m_Command(Commands::GetCommand<ColorCommand>(id)),
|
||||
m_LabelOverride(labelOverride)
|
||||
{
|
||||
}
|
||||
|
||||
void ColorCommandItem::Draw()
|
||||
{
|
||||
if (!m_Command)
|
||||
{
|
||||
ImGui::Text("Unknown color picker!");
|
||||
return;
|
||||
}
|
||||
|
||||
auto color = m_Command->GetState();
|
||||
auto label = m_LabelOverride.has_value() ? m_LabelOverride.value().c_str() : m_Command->GetLabel().c_str();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::SetNextItemWidth(150);
|
||||
if (ImGui::ColorButton(label, color))
|
||||
{
|
||||
ImGui::OpenPopup(label);
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopup(label))
|
||||
{
|
||||
if (ImGui::ColorPicker4("##picker", (float*)&color))
|
||||
{
|
||||
m_Command->SetState(color);
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "Items.hpp"
|
||||
#include "core/commands/Commands.hpp"
|
||||
#include "core/commands/Command.hpp"
|
||||
#include "core/backend/FiberPool.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
CommandItem::CommandItem(joaat_t id, std::optional<std::string> label_override) :
|
||||
m_Command(Commands::GetCommand<Command>(id)),
|
||||
m_LabelOverride(label_override)
|
||||
{
|
||||
}
|
||||
|
||||
void CommandItem::Draw()
|
||||
{
|
||||
if (!m_Command)
|
||||
{
|
||||
ImGui::Text("Unknown!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ImGui::Button(m_LabelOverride.has_value() ? m_LabelOverride.value().data() : m_Command->GetLabel().data()))
|
||||
{
|
||||
FiberPool::Push([this] {
|
||||
m_Command->Call();
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: refactor this
|
||||
|
||||
auto windowLabel = std::format("{} Hotkey", m_Command->GetLabel());
|
||||
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
ImGui::SetTooltip("%s", m_Command->GetDescription().data());
|
||||
if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
|
||||
ImGui::OpenPopup(std::format("{} Hotkey", m_Command->GetLabel()).data());
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(500, 120));
|
||||
if (ImGui::BeginPopupModal(windowLabel.data(), nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar))
|
||||
{
|
||||
ImGui::BulletText("Hold the command name clicked to change its hotkey");
|
||||
ImGui::BulletText("Press any registered key to remove");
|
||||
ImGui::Separator();
|
||||
|
||||
HotkeySetter(m_Command->GetHash()).Draw();
|
||||
|
||||
|
||||
ImGui::Spacing();
|
||||
if (ImGui::Button("Close") || ((!ImGui::IsWindowHovered() && !ImGui::IsAnyItemHovered()) && ImGui::IsMouseClicked(ImGuiMouseButton_Left)))
|
||||
ImGui::CloseCurrentPopup();
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#include "Items.hpp"
|
||||
#include "core/commands/Command.hpp"
|
||||
#include "core/commands/Commands.hpp"
|
||||
#include "core/commands/BoolCommand.hpp"
|
||||
#include "core/frontend/widgets/toggle/imgui_toggle.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
ConditionalItem::ConditionalItem(joaat_t bool_cmd_id, std::shared_ptr<UIItem> to_draw) :
|
||||
m_Condition(Commands::GetCommand<BoolCommand>(bool_cmd_id)),
|
||||
m_Item(to_draw)
|
||||
{
|
||||
}
|
||||
|
||||
void ConditionalItem::Draw()
|
||||
{
|
||||
if (!m_Condition)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_Condition->GetState())
|
||||
m_Item->Draw();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#include "Items.hpp"
|
||||
#include "core/commands/Command.hpp"
|
||||
#include "core/commands/Commands.hpp"
|
||||
#include "core/commands/FloatCommand.hpp"
|
||||
#include "core/frontend/widgets/toggle/imgui_toggle.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
FloatCommandItem::FloatCommandItem(joaat_t id, std::optional<std::string> label_override) :
|
||||
m_Command(Commands::GetCommand<FloatCommand>(id)),
|
||||
m_LabelOverride(label_override)
|
||||
{
|
||||
}
|
||||
|
||||
void FloatCommandItem::Draw()
|
||||
{
|
||||
if (!m_Command)
|
||||
{
|
||||
ImGui::Text("Unknown!");
|
||||
return;
|
||||
}
|
||||
|
||||
auto value = m_Command->GetState();
|
||||
auto label = m_LabelOverride.has_value() ? m_LabelOverride.value().c_str() : m_Command->GetLabel().c_str();
|
||||
if (!m_Command->GetMinimum().has_value() || !m_Command->GetMaximum().has_value())
|
||||
{
|
||||
ImGui::SetNextItemWidth(150);
|
||||
if (ImGui::InputFloat(label, &value))
|
||||
{
|
||||
m_Command->SetState(value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::SetNextItemWidth(150);
|
||||
if (ImGui::SliderFloat(label, &value, m_Command->GetMinimum().value(), m_Command->GetMaximum().value()))
|
||||
{
|
||||
m_Command->SetState(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "Items.hpp"
|
||||
#include "core/commands/Command.hpp"
|
||||
#include "core/commands/Commands.hpp"
|
||||
#include "core/backend/FiberPool.hpp"
|
||||
#include "game/frontend/Menu.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
Group::Group(const std::string& name, int items_per_row) :
|
||||
m_Name(name),
|
||||
m_ItemsPerRow(items_per_row)
|
||||
{
|
||||
}
|
||||
|
||||
void Group::Draw()
|
||||
{
|
||||
if (!m_Name.empty())
|
||||
{
|
||||
ImGui::PushFont(Menu::Font::g_ChildTitleFont);
|
||||
ImGui::Text("%s", m_Name.c_str());
|
||||
ImGui::PopFont();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
}
|
||||
|
||||
int item_count = 0;
|
||||
|
||||
ImGui::BeginGroup();
|
||||
for (auto& item : m_Items)
|
||||
{
|
||||
item->Draw();
|
||||
item_count++;
|
||||
|
||||
if (m_ItemsPerRow != -1 && item_count % m_ItemsPerRow == 0)
|
||||
{
|
||||
ImGui::EndGroup();
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginGroup();
|
||||
}
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
#include "Items.hpp"
|
||||
#include "core/commands/Command.hpp"
|
||||
#include "core/commands/Commands.hpp"
|
||||
#include "core/commands/HotkeySystem.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
HotkeySetter::HotkeySetter(joaat_t command_id) :
|
||||
m_Id(command_id)
|
||||
{
|
||||
}
|
||||
|
||||
void HotkeySetter::Draw()
|
||||
{
|
||||
auto Command = Commands::GetCommand(m_Id);
|
||||
|
||||
if (!Command)
|
||||
ImGui::Text("Unkown Command");
|
||||
else
|
||||
{
|
||||
CommandLink* CommandHotkeyLink = &g_HotkeySystem.m_CommandHotkeys.at(Command->GetHash());
|
||||
|
||||
if (!CommandHotkeyLink)
|
||||
{
|
||||
ImGui::Text("Unkown CommandLink");
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::Button(Command->GetLabel().data());
|
||||
CommandHotkeyLink->m_BeingModified = ImGui::IsItemActive();
|
||||
|
||||
if (CommandHotkeyLink->m_BeingModified)
|
||||
{
|
||||
g_HotkeySystem.CreateHotkey(CommandHotkeyLink->m_Chain);
|
||||
}
|
||||
|
||||
ImGui::SameLine(200);
|
||||
ImGui::BeginGroup();
|
||||
|
||||
if (CommandHotkeyLink->m_Chain.empty())
|
||||
{
|
||||
if (CommandHotkeyLink->m_BeingModified)
|
||||
ImGui::Text("Press any button...");
|
||||
else
|
||||
ImGui::Text("No Hotkey Assigned");
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::PushItemWidth(35);
|
||||
for (auto HotkeyModifier : CommandHotkeyLink->m_Chain)
|
||||
{
|
||||
char KeyLabel[32];
|
||||
strcpy(KeyLabel, g_HotkeySystem.GetHotkeyLabel(HotkeyModifier).data());
|
||||
ImGui::InputText("##keylabel", KeyLabel, 32, ImGuiInputTextFlags_ReadOnly);
|
||||
if (ImGui::IsItemClicked())
|
||||
std::erase_if(CommandHotkeyLink->m_Chain, [HotkeyModifier](int i) {
|
||||
return i == HotkeyModifier;
|
||||
});
|
||||
|
||||
ImGui::SameLine();
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Clear"))
|
||||
{
|
||||
CommandHotkeyLink->m_Chain.clear();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#include "Items.hpp"
|
||||
#include "core/backend/FiberPool.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
ImGuiItem::ImGuiItem(std::function<void()> callback) :
|
||||
m_Callback(callback)
|
||||
{
|
||||
}
|
||||
|
||||
void ImGuiItem::Draw()
|
||||
{
|
||||
m_Callback();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#include "Items.hpp"
|
||||
#include "misc/cpp/imgui_stdlib.h"
|
||||
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
InputTextWithHint::InputTextWithHint(std::string label, std::string hint, std::string* buf, int flags, std::function<void()> cb, ImGuiInputTextCallback inputCallback) :
|
||||
m_Id(label),
|
||||
m_Hint(hint),
|
||||
m_Buf(buf),
|
||||
m_Flags(flags),
|
||||
m_Callback(cb),
|
||||
m_ImGuiInputTextCallback(inputCallback)
|
||||
{
|
||||
}
|
||||
|
||||
void InputTextWithHint::Draw()
|
||||
{
|
||||
if (ImGui::InputTextWithHint(m_Id.data(), m_Hint.data(), m_Buf, m_Flags, m_ImGuiInputTextCallback))
|
||||
;
|
||||
{
|
||||
if (m_Callback != nullptr)
|
||||
{
|
||||
m_Callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#include "Items.hpp"
|
||||
#include "core/commands/Command.hpp"
|
||||
#include "core/commands/Commands.hpp"
|
||||
#include "core/commands/IntCommand.hpp"
|
||||
#include "core/frontend/widgets/toggle/imgui_toggle.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
IntCommandItem::IntCommandItem(joaat_t id, std::optional<std::string> label_override) :
|
||||
m_Command(Commands::GetCommand<IntCommand>(id)),
|
||||
m_LabelOverride(label_override)
|
||||
{
|
||||
}
|
||||
|
||||
void IntCommandItem::Draw()
|
||||
{
|
||||
if (!m_Command)
|
||||
{
|
||||
ImGui::Text("Unknown!");
|
||||
return;
|
||||
}
|
||||
|
||||
int value = m_Command->GetState();
|
||||
auto label = m_LabelOverride.has_value() ? m_LabelOverride.value().c_str() : m_Command->GetLabel().c_str();
|
||||
if (!m_Command->GetMinimum().has_value() || !m_Command->GetMaximum().has_value())
|
||||
{
|
||||
ImGui::SetNextItemWidth(150);
|
||||
if (ImGui::InputInt(label, &value))
|
||||
{
|
||||
m_Command->SetState(value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::SetNextItemWidth(150);
|
||||
if (ImGui::SliderInt(label, &value, m_Command->GetMinimum().value(), m_Command->GetMaximum().value()))
|
||||
{
|
||||
m_Command->SetState(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
#pragma once
|
||||
#include "core/frontend/manager/UIItem.hpp"
|
||||
#include "core/util/Joaat.hpp"
|
||||
#include "game/frontend/GUI.hpp"
|
||||
|
||||
namespace YimMenu
|
||||
{
|
||||
class BoolCommand;
|
||||
class PlayerCommand;
|
||||
class ListCommand;
|
||||
class IntCommand;
|
||||
class FloatCommand;
|
||||
class Vector3Command;
|
||||
class Command;
|
||||
class ColorCommand;
|
||||
class StringCommand;
|
||||
|
||||
class Button : public UIItem
|
||||
{
|
||||
public:
|
||||
explicit Button(const std::string_view& Name, std::function<void()> OnClick, ImVec2 size = {0, 25}, const std::string_view& information = "Empty");
|
||||
void Draw() override;
|
||||
|
||||
private:
|
||||
std::string_view m_Name;
|
||||
std::string_view m_Information;
|
||||
std::function<void()> m_OnClick;
|
||||
ImVec2 m_Size;
|
||||
};
|
||||
|
||||
class CommandItem : public UIItem
|
||||
{
|
||||
public:
|
||||
explicit CommandItem(joaat_t id, std::optional<std::string> label_override = std::nullopt);
|
||||
void Draw() override;
|
||||
|
||||
private:
|
||||
Command* m_Command;
|
||||
std::optional<std::string> m_LabelOverride;
|
||||
};
|
||||
|
||||
class PlayerCommandItem : public UIItem
|
||||
{
|
||||
public:
|
||||
explicit PlayerCommandItem(joaat_t id, std::optional<std::string> label_override = std::nullopt);
|
||||
void Draw() override;
|
||||
|
||||
private:
|
||||
PlayerCommand* m_Command;
|
||||
std::optional<std::string> m_LabelOverride;
|
||||
};
|
||||
|
||||
class BoolCommandItem : public UIItem
|
||||
{
|
||||
public:
|
||||
explicit BoolCommandItem(joaat_t id, std::optional<std::string> label_override = std::nullopt);
|
||||
void Draw() override;
|
||||
|
||||
private:
|
||||
BoolCommand* m_Command;
|
||||
std::optional<std::string> m_LabelOverride;
|
||||
};
|
||||
|
||||
class IntCommandItem : public UIItem
|
||||
{
|
||||
public:
|
||||
explicit IntCommandItem(joaat_t id, std::optional<std::string> label_override = std::nullopt);
|
||||
void Draw() override;
|
||||
|
||||
private:
|
||||
IntCommand* m_Command;
|
||||
std::optional<std::string> m_LabelOverride;
|
||||
};
|
||||
|
||||
class FloatCommandItem : public UIItem
|
||||
{
|
||||
public:
|
||||
explicit FloatCommandItem(joaat_t id, std::optional<std::string> label_override = std::nullopt);
|
||||
void Draw() override;
|
||||
|
||||
private:
|
||||
FloatCommand* m_Command;
|
||||
std::optional<std::string> m_LabelOverride;
|
||||
};
|
||||
|
||||
class Vector3CommandItem : public UIItem
|
||||
{
|
||||
public:
|
||||
explicit Vector3CommandItem(joaat_t id, std::optional<std::string> label_override = std::nullopt);
|
||||
void Draw() override;
|
||||
|
||||
private:
|
||||
Vector3Command* m_Command;
|
||||
std::optional<std::string> m_LabelOverride;
|
||||
std::string m_CurrentCategory{};
|
||||
std::string m_CurrentFilter{};
|
||||
};
|
||||
|
||||
class ListCommandItem : public UIItem
|
||||
{
|
||||
public:
|
||||
explicit ListCommandItem(joaat_t id, std::optional<std::string> label_override = std::nullopt);
|
||||
void Draw() override;
|
||||
|
||||
private:
|
||||
ListCommand* m_Command;
|
||||
std::optional<std::string> m_LabelOverride;
|
||||
std::optional<int> m_ItemWidth = std::nullopt;
|
||||
std::optional<std::string> m_SelectedItem = std::nullopt;
|
||||
};
|
||||
|
||||
class ConditionalItem : public UIItem
|
||||
{
|
||||
public:
|
||||
explicit ConditionalItem(joaat_t bool_cmd_id, std::shared_ptr<UIItem> to_draw);
|
||||
void Draw() override;
|
||||
|
||||
private:
|
||||
BoolCommand* m_Condition;
|
||||
std::shared_ptr<UIItem> m_Item;
|
||||
};
|
||||
|
||||
class ImGuiItem : public UIItem
|
||||
{
|
||||
public:
|
||||
explicit ImGuiItem(std::function<void()> callback);
|
||||
void Draw() override;
|
||||
|
||||
private:
|
||||
std::function<void()> m_Callback;
|
||||
};
|
||||
|
||||
class HotkeySetter : public UIItem
|
||||
{
|
||||
public:
|
||||
|
||||
explicit HotkeySetter(joaat_t);
|
||||
void Draw() override;
|
||||
|
||||
private:
|
||||
joaat_t m_Id;
|
||||
};
|
||||
|
||||
class Group : public UIItem
|
||||
{
|
||||
public:
|
||||
explicit Group(const std::string& name, int items_per_row = 7);
|
||||
void Draw() override;
|
||||
|
||||
void AddItem(std::shared_ptr<UIItem>&& item)
|
||||
{
|
||||
m_Items.push_back(std::move(item));
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_Name;
|
||||
int m_ItemsPerRow;
|
||||
std::vector<std::shared_ptr<UIItem>> m_Items;
|
||||
};
|
||||
|
||||
class InputTextWithHint : public UIItem
|
||||
{
|
||||
public:
|
||||
explicit InputTextWithHint(std::string label, std::string hint, std::string* buf, int flags = ImGuiInputTextFlags_None, std::function<void()> cb = nullptr, ImGuiInputTextCallback inputCallback = nullptr);
|
||||
void Draw() override;
|
||||
|
||||
private:
|
||||
std::string m_Id;
|
||||
std::string m_Hint;
|
||||
std::string* m_Buf;
|
||||
int m_Flags;
|
||||
std::function<void()> m_Callback;
|
||||
ImGuiInputTextCallback m_ImGuiInputTextCallback;
|
||||
};
|
||||
|
||||
class ColorCommandItem : public UIItem
|
||||
{
|
||||
public:
|
||||
explicit ColorCommandItem(joaat_t id, std::optional<std::string> label_override = std::nullopt);
|
||||
void Draw() override;
|
||||
|
||||
private:
|
||||
ColorCommand* m_Command;
|
||||
std::optional<std::string> m_LabelOverride;
|
||||
};
|
||||
|
||||
class StringCommandItem : public UIItem
|
||||
{
|
||||
public:
|
||||
explicit StringCommandItem(joaat_t id, std::optional<std::string> label_override = std::nullopt);
|
||||
void Draw() override;
|
||||
|
||||
private:
|
||||
StringCommand* m_Command;
|
||||
std::optional<std::string> m_LabelOverride;
|
||||
std::optional<std::string> m_CurrentInput = std::nullopt;
|
||||
};
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user