diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..33a750b
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,34 @@
+cmake_minimum_required(VERSION 3.0)
+
+set(CMAKE_CXX_STANDARD 20)
+
+project(libhat)
+
+if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
+ set_source_files_properties(src/arch/x86/AVX2.cpp PROPERTIES COMPILE_FLAGS "/arch:AVX2")
+ set_source_files_properties(src/arch/x86/AVX512.cpp PROPERTIES COMPILE_FLAGS "/arch:AVX512")
+elseif (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
+ set_source_files_properties(src/arch/x86/AVX2.cpp PROPERTIES COMPILE_FLAGS "-mavx -mavx2 -mbmi")
+ set_source_files_properties(src/arch/x86/AVX512.cpp PROPERTIES COMPILE_FLAGS "-mavx512f -mavx512bw -mbmi")
+ set_source_files_properties(src/arch/x86/System.cpp PROPERTIES COMPILE_FLAGS "-mxsave")
+endif ()
+
+set(LIBHAT_SRC
+ src/Process.cpp
+ src/Scanner.cpp
+ src/System.cpp
+
+ src/os/Win32.cpp
+
+ src/arch/x86/AVX2.cpp
+ src/arch/x86/AVX512.cpp
+ src/arch/x86/System.cpp
+
+ src/arch/arm/Neon.cpp
+ src/arch/arm/System.cpp)
+
+add_library(libhat ${LIBHAT_SRC})
+
+target_include_directories(libhat PUBLIC
+ $
+ $)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0fb72c2
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+# libhat
\ No newline at end of file
diff --git a/include/libhat.h b/include/libhat.h
new file mode 100644
index 0000000..14a363c
--- /dev/null
+++ b/include/libhat.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
diff --git a/include/libhat/CompileTime.hpp b/include/libhat/CompileTime.hpp
new file mode 100644
index 0000000..ad59573
--- /dev/null
+++ b/include/libhat/CompileTime.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include
+#include
+#include
+
+namespace hat {
+
+ template
+ struct string_literal {
+ constexpr string_literal(const char (&str)[N]) {
+ std::copy_n(str, N, value);
+ }
+
+ [[nodiscard]] constexpr const char* c_str() const {
+ return (const char*) &this->value[0];
+ }
+
+ char value[N];
+ };
+
+ static constexpr int atoi(std::string_view str, int base = 10) {
+ if (base < 2 || base > 36) {
+ throw std::invalid_argument("Invalid base specified");
+ }
+
+ int value = 0;
+ auto digits = base < 10 ? base : 10;
+ auto letters = base > 10 ? base - 10 : 0;
+
+ for (char ch : str) {
+ value *= base;
+ if (ch >= '0' && ch < '0' + digits) {
+ value += (ch - '0');
+ } else if (ch >= 'A' && ch < 'A' + letters) {
+ value += (ch - 'A' + 10);
+ } else if (ch >= 'a' && ch < 'a' + letters) {
+ value += (ch - 'a' + 10);
+ } else {
+ // Throws an exception at runtime AND prevents constexpr evaluation
+ throw std::invalid_argument("Unexpected character in integer string");
+ }
+ }
+ return value;
+ }
+}
diff --git a/include/libhat/Defines.hpp b/include/libhat/Defines.hpp
new file mode 100644
index 0000000..e27d1c0
--- /dev/null
+++ b/include/libhat/Defines.hpp
@@ -0,0 +1,32 @@
+#pragma once
+
+// Detect CPU Architecture
+#if defined(_M_X64) || defined(__amd64__) || defined(_M_IX86) || defined(__i386__)
+ #define LIBHAT_X86
+ #if defined(_M_X64) || defined(__amd64__)
+ #define LIBHAT_X86_64
+ #endif
+#elif defined(_M_ARM64) || defined(__aarch64__) || defined(_M_ARM) || defined(__arm__)
+ #define LIBHAT_ARM
+#else
+ #error Unsupported Architecture
+#endif
+
+// Detect Operating System
+#if defined(_WIN32)
+ #define LIBHAT_WINDOWS
+#else
+ #error Unsupported Operating System
+#endif
+
+// Macros wrapping intrinsics
+#ifdef LIBHAT_X86
+ #ifdef LIBHAT_X86_64
+ #define LIBHAT_TZCNT64(num) _tzcnt_u64(num)
+ #define LIBHAT_BLSR64(num) _blsr_u64(num)
+ #else
+ #include
+ #define LIBHAT_TZCNT64(num) std::countl_zero(num)
+ #define LIBHAT_BLSR64(num) num & (num - 1)
+ #endif
+#endif
diff --git a/include/libhat/MemoryProtector.hpp b/include/libhat/MemoryProtector.hpp
new file mode 100644
index 0000000..5284ab2
--- /dev/null
+++ b/include/libhat/MemoryProtector.hpp
@@ -0,0 +1,34 @@
+#pragma once
+
+#include
+#include
+
+namespace hat {
+
+ enum class protection : uint8_t {
+ Read = 0b001,
+ Write = 0b010,
+ Execute = 0b100
+ };
+
+ constexpr protection operator|(protection lhs, protection rhs) {
+ using U = std::underlying_type_t;
+ return static_cast(static_cast(lhs) | static_cast(rhs));
+ }
+
+ constexpr protection operator&(protection lhs, protection rhs) {
+ using U = std::underlying_type_t;
+ return static_cast(static_cast(lhs) & static_cast(rhs));
+ }
+
+ /// RAII wrapper for setting memory protection flags
+ class memory_protector {
+ public:
+ memory_protector(uintptr_t address, size_t size, protection flags);
+ ~memory_protector();
+ private:
+ uintptr_t address;
+ size_t size;
+ uint32_t oldProtection; // Memory protection flags native to Operating System
+ };
+}
diff --git a/include/libhat/Process.hpp b/include/libhat/Process.hpp
new file mode 100644
index 0000000..4144400
--- /dev/null
+++ b/include/libhat/Process.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+#include
+#include
+
+namespace hat::process {
+
+ // TODO: Consider using a typedef or class instead? idk
+ enum class module_t : uintptr_t {};
+
+ /// Returns the module for the curent process's base executable
+ auto get_process_module() -> module_t;
+
+ /// Returns a module by its given name in the current process
+ auto get_module(std::string_view name) -> module_t;
+
+ /// Returns the module located at the specified base address
+ auto module_at(uintptr_t address) -> module_t;
+
+ /// Returns the module located at the specified base address
+ auto module_at(std::byte* address) -> module_t;
+
+ /// Returns the complete memory region for the given module. This may include portions which are uncommitted.
+ auto get_module_data(module_t module) -> std::span;
+
+ /// Returns the memory region for a named section in the given module
+ auto get_section_data(module_t module, std::string_view name) -> std::span;
+}
diff --git a/include/libhat/ScanMode.hpp b/include/libhat/ScanMode.hpp
new file mode 100644
index 0000000..08b3b12
--- /dev/null
+++ b/include/libhat/ScanMode.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+namespace hat {
+
+ enum class scan_mode {
+ Search,
+ FastFirst,
+ AVX2,
+ AVX512,
+ Neon
+ };
+
+ template
+ scan_result find_pattern(std::byte* begin, std::byte* end, signature_view signature);
+}
diff --git a/include/libhat/Scanner.hpp b/include/libhat/Scanner.hpp
new file mode 100644
index 0000000..5158778
--- /dev/null
+++ b/include/libhat/Scanner.hpp
@@ -0,0 +1,81 @@
+#pragma once
+
+#include
+#include
+#include
+
+#include "Process.hpp"
+#include "Signature.hpp"
+
+namespace hat {
+
+ class scan_result {
+ using rel_t = int32_t;
+ public:
+ constexpr scan_result() : result(nullptr) {}
+ constexpr scan_result(std::byte* result) : result(result) {} // NOLINT(google-explicit-constructor)
+
+ /// Reads an integer of the specified type located at an offset from the signature result
+ template
+ [[nodiscard]] constexpr Int read(size_t offset) const {
+ return *reinterpret_cast(this->result + offset);
+ }
+
+ /// Reads an integer of the specified type which represents an index into an array with the given element type
+ template
+ [[nodiscard]] constexpr size_t index(size_t offset) const {
+ return static_cast(read(offset)) / sizeof(ArrayType);
+ }
+
+ /// Resolve the relative address located at an offset from the signature result
+ [[nodiscard]] constexpr std::byte* rel(size_t offset) const {
+ return this->has_result() ? this->result + this->read(offset) + offset + sizeof(rel_t) : nullptr;
+ }
+
+ [[nodiscard]] constexpr bool has_result() const {
+ return this->result != nullptr;
+ }
+
+ [[nodiscard]] constexpr std::byte* operator*() const {
+ return this->result;
+ }
+
+ [[nodiscard]] constexpr std::byte* get() const {
+ return this->result;
+ }
+ private:
+ std::byte* result;
+ };
+
+ enum class compiler_type {
+ MSVC,
+ MinGW
+ };
+
+ /// Gets the VTable address for a class by its mangled name
+ template
+ scan_result find_vtable(
+ const std::string& className,
+ process::module_t module = process::get_process_module()
+ );
+
+ /// Perform a signature scan on the entirety of the process module or a specified module
+ scan_result find_pattern(
+ signature_view signature,
+ process::module_t module = process::get_process_module()
+ );
+
+ /// Perform a signature scan on a specific section of the process module or a specified module
+ scan_result find_pattern(
+ signature_view signature,
+ std::string_view section,
+ process::module_t module = process::get_process_module()
+ );
+
+ /// Root implementation of FindPattern
+ scan_result find_pattern(
+ std::byte* begin,
+ std::byte* end,
+ signature_view signature
+ );
+}
diff --git a/include/libhat/Signature.hpp b/include/libhat/Signature.hpp
new file mode 100644
index 0000000..6d438a6
--- /dev/null
+++ b/include/libhat/Signature.hpp
@@ -0,0 +1,64 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+
+#include "CompileTime.hpp"
+
+namespace hat {
+
+ using signature_element = std::optional;
+ using signature = std::vector;
+ using signature_view = std::span;
+
+ template
+ using fixed_signature = std::array;
+
+ template
+ inline signature object_to_signature(const T& value) {
+ auto bytes = reinterpret_cast(&value);
+ return {bytes, bytes + sizeof(T)};
+ }
+
+ /// Convert raw byte storage into a signature
+ constexpr signature bytes_to_signature(std::span bytes) {
+ return {bytes.begin(), bytes.end()};
+ }
+
+ inline signature string_to_signature(std::string_view str) {
+ return bytes_to_signature({reinterpret_cast(str.data()), str.size()});
+ }
+
+ constexpr signature parse_signature(std::string_view str) {
+ signature sig{};
+ for (const auto& word : str | std::views::split(' ')) {
+ if (word.empty()) {
+ continue;
+ } else if (word[0] == '?') {
+ if (sig.empty()) {
+ throw std::invalid_argument("First byte cannot be a wildcard");
+ }
+ sig.emplace_back(std::nullopt);
+ } else {
+ const auto sv = std::string_view{word.begin(), word.end()};
+ sig.emplace_back(static_cast(atoi(sv, 16) & 0xFF));
+ }
+ }
+ return sig;
+ }
+
+ /// Parses a signature string at compile time, and provides a signature_view which exists for the program's lifetime
+ template
+ inline signature_view compile_signature() {
+ static constexpr auto compiled = ([]() consteval -> auto {
+ const auto sig = parse_signature(str.c_str());
+ constexpr auto N = parse_signature(str.c_str()).size();
+ fixed_signature arr{};
+ std::ranges::move(sig, arr.begin());
+ return arr;
+ })();
+ return compiled;
+ }
+}
diff --git a/include/libhat/System.hpp b/include/libhat/System.hpp
new file mode 100644
index 0000000..68d1550
--- /dev/null
+++ b/include/libhat/System.hpp
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "Defines.hpp"
+
+#if defined(LIBHAT_X86)
+#include "../../src/arch/x86/System.hpp"
+#elif defined(LIBHAT_ARM)
+#include "../../src/arch/arm/System.hpp"
+#endif
+
+namespace hat {
+
+ const system_info& get_system();
+}
diff --git a/src/Process.cpp b/src/Process.cpp
new file mode 100644
index 0000000..eb48846
--- /dev/null
+++ b/src/Process.cpp
@@ -0,0 +1,12 @@
+#include
+
+namespace hat::process {
+
+ module_t module_at(uintptr_t address) {
+ return module_t{address};
+ }
+
+ module_t module_at(std::byte* address) {
+ return module_at(reinterpret_cast(address));
+ }
+}
\ No newline at end of file
diff --git a/src/Scanner.cpp b/src/Scanner.cpp
new file mode 100644
index 0000000..676f2d2
--- /dev/null
+++ b/src/Scanner.cpp
@@ -0,0 +1,146 @@
+#include
+
+#include