mirror of
https://github.com/orange-cpp/omath.git
synced 2026-06-10 17:24:35 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a2c9084e5f | |||
| ad2523dc3a | |||
| 3599a7d592 | |||
| 00287c7a58 | |||
| ce589b4f17 | |||
| 8f6341e840 | |||
| b9da58e138 | |||
| 87896117d4 | |||
| de8a540426 | |||
| d1eaee6e6e | |||
| b9522fe191 | |||
| 2e1ff91156 | |||
| 40b2d2b332 | |||
| 8810ec20a5 | |||
| b7210dec4c |
@@ -81,7 +81,7 @@ if (auto screen = camera.world_to_screen(world_position)) {
|
|||||||
- **Collision Detection**: Production ready code to handle collision detection by using simple interfaces.
|
- **Collision Detection**: Production ready code to handle collision detection by using simple interfaces.
|
||||||
- **No Additional Dependencies**: No additional dependencies need to use OMath except unit test execution
|
- **No Additional Dependencies**: No additional dependencies need to use OMath except unit test execution
|
||||||
- **Ready for meta-programming**: Omath use templates for common types like Vectors, Matrixes etc, to handle all types!
|
- **Ready for meta-programming**: Omath use templates for common types like Vectors, Matrixes etc, to handle all types!
|
||||||
- **Engine support**: Supports coordinate systems of **Source, Unity, Unreal, Frostbite, IWEngine, CryEngine and canonical OpenGL**.
|
- **Engine support**: Supports coordinate systems of **Source, Rage, Unity, Unreal, Frostbite, IWEngine, CryEngine and canonical OpenGL**.
|
||||||
- **Cross platform**: Supports Windows, MacOS and Linux.
|
- **Cross platform**: Supports Windows, MacOS and Linux.
|
||||||
- **Algorithms**: Has ability to scan for byte pattern with wildcards in ELF/Mach-O/PE files/modules, binary slices, works even with Wine apps.
|
- **Algorithms**: Has ability to scan for byte pattern with wildcards in ELF/Mach-O/PE files/modules, binary slices, works even with Wine apps.
|
||||||
- **Scripting**: Supports to make scripts in Lua out of box.
|
- **Scripting**: Supports to make scripts in Lua out of box.
|
||||||
@@ -113,6 +113,10 @@ if (auto screen = camera.world_to_screen(world_position)) {
|
|||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
![GTA5 Preview]
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
![OpenGL Preview]
|
![OpenGL Preview]
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
@@ -144,6 +148,7 @@ if (auto screen = camera.world_to_screen(world_position)) {
|
|||||||
[BO2 Preview]: docs/images/showcase/cod_bo2.png
|
[BO2 Preview]: docs/images/showcase/cod_bo2.png
|
||||||
[CS2 Preview]: docs/images/showcase/cs2.jpeg
|
[CS2 Preview]: docs/images/showcase/cs2.jpeg
|
||||||
[TF2 Preview]: docs/images/showcase/tf2.jpg
|
[TF2 Preview]: docs/images/showcase/tf2.jpg
|
||||||
|
[GTA5 Preview]: https://i.imgur.com/W7T8RhZ.png
|
||||||
[OpenGL Preview]: docs/images/showcase/opengl.png
|
[OpenGL Preview]: docs/images/showcase/opengl.png
|
||||||
<!----------------------------------{ Buttons }--------------------------------->
|
<!----------------------------------{ Buttons }--------------------------------->
|
||||||
[QUICKSTART]: docs/getting_started.md
|
[QUICKSTART]: docs/getting_started.md
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ namespace imgui_desktop::gui
|
|||||||
ImGui::SliderFloat("X##ent", &m_entity_x, 100.f, vp->Size.x - 100.f);
|
ImGui::SliderFloat("X##ent", &m_entity_x, 100.f, vp->Size.x - 100.f);
|
||||||
ImGui::SliderFloat("Top Y", &m_entity_top_y, 20.f, m_entity_bottom_y - 20.f);
|
ImGui::SliderFloat("Top Y", &m_entity_top_y, 20.f, m_entity_bottom_y - 20.f);
|
||||||
ImGui::SliderFloat("Bottom Y", &m_entity_bottom_y, m_entity_top_y + 20.f, vp->Size.y - 20.f);
|
ImGui::SliderFloat("Bottom Y", &m_entity_bottom_y, m_entity_top_y + 20.f, vp->Size.y - 20.f);
|
||||||
|
ImGui::SliderFloat("Aspect", &m_entity_aspect, 1.f, 10.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::CollapsingHeader("Box", ImGuiTreeNodeFlags_DefaultOpen))
|
if (ImGui::CollapsingHeader("Box", ImGuiTreeNodeFlags_DefaultOpen))
|
||||||
@@ -196,7 +197,11 @@ namespace imgui_desktop::gui
|
|||||||
const DashedBar dbar{m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width,
|
const DashedBar dbar{m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width,
|
||||||
m_bar_value, m_bar_dash_len, m_bar_dash_gap, m_bar_offset};
|
m_bar_value, m_bar_dash_len, m_bar_dash_gap, m_bar_offset};
|
||||||
|
|
||||||
omath::hud::EntityOverlay({m_entity_x, m_entity_top_y}, {m_entity_x, m_entity_bottom_y},
|
auto outline_helper = [](const bool is_outline) -> Outlined
|
||||||
|
{
|
||||||
|
return is_outline ? Outlined::On : Outlined::Off;
|
||||||
|
};
|
||||||
|
omath::hud::EntityOverlay({m_entity_x, m_entity_top_y}, {m_entity_x, m_entity_bottom_y}, m_entity_aspect,
|
||||||
std::make_shared<omath::hud::ImguiHudRenderer>())
|
std::make_shared<omath::hud::ImguiHudRenderer>())
|
||||||
.contents(
|
.contents(
|
||||||
// ── Boxes ────────────────────────────────────────────────────
|
// ── Boxes ────────────────────────────────────────────────────
|
||||||
@@ -207,12 +212,18 @@ namespace imgui_desktop::gui
|
|||||||
RightSide{
|
RightSide{
|
||||||
when(m_show_right_bar, bar),
|
when(m_show_right_bar, bar),
|
||||||
when(m_show_right_dashed_bar, dbar),
|
when(m_show_right_dashed_bar, dbar),
|
||||||
when(m_show_right_labels,
|
when(m_show_right_labels, Label{{0.f, 1.f, 0.f, 1.f},
|
||||||
Label{{0.f, 1.f, 0.f, 1.f}, m_label_offset, m_outlined, "Health: 100/100"}),
|
m_label_offset,
|
||||||
when(m_show_right_labels,
|
outline_helper(m_outlined),
|
||||||
Label{{1.f, 0.f, 0.f, 1.f}, m_label_offset, m_outlined, "Shield: 125/125"}),
|
"Health: 100/100"}),
|
||||||
when(m_show_right_labels,
|
when(m_show_right_labels, Label{{1.f, 0.f, 0.f, 1.f},
|
||||||
Label{{1.f, 0.f, 1.f, 1.f}, m_label_offset, m_outlined, "*LOCKED*"}),
|
m_label_offset,
|
||||||
|
outline_helper(m_outlined),
|
||||||
|
"Shield: 125/125"}),
|
||||||
|
when(m_show_right_labels, Label{{1.f, 0.f, 1.f, 1.f},
|
||||||
|
m_label_offset,
|
||||||
|
outline_helper(m_outlined),
|
||||||
|
"*LOCKED*"}),
|
||||||
|
|
||||||
SpaceVertical{10},
|
SpaceVertical{10},
|
||||||
when(m_show_ring, ProgressRing{m_ring_color, m_ring_bg, m_ring_radius, m_ring_ratio,
|
when(m_show_ring, ProgressRing{m_ring_color, m_ring_bg, m_ring_radius, m_ring_ratio,
|
||||||
@@ -221,33 +232,41 @@ namespace imgui_desktop::gui
|
|||||||
LeftSide{
|
LeftSide{
|
||||||
when(m_show_left_bar, bar),
|
when(m_show_left_bar, bar),
|
||||||
when(m_show_left_dashed_bar, dbar),
|
when(m_show_left_dashed_bar, dbar),
|
||||||
when(m_show_left_labels, Label{omath::Color::from_rgba(255, 128, 0, 255),
|
when(m_show_left_labels,
|
||||||
m_label_offset, m_outlined, "Armor: 75"}),
|
Label{omath::Color::from_rgba(255, 128, 0, 255), m_label_offset,
|
||||||
when(m_show_left_labels, Label{omath::Color::from_rgba(0, 200, 255, 255),
|
outline_helper(m_outlined), "Armor: 75"}),
|
||||||
m_label_offset, m_outlined, "Level: 42"}),
|
when(m_show_left_labels,
|
||||||
|
Label{omath::Color::from_rgba(0, 200, 255, 255), m_label_offset,
|
||||||
|
outline_helper(m_outlined), "Level: 42"}),
|
||||||
},
|
},
|
||||||
TopSide{
|
TopSide{
|
||||||
when(m_show_top_bar, bar),
|
when(m_show_top_bar, bar),
|
||||||
when(m_show_top_dashed_bar, dbar),
|
when(m_show_top_dashed_bar, dbar),
|
||||||
when(m_show_centered_top, Centered{Label{omath::Color::from_rgba(0, 255, 255, 255),
|
when(m_show_centered_top,
|
||||||
m_label_offset, m_outlined, "*VISIBLE*"}}),
|
Centered{Label{omath::Color::from_rgba(0, 255, 255, 255), m_label_offset,
|
||||||
|
outline_helper(m_outlined), "*VISIBLE*"}}),
|
||||||
when(m_show_top_labels, Label{omath::Color::from_rgba(255, 255, 0, 255), m_label_offset,
|
when(m_show_top_labels, Label{omath::Color::from_rgba(255, 255, 0, 255), m_label_offset,
|
||||||
m_outlined, "*SCOPED*"}),
|
outline_helper(m_outlined), "*SCOPED*"}),
|
||||||
when(m_show_top_labels, Label{omath::Color::from_rgba(255, 0, 0, 255), m_label_offset,
|
when(m_show_top_labels, Label{omath::Color::from_rgba(255, 0, 0, 255), m_label_offset,
|
||||||
m_outlined, "*BLEEDING*"}),
|
outline_helper(m_outlined), "*BLEEDING*"}),
|
||||||
},
|
},
|
||||||
BottomSide{
|
BottomSide{
|
||||||
when(m_show_bottom_bar, bar),
|
when(m_show_bottom_bar, bar),
|
||||||
when(m_show_bottom_dashed_bar, dbar),
|
when(m_show_bottom_dashed_bar, dbar),
|
||||||
when(m_show_centered_bottom, Centered{Label{omath::Color::from_rgba(255, 255, 255, 255),
|
when(m_show_centered_bottom,
|
||||||
m_label_offset, m_outlined, "PlayerName"}}),
|
Centered{Label{omath::Color::from_rgba(255, 255, 255, 255), m_label_offset,
|
||||||
|
outline_helper(m_outlined), "PlayerName"}}),
|
||||||
when(m_show_bottom_labels, Label{omath::Color::from_rgba(200, 200, 0, 255),
|
when(m_show_bottom_labels, Label{omath::Color::from_rgba(200, 200, 0, 255),
|
||||||
m_label_offset, m_outlined, "42m"}),
|
m_label_offset, outline_helper(m_outlined), "42m"}),
|
||||||
},
|
},
|
||||||
when(m_show_aim, AimDot{{m_entity_x, m_entity_top_y + 40.f}, m_aim_color, m_aim_radius}),
|
when(m_show_aim, AimDot{{m_entity_x, m_entity_top_y + 40.f}, m_aim_color, m_aim_radius}),
|
||||||
when(m_show_scan, ScanMarker{m_scan_color, m_scan_outline, m_scan_outline_thickness}),
|
when(m_show_scan, ScanMarker{m_scan_color, m_scan_outline, m_scan_outline_thickness}),
|
||||||
when(m_show_skeleton, Skeleton{m_skel_color, m_skel_thickness}),
|
when(m_show_skeleton, Skeleton{m_skel_color, m_skel_thickness}),
|
||||||
when(m_show_proj, ProjectileAim{{m_proj_pos_x, m_proj_pos_y}, m_proj_color, m_proj_size, m_proj_line_width, static_cast<ProjectileAim::Figure>(m_proj_figure)}),
|
when(m_show_proj, ProjectileAim{{m_proj_pos_x, m_proj_pos_y},
|
||||||
|
m_proj_color,
|
||||||
|
m_proj_size,
|
||||||
|
m_proj_line_width,
|
||||||
|
static_cast<ProjectileAim::Figure>(m_proj_figure)}),
|
||||||
when(m_show_snap, SnapLine{{vp->Size.x / 2.f, vp->Size.y}, m_snap_color, m_snap_width}));
|
when(m_show_snap, SnapLine{{vp->Size.x / 2.f, vp->Size.y}, m_snap_color, m_snap_width}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,7 @@ namespace imgui_desktop::gui
|
|||||||
bool m_opened = true;
|
bool m_opened = true;
|
||||||
|
|
||||||
// Entity
|
// Entity
|
||||||
float m_entity_x = 550.f, m_entity_top_y = 150.f, m_entity_bottom_y = 450.f;
|
float m_entity_x = 550.f, m_entity_top_y = 150.f, m_entity_bottom_y = 450.f, m_entity_aspect = 4.f;
|
||||||
|
|
||||||
// Box
|
// Box
|
||||||
omath::Color m_box_color{1.f, 1.f, 1.f, 1.f};
|
omath::Color m_box_color{1.f, 1.f, 1.f, 1.f};
|
||||||
omath::Color m_box_fill{0.f, 0.f, 0.f, 0.f};
|
omath::Color m_box_fill{0.f, 0.f, 0.f, 0.f};
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// Created by Orange on 6/3/2026.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "omath/engines/rage_engine/constants.hpp"
|
||||||
|
#include "omath/projection/camera.hpp"
|
||||||
|
#include "traits/camera_trait.hpp"
|
||||||
|
|
||||||
|
namespace omath::rage_engine
|
||||||
|
{
|
||||||
|
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, NDCDepthRange::ZERO_TO_ONE>;
|
||||||
|
} // namespace omath::rage_engine
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// Created by Orange on 6/3/2026.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "omath/linear_algebra/mat.hpp"
|
||||||
|
#include "omath/linear_algebra/vector3.hpp"
|
||||||
|
#include <omath/trigonometry/angle.hpp>
|
||||||
|
#include <omath/trigonometry/view_angles.hpp>
|
||||||
|
|
||||||
|
namespace omath::rage_engine
|
||||||
|
{
|
||||||
|
constexpr Vector3<float> k_abs_up = {0, 0, 1};
|
||||||
|
constexpr Vector3<float> k_abs_right = {1, 0, 0};
|
||||||
|
constexpr Vector3<float> k_abs_forward = {0, 1, 0};
|
||||||
|
|
||||||
|
using Mat4X4 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>;
|
||||||
|
using Mat3X3 = Mat<3, 3, float, MatStoreType::ROW_MAJOR>;
|
||||||
|
using Mat1X3 = Mat<1, 3, float, MatStoreType::ROW_MAJOR>;
|
||||||
|
using PitchAngle = Angle<float, -90.f, 90.f, AngleFlags::Clamped>;
|
||||||
|
using YawAngle = Angle<float, -180.f, 180.f, AngleFlags::Normalized>;
|
||||||
|
using RollAngle = Angle<float, -180.f, 180.f, AngleFlags::Normalized>;
|
||||||
|
|
||||||
|
using ViewAngles = omath::ViewAngles<PitchAngle, YawAngle, RollAngle>;
|
||||||
|
} // namespace omath::rage_engine
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
//
|
||||||
|
// Created by Orange on 6/3/2026.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "omath/engines/rage_engine/constants.hpp"
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace omath::rage_engine
|
||||||
|
{
|
||||||
|
[[nodiscard]]
|
||||||
|
Vector3<float> forward_vector(const ViewAngles& angles) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
Vector3<float> right_vector(const ViewAngles& angles) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
Vector3<float> up_vector(const ViewAngles& angles) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
Vector3<float> extract_origin(const Mat4X4& mat) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
Vector3<float> extract_scale(const Mat4X4& mat) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far,
|
||||||
|
NDCDepthRange ndc_depth_range = NDCDepthRange::ZERO_TO_ONE) noexcept;
|
||||||
|
|
||||||
|
template<class FloatingType>
|
||||||
|
requires std::is_floating_point_v<FloatingType>
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr FloatingType units_to_centimeters(const FloatingType& units)
|
||||||
|
{
|
||||||
|
return units / static_cast<FloatingType>(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class FloatingType>
|
||||||
|
requires std::is_floating_point_v<FloatingType>
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr FloatingType units_to_meters(const FloatingType& units)
|
||||||
|
{
|
||||||
|
return units;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class FloatingType>
|
||||||
|
requires std::is_floating_point_v<FloatingType>
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr FloatingType units_to_kilometers(const FloatingType& units)
|
||||||
|
{
|
||||||
|
return units_to_meters(units) / static_cast<FloatingType>(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class FloatingType>
|
||||||
|
requires std::is_floating_point_v<FloatingType>
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr FloatingType centimeters_to_units(const FloatingType& centimeters)
|
||||||
|
{
|
||||||
|
return centimeters * static_cast<FloatingType>(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class FloatingType>
|
||||||
|
requires std::is_floating_point_v<FloatingType>
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr FloatingType meters_to_units(const FloatingType& meters)
|
||||||
|
{
|
||||||
|
return meters;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class FloatingType>
|
||||||
|
requires std::is_floating_point_v<FloatingType>
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
|
||||||
|
{
|
||||||
|
return meters_to_units(kilometers * static_cast<FloatingType>(1000));
|
||||||
|
}
|
||||||
|
} // namespace omath::rage_engine
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// Created by Orange on 6/3/2026.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "constants.hpp"
|
||||||
|
#include "omath/3d_primitives/mesh.hpp"
|
||||||
|
#include "traits/mesh_trait.hpp"
|
||||||
|
|
||||||
|
namespace omath::rage_engine
|
||||||
|
{
|
||||||
|
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait>;
|
||||||
|
} // namespace omath::rage_engine
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// Created by Orange on 6/3/2026.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "omath/engines/rage_engine/formulas.hpp"
|
||||||
|
#include "omath/projection/camera.hpp"
|
||||||
|
|
||||||
|
namespace omath::rage_engine
|
||||||
|
{
|
||||||
|
class CameraTrait final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
[[nodiscard]]
|
||||||
|
static ViewAngles calc_look_at_angle(const Vector3<float>& cam_origin, const Vector3<float>& look_at) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept;
|
||||||
|
[[nodiscard]]
|
||||||
|
static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port,
|
||||||
|
float near, float far, NDCDepthRange ndc_depth_range) noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace omath::rage_engine
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
//
|
||||||
|
// Created by Orange on 6/3/2026.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <omath/engines/rage_engine/constants.hpp>
|
||||||
|
#include <omath/engines/rage_engine/formulas.hpp>
|
||||||
|
|
||||||
|
namespace omath::rage_engine
|
||||||
|
{
|
||||||
|
class MeshTrait final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
[[nodiscard]]
|
||||||
|
static Mat4X4 rotation_matrix(const ViewAngles& rotation)
|
||||||
|
{
|
||||||
|
return rage_engine::rotation_matrix(rotation);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace omath::rage_engine
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
//
|
||||||
|
// Created by Orange on 6/3/2026.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "omath/engines/rage_engine/formulas.hpp"
|
||||||
|
#include "omath/projectile_prediction/projectile.hpp"
|
||||||
|
#include "omath/projectile_prediction/target.hpp"
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace omath::rage_engine
|
||||||
|
{
|
||||||
|
class PredEngineTrait final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
constexpr static Vector3<float>
|
||||||
|
predict_projectile_position(const projectile_prediction::Projectile<float>& projectile, const float pitch,
|
||||||
|
const float yaw, const float time, const float gravity) noexcept
|
||||||
|
{
|
||||||
|
const auto launch_pos = projectile.m_origin + projectile.m_launch_offset;
|
||||||
|
auto current_pos = launch_pos
|
||||||
|
+ forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw),
|
||||||
|
RollAngle::from_degrees(0)})
|
||||||
|
* projectile.m_launch_speed * time;
|
||||||
|
current_pos.z -= (gravity * projectile.m_gravity_scale) * (time * time) * 0.5f;
|
||||||
|
|
||||||
|
return current_pos;
|
||||||
|
}
|
||||||
|
[[nodiscard]]
|
||||||
|
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target<float>& target,
|
||||||
|
const float time, const float gravity) noexcept
|
||||||
|
{
|
||||||
|
auto predicted = target.m_origin + target.m_velocity * time;
|
||||||
|
|
||||||
|
if (target.m_is_airborne)
|
||||||
|
predicted.z -= gravity * (time * time) * 0.5f;
|
||||||
|
|
||||||
|
return predicted;
|
||||||
|
}
|
||||||
|
[[nodiscard]]
|
||||||
|
static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept
|
||||||
|
{
|
||||||
|
return std::sqrt(delta.x * delta.x + delta.y * delta.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept
|
||||||
|
{
|
||||||
|
return vec.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile<float>& projectile,
|
||||||
|
Vector3<float> predicted_target_position,
|
||||||
|
const std::optional<float> projectile_pitch) noexcept
|
||||||
|
{
|
||||||
|
const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin);
|
||||||
|
const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value()));
|
||||||
|
|
||||||
|
return {predicted_target_position.x, predicted_target_position.y, projectile.m_origin.z + height};
|
||||||
|
}
|
||||||
|
[[nodiscard]]
|
||||||
|
static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
|
||||||
|
{
|
||||||
|
const auto direction = (view_to - origin).normalized();
|
||||||
|
return angles::radians_to_degrees(std::asin(direction.z));
|
||||||
|
}
|
||||||
|
[[nodiscard]]
|
||||||
|
static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
|
||||||
|
{
|
||||||
|
const auto direction = (view_to - origin).normalized();
|
||||||
|
|
||||||
|
return angles::radians_to_degrees(-std::atan2(direction.x, direction.y));
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} // namespace omath::rage_engine
|
||||||
@@ -15,7 +15,7 @@ namespace omath::hud
|
|||||||
class EntityOverlay final
|
class EntityOverlay final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
EntityOverlay(const Vector2<float>& top, const Vector2<float>& bottom,
|
EntityOverlay(const Vector2<float>& top, const Vector2<float>& bottom, float aspect,
|
||||||
const std::shared_ptr<HudRendererInterface>& renderer);
|
const std::shared_ptr<HudRendererInterface>& renderer);
|
||||||
|
|
||||||
// ── Boxes ────────────────────────────────────────────────────────
|
// ── Boxes ────────────────────────────────────────────────────────
|
||||||
@@ -57,22 +57,22 @@ namespace omath::hud
|
|||||||
float offset = 5.f);
|
float offset = 5.f);
|
||||||
|
|
||||||
// ── Labels ───────────────────────────────────────────────────────
|
// ── Labels ───────────────────────────────────────────────────────
|
||||||
EntityOverlay& add_right_label(const Color& color, float offset, bool outlined, const std::string_view& text);
|
EntityOverlay& add_right_label(const Color& color, float offset, widget::Outlined outlined, const std::string_view& text);
|
||||||
|
|
||||||
EntityOverlay& add_left_label(const Color& color, float offset, bool outlined, const std::string_view& text);
|
EntityOverlay& add_left_label(const Color& color, float offset, widget::Outlined outlined, const std::string_view& text);
|
||||||
|
|
||||||
EntityOverlay& add_top_label(const Color& color, float offset, bool outlined, std::string_view text);
|
EntityOverlay& add_top_label(const Color& color, float offset, widget::Outlined outlined, std::string_view text);
|
||||||
|
|
||||||
EntityOverlay& add_bottom_label(const Color& color, float offset, bool outlined, std::string_view text);
|
EntityOverlay& add_bottom_label(const Color& color, float offset, widget::Outlined outlined, std::string_view text);
|
||||||
|
|
||||||
EntityOverlay& add_centered_top_label(const Color& color, float offset, bool outlined,
|
EntityOverlay& add_centered_top_label(const Color& color, float offset, widget::Outlined outlined,
|
||||||
const std::string_view& text);
|
const std::string_view& text);
|
||||||
|
|
||||||
EntityOverlay& add_centered_bottom_label(const Color& color, float offset, bool outlined,
|
EntityOverlay& add_centered_bottom_label(const Color& color, float offset, widget::Outlined outlined,
|
||||||
const std::string_view& text);
|
const std::string_view& text);
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
EntityOverlay& add_right_label(const Color& color, const float offset, const bool outlined, std::format_string<Args...> fmt,
|
EntityOverlay& add_right_label(const Color& color, const float offset, const widget::Outlined outlined, std::format_string<Args...> fmt,
|
||||||
Args&&... args)
|
Args&&... args)
|
||||||
{
|
{
|
||||||
return add_right_label(color, offset, outlined,
|
return add_right_label(color, offset, outlined,
|
||||||
@@ -80,7 +80,7 @@ namespace omath::hud
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
EntityOverlay& add_left_label(const Color& color, const float offset, const bool outlined, std::format_string<Args...> fmt,
|
EntityOverlay& add_left_label(const Color& color, const float offset, const widget::Outlined outlined, std::format_string<Args...> fmt,
|
||||||
Args&&... args)
|
Args&&... args)
|
||||||
{
|
{
|
||||||
return add_left_label(color, offset, outlined,
|
return add_left_label(color, offset, outlined,
|
||||||
@@ -88,7 +88,7 @@ namespace omath::hud
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
EntityOverlay& add_top_label(const Color& color, const float offset, const bool outlined, std::format_string<Args...> fmt,
|
EntityOverlay& add_top_label(const Color& color, const float offset, const widget::Outlined outlined, std::format_string<Args...> fmt,
|
||||||
Args&&... args)
|
Args&&... args)
|
||||||
{
|
{
|
||||||
return add_top_label(color, offset, outlined,
|
return add_top_label(color, offset, outlined,
|
||||||
@@ -96,7 +96,7 @@ namespace omath::hud
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
EntityOverlay& add_bottom_label(const Color& color, const float offset, const bool outlined,
|
EntityOverlay& add_bottom_label(const Color& color, const float offset, const widget::Outlined outlined,
|
||||||
std::format_string<Args...> fmt, Args&&... args)
|
std::format_string<Args...> fmt, Args&&... args)
|
||||||
{
|
{
|
||||||
return add_bottom_label(color, offset, outlined,
|
return add_bottom_label(color, offset, outlined,
|
||||||
@@ -104,7 +104,7 @@ namespace omath::hud
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
EntityOverlay& add_centered_top_label(const Color& color, const float offset, const bool outlined,
|
EntityOverlay& add_centered_top_label(const Color& color, const float offset, const widget::Outlined outlined,
|
||||||
std::format_string<Args...> fmt, Args&&... args)
|
std::format_string<Args...> fmt, Args&&... args)
|
||||||
{
|
{
|
||||||
return add_centered_top_label(color, offset, outlined,
|
return add_centered_top_label(color, offset, outlined,
|
||||||
@@ -112,7 +112,7 @@ namespace omath::hud
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
EntityOverlay& add_centered_bottom_label(const Color& color, const float offset, const bool outlined,
|
EntityOverlay& add_centered_bottom_label(const Color& color, const float offset, const widget::Outlined outlined,
|
||||||
std::format_string<Args...> fmt, Args&&... args)
|
std::format_string<Args...> fmt, Args&&... args)
|
||||||
{
|
{
|
||||||
return add_centered_bottom_label(color, offset, outlined,
|
return add_centered_bottom_label(color, offset, outlined,
|
||||||
|
|||||||
@@ -30,6 +30,11 @@ namespace omath::hud::widget
|
|||||||
float thickness = 1.f;
|
float thickness = 1.f;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class Outlined
|
||||||
|
{
|
||||||
|
Off,
|
||||||
|
On,
|
||||||
|
};
|
||||||
struct CorneredBox
|
struct CorneredBox
|
||||||
{
|
{
|
||||||
Color color;
|
Color color;
|
||||||
@@ -118,7 +123,7 @@ namespace omath::hud::widget
|
|||||||
{
|
{
|
||||||
Color color;
|
Color color;
|
||||||
float offset;
|
float offset;
|
||||||
bool outlined;
|
Outlined outlined;
|
||||||
std::string_view text;
|
std::string_view text;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace omath::hud
|
|||||||
const Color& tint = Color{1.f, 1.f, 1.f, 1.f}) override;
|
const Color& tint = Color{1.f, 1.f, 1.f, 1.f}) override;
|
||||||
void add_text(const Vector2<float>& position, const Color& color, const std::string_view& text) override;
|
void add_text(const Vector2<float>& position, const Color& color, const std::string_view& text) override;
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
virtual Vector2<float> calc_text_size(const std::string_view& text) override;
|
Vector2<float> calc_text_size(const std::string_view& text) override;
|
||||||
};
|
};
|
||||||
} // namespace omath::hud
|
} // namespace omath::hud
|
||||||
#endif // OMATH_IMGUI_INTEGRATION
|
#endif // OMATH_IMGUI_INTEGRATION
|
||||||
@@ -87,6 +87,12 @@
|
|||||||
#include "omath/engines/frostbite_engine/traits/camera_trait.hpp"
|
#include "omath/engines/frostbite_engine/traits/camera_trait.hpp"
|
||||||
#include "omath/engines/frostbite_engine/traits/pred_engine_trait.hpp"
|
#include "omath/engines/frostbite_engine/traits/pred_engine_trait.hpp"
|
||||||
|
|
||||||
|
// RAGE Engine
|
||||||
|
#include "omath/engines/rage_engine/constants.hpp"
|
||||||
|
#include "omath/engines/rage_engine/formulas.hpp"
|
||||||
|
#include "omath/engines/rage_engine/camera.hpp"
|
||||||
|
#include "omath/engines/rage_engine/traits/camera_trait.hpp"
|
||||||
|
#include "omath/engines/rage_engine/traits/pred_engine_trait.hpp"
|
||||||
|
|
||||||
// Unreal Engine
|
// Unreal Engine
|
||||||
#include "omath/engines/unreal_engine/constants.hpp"
|
#include "omath/engines/unreal_engine/constants.hpp"
|
||||||
|
|||||||
@@ -2,12 +2,13 @@
|
|||||||
// Created by Vladislav on 30.12.2025.
|
// Created by Vladislav on 30.12.2025.
|
||||||
//
|
//
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "pattern_scan.hpp"
|
||||||
|
#include "section_scan_result.hpp"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include "section_scan_result.hpp"
|
|
||||||
namespace omath
|
namespace omath
|
||||||
{
|
{
|
||||||
class ElfPatternScanner final
|
class ElfPatternScanner final
|
||||||
@@ -18,14 +19,74 @@ namespace omath
|
|||||||
scan_for_pattern_in_loaded_module(const void* module_base_address, const std::string_view& pattern,
|
scan_for_pattern_in_loaded_module(const void* module_base_address, const std::string_view& pattern,
|
||||||
const std::string_view& target_section_name = ".text");
|
const std::string_view& target_section_name = ".text");
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<std::uintptr_t>
|
||||||
|
scan_for_pattern_in_loaded_module(const void* module_base_address,
|
||||||
|
const std::string_view& target_section_name = ".text")
|
||||||
|
{
|
||||||
|
return scan_for_pattern_in_loaded_module(module_base_address, target_section_name,
|
||||||
|
&ElfPatternScanner::scan_section_for_pattern<Pattern>);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static std::optional<SectionScanResult>
|
static std::optional<SectionScanResult>
|
||||||
scan_for_pattern_in_file(const std::filesystem::path& path_to_file, const std::string_view& pattern,
|
scan_for_pattern_in_file(const std::filesystem::path& path_to_file, const std::string_view& pattern,
|
||||||
const std::string_view& target_section_name = ".text");
|
const std::string_view& target_section_name = ".text");
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult>
|
||||||
|
scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
||||||
|
const std::string_view& target_section_name = ".text")
|
||||||
|
{
|
||||||
|
return scan_for_pattern_in_file(path_to_file, target_section_name,
|
||||||
|
&ElfPatternScanner::scan_section_for_pattern<Pattern>);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static std::optional<SectionScanResult>
|
static std::optional<SectionScanResult>
|
||||||
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data, const std::string_view& pattern,
|
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data, const std::string_view& pattern,
|
||||||
const std::string_view& target_section_name = ".text");
|
const std::string_view& target_section_name = ".text");
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult>
|
||||||
|
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data,
|
||||||
|
const std::string_view& target_section_name = ".text")
|
||||||
|
{
|
||||||
|
return scan_for_pattern_in_memory_file(file_data, target_section_name,
|
||||||
|
&ElfPatternScanner::scan_section_for_pattern<Pattern>);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
using SectionScanFunction = std::optional<std::ptrdiff_t> (*)(std::span<const std::byte>);
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<std::ptrdiff_t> scan_section_for_pattern(const std::span<const std::byte> section_data)
|
||||||
|
{
|
||||||
|
const auto result = PatternScanner::scan_for_pattern<Pattern>(section_data.begin(), section_data.end());
|
||||||
|
|
||||||
|
if (result == section_data.end())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return result - section_data.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<std::uintptr_t>
|
||||||
|
scan_for_pattern_in_loaded_module(const void* module_base_address, const std::string_view& target_section_name,
|
||||||
|
SectionScanFunction scan_pattern);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult> scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
SectionScanFunction scan_pattern);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult>
|
||||||
|
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data,
|
||||||
|
const std::string_view& target_section_name, SectionScanFunction scan_pattern);
|
||||||
};
|
};
|
||||||
} // namespace omath
|
} // namespace omath
|
||||||
@@ -2,12 +2,13 @@
|
|||||||
// Created by Copilot on 04.02.2026.
|
// Created by Copilot on 04.02.2026.
|
||||||
//
|
//
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "pattern_scan.hpp"
|
||||||
|
#include "section_scan_result.hpp"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include "section_scan_result.hpp"
|
|
||||||
namespace omath
|
namespace omath
|
||||||
{
|
{
|
||||||
class MachOPatternScanner final
|
class MachOPatternScanner final
|
||||||
@@ -18,14 +19,74 @@ namespace omath
|
|||||||
scan_for_pattern_in_loaded_module(const void* module_base_address, const std::string_view& pattern,
|
scan_for_pattern_in_loaded_module(const void* module_base_address, const std::string_view& pattern,
|
||||||
const std::string_view& target_section_name = "__text");
|
const std::string_view& target_section_name = "__text");
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<std::uintptr_t>
|
||||||
|
scan_for_pattern_in_loaded_module(const void* module_base_address,
|
||||||
|
const std::string_view& target_section_name = "__text")
|
||||||
|
{
|
||||||
|
return scan_for_pattern_in_loaded_module(module_base_address, target_section_name,
|
||||||
|
&MachOPatternScanner::scan_section_for_pattern<Pattern>);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static std::optional<SectionScanResult>
|
static std::optional<SectionScanResult>
|
||||||
scan_for_pattern_in_file(const std::filesystem::path& path_to_file, const std::string_view& pattern,
|
scan_for_pattern_in_file(const std::filesystem::path& path_to_file, const std::string_view& pattern,
|
||||||
const std::string_view& target_section_name = "__text");
|
const std::string_view& target_section_name = "__text");
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult>
|
||||||
|
scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
||||||
|
const std::string_view& target_section_name = "__text")
|
||||||
|
{
|
||||||
|
return scan_for_pattern_in_file(path_to_file, target_section_name,
|
||||||
|
&MachOPatternScanner::scan_section_for_pattern<Pattern>);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static std::optional<SectionScanResult>
|
static std::optional<SectionScanResult>
|
||||||
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data, const std::string_view& pattern,
|
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data, const std::string_view& pattern,
|
||||||
const std::string_view& target_section_name = "__text");
|
const std::string_view& target_section_name = "__text");
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult>
|
||||||
|
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data,
|
||||||
|
const std::string_view& target_section_name = "__text")
|
||||||
|
{
|
||||||
|
return scan_for_pattern_in_memory_file(file_data, target_section_name,
|
||||||
|
&MachOPatternScanner::scan_section_for_pattern<Pattern>);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
using SectionScanFunction = std::optional<std::ptrdiff_t> (*)(std::span<const std::byte>);
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<std::ptrdiff_t> scan_section_for_pattern(const std::span<const std::byte> section_data)
|
||||||
|
{
|
||||||
|
const auto result = PatternScanner::scan_for_pattern<Pattern>(section_data.begin(), section_data.end());
|
||||||
|
|
||||||
|
if (result == section_data.end())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return result - section_data.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<std::uintptr_t>
|
||||||
|
scan_for_pattern_in_loaded_module(const void* module_base_address, const std::string_view& target_section_name,
|
||||||
|
SectionScanFunction scan_pattern);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult> scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
SectionScanFunction scan_pattern);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult>
|
||||||
|
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data,
|
||||||
|
const std::string_view& target_section_name, SectionScanFunction scan_pattern);
|
||||||
};
|
};
|
||||||
} // namespace omath
|
} // namespace omath
|
||||||
|
|||||||
@@ -3,9 +3,12 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <expected>
|
#include <expected>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
#include <stdexcept>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -15,6 +18,8 @@ class unit_test_pattern_scan_corner_case_1_Test;
|
|||||||
class unit_test_pattern_scan_corner_case_2_Test;
|
class unit_test_pattern_scan_corner_case_2_Test;
|
||||||
class unit_test_pattern_scan_corner_case_3_Test;
|
class unit_test_pattern_scan_corner_case_3_Test;
|
||||||
class unit_test_pattern_scan_corner_case_4_Test;
|
class unit_test_pattern_scan_corner_case_4_Test;
|
||||||
|
class unit_test_pattern_scan_consteval_read_test_Test;
|
||||||
|
class unit_test_pattern_scan_consteval_spacing_and_case_Test;
|
||||||
// ReSharper restore CppInconsistentNaming
|
// ReSharper restore CppInconsistentNaming
|
||||||
namespace omath
|
namespace omath
|
||||||
{
|
{
|
||||||
@@ -29,8 +34,22 @@ namespace omath
|
|||||||
friend unit_test_pattern_scan_corner_case_2_Test;
|
friend unit_test_pattern_scan_corner_case_2_Test;
|
||||||
friend unit_test_pattern_scan_corner_case_3_Test;
|
friend unit_test_pattern_scan_corner_case_3_Test;
|
||||||
friend unit_test_pattern_scan_corner_case_4_Test;
|
friend unit_test_pattern_scan_corner_case_4_Test;
|
||||||
|
friend unit_test_pattern_scan_consteval_read_test_Test;
|
||||||
|
friend unit_test_pattern_scan_consteval_spacing_and_case_Test;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
template<std::size_t N>
|
||||||
|
struct ConstevalPattern final
|
||||||
|
{
|
||||||
|
char value[N]{};
|
||||||
|
|
||||||
|
// ReSharper disable once CppNonExplicitConvertingConstructor
|
||||||
|
constexpr ConstevalPattern(const char (&text)[N]) // NOLINT(*-explicit-constructor)
|
||||||
|
{
|
||||||
|
std::ranges::copy(text, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static std::span<std::byte>::iterator scan_for_pattern(const std::span<std::byte>& range,
|
static std::span<std::byte>::iterator scan_for_pattern(const std::span<std::byte>& range,
|
||||||
const std::string_view& pattern);
|
const std::string_view& pattern);
|
||||||
@@ -49,9 +68,26 @@ namespace omath
|
|||||||
if (!parsed_pattern) [[unlikely]]
|
if (!parsed_pattern) [[unlikely]]
|
||||||
return end;
|
return end;
|
||||||
|
|
||||||
|
return scan_for_parsed_pattern(begin, end, parsed_pattern.value());
|
||||||
|
}
|
||||||
|
template<ConstevalPattern Pattern, class IteratorType>
|
||||||
|
requires std::input_or_output_iterator<std::remove_cvref_t<IteratorType>>
|
||||||
|
static IteratorType scan_for_pattern(const IteratorType& begin, const IteratorType& end)
|
||||||
|
{
|
||||||
|
constexpr auto parsed_pattern = parse_pattern<Pattern>();
|
||||||
|
|
||||||
|
return scan_for_parsed_pattern(begin, end, parsed_pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<class IteratorType, class ParsedPattern>
|
||||||
|
requires std::input_or_output_iterator<std::remove_cvref_t<IteratorType>>
|
||||||
|
static IteratorType scan_for_parsed_pattern(const IteratorType& begin, const IteratorType& end,
|
||||||
|
const ParsedPattern& parsed_pattern)
|
||||||
|
{
|
||||||
const auto whole_range_size = static_cast<std::ptrdiff_t>(std::distance(begin, end));
|
const auto whole_range_size = static_cast<std::ptrdiff_t>(std::distance(begin, end));
|
||||||
|
|
||||||
const auto pattern_size = static_cast<std::ptrdiff_t>(parsed_pattern->size());
|
const auto pattern_size = static_cast<std::ptrdiff_t>(parsed_pattern.size());
|
||||||
const std::ptrdiff_t scan_size = whole_range_size - pattern_size;
|
const std::ptrdiff_t scan_size = whole_range_size - pattern_size;
|
||||||
|
|
||||||
if (scan_size < 0)
|
if (scan_size < 0)
|
||||||
@@ -61,9 +97,9 @@ namespace omath
|
|||||||
{
|
{
|
||||||
bool found = true;
|
bool found = true;
|
||||||
|
|
||||||
for (std::ptrdiff_t j = 0; j < static_cast<std::ptrdiff_t>(parsed_pattern->size()); j++)
|
for (std::ptrdiff_t j = 0; j < static_cast<std::ptrdiff_t>(parsed_pattern.size()); j++)
|
||||||
{
|
{
|
||||||
found = parsed_pattern->at(j) == std::nullopt || parsed_pattern->at(j) == *(begin + i + j);
|
found = parsed_pattern.at(j) == std::nullopt || parsed_pattern.at(j) == *(begin + i + j);
|
||||||
|
|
||||||
if (!found)
|
if (!found)
|
||||||
break;
|
break;
|
||||||
@@ -73,10 +109,97 @@ namespace omath
|
|||||||
}
|
}
|
||||||
return end;
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static std::expected<std::vector<std::optional<std::byte>>, PatternScanError>
|
static std::expected<std::vector<std::optional<std::byte>>, PatternScanError>
|
||||||
parse_pattern(const std::string_view& pattern_string);
|
parse_pattern(const std::string_view& pattern_string);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr static bool is_space(const char c)
|
||||||
|
{
|
||||||
|
return c == ' ' || c == '\t' || c == '\n' || c == '\r';
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr static int hex_value(const char c)
|
||||||
|
{
|
||||||
|
if (c >= '0' && c <= '9')
|
||||||
|
return c - '0';
|
||||||
|
if (c >= 'A' && c <= 'F')
|
||||||
|
return c - 'A' + 10;
|
||||||
|
if (c >= 'a' && c <= 'f')
|
||||||
|
return c - 'a' + 10;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
template<ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static consteval std::size_t signature_size()
|
||||||
|
{
|
||||||
|
std::size_t count = 0;
|
||||||
|
bool in_token = false;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i + 1 < sizeof(Pattern.value); ++i)
|
||||||
|
{
|
||||||
|
if (is_space(Pattern.value[i]))
|
||||||
|
{
|
||||||
|
in_token = false;
|
||||||
|
}
|
||||||
|
else if (!in_token)
|
||||||
|
{
|
||||||
|
++count;
|
||||||
|
in_token = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<ConstevalPattern Pattern>
|
||||||
|
static consteval std::array<std::optional<std::byte>, signature_size<Pattern>()> parse_pattern()
|
||||||
|
{
|
||||||
|
std::array<std::optional<std::byte>, signature_size<Pattern>()> result{};
|
||||||
|
std::size_t out = 0;
|
||||||
|
std::size_t i = 0;
|
||||||
|
|
||||||
|
while (i + 1 < sizeof(Pattern.value))
|
||||||
|
{
|
||||||
|
while (i + 1 < sizeof(Pattern.value) && is_space(Pattern.value[i]))
|
||||||
|
++i;
|
||||||
|
|
||||||
|
const std::size_t token_start = i;
|
||||||
|
|
||||||
|
while (i + 1 < sizeof(Pattern.value) && !is_space(Pattern.value[i]))
|
||||||
|
++i;
|
||||||
|
|
||||||
|
const std::size_t token_size = i - token_start;
|
||||||
|
|
||||||
|
if (token_size == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// ReSharper disable once CppTooWideScope
|
||||||
|
const bool is_wildcard = (token_size == 1 || token_size == 2) && Pattern.value[token_start] == '?';
|
||||||
|
|
||||||
|
if (is_wildcard)
|
||||||
|
{
|
||||||
|
if (token_size == 2 && Pattern.value[token_start + 1] != '?')
|
||||||
|
throw std::logic_error("invalid wildcard token");
|
||||||
|
|
||||||
|
result[out++] = std::nullopt;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token_size != 2)
|
||||||
|
throw std::logic_error("invalid byte token");
|
||||||
|
|
||||||
|
const int high = hex_value(Pattern.value[token_start]);
|
||||||
|
const int low = hex_value(Pattern.value[token_start + 1]);
|
||||||
|
|
||||||
|
if (high < 0 || low < 0)
|
||||||
|
throw std::logic_error("invalid hex byte");
|
||||||
|
|
||||||
|
result[out++] = static_cast<std::byte>((high << 4) | low);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} // namespace omath
|
} // namespace omath
|
||||||
@@ -3,12 +3,13 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "pattern_scan.hpp"
|
||||||
|
#include "section_scan_result.hpp"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include "section_scan_result.hpp"
|
|
||||||
namespace omath
|
namespace omath
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -20,14 +21,74 @@ namespace omath
|
|||||||
scan_for_pattern_in_loaded_module(const void* module_base_address, const std::string_view& pattern,
|
scan_for_pattern_in_loaded_module(const void* module_base_address, const std::string_view& pattern,
|
||||||
const std::string_view& target_section_name = ".text");
|
const std::string_view& target_section_name = ".text");
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<std::uintptr_t>
|
||||||
|
scan_for_pattern_in_loaded_module(const void* module_base_address,
|
||||||
|
const std::string_view& target_section_name = ".text")
|
||||||
|
{
|
||||||
|
return scan_for_pattern_in_loaded_module(module_base_address, target_section_name,
|
||||||
|
&PePatternScanner::scan_section_for_pattern<Pattern>);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static std::optional<SectionScanResult>
|
static std::optional<SectionScanResult>
|
||||||
scan_for_pattern_in_file(const std::filesystem::path& path_to_file, const std::string_view& pattern,
|
scan_for_pattern_in_file(const std::filesystem::path& path_to_file, const std::string_view& pattern,
|
||||||
const std::string_view& target_section_name = ".text");
|
const std::string_view& target_section_name = ".text");
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult>
|
||||||
|
scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
||||||
|
const std::string_view& target_section_name = ".text")
|
||||||
|
{
|
||||||
|
return scan_for_pattern_in_file(path_to_file, target_section_name,
|
||||||
|
&PePatternScanner::scan_section_for_pattern<Pattern>);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static std::optional<SectionScanResult>
|
static std::optional<SectionScanResult>
|
||||||
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data, const std::string_view& pattern,
|
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data, const std::string_view& pattern,
|
||||||
const std::string_view& target_section_name = ".text");
|
const std::string_view& target_section_name = ".text");
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult>
|
||||||
|
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data,
|
||||||
|
const std::string_view& target_section_name = ".text")
|
||||||
|
{
|
||||||
|
return scan_for_pattern_in_memory_file(file_data, target_section_name,
|
||||||
|
&PePatternScanner::scan_section_for_pattern<Pattern>);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
using SectionScanFunction = std::optional<std::ptrdiff_t> (*)(std::span<const std::byte>);
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<std::ptrdiff_t> scan_section_for_pattern(const std::span<const std::byte> section_data)
|
||||||
|
{
|
||||||
|
const auto result = PatternScanner::scan_for_pattern<Pattern>(section_data.begin(), section_data.end());
|
||||||
|
|
||||||
|
if (result == section_data.end())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return result - section_data.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<std::uintptr_t>
|
||||||
|
scan_for_pattern_in_loaded_module(const void* module_base_address, const std::string_view& target_section_name,
|
||||||
|
SectionScanFunction scan_pattern);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult> scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
SectionScanFunction scan_pattern);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult>
|
||||||
|
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data,
|
||||||
|
const std::string_view& target_section_name, SectionScanFunction scan_pattern);
|
||||||
};
|
};
|
||||||
} // namespace omath
|
} // namespace omath
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
//
|
||||||
|
// Created by Orange on 6/3/2026.
|
||||||
|
//
|
||||||
|
#include "omath/engines/rage_engine/formulas.hpp"
|
||||||
|
|
||||||
|
namespace omath::rage_engine
|
||||||
|
{
|
||||||
|
Vector3<float> forward_vector(const ViewAngles& angles) noexcept
|
||||||
|
{
|
||||||
|
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_forward);
|
||||||
|
|
||||||
|
return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
|
||||||
|
}
|
||||||
|
Vector3<float> right_vector(const ViewAngles& angles) noexcept
|
||||||
|
{
|
||||||
|
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_right);
|
||||||
|
|
||||||
|
return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
|
||||||
|
}
|
||||||
|
Vector3<float> up_vector(const ViewAngles& angles) noexcept
|
||||||
|
{
|
||||||
|
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_up);
|
||||||
|
|
||||||
|
return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
|
||||||
|
}
|
||||||
|
Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
|
||||||
|
{
|
||||||
|
return mat_camera_view<float, MatStoreType::ROW_MAJOR>(forward_vector(angles), right_vector(angles),
|
||||||
|
up_vector(angles), cam_origin);
|
||||||
|
}
|
||||||
|
Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept
|
||||||
|
{
|
||||||
|
return mat_rotation_axis_z<float, MatStoreType::ROW_MAJOR>(angles.yaw)
|
||||||
|
* mat_rotation_axis_y<float, MatStoreType::ROW_MAJOR>(angles.roll)
|
||||||
|
* mat_rotation_axis_x<float, MatStoreType::ROW_MAJOR>(angles.pitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3<float> extract_origin(const Mat4X4& mat) noexcept
|
||||||
|
{
|
||||||
|
return mat_extract_origin(mat);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3<float> extract_scale(const Mat4X4& mat) noexcept
|
||||||
|
{
|
||||||
|
return mat_extract_scale(mat);
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept
|
||||||
|
{
|
||||||
|
const auto angles = mat_extract_rotation_zyx(mat);
|
||||||
|
return {
|
||||||
|
PitchAngle::from_degrees(angles.x),
|
||||||
|
YawAngle::from_degrees(angles.z),
|
||||||
|
RollAngle::from_degrees(angles.y),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Mat4X4 calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near,
|
||||||
|
const float far, const NDCDepthRange ndc_depth_range) noexcept
|
||||||
|
{
|
||||||
|
if (ndc_depth_range == NDCDepthRange::ZERO_TO_ONE)
|
||||||
|
return mat_perspective_left_handed_vertical_fov<float, MatStoreType::ROW_MAJOR, NDCDepthRange::ZERO_TO_ONE>(
|
||||||
|
field_of_view, aspect_ratio, near, far);
|
||||||
|
|
||||||
|
if (ndc_depth_range == NDCDepthRange::NEGATIVE_ONE_TO_ONE)
|
||||||
|
return mat_perspective_left_handed_vertical_fov<float, MatStoreType::ROW_MAJOR,
|
||||||
|
NDCDepthRange::NEGATIVE_ONE_TO_ONE>(
|
||||||
|
field_of_view, aspect_ratio, near, far);
|
||||||
|
std::unreachable();
|
||||||
|
}
|
||||||
|
} // namespace omath::rage_engine
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// Created by Orange on 6/3/2026.
|
||||||
|
//
|
||||||
|
#include "omath/engines/rage_engine/traits/camera_trait.hpp"
|
||||||
|
|
||||||
|
namespace omath::rage_engine
|
||||||
|
{
|
||||||
|
|
||||||
|
ViewAngles CameraTrait::calc_look_at_angle(const Vector3<float>& cam_origin, const Vector3<float>& look_at) noexcept
|
||||||
|
{
|
||||||
|
const auto direction = (look_at - cam_origin).normalized();
|
||||||
|
|
||||||
|
return {PitchAngle::from_radians(std::asin(direction.z)),
|
||||||
|
YawAngle::from_radians(-std::atan2(direction.x, direction.y)), RollAngle::from_radians(0.f)};
|
||||||
|
}
|
||||||
|
Mat4X4 CameraTrait::calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
|
||||||
|
{
|
||||||
|
return rage_engine::calc_view_matrix(angles, cam_origin);
|
||||||
|
}
|
||||||
|
Mat4X4 CameraTrait::calc_projection_matrix(const projection::FieldOfView& fov,
|
||||||
|
const projection::ViewPort& view_port, const float near, const float far,
|
||||||
|
const NDCDepthRange ndc_depth_range) noexcept
|
||||||
|
{
|
||||||
|
return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near, far,
|
||||||
|
ndc_depth_range);
|
||||||
|
}
|
||||||
|
} // namespace omath::rage_engine
|
||||||
@@ -660,7 +660,7 @@ namespace omath::hooks
|
|||||||
|
|
||||||
LRESULT __stdcall HooksManager::wnd_proc_detour(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param)
|
LRESULT __stdcall HooksManager::wnd_proc_detour(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param)
|
||||||
{
|
{
|
||||||
auto& mgr = get();
|
const auto& mgr = get();
|
||||||
callback_ptr<wnd_proc_callback> cb;
|
callback_ptr<wnd_proc_callback> cb;
|
||||||
WNDPROC original;
|
WNDPROC original;
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -98,10 +98,11 @@ namespace omath::hud
|
|||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
EntityOverlay& EntityOverlay::add_right_label(const Color& color, const float offset, const bool outlined,
|
EntityOverlay& EntityOverlay::add_right_label(const Color& color, const float offset,
|
||||||
|
const widget::Outlined outlined,
|
||||||
const std::string_view& text)
|
const std::string_view& text)
|
||||||
{
|
{
|
||||||
if (outlined)
|
if (outlined == widget::Outlined::On)
|
||||||
draw_outlined_text(m_text_cursor_right + Vector2<float>{offset, 0.f}, color, text);
|
draw_outlined_text(m_text_cursor_right + Vector2<float>{offset, 0.f}, color, text);
|
||||||
else
|
else
|
||||||
m_renderer->add_text(m_text_cursor_right + Vector2<float>{offset, 0.f}, color, text.data());
|
m_renderer->add_text(m_text_cursor_right + Vector2<float>{offset, 0.f}, color, text.data());
|
||||||
@@ -110,12 +111,12 @@ namespace omath::hud
|
|||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
EntityOverlay& EntityOverlay::add_top_label(const Color& color, const float offset, const bool outlined,
|
EntityOverlay& EntityOverlay::add_top_label(const Color& color, const float offset, const widget::Outlined outlined,
|
||||||
const std::string_view text)
|
const std::string_view text)
|
||||||
{
|
{
|
||||||
m_text_cursor_top.y -= m_renderer->calc_text_size(text.data()).y;
|
m_text_cursor_top.y -= m_renderer->calc_text_size(text.data()).y;
|
||||||
|
|
||||||
if (outlined)
|
if (outlined == widget::Outlined::On)
|
||||||
draw_outlined_text(m_text_cursor_top + Vector2<float>{0.f, -offset}, color, text);
|
draw_outlined_text(m_text_cursor_top + Vector2<float>{0.f, -offset}, color, text);
|
||||||
else
|
else
|
||||||
m_renderer->add_text(m_text_cursor_top + Vector2<float>{0.f, -offset}, color, text.data());
|
m_renderer->add_text(m_text_cursor_top + Vector2<float>{0.f, -offset}, color, text.data());
|
||||||
@@ -392,12 +393,12 @@ namespace omath::hud
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityOverlay& EntityOverlay::add_bottom_label(const Color& color, const float offset, const bool outlined,
|
EntityOverlay& EntityOverlay::add_bottom_label(const Color& color, const float offset, const widget::Outlined outlined,
|
||||||
const std::string_view text)
|
const std::string_view text)
|
||||||
{
|
{
|
||||||
const auto text_size = m_renderer->calc_text_size(text);
|
const auto text_size = m_renderer->calc_text_size(text);
|
||||||
|
|
||||||
if (outlined)
|
if (outlined == widget::Outlined::On)
|
||||||
draw_outlined_text(m_text_cursor_bottom + Vector2<float>{0.f, offset}, color, text);
|
draw_outlined_text(m_text_cursor_bottom + Vector2<float>{0.f, offset}, color, text);
|
||||||
else
|
else
|
||||||
m_renderer->add_text(m_text_cursor_bottom + Vector2<float>{0.f, offset}, color, text);
|
m_renderer->add_text(m_text_cursor_bottom + Vector2<float>{0.f, offset}, color, text);
|
||||||
@@ -407,13 +408,13 @@ namespace omath::hud
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityOverlay& EntityOverlay::add_left_label(const Color& color, const float offset, const bool outlined,
|
EntityOverlay& EntityOverlay::add_left_label(const Color& color, const float offset, const widget::Outlined outlined,
|
||||||
const std::string_view& text)
|
const std::string_view& text)
|
||||||
{
|
{
|
||||||
const auto text_size = m_renderer->calc_text_size(text);
|
const auto text_size = m_renderer->calc_text_size(text);
|
||||||
const auto pos = m_text_cursor_left + Vector2<float>{-(offset + text_size.x), 0.f};
|
const auto pos = m_text_cursor_left + Vector2<float>{-(offset + text_size.x), 0.f};
|
||||||
|
|
||||||
if (outlined)
|
if (outlined == widget::Outlined::On)
|
||||||
draw_outlined_text(pos, color, text);
|
draw_outlined_text(pos, color, text);
|
||||||
else
|
else
|
||||||
m_renderer->add_text(pos, color, text);
|
m_renderer->add_text(pos, color, text);
|
||||||
@@ -423,7 +424,7 @@ namespace omath::hud
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityOverlay& EntityOverlay::add_centered_bottom_label(const Color& color, const float offset, const bool outlined,
|
EntityOverlay& EntityOverlay::add_centered_bottom_label(const Color& color, const float offset, const widget::Outlined outlined,
|
||||||
const std::string_view& text)
|
const std::string_view& text)
|
||||||
{
|
{
|
||||||
const auto text_size = m_renderer->calc_text_size(text);
|
const auto text_size = m_renderer->calc_text_size(text);
|
||||||
@@ -431,7 +432,7 @@ namespace omath::hud
|
|||||||
m_canvas.bottom_left_corner.x + (m_canvas.bottom_right_corner.x - m_canvas.bottom_left_corner.x) / 2.f;
|
m_canvas.bottom_left_corner.x + (m_canvas.bottom_right_corner.x - m_canvas.bottom_left_corner.x) / 2.f;
|
||||||
const auto pos = Vector2<float>{box_center_x - text_size.x / 2.f, m_text_cursor_bottom.y + offset};
|
const auto pos = Vector2<float>{box_center_x - text_size.x / 2.f, m_text_cursor_bottom.y + offset};
|
||||||
|
|
||||||
if (outlined)
|
if (outlined == widget::Outlined::On)
|
||||||
draw_outlined_text(pos, color, text);
|
draw_outlined_text(pos, color, text);
|
||||||
else
|
else
|
||||||
m_renderer->add_text(pos, color, text);
|
m_renderer->add_text(pos, color, text);
|
||||||
@@ -441,7 +442,7 @@ namespace omath::hud
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityOverlay& EntityOverlay::add_centered_top_label(const Color& color, const float offset, const bool outlined,
|
EntityOverlay& EntityOverlay::add_centered_top_label(const Color& color, const float offset, const widget::Outlined outlined,
|
||||||
const std::string_view& text)
|
const std::string_view& text)
|
||||||
{
|
{
|
||||||
const auto text_size = m_renderer->calc_text_size(text);
|
const auto text_size = m_renderer->calc_text_size(text);
|
||||||
@@ -451,7 +452,7 @@ namespace omath::hud
|
|||||||
m_text_cursor_top.y -= text_size.y;
|
m_text_cursor_top.y -= text_size.y;
|
||||||
const auto pos = Vector2<float>{box_center_x - text_size.x / 2.f, m_text_cursor_top.y - offset};
|
const auto pos = Vector2<float>{box_center_x - text_size.x / 2.f, m_text_cursor_top.y - offset};
|
||||||
|
|
||||||
if (outlined)
|
if (outlined == widget::Outlined::On)
|
||||||
draw_outlined_text(pos, color, text);
|
draw_outlined_text(pos, color, text);
|
||||||
else
|
else
|
||||||
m_renderer->add_text(pos, color, text);
|
m_renderer->add_text(pos, color, text);
|
||||||
@@ -459,9 +460,9 @@ namespace omath::hud
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityOverlay::EntityOverlay(const Vector2<float>& top, const Vector2<float>& bottom,
|
EntityOverlay::EntityOverlay(const Vector2<float>& top, const Vector2<float>& bottom, const float aspect,
|
||||||
const std::shared_ptr<HudRendererInterface>& renderer)
|
const std::shared_ptr<HudRendererInterface>& renderer)
|
||||||
: m_canvas(top, bottom), m_text_cursor_right(m_canvas.top_right_corner),
|
: m_canvas(top, bottom, aspect), m_text_cursor_right(m_canvas.top_right_corner),
|
||||||
m_text_cursor_top(m_canvas.top_left_corner), m_text_cursor_bottom(m_canvas.bottom_left_corner),
|
m_text_cursor_top(m_canvas.top_left_corner), m_text_cursor_bottom(m_canvas.bottom_left_corner),
|
||||||
m_text_cursor_left(m_canvas.top_left_corner), m_renderer(renderer)
|
m_text_cursor_left(m_canvas.top_left_corner), m_renderer(renderer)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <omath/engines/frostbite_engine/camera.hpp>
|
#include <omath/engines/frostbite_engine/camera.hpp>
|
||||||
#include <omath/engines/iw_engine/camera.hpp>
|
#include <omath/engines/iw_engine/camera.hpp>
|
||||||
#include <omath/engines/opengl_engine/camera.hpp>
|
#include <omath/engines/opengl_engine/camera.hpp>
|
||||||
|
#include <omath/engines/rage_engine/camera.hpp>
|
||||||
#include <omath/engines/source_engine/camera.hpp>
|
#include <omath/engines/source_engine/camera.hpp>
|
||||||
#include <omath/engines/unity_engine/camera.hpp>
|
#include <omath/engines/unity_engine/camera.hpp>
|
||||||
#include <omath/engines/unreal_engine/camera.hpp>
|
#include <omath/engines/unreal_engine/camera.hpp>
|
||||||
@@ -181,6 +182,12 @@ namespace
|
|||||||
using ViewAngles = omath::source_engine::ViewAngles;
|
using ViewAngles = omath::source_engine::ViewAngles;
|
||||||
using Camera = omath::source_engine::Camera;
|
using Camera = omath::source_engine::Camera;
|
||||||
};
|
};
|
||||||
|
struct RageEngineTraits
|
||||||
|
{
|
||||||
|
using PitchAngle = omath::rage_engine::PitchAngle;
|
||||||
|
using ViewAngles = omath::rage_engine::ViewAngles;
|
||||||
|
using Camera = omath::rage_engine::Camera;
|
||||||
|
};
|
||||||
struct UnityEngineTraits
|
struct UnityEngineTraits
|
||||||
{
|
{
|
||||||
using PitchAngle = omath::unity_engine::PitchAngle;
|
using PitchAngle = omath::unity_engine::PitchAngle;
|
||||||
@@ -254,6 +261,7 @@ namespace omath::lua
|
|||||||
register_engine<FrostbiteEngineTraits>(omath_table, "frostbite");
|
register_engine<FrostbiteEngineTraits>(omath_table, "frostbite");
|
||||||
register_engine<IWEngineTraits>(omath_table, "iw");
|
register_engine<IWEngineTraits>(omath_table, "iw");
|
||||||
register_engine<SourceEngineTraits>(omath_table, "source");
|
register_engine<SourceEngineTraits>(omath_table, "source");
|
||||||
|
register_engine<RageEngineTraits>(omath_table, "rage");
|
||||||
register_engine<UnityEngineTraits>(omath_table, "unity");
|
register_engine<UnityEngineTraits>(omath_table, "unity");
|
||||||
register_engine<UnrealEngineTraits, double>(omath_table, "unreal");
|
register_engine<UnrealEngineTraits, double>(omath_table, "unreal");
|
||||||
register_engine<CryEngineTraits>(omath_table, "cry");
|
register_engine<CryEngineTraits>(omath_table, "cry");
|
||||||
|
|||||||
+13
-7
@@ -159,13 +159,19 @@ namespace
|
|||||||
return {1.f, 1.f, 1.f, 1.f};
|
return {1.f, 1.f, 1.f, 1.f};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
omath::hud::widget::Outlined outlined_from_bool(const bool outlined)
|
||||||
|
{
|
||||||
|
return outlined ? omath::hud::widget::Outlined::On : omath::hud::widget::Outlined::Off;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
omath::hud::EntityOverlay make_overlay(const omath::Vector2<float>& top, const omath::Vector2<float>& bottom,
|
omath::hud::EntityOverlay make_overlay(const omath::Vector2<float>& top, const omath::Vector2<float>& bottom,
|
||||||
const std::shared_ptr<LuaHudRenderer>& renderer)
|
const std::shared_ptr<LuaHudRenderer>& renderer)
|
||||||
{
|
{
|
||||||
if (!renderer)
|
if (!renderer)
|
||||||
throw std::invalid_argument("hud renderer must not be nil");
|
throw std::invalid_argument("hud renderer must not be nil");
|
||||||
return {top, bottom, renderer};
|
return {top, bottom, 4.f, renderer};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
@@ -374,37 +380,37 @@ namespace omath::lua
|
|||||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
|
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
|
||||||
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
|
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
|
||||||
{
|
{
|
||||||
return overlay.add_right_label(color, offset, outlined, text);
|
return overlay.add_right_label(color, offset, outlined_from_bool(outlined), text);
|
||||||
},
|
},
|
||||||
"add_left_label",
|
"add_left_label",
|
||||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
|
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
|
||||||
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
|
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
|
||||||
{
|
{
|
||||||
return overlay.add_left_label(color, offset, outlined, text);
|
return overlay.add_left_label(color, offset, outlined_from_bool(outlined), text);
|
||||||
},
|
},
|
||||||
"add_top_label",
|
"add_top_label",
|
||||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
|
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
|
||||||
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
|
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
|
||||||
{
|
{
|
||||||
return overlay.add_top_label(color, offset, outlined, text);
|
return overlay.add_top_label(color, offset, outlined_from_bool(outlined), text);
|
||||||
},
|
},
|
||||||
"add_bottom_label",
|
"add_bottom_label",
|
||||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
|
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
|
||||||
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
|
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
|
||||||
{
|
{
|
||||||
return overlay.add_bottom_label(color, offset, outlined, text);
|
return overlay.add_bottom_label(color, offset, outlined_from_bool(outlined), text);
|
||||||
},
|
},
|
||||||
"add_centered_top_label",
|
"add_centered_top_label",
|
||||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
|
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
|
||||||
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
|
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
|
||||||
{
|
{
|
||||||
return overlay.add_centered_top_label(color, offset, outlined, text);
|
return overlay.add_centered_top_label(color, offset, outlined_from_bool(outlined), text);
|
||||||
},
|
},
|
||||||
"add_centered_bottom_label",
|
"add_centered_bottom_label",
|
||||||
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
|
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
|
||||||
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
|
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
|
||||||
{
|
{
|
||||||
return overlay.add_centered_bottom_label(color, offset, outlined, text);
|
return overlay.add_centered_bottom_label(color, offset, outlined_from_bool(outlined), text);
|
||||||
},
|
},
|
||||||
|
|
||||||
"add_right_space_vertical", &omath::hud::EntityOverlay::add_right_space_vertical,
|
"add_right_space_vertical", &omath::hud::EntityOverlay::add_right_space_vertical,
|
||||||
|
|||||||
@@ -304,8 +304,8 @@ namespace
|
|||||||
elf_headers);
|
elf_headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class FileHeader, class SectionHeader>
|
template<class FileHeader, class SectionHeader, class ScanPattern>
|
||||||
std::optional<std::uintptr_t> scan_in_module_impl(const std::byte* base, const std::string_view pattern,
|
std::optional<std::uintptr_t> scan_in_module_impl(const std::byte* base, const ScanPattern scan_pattern,
|
||||||
const std::string_view target_section_name)
|
const std::string_view target_section_name)
|
||||||
{
|
{
|
||||||
const auto* file_header = reinterpret_cast<const FileHeader*>(base);
|
const auto* file_header = reinterpret_cast<const FileHeader*>(base);
|
||||||
@@ -336,13 +336,14 @@ namespace
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
const auto* section_begin = base + static_cast<std::size_t>(section->sh_addr);
|
const auto* section_begin = base + static_cast<std::size_t>(section->sh_addr);
|
||||||
const auto* section_end = section_begin + static_cast<std::size_t>(section->sh_size);
|
const auto scan_range =
|
||||||
|
std::span<const std::byte>{section_begin, static_cast<std::size_t>(section->sh_size)};
|
||||||
|
|
||||||
const auto scan_result = omath::PatternScanner::scan_for_pattern(section_begin, section_end, pattern);
|
const auto target_offset = scan_pattern(scan_range);
|
||||||
if (scan_result == section_end)
|
if (!target_offset.has_value())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
return reinterpret_cast<std::uintptr_t>(scan_result);
|
return reinterpret_cast<std::uintptr_t>(section_begin + target_offset.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
@@ -374,15 +375,58 @@ namespace omath
|
|||||||
if (!arch.has_value()) [[unlikely]]
|
if (!arch.has_value()) [[unlikely]]
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto scan_pattern =
|
||||||
|
[&pattern](const std::span<const std::byte> section_data) -> std::optional<std::ptrdiff_t>
|
||||||
|
{
|
||||||
|
const auto scan_result =
|
||||||
|
PatternScanner::scan_for_pattern(section_data.begin(), section_data.end(), pattern);
|
||||||
|
|
||||||
|
if (scan_result == section_data.end())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return scan_result - section_data.begin();
|
||||||
|
};
|
||||||
|
|
||||||
if (arch == FileArch::x64)
|
if (arch == FileArch::x64)
|
||||||
return scan_in_module_impl<Elf64Ehdr, Elf64Shdr>(static_cast<const std::byte*>(module_base_address),
|
return scan_in_module_impl<Elf64Ehdr, Elf64Shdr>(static_cast<const std::byte*>(module_base_address),
|
||||||
pattern, target_section_name);
|
scan_pattern, target_section_name);
|
||||||
if (arch == FileArch::x32)
|
if (arch == FileArch::x32)
|
||||||
return scan_in_module_impl<Elf32Ehdr, Elf32Shdr>(static_cast<const std::byte*>(module_base_address),
|
return scan_in_module_impl<Elf32Ehdr, Elf32Shdr>(static_cast<const std::byte*>(module_base_address),
|
||||||
pattern, target_section_name);
|
scan_pattern, target_section_name);
|
||||||
|
|
||||||
std::unreachable();
|
std::unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::uintptr_t>
|
||||||
|
ElfPatternScanner::scan_for_pattern_in_loaded_module(const void* module_base_address,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
const SectionScanFunction scan_pattern)
|
||||||
|
{
|
||||||
|
if (module_base_address == nullptr) [[unlikely]]
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto* base = static_cast<const std::byte*>(module_base_address);
|
||||||
|
|
||||||
|
constexpr std::string_view valid_elf_signature = "\x7F"
|
||||||
|
"ELF";
|
||||||
|
if (std::string_view{reinterpret_cast<const char*>(base), valid_elf_signature.size()} != valid_elf_signature)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto ei_class_value = static_cast<uint8_t>(base[ei_class]);
|
||||||
|
const auto arch = ei_class_value == elfclass64 ? FileArch::x64
|
||||||
|
: ei_class_value == elfclass32 ? FileArch::x32
|
||||||
|
: std::optional<FileArch>{};
|
||||||
|
if (!arch.has_value()) [[unlikely]]
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
if (arch == FileArch::x64)
|
||||||
|
return scan_in_module_impl<Elf64Ehdr, Elf64Shdr>(base, scan_pattern, target_section_name);
|
||||||
|
if (arch == FileArch::x32)
|
||||||
|
return scan_in_module_impl<Elf32Ehdr, Elf32Shdr>(base, scan_pattern, target_section_name);
|
||||||
|
|
||||||
|
std::unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<SectionScanResult>
|
std::optional<SectionScanResult>
|
||||||
ElfPatternScanner::scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
ElfPatternScanner::scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
||||||
const std::string_view& pattern,
|
const std::string_view& pattern,
|
||||||
@@ -405,6 +449,26 @@ namespace omath
|
|||||||
.target_offset = offset};
|
.target_offset = offset};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<SectionScanResult>
|
||||||
|
ElfPatternScanner::scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
const SectionScanFunction scan_pattern)
|
||||||
|
{
|
||||||
|
const auto section = get_elf_section_by_name(path_to_file, target_section_name);
|
||||||
|
|
||||||
|
if (!section.has_value()) [[unlikely]]
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto target_offset = scan_pattern(std::span<const std::byte>{section->data.data(), section->data.size()});
|
||||||
|
|
||||||
|
if (!target_offset.has_value())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return SectionScanResult{.virtual_base_addr = section->virtual_base_addr,
|
||||||
|
.raw_base_addr = section->raw_base_addr,
|
||||||
|
.target_offset = target_offset.value()};
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<SectionScanResult>
|
std::optional<SectionScanResult>
|
||||||
ElfPatternScanner::scan_for_pattern_in_memory_file(const std::span<const std::byte> file_data,
|
ElfPatternScanner::scan_for_pattern_in_memory_file(const std::span<const std::byte> file_data,
|
||||||
const std::string_view& pattern,
|
const std::string_view& pattern,
|
||||||
@@ -427,4 +491,24 @@ namespace omath
|
|||||||
.raw_base_addr = section->raw_base_addr,
|
.raw_base_addr = section->raw_base_addr,
|
||||||
.target_offset = offset};
|
.target_offset = offset};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<SectionScanResult>
|
||||||
|
ElfPatternScanner::scan_for_pattern_in_memory_file(const std::span<const std::byte> file_data,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
const SectionScanFunction scan_pattern)
|
||||||
|
{
|
||||||
|
const auto section = get_elf_section_by_name_from_memory(file_data, target_section_name);
|
||||||
|
|
||||||
|
if (!section.has_value()) [[unlikely]]
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto target_offset = scan_pattern(std::span<const std::byte>{section->data.data(), section->data.size()});
|
||||||
|
|
||||||
|
if (!target_offset.has_value())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return SectionScanResult{.virtual_base_addr = section->virtual_base_addr,
|
||||||
|
.raw_base_addr = section->raw_base_addr,
|
||||||
|
.target_offset = target_offset.value()};
|
||||||
|
}
|
||||||
} // namespace omath
|
} // namespace omath
|
||||||
@@ -316,8 +316,8 @@ namespace
|
|||||||
data, section_name);
|
data, section_name);
|
||||||
|
|
||||||
if (magic == mh_magic_32 || magic == mh_cigam_32)
|
if (magic == mh_magic_32 || magic == mh_cigam_32)
|
||||||
return extract_section_from_memory_impl<MachHeader32, SegmentCommand32, Section32, lc_segment>(data,
|
return extract_section_from_memory_impl<MachHeader32, SegmentCommand32, Section32, lc_segment>(
|
||||||
section_name);
|
data, section_name);
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@@ -344,8 +344,9 @@ namespace
|
|||||||
return extract_section_impl<MachHeader32, SegmentCommand32, Section32, lc_segment>(file, section_name);
|
return extract_section_impl<MachHeader32, SegmentCommand32, Section32, lc_segment>(file, section_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename HeaderType, typename SegmentType, typename SectionType, std::uint32_t segment_cmd>
|
template<typename HeaderType, typename SegmentType, typename SectionType, std::uint32_t segment_cmd,
|
||||||
std::optional<std::uintptr_t> scan_in_module_impl(const std::byte* base, const std::string_view pattern,
|
class ScanPattern>
|
||||||
|
std::optional<std::uintptr_t> scan_in_module_impl(const std::byte* base, const ScanPattern scan_pattern,
|
||||||
const std::string_view target_section_name)
|
const std::string_view target_section_name)
|
||||||
{
|
{
|
||||||
const auto* header = reinterpret_cast<const HeaderType*>(base);
|
const auto* header = reinterpret_cast<const HeaderType*>(base);
|
||||||
@@ -374,12 +375,13 @@ namespace
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto* section_begin = base + static_cast<std::size_t>(section->addr);
|
const auto* section_begin = base + static_cast<std::size_t>(section->addr);
|
||||||
const auto* section_end = section_begin + static_cast<std::size_t>(section->size);
|
const auto scan_range =
|
||||||
|
std::span<const std::byte>{section_begin, static_cast<std::size_t>(section->size)};
|
||||||
|
|
||||||
const auto scan_result = omath::PatternScanner::scan_for_pattern(section_begin, section_end, pattern);
|
const auto target_offset = scan_pattern(scan_range);
|
||||||
|
|
||||||
if (scan_result != section_end)
|
if (target_offset.has_value())
|
||||||
return reinterpret_cast<std::uintptr_t>(scan_result);
|
return reinterpret_cast<std::uintptr_t>(section_begin + target_offset.value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,12 +406,48 @@ namespace omath
|
|||||||
std::uint32_t magic{};
|
std::uint32_t magic{};
|
||||||
std::memcpy(&magic, base, sizeof(magic));
|
std::memcpy(&magic, base, sizeof(magic));
|
||||||
|
|
||||||
|
const auto scan_pattern =
|
||||||
|
[&pattern](const std::span<const std::byte> section_data) -> std::optional<std::ptrdiff_t>
|
||||||
|
{
|
||||||
|
const auto scan_result =
|
||||||
|
PatternScanner::scan_for_pattern(section_data.begin(), section_data.end(), pattern);
|
||||||
|
|
||||||
|
if (scan_result == section_data.end())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return scan_result - section_data.begin();
|
||||||
|
};
|
||||||
|
|
||||||
if (magic == mh_magic_64 || magic == mh_cigam_64)
|
if (magic == mh_magic_64 || magic == mh_cigam_64)
|
||||||
return scan_in_module_impl<MachHeader64, SegmentCommand64, Section64, lc_segment_64>(base, pattern,
|
return scan_in_module_impl<MachHeader64, SegmentCommand64, Section64, lc_segment_64>(base, scan_pattern,
|
||||||
target_section_name);
|
target_section_name);
|
||||||
|
|
||||||
if (magic == mh_magic_32 || magic == mh_cigam_32)
|
if (magic == mh_magic_32 || magic == mh_cigam_32)
|
||||||
return scan_in_module_impl<MachHeader32, SegmentCommand32, Section32, lc_segment>(base, pattern,
|
return scan_in_module_impl<MachHeader32, SegmentCommand32, Section32, lc_segment>(base, scan_pattern,
|
||||||
|
target_section_name);
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::uintptr_t>
|
||||||
|
MachOPatternScanner::scan_for_pattern_in_loaded_module(const void* module_base_address,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
const SectionScanFunction scan_pattern)
|
||||||
|
{
|
||||||
|
if (module_base_address == nullptr) [[unlikely]]
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto* base = static_cast<const std::byte*>(module_base_address);
|
||||||
|
|
||||||
|
std::uint32_t magic{};
|
||||||
|
std::memcpy(&magic, base, sizeof(magic));
|
||||||
|
|
||||||
|
if (magic == mh_magic_64 || magic == mh_cigam_64)
|
||||||
|
return scan_in_module_impl<MachHeader64, SegmentCommand64, Section64, lc_segment_64>(base, scan_pattern,
|
||||||
|
target_section_name);
|
||||||
|
|
||||||
|
if (magic == mh_magic_32 || magic == mh_cigam_32)
|
||||||
|
return scan_in_module_impl<MachHeader32, SegmentCommand32, Section32, lc_segment>(base, scan_pattern,
|
||||||
target_section_name);
|
target_section_name);
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
@@ -438,6 +476,27 @@ namespace omath
|
|||||||
.target_offset = offset};
|
.target_offset = offset};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<SectionScanResult>
|
||||||
|
MachOPatternScanner::scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
const SectionScanFunction scan_pattern)
|
||||||
|
{
|
||||||
|
const auto macho_section = get_macho_section_by_name(path_to_file, target_section_name);
|
||||||
|
|
||||||
|
if (!macho_section.has_value()) [[unlikely]]
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto target_offset =
|
||||||
|
scan_pattern(std::span<const std::byte>{macho_section->data.data(), macho_section->data.size()});
|
||||||
|
|
||||||
|
if (!target_offset.has_value())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return SectionScanResult{.virtual_base_addr = macho_section->virtual_base_addr,
|
||||||
|
.raw_base_addr = macho_section->raw_base_addr,
|
||||||
|
.target_offset = target_offset.value()};
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<SectionScanResult>
|
std::optional<SectionScanResult>
|
||||||
MachOPatternScanner::scan_for_pattern_in_memory_file(const std::span<const std::byte> file_data,
|
MachOPatternScanner::scan_for_pattern_in_memory_file(const std::span<const std::byte> file_data,
|
||||||
const std::string_view& pattern,
|
const std::string_view& pattern,
|
||||||
@@ -460,4 +519,24 @@ namespace omath
|
|||||||
.raw_base_addr = section->raw_base_addr,
|
.raw_base_addr = section->raw_base_addr,
|
||||||
.target_offset = offset};
|
.target_offset = offset};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<SectionScanResult>
|
||||||
|
MachOPatternScanner::scan_for_pattern_in_memory_file(const std::span<const std::byte> file_data,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
const SectionScanFunction scan_pattern)
|
||||||
|
{
|
||||||
|
const auto section = get_macho_section_by_name_from_memory(file_data, target_section_name);
|
||||||
|
|
||||||
|
if (!section.has_value()) [[unlikely]]
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto target_offset = scan_pattern(std::span<const std::byte>{section->data.data(), section->data.size()});
|
||||||
|
|
||||||
|
if (!target_offset.has_value())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return SectionScanResult{.virtual_base_addr = section->virtual_base_addr,
|
||||||
|
.raw_base_addr = section->raw_base_addr,
|
||||||
|
.target_offset = target_offset.value()};
|
||||||
|
}
|
||||||
} // namespace omath
|
} // namespace omath
|
||||||
|
|||||||
@@ -234,7 +234,11 @@ namespace
|
|||||||
constexpr bool invalid_nt_header_file(const NtHeaderVariant& variant)
|
constexpr bool invalid_nt_header_file(const NtHeaderVariant& variant)
|
||||||
{
|
{
|
||||||
constexpr std::uint32_t nt_hdr_magic = 0x4550;
|
constexpr std::uint32_t nt_hdr_magic = 0x4550;
|
||||||
return std::visit([&nt_hdr_magic](const auto& header) -> bool { return header.signature != nt_hdr_magic; },
|
return std::visit(
|
||||||
|
[&nt_hdr_magic](const auto& header) -> bool
|
||||||
|
{
|
||||||
|
return header.signature != nt_hdr_magic;
|
||||||
|
},
|
||||||
variant);
|
variant);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,8 +266,7 @@ namespace
|
|||||||
if (nt_off + sizeof(ImageNtHeaders<NtArchitecture::x32_bit>) > data.size())
|
if (nt_off + sizeof(ImageNtHeaders<NtArchitecture::x32_bit>) > data.size())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
const auto* x86_hdrs =
|
const auto* x86_hdrs = reinterpret_cast<const ImageNtHeaders<NtArchitecture::x32_bit>*>(data.data() + nt_off);
|
||||||
reinterpret_cast<const ImageNtHeaders<NtArchitecture::x32_bit>*>(data.data() + nt_off);
|
|
||||||
|
|
||||||
NtHeaderVariant nt_headers;
|
NtHeaderVariant nt_headers;
|
||||||
if (x86_hdrs->optional_header.magic == opt_hdr32_magic)
|
if (x86_hdrs->optional_header.magic == opt_hdr32_magic)
|
||||||
@@ -284,8 +287,8 @@ namespace
|
|||||||
[&data, §ion_name, nt_off](const auto& concrete_headers) -> std::optional<ExtractedSection>
|
[&data, §ion_name, nt_off](const auto& concrete_headers) -> std::optional<ExtractedSection>
|
||||||
{
|
{
|
||||||
constexpr std::size_t sig_size = sizeof(concrete_headers.signature);
|
constexpr std::size_t sig_size = sizeof(concrete_headers.signature);
|
||||||
const auto section_table_off = nt_off + sig_size + sizeof(FileHeader)
|
const auto section_table_off =
|
||||||
+ concrete_headers.file_header.size_optional_header;
|
nt_off + sig_size + sizeof(FileHeader) + concrete_headers.file_header.size_optional_header;
|
||||||
|
|
||||||
for (std::size_t i = 0; i < concrete_headers.file_header.num_sections; ++i)
|
for (std::size_t i = 0; i < concrete_headers.file_header.num_sections; ++i)
|
||||||
{
|
{
|
||||||
@@ -435,6 +438,65 @@ namespace omath
|
|||||||
},
|
},
|
||||||
nt_header_variant.value());
|
nt_header_variant.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::uintptr_t>
|
||||||
|
PePatternScanner::scan_for_pattern_in_loaded_module(const void* module_base_address,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
const SectionScanFunction scan_pattern)
|
||||||
|
{
|
||||||
|
const auto base_address = reinterpret_cast<std::uintptr_t>(module_base_address);
|
||||||
|
const auto* base_bytes = static_cast<const std::byte*>(module_base_address);
|
||||||
|
|
||||||
|
if (!base_address)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto* dos_header = static_cast<const DosHeader*>(module_base_address);
|
||||||
|
|
||||||
|
if (invalid_dos_header_file(*dos_header)) [[unlikely]]
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto nt_header_variant = get_nt_header_from_loaded_module(module_base_address);
|
||||||
|
|
||||||
|
if (!nt_header_variant) [[unlikely]]
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return std::visit(
|
||||||
|
[base_bytes, base_address, lfanew = dos_header->e_lfanew, &target_section_name,
|
||||||
|
scan_pattern](const auto& nt_header) -> std::optional<std::uintptr_t>
|
||||||
|
{
|
||||||
|
constexpr std::size_t signature_size = sizeof(nt_header.signature);
|
||||||
|
const auto section_table_off = static_cast<std::size_t>(lfanew) + signature_size
|
||||||
|
+ sizeof(FileHeader) + nt_header.file_header.size_optional_header;
|
||||||
|
|
||||||
|
const auto* section_table = reinterpret_cast<const SectionHeader*>(base_bytes + section_table_off);
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < nt_header.file_header.num_sections; ++i)
|
||||||
|
{
|
||||||
|
const auto* section = section_table + i;
|
||||||
|
|
||||||
|
if (std::string_view{section->name, sizeof(section->name)} != target_section_name
|
||||||
|
|| section->size_raw_data == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto section_size = section->virtual_size != 0
|
||||||
|
? static_cast<std::size_t>(section->virtual_size)
|
||||||
|
: static_cast<std::size_t>(section->size_raw_data);
|
||||||
|
|
||||||
|
const auto* section_begin =
|
||||||
|
reinterpret_cast<const std::byte*>(base_address + section->virtual_address);
|
||||||
|
const auto scan_range = std::span<const std::byte>{section_begin, section_size};
|
||||||
|
|
||||||
|
const auto target_offset = scan_pattern(scan_range);
|
||||||
|
|
||||||
|
if (target_offset.has_value())
|
||||||
|
return reinterpret_cast<std::uintptr_t>(section_begin + target_offset.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
},
|
||||||
|
nt_header_variant.value());
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<SectionScanResult>
|
std::optional<SectionScanResult>
|
||||||
PePatternScanner::scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
PePatternScanner::scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
||||||
const std::string_view& pattern,
|
const std::string_view& pattern,
|
||||||
@@ -457,6 +519,27 @@ namespace omath
|
|||||||
.target_offset = offset};
|
.target_offset = offset};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<SectionScanResult>
|
||||||
|
PePatternScanner::scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
const SectionScanFunction scan_pattern)
|
||||||
|
{
|
||||||
|
const auto pe_section = extract_section_from_pe_file(path_to_file, target_section_name);
|
||||||
|
|
||||||
|
if (!pe_section.has_value()) [[unlikely]]
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto target_offset =
|
||||||
|
scan_pattern(std::span<const std::byte>{pe_section->data.data(), pe_section->data.size()});
|
||||||
|
|
||||||
|
if (!target_offset.has_value())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return SectionScanResult{.virtual_base_addr = pe_section->virtual_base_addr,
|
||||||
|
.raw_base_addr = pe_section->raw_base_addr,
|
||||||
|
.target_offset = target_offset.value()};
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<SectionScanResult>
|
std::optional<SectionScanResult>
|
||||||
PePatternScanner::scan_for_pattern_in_memory_file(const std::span<const std::byte> file_data,
|
PePatternScanner::scan_for_pattern_in_memory_file(const std::span<const std::byte> file_data,
|
||||||
const std::string_view& pattern,
|
const std::string_view& pattern,
|
||||||
@@ -479,4 +562,25 @@ namespace omath
|
|||||||
.raw_base_addr = pe_section->raw_base_addr,
|
.raw_base_addr = pe_section->raw_base_addr,
|
||||||
.target_offset = offset};
|
.target_offset = offset};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<SectionScanResult>
|
||||||
|
PePatternScanner::scan_for_pattern_in_memory_file(const std::span<const std::byte> file_data,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
const SectionScanFunction scan_pattern)
|
||||||
|
{
|
||||||
|
const auto pe_section = extract_section_from_pe_memory(file_data, target_section_name);
|
||||||
|
|
||||||
|
if (!pe_section.has_value()) [[unlikely]]
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto target_offset =
|
||||||
|
scan_pattern(std::span<const std::byte>{pe_section->data.data(), pe_section->data.size()});
|
||||||
|
|
||||||
|
if (!target_offset.has_value())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return SectionScanResult{.virtual_base_addr = pe_section->virtual_base_addr,
|
||||||
|
.raw_base_addr = pe_section->raw_base_addr,
|
||||||
|
.target_offset = target_offset.value()};
|
||||||
|
}
|
||||||
} // namespace omath
|
} // namespace omath
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
//
|
||||||
|
// Created by Orange on 6/3/2026.
|
||||||
|
//
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <omath/engines/rage_engine/camera.hpp>
|
||||||
|
#include <omath/engines/rage_engine/constants.hpp>
|
||||||
|
#include <omath/engines/rage_engine/formulas.hpp>
|
||||||
|
#include <omath/engines/rage_engine/traits/mesh_trait.hpp>
|
||||||
|
#include <omath/engines/rage_engine/traits/pred_engine_trait.hpp>
|
||||||
|
#include <omath/projectile_prediction/projectile.hpp>
|
||||||
|
#include <ranges>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
using namespace omath;
|
||||||
|
|
||||||
|
static_assert(std::is_same_v<rage_engine::Mat4X4::ContainedType, float>);
|
||||||
|
|
||||||
|
static void expect_rage_vector_near(const Vector3<float>& actual, const Vector3<float>& expected)
|
||||||
|
{
|
||||||
|
for (const auto& [result, etalon] : std::views::zip(actual.as_array(), expected.as_array()))
|
||||||
|
EXPECT_NEAR(result, etalon, 0.0001f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_rage_engine, ForwardVector)
|
||||||
|
{
|
||||||
|
const auto forward = rage_engine::forward_vector({});
|
||||||
|
|
||||||
|
EXPECT_EQ(forward, rage_engine::k_abs_forward);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_rage_engine, RightVector)
|
||||||
|
{
|
||||||
|
const auto right = rage_engine::right_vector({});
|
||||||
|
|
||||||
|
EXPECT_EQ(right, rage_engine::k_abs_right);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_rage_engine, UpVector)
|
||||||
|
{
|
||||||
|
const auto up = rage_engine::up_vector({});
|
||||||
|
EXPECT_EQ(up, rage_engine::k_abs_up);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_rage_engine, LookAtForward)
|
||||||
|
{
|
||||||
|
const auto angles = rage_engine::CameraTrait::calc_look_at_angle({}, rage_engine::k_abs_forward);
|
||||||
|
|
||||||
|
expect_rage_vector_near(rage_engine::forward_vector(angles), rage_engine::k_abs_forward);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_rage_engine, LookAtRight)
|
||||||
|
{
|
||||||
|
const auto angles = rage_engine::CameraTrait::calc_look_at_angle({}, rage_engine::k_abs_right);
|
||||||
|
|
||||||
|
expect_rage_vector_near(rage_engine::forward_vector(angles), rage_engine::k_abs_right);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_rage_engine, LookAtUp)
|
||||||
|
{
|
||||||
|
const auto angles = rage_engine::CameraTrait::calc_look_at_angle({}, rage_engine::k_abs_up);
|
||||||
|
|
||||||
|
expect_rage_vector_near(rage_engine::forward_vector(angles), rage_engine::k_abs_up);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_rage_engine, ProjectTargetMovedFromCamera)
|
||||||
|
{
|
||||||
|
constexpr auto fov = projection::FieldOfView::from_degrees(60.f);
|
||||||
|
const auto cam = rage_engine::Camera({0, 0, 0}, {}, {1280.f, 720.f}, fov, 0.01f, 1000.f);
|
||||||
|
|
||||||
|
const auto projected = cam.world_to_screen({0, 10.f, 0});
|
||||||
|
|
||||||
|
ASSERT_TRUE(projected.has_value());
|
||||||
|
EXPECT_NEAR(projected->x, 640.f, 0.0001f);
|
||||||
|
EXPECT_NEAR(projected->y, 360.f, 0.0001f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_rage_engine, PredEngineTraitUsesZAsHeight)
|
||||||
|
{
|
||||||
|
projectile_prediction::Projectile<float> projectile;
|
||||||
|
projectile.m_origin = {0.f, 0.f, 0.f};
|
||||||
|
projectile.m_launch_speed = 10.f;
|
||||||
|
projectile.m_gravity_scale = 1.f;
|
||||||
|
|
||||||
|
const auto pos = rage_engine::PredEngineTrait::predict_projectile_position(projectile, 0.f, 0.f, 1.f, 9.81f);
|
||||||
|
|
||||||
|
EXPECT_NEAR(pos.x, 0.f, 0.0001f);
|
||||||
|
EXPECT_NEAR(pos.y, 10.f, 0.0001f);
|
||||||
|
EXPECT_NEAR(pos.z, -9.81f * 0.5f, 0.0001f);
|
||||||
|
EXPECT_NEAR(rage_engine::PredEngineTrait::get_vector_height_coordinate({1.f, 2.f, 3.f}), 3.f, 0.0001f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_rage_engine, MeshTraitForwardsRotationMatrix)
|
||||||
|
{
|
||||||
|
const rage_engine::ViewAngles angles{
|
||||||
|
rage_engine::PitchAngle::from_degrees(20.f),
|
||||||
|
rage_engine::YawAngle::from_degrees(-35.f),
|
||||||
|
rage_engine::RollAngle::from_degrees(15.f),
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPECT_EQ(rage_engine::MeshTrait::rotation_matrix(angles), rage_engine::rotation_matrix(angles));
|
||||||
|
}
|
||||||
@@ -35,13 +35,22 @@ static std::vector<std::byte> make_elf64_with_text_section(const std::vector<std
|
|||||||
|
|
||||||
std::vector<std::byte> buf(total_size, std::byte{0});
|
std::vector<std::byte> buf(total_size, std::byte{0});
|
||||||
|
|
||||||
auto w8 = [&](std::size_t off, std::uint8_t v) { buf[off] = std::byte{v}; };
|
auto w8 = [&](std::size_t off, std::uint8_t v)
|
||||||
|
{
|
||||||
|
buf[off] = std::byte{v};
|
||||||
|
};
|
||||||
auto w16 = [&](std::size_t off, std::uint16_t v)
|
auto w16 = [&](std::size_t off, std::uint16_t v)
|
||||||
{ std::memcpy(buf.data() + off, &v, 2); };
|
{
|
||||||
|
std::memcpy(buf.data() + off, &v, 2);
|
||||||
|
};
|
||||||
auto w32 = [&](std::size_t off, std::uint32_t v)
|
auto w32 = [&](std::size_t off, std::uint32_t v)
|
||||||
{ std::memcpy(buf.data() + off, &v, 4); };
|
{
|
||||||
|
std::memcpy(buf.data() + off, &v, 4);
|
||||||
|
};
|
||||||
auto w64 = [&](std::size_t off, std::uint64_t v)
|
auto w64 = [&](std::size_t off, std::uint64_t v)
|
||||||
{ std::memcpy(buf.data() + off, &v, 8); };
|
{
|
||||||
|
std::memcpy(buf.data() + off, &v, 8);
|
||||||
|
};
|
||||||
|
|
||||||
// --- ELF64 file header ---
|
// --- ELF64 file header ---
|
||||||
// e_ident
|
// e_ident
|
||||||
@@ -151,6 +160,18 @@ TEST(unit_test_elf_pattern_scan_memory, finds_pattern_with_wildcard)
|
|||||||
EXPECT_EQ(result->target_offset, 0);
|
EXPECT_EQ(result->target_offset, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_elf_pattern_scan_memory, consteval_finds_pattern_with_wildcard)
|
||||||
|
{
|
||||||
|
const std::vector<std::uint8_t> code = {0x00, 0xDE, 0xAD, 0xBE, 0xEF};
|
||||||
|
const auto buf = make_elf64_with_text_section(code);
|
||||||
|
|
||||||
|
const auto result =
|
||||||
|
ElfPatternScanner::scan_for_pattern_in_memory_file<"DE ?? BE EF">(std::span<const std::byte>{buf}, ".text");
|
||||||
|
|
||||||
|
ASSERT_TRUE(result.has_value());
|
||||||
|
EXPECT_EQ(result->target_offset, 1);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(unit_test_elf_pattern_scan_memory, pattern_not_found_returns_nullopt)
|
TEST(unit_test_elf_pattern_scan_memory, pattern_not_found_returns_nullopt)
|
||||||
{
|
{
|
||||||
const std::vector<std::uint8_t> code = {0x01, 0x02, 0x03, 0x04};
|
const std::vector<std::uint8_t> code = {0x01, 0x02, 0x03, 0x04};
|
||||||
@@ -182,8 +203,8 @@ TEST(unit_test_elf_pattern_scan_memory, missing_section_returns_nullopt)
|
|||||||
const std::vector<std::uint8_t> code = {0x90, 0x90};
|
const std::vector<std::uint8_t> code = {0x90, 0x90};
|
||||||
const auto buf = make_elf64_with_text_section(code);
|
const auto buf = make_elf64_with_text_section(code);
|
||||||
|
|
||||||
const auto result = ElfPatternScanner::scan_for_pattern_in_memory_file(std::span<const std::byte>{buf},
|
const auto result = ElfPatternScanner::scan_for_pattern_in_memory_file(std::span<const std::byte>{buf}, "90 90",
|
||||||
"90 90", ".nonexistent");
|
".nonexistent");
|
||||||
|
|
||||||
EXPECT_FALSE(result.has_value());
|
EXPECT_FALSE(result.has_value());
|
||||||
}
|
}
|
||||||
@@ -201,8 +222,8 @@ TEST(unit_test_elf_pattern_scan_memory, matches_file_scan)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto file_result = ElfPatternScanner::scan_for_pattern_in_file(tmp_path, "48 89 E5 DE AD", ".text");
|
const auto file_result = ElfPatternScanner::scan_for_pattern_in_file(tmp_path, "48 89 E5 DE AD", ".text");
|
||||||
const auto mem_result =
|
const auto mem_result = ElfPatternScanner::scan_for_pattern_in_memory_file(std::span<const std::byte>{buf},
|
||||||
ElfPatternScanner::scan_for_pattern_in_memory_file(std::span<const std::byte>{buf}, "48 89 E5 DE AD", ".text");
|
"48 89 E5 DE AD", ".text");
|
||||||
|
|
||||||
std::filesystem::remove(tmp_path);
|
std::filesystem::remove(tmp_path);
|
||||||
|
|
||||||
@@ -212,3 +233,27 @@ TEST(unit_test_elf_pattern_scan_memory, matches_file_scan)
|
|||||||
EXPECT_EQ(file_result->raw_base_addr, mem_result->raw_base_addr);
|
EXPECT_EQ(file_result->raw_base_addr, mem_result->raw_base_addr);
|
||||||
EXPECT_EQ(file_result->target_offset, mem_result->target_offset);
|
EXPECT_EQ(file_result->target_offset, mem_result->target_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_elf_pattern_scan_memory, consteval_file_scan_finds_pattern)
|
||||||
|
{
|
||||||
|
const std::vector<std::uint8_t> code = {0x48, 0x89, 0xE5, 0xDE, 0xAD};
|
||||||
|
const auto buf = make_elf64_with_text_section(code);
|
||||||
|
const auto tmp_path = std::filesystem::temp_directory_path() / "omath_elf_consteval_test.elf";
|
||||||
|
{
|
||||||
|
std::ofstream out(tmp_path, std::ios::binary);
|
||||||
|
out.write(reinterpret_cast<const char*>(buf.data()), static_cast<std::streamsize>(buf.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto result = ElfPatternScanner::scan_for_pattern_in_file<"48 ?? E5">(tmp_path, ".text");
|
||||||
|
|
||||||
|
std::filesystem::remove(tmp_path);
|
||||||
|
|
||||||
|
ASSERT_TRUE(result.has_value());
|
||||||
|
EXPECT_EQ(result->target_offset, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_elf_pattern_scan_memory, consteval_loaded_module_null_returns_nullopt)
|
||||||
|
{
|
||||||
|
const auto result = ElfPatternScanner::scan_for_pattern_in_loaded_module<"DE AD">(nullptr);
|
||||||
|
EXPECT_FALSE(result.has_value());
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
// Tests for MachOPatternScanner::scan_for_pattern_in_memory_file
|
// Tests for MachOPatternScanner::scan_for_pattern_in_memory_file
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <omath/utility/macho_pattern_scan.hpp>
|
#include <omath/utility/macho_pattern_scan.hpp>
|
||||||
#include <span>
|
#include <span>
|
||||||
@@ -37,13 +39,18 @@ static std::vector<std::byte> make_macho64_with_text_section(const std::vector<s
|
|||||||
constexpr std::size_t total_size = text_raw_off + text_raw_size;
|
constexpr std::size_t total_size = text_raw_off + text_raw_size;
|
||||||
constexpr std::uint64_t text_vmaddr = 0x1000ULL;
|
constexpr std::uint64_t text_vmaddr = 0x1000ULL;
|
||||||
|
|
||||||
constexpr std::uint32_t cmd_size =
|
constexpr std::uint32_t cmd_size = static_cast<std::uint32_t>(seg_size + sect_hdr_size); // segment + 1 section
|
||||||
static_cast<std::uint32_t>(seg_size + sect_hdr_size); // segment + 1 section
|
|
||||||
|
|
||||||
std::vector<std::byte> buf(total_size, std::byte{0});
|
std::vector<std::byte> buf(total_size, std::byte{0});
|
||||||
|
|
||||||
auto w32 = [&](std::size_t off, std::uint32_t v) { std::memcpy(buf.data() + off, &v, 4); };
|
auto w32 = [&](std::size_t off, std::uint32_t v)
|
||||||
auto w64 = [&](std::size_t off, std::uint64_t v) { std::memcpy(buf.data() + off, &v, 8); };
|
{
|
||||||
|
std::memcpy(buf.data() + off, &v, 4);
|
||||||
|
};
|
||||||
|
auto w64 = [&](std::size_t off, std::uint64_t v)
|
||||||
|
{
|
||||||
|
std::memcpy(buf.data() + off, &v, 8);
|
||||||
|
};
|
||||||
|
|
||||||
// MachHeader64
|
// MachHeader64
|
||||||
w32(0, mh_magic_64);
|
w32(0, mh_magic_64);
|
||||||
@@ -105,6 +112,18 @@ TEST(unit_test_macho_memory_file_scan, finds_pattern_with_wildcard)
|
|||||||
EXPECT_EQ(result->target_offset, 0);
|
EXPECT_EQ(result->target_offset, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_macho_memory_file_scan, consteval_finds_pattern_with_wildcard)
|
||||||
|
{
|
||||||
|
const std::vector<std::uint8_t> code = {0x00, 0xDE, 0xAD, 0xBE, 0xEF};
|
||||||
|
const auto buf = make_macho64_with_text_section(code);
|
||||||
|
|
||||||
|
const auto result =
|
||||||
|
MachOPatternScanner::scan_for_pattern_in_memory_file<"DE ?? BE EF">(std::span<const std::byte>{buf});
|
||||||
|
|
||||||
|
ASSERT_TRUE(result.has_value());
|
||||||
|
EXPECT_EQ(result->target_offset, 1);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(unit_test_macho_memory_file_scan, pattern_not_found_returns_nullopt)
|
TEST(unit_test_macho_memory_file_scan, pattern_not_found_returns_nullopt)
|
||||||
{
|
{
|
||||||
const std::vector<std::uint8_t> code = {0x01, 0x02, 0x03};
|
const std::vector<std::uint8_t> code = {0x01, 0x02, 0x03};
|
||||||
@@ -143,3 +162,27 @@ TEST(unit_test_macho_memory_file_scan, raw_addr_and_virtual_addr_correct)
|
|||||||
EXPECT_EQ(result->raw_base_addr, expected_raw_off);
|
EXPECT_EQ(result->raw_base_addr, expected_raw_off);
|
||||||
EXPECT_EQ(result->virtual_base_addr, 0x1000u);
|
EXPECT_EQ(result->virtual_base_addr, 0x1000u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_macho_memory_file_scan, consteval_file_scan_finds_pattern)
|
||||||
|
{
|
||||||
|
const std::vector<std::uint8_t> code = {0x48, 0x89, 0xE5, 0xDE, 0xAD};
|
||||||
|
const auto buf = make_macho64_with_text_section(code);
|
||||||
|
const auto tmp_path = std::filesystem::temp_directory_path() / "omath_macho_consteval_test.bin";
|
||||||
|
{
|
||||||
|
std::ofstream out(tmp_path, std::ios::binary);
|
||||||
|
out.write(reinterpret_cast<const char*>(buf.data()), static_cast<std::streamsize>(buf.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto result = MachOPatternScanner::scan_for_pattern_in_file<"48 ?? E5">(tmp_path);
|
||||||
|
|
||||||
|
std::filesystem::remove(tmp_path);
|
||||||
|
|
||||||
|
ASSERT_TRUE(result.has_value());
|
||||||
|
EXPECT_EQ(result->target_offset, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_macho_memory_file_scan, consteval_loaded_module_null_returns_nullopt)
|
||||||
|
{
|
||||||
|
const auto result = MachOPatternScanner::scan_for_pattern_in_loaded_module<"DE AD">(nullptr);
|
||||||
|
EXPECT_FALSE(result.has_value());
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
#include "omath/utility/pe_pattern_scan.hpp"
|
#include "omath/utility/pe_pattern_scan.hpp"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include <omath/utility/pattern_scan.hpp>
|
#include <omath/utility/pattern_scan.hpp>
|
||||||
#include <source_location>
|
|
||||||
#include <print>
|
#include <print>
|
||||||
|
#include <source_location>
|
||||||
TEST(unit_test_pattern_scan, read_test)
|
TEST(unit_test_pattern_scan, read_test)
|
||||||
{
|
{
|
||||||
const auto result = omath::PatternScanner::parse_pattern("FF ? ?? E9");
|
const auto result = omath::PatternScanner::parse_pattern("FF ? ?? E9");
|
||||||
@@ -51,3 +51,37 @@ TEST(unit_test_pattern_scan, corner_case_4)
|
|||||||
const auto result = omath::PatternScanner::parse_pattern("X ? ?? E9 ");
|
const auto result = omath::PatternScanner::parse_pattern("X ? ?? E9 ");
|
||||||
EXPECT_FALSE(result.has_value());
|
EXPECT_FALSE(result.has_value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_pattern_scan, consteval_read_test)
|
||||||
|
{
|
||||||
|
constexpr auto result = omath::PatternScanner::parse_pattern<"FF ? ?? E9">();
|
||||||
|
|
||||||
|
static_assert(result.size() == 4);
|
||||||
|
static_assert(result[0] == static_cast<std::byte>(0xFF));
|
||||||
|
static_assert(result[1] == std::nullopt);
|
||||||
|
static_assert(result[2] == std::nullopt);
|
||||||
|
static_assert(result[3] == static_cast<std::byte>(0xE9));
|
||||||
|
|
||||||
|
EXPECT_EQ(result[0], static_cast<std::byte>(0xFF));
|
||||||
|
EXPECT_EQ(result[1], std::nullopt);
|
||||||
|
EXPECT_EQ(result[2], std::nullopt);
|
||||||
|
EXPECT_EQ(result[3], static_cast<std::byte>(0xE9));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_pattern_scan, consteval_spacing_and_case)
|
||||||
|
{
|
||||||
|
constexpr auto result = omath::PatternScanner::parse_pattern<" \tde\nAD\r?? ? ef ">();
|
||||||
|
|
||||||
|
static_assert(result.size() == 5);
|
||||||
|
static_assert(result[0] == static_cast<std::byte>(0xDE));
|
||||||
|
static_assert(result[1] == static_cast<std::byte>(0xAD));
|
||||||
|
static_assert(result[2] == std::nullopt);
|
||||||
|
static_assert(result[3] == std::nullopt);
|
||||||
|
static_assert(result[4] == static_cast<std::byte>(0xEF));
|
||||||
|
|
||||||
|
EXPECT_EQ(result[0], static_cast<std::byte>(0xDE));
|
||||||
|
EXPECT_EQ(result[1], static_cast<std::byte>(0xAD));
|
||||||
|
EXPECT_EQ(result[2], std::nullopt);
|
||||||
|
EXPECT_EQ(result[3], std::nullopt);
|
||||||
|
EXPECT_EQ(result[4], static_cast<std::byte>(0xEF));
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,6 +14,16 @@ TEST(unit_test_pattern_scan_extra, IteratorScanFound)
|
|||||||
EXPECT_EQ(std::distance(buf.begin(), it), 0);
|
EXPECT_EQ(std::distance(buf.begin(), it), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_pattern_scan_extra, ConstevalIteratorScan)
|
||||||
|
{
|
||||||
|
std::vector<std::byte> buf = {static_cast<std::byte>(0x00), static_cast<std::byte>(0xDE),
|
||||||
|
static_cast<std::byte>(0xAD), static_cast<std::byte>(0xBE),
|
||||||
|
static_cast<std::byte>(0xEF), static_cast<std::byte>(0x11)};
|
||||||
|
const auto it = PatternScanner::scan_for_pattern<"DE ?? BE EF">(buf.begin(), buf.end());
|
||||||
|
EXPECT_NE(it, buf.end());
|
||||||
|
EXPECT_EQ(std::distance(buf.begin(), it), 1);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(unit_test_pattern_scan_extra, IteratorScanNotFound)
|
TEST(unit_test_pattern_scan_extra, IteratorScanNotFound)
|
||||||
{
|
{
|
||||||
std::vector<std::byte> buf = {static_cast<std::byte>(0x00), static_cast<std::byte>(0x11),
|
std::vector<std::byte> buf = {static_cast<std::byte>(0x00), static_cast<std::byte>(0x11),
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
// Tests for PePatternScanner::scan_for_pattern_in_memory_file
|
// Tests for PePatternScanner::scan_for_pattern_in_memory_file
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <omath/utility/pe_pattern_scan.hpp>
|
#include <omath/utility/pe_pattern_scan.hpp>
|
||||||
#include <span>
|
#include <span>
|
||||||
@@ -10,8 +12,7 @@ using namespace omath;
|
|||||||
// Reuse the fake-module builder from unit_test_pe_pattern_scan_loaded.cpp but
|
// Reuse the fake-module builder from unit_test_pe_pattern_scan_loaded.cpp but
|
||||||
// lay out the buffer as a raw PE *file* (ptr_raw_data != virtual_address).
|
// lay out the buffer as a raw PE *file* (ptr_raw_data != virtual_address).
|
||||||
static std::vector<std::byte> make_fake_pe_file(std::uint32_t virtual_address, std::uint32_t ptr_raw_data,
|
static std::vector<std::byte> make_fake_pe_file(std::uint32_t virtual_address, std::uint32_t ptr_raw_data,
|
||||||
std::uint32_t section_size,
|
std::uint32_t section_size, const std::vector<std::uint8_t>& code_bytes)
|
||||||
const std::vector<std::uint8_t>& code_bytes)
|
|
||||||
{
|
{
|
||||||
constexpr std::uint32_t e_lfanew = 0x80;
|
constexpr std::uint32_t e_lfanew = 0x80;
|
||||||
constexpr std::uint32_t nt_sig = 0x4550;
|
constexpr std::uint32_t nt_sig = 0x4550;
|
||||||
@@ -24,9 +25,18 @@ static std::vector<std::byte> make_fake_pe_file(std::uint32_t virtual_address, s
|
|||||||
const std::uint32_t total_size = ptr_raw_data + section_size + 0x100;
|
const std::uint32_t total_size = ptr_raw_data + section_size + 0x100;
|
||||||
std::vector<std::byte> buf(total_size, std::byte{0});
|
std::vector<std::byte> buf(total_size, std::byte{0});
|
||||||
|
|
||||||
auto w16 = [&](std::size_t off, std::uint16_t v) { std::memcpy(buf.data() + off, &v, 2); };
|
auto w16 = [&](std::size_t off, std::uint16_t v)
|
||||||
auto w32 = [&](std::size_t off, std::uint32_t v) { std::memcpy(buf.data() + off, &v, 4); };
|
{
|
||||||
auto w64 = [&](std::size_t off, std::uint64_t v) { std::memcpy(buf.data() + off, &v, 8); };
|
std::memcpy(buf.data() + off, &v, 2);
|
||||||
|
};
|
||||||
|
auto w32 = [&](std::size_t off, std::uint32_t v)
|
||||||
|
{
|
||||||
|
std::memcpy(buf.data() + off, &v, 4);
|
||||||
|
};
|
||||||
|
auto w64 = [&](std::size_t off, std::uint64_t v)
|
||||||
|
{
|
||||||
|
std::memcpy(buf.data() + off, &v, 8);
|
||||||
|
};
|
||||||
|
|
||||||
// DOS header
|
// DOS header
|
||||||
w16(0x00, 0x5A4D);
|
w16(0x00, 0x5A4D);
|
||||||
@@ -87,13 +97,24 @@ TEST(unit_test_pe_memory_file_scan, finds_pattern_with_wildcard)
|
|||||||
EXPECT_EQ(result->target_offset, 0);
|
EXPECT_EQ(result->target_offset, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_pe_memory_file_scan, consteval_finds_pattern_with_wildcard)
|
||||||
|
{
|
||||||
|
const std::vector<std::uint8_t> code = {0x00, 0xDE, 0xAD, 0xBE, 0xEF};
|
||||||
|
const auto buf = make_fake_pe_file(0x2000, 0x600, static_cast<std::uint32_t>(code.size()), code);
|
||||||
|
|
||||||
|
const auto result =
|
||||||
|
PePatternScanner::scan_for_pattern_in_memory_file<"DE ?? BE EF">(std::span<const std::byte>{buf});
|
||||||
|
|
||||||
|
ASSERT_TRUE(result.has_value());
|
||||||
|
EXPECT_EQ(result->target_offset, 1);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(unit_test_pe_memory_file_scan, pattern_not_found_returns_nullopt)
|
TEST(unit_test_pe_memory_file_scan, pattern_not_found_returns_nullopt)
|
||||||
{
|
{
|
||||||
const std::vector<std::uint8_t> code = {0x01, 0x02, 0x03};
|
const std::vector<std::uint8_t> code = {0x01, 0x02, 0x03};
|
||||||
const auto buf = make_fake_pe_file(0x1000, 0x400, static_cast<std::uint32_t>(code.size()), code);
|
const auto buf = make_fake_pe_file(0x1000, 0x400, static_cast<std::uint32_t>(code.size()), code);
|
||||||
|
|
||||||
const auto result =
|
const auto result = PePatternScanner::scan_for_pattern_in_memory_file(std::span<const std::byte>{buf}, "AA BB CC");
|
||||||
PePatternScanner::scan_for_pattern_in_memory_file(std::span<const std::byte>{buf}, "AA BB CC");
|
|
||||||
|
|
||||||
EXPECT_FALSE(result.has_value());
|
EXPECT_FALSE(result.has_value());
|
||||||
}
|
}
|
||||||
@@ -126,3 +147,27 @@ TEST(unit_test_pe_memory_file_scan, raw_addr_differs_from_virtual_address)
|
|||||||
// virtual_base_addr = virtual_address + image_base (image_base = 0)
|
// virtual_base_addr = virtual_address + image_base (image_base = 0)
|
||||||
EXPECT_EQ(result->virtual_base_addr, 0x3000u);
|
EXPECT_EQ(result->virtual_base_addr, 0x3000u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_pe_memory_file_scan, consteval_file_scan_finds_pattern)
|
||||||
|
{
|
||||||
|
const std::vector<std::uint8_t> code = {0x48, 0x89, 0xE5, 0xDE, 0xAD};
|
||||||
|
const auto buf = make_fake_pe_file(0x2000, 0x600, static_cast<std::uint32_t>(code.size()), code);
|
||||||
|
const auto tmp_path = std::filesystem::temp_directory_path() / "omath_pe_consteval_test.exe";
|
||||||
|
{
|
||||||
|
std::ofstream out(tmp_path, std::ios::binary);
|
||||||
|
out.write(reinterpret_cast<const char*>(buf.data()), static_cast<std::streamsize>(buf.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto result = PePatternScanner::scan_for_pattern_in_file<"48 ?? E5">(tmp_path);
|
||||||
|
|
||||||
|
std::filesystem::remove(tmp_path);
|
||||||
|
|
||||||
|
ASSERT_TRUE(result.has_value());
|
||||||
|
EXPECT_EQ(result->target_offset, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_pe_memory_file_scan, consteval_loaded_module_null_returns_nullopt)
|
||||||
|
{
|
||||||
|
const auto result = PePatternScanner::scan_for_pattern_in_loaded_module<"DE AD">(nullptr);
|
||||||
|
EXPECT_FALSE(result.has_value());
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <omath/engines/frostbite_engine/camera.hpp>
|
#include <omath/engines/frostbite_engine/camera.hpp>
|
||||||
#include <omath/engines/iw_engine/camera.hpp>
|
#include <omath/engines/iw_engine/camera.hpp>
|
||||||
#include <omath/engines/opengl_engine/camera.hpp>
|
#include <omath/engines/opengl_engine/camera.hpp>
|
||||||
|
#include <omath/engines/rage_engine/camera.hpp>
|
||||||
#include <omath/engines/source_engine/camera.hpp>
|
#include <omath/engines/source_engine/camera.hpp>
|
||||||
#include <omath/engines/unreal_engine/camera.hpp>
|
#include <omath/engines/unreal_engine/camera.hpp>
|
||||||
#include <omath/linear_algebra/triangle.hpp>
|
#include <omath/linear_algebra/triangle.hpp>
|
||||||
@@ -16,6 +17,49 @@
|
|||||||
#include <print>
|
#include <print>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
|
||||||
|
template<class NumericType>
|
||||||
|
static NumericType random_visible_component(std::mt19937& gen, std::uniform_real_distribution<NumericType>& dist)
|
||||||
|
{
|
||||||
|
auto value = dist(gen);
|
||||||
|
if (std::abs(value) < static_cast<NumericType>(50))
|
||||||
|
value += value < NumericType{} ? -static_cast<NumericType>(50) : static_cast<NumericType>(50);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class CameraType, class ViewAnglesType, class NumericType>
|
||||||
|
static void verify_random_look_at_targets_project_to_screen_center(const omath::Vector3<NumericType>& origin,
|
||||||
|
const ViewAnglesType& view_angles,
|
||||||
|
const omath::projection::ViewPort& view_port,
|
||||||
|
const NumericType near_plane,
|
||||||
|
const NumericType far_plane, const unsigned int seed)
|
||||||
|
{
|
||||||
|
std::mt19937 gen(seed);
|
||||||
|
std::uniform_real_distribution<NumericType> dist(static_cast<NumericType>(-500), static_cast<NumericType>(500));
|
||||||
|
|
||||||
|
constexpr auto fov = omath::projection::FieldOfView::from_degrees(75.f);
|
||||||
|
auto cam = CameraType(origin, view_angles, view_port, fov, near_plane, far_plane);
|
||||||
|
const auto screen_center_x = static_cast<NumericType>(view_port.m_width / 2.f);
|
||||||
|
const auto screen_center_y = static_cast<NumericType>(view_port.m_height / 2.f);
|
||||||
|
|
||||||
|
for (int i = 0; i < 100; i++)
|
||||||
|
{
|
||||||
|
const auto target = origin
|
||||||
|
+ omath::Vector3<NumericType>{
|
||||||
|
random_visible_component(gen, dist),
|
||||||
|
random_visible_component(gen, dist),
|
||||||
|
random_visible_component(gen, dist),
|
||||||
|
};
|
||||||
|
|
||||||
|
cam.look_at(target);
|
||||||
|
|
||||||
|
const auto projected = cam.world_to_screen(target);
|
||||||
|
ASSERT_TRUE(projected.has_value()) << "iteration: " << i;
|
||||||
|
EXPECT_NEAR(projected->x, screen_center_x, static_cast<NumericType>(0.1));
|
||||||
|
EXPECT_NEAR(projected->y, screen_center_y, static_cast<NumericType>(0.1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST(UnitTestProjection, Projection)
|
TEST(UnitTestProjection, Projection)
|
||||||
{
|
{
|
||||||
constexpr auto fov = omath::Angle<float, 0.f, 180.f, omath::AngleFlags::Clamped>::from_degrees(90.f);
|
constexpr auto fov = omath::Angle<float, 0.f, 180.f, omath::AngleFlags::Clamped>::from_degrees(90.f);
|
||||||
@@ -1115,6 +1159,54 @@ TEST(UnitTestProjection, LookAt_ForwardVectorPointsAtTarget)
|
|||||||
EXPECT_NEAR(fwd.z, 0.f, 1e-4f);
|
EXPECT_NEAR(fwd.z, 0.f, 1e-4f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(UnitTestProjection, SourceEngine_LookAtRandomWorldPointsProjectToScreenCenter)
|
||||||
|
{
|
||||||
|
verify_random_look_at_targets_project_to_screen_center<omath::source_engine::Camera>(
|
||||||
|
{10.f, -20.f, 30.f}, omath::source_engine::ViewAngles{}, {1920.f, 1080.f}, 0.01f, 5000.f, 101);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(UnitTestProjection, UnityEngine_LookAtRandomWorldPointsProjectToScreenCenter)
|
||||||
|
{
|
||||||
|
verify_random_look_at_targets_project_to_screen_center<omath::unity_engine::Camera>(
|
||||||
|
{-15.f, 25.f, -35.f}, omath::unity_engine::ViewAngles{}, {1280.f, 720.f}, 0.03f, 5000.f, 102);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(UnitTestProjection, UnrealEngine_LookAtRandomWorldPointsProjectToScreenCenter)
|
||||||
|
{
|
||||||
|
verify_random_look_at_targets_project_to_screen_center<omath::unreal_engine::Camera>(
|
||||||
|
{100.0, -50.0, 25.0}, omath::unreal_engine::ViewAngles{}, {1920.f, 1080.f}, 0.01, 5000.0, 103);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(UnitTestProjection, FrostbiteEngine_LookAtRandomWorldPointsProjectToScreenCenter)
|
||||||
|
{
|
||||||
|
verify_random_look_at_targets_project_to_screen_center<omath::frostbite_engine::Camera>(
|
||||||
|
{-4.f, 2.f, 8.f}, omath::frostbite_engine::ViewAngles{}, {1600.f, 900.f}, 0.01f, 5000.f, 104);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(UnitTestProjection, IWEngine_LookAtRandomWorldPointsProjectToScreenCenter)
|
||||||
|
{
|
||||||
|
verify_random_look_at_targets_project_to_screen_center<omath::iw_engine::Camera>(
|
||||||
|
{40.f, -60.f, 20.f}, omath::iw_engine::ViewAngles{}, {1920.f, 1080.f}, 0.01f, 5000.f, 105);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(UnitTestProjection, OpenGLEngine_LookAtRandomWorldPointsProjectToScreenCenter)
|
||||||
|
{
|
||||||
|
verify_random_look_at_targets_project_to_screen_center<omath::opengl_engine::Camera>(
|
||||||
|
{-30.f, 70.f, 15.f}, omath::opengl_engine::ViewAngles{}, {1920.f, 1080.f}, 0.01f, 5000.f, 106);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(UnitTestProjection, CryEngine_LookAtRandomWorldPointsProjectToScreenCenter)
|
||||||
|
{
|
||||||
|
verify_random_look_at_targets_project_to_screen_center<omath::cry_engine::Camera>(
|
||||||
|
{5.f, 15.f, -25.f}, omath::cry_engine::ViewAngles{}, {1280.f, 720.f}, 0.01f, 5000.f, 107);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(UnitTestProjection, RageEngine_LookAtRandomWorldPointsProjectToScreenCenter)
|
||||||
|
{
|
||||||
|
verify_random_look_at_targets_project_to_screen_center<omath::rage_engine::Camera>(
|
||||||
|
{-5.f, 45.f, 10.f}, omath::rage_engine::ViewAngles{}, {1280.f, 720.f}, 0.01f, 5000.f, 108);
|
||||||
|
}
|
||||||
|
|
||||||
// ---- is_culled_by_frustum (triangle) ----
|
// ---- is_culled_by_frustum (triangle) ----
|
||||||
|
|
||||||
TEST(UnitTestProjection, TriangleInsideFrustumNotCulled)
|
TEST(UnitTestProjection, TriangleInsideFrustumNotCulled)
|
||||||
|
|||||||
Reference in New Issue
Block a user