From c90ef343c1f6c8c1ed6b9bb65a6786562364800b Mon Sep 17 00:00:00 2001 From: WerWolv <werwolv98@gmail.com> Date: Sat, 28 Nov 2020 21:55:52 +0100 Subject: [PATCH] Added math evaluator / calculator to tools window --- CMakeLists.txt | 8 +- include/{ => helpers}/crypto.hpp | 0 include/{ => helpers}/event.hpp | 0 include/helpers/math_evaluator.hpp | 88 +++++ include/{ => helpers}/patches.hpp | 0 include/{ => helpers}/utils.hpp | 0 include/lang/pattern_data.hpp | 2 +- include/views/view.hpp | 2 +- include/views/view_bookmarks.hpp | 2 +- include/views/view_hexeditor.hpp | 2 +- include/views/view_tools.hpp | 7 + libs/ImGui/include/imgui_memory_editor.h | 2 +- source/{ => helpers}/crypto.cpp | 2 +- source/helpers/math_evaluator.cpp | 390 +++++++++++++++++++++++ source/{ => helpers}/patches.cpp | 4 +- source/{ => helpers}/utils.cpp | 2 +- source/lang/parser.cpp | 2 +- source/lang/validator.cpp | 2 +- source/providers/file_provider.cpp | 2 +- source/views/view_data_inspector.cpp | 2 +- source/views/view_disassembler.cpp | 2 +- source/views/view_hashes.cpp | 4 +- source/views/view_hexeditor.cpp | 4 +- source/views/view_information.cpp | 2 +- source/views/view_pattern.cpp | 2 +- source/views/view_strings.cpp | 2 +- source/views/view_tools.cpp | 138 +++++++- 27 files changed, 647 insertions(+), 26 deletions(-) rename include/{ => helpers}/crypto.hpp (100%) rename include/{ => helpers}/event.hpp (100%) create mode 100644 include/helpers/math_evaluator.hpp rename include/{ => helpers}/patches.hpp (100%) rename include/{ => helpers}/utils.hpp (100%) rename source/{ => helpers}/crypto.cpp (99%) create mode 100644 source/helpers/math_evaluator.cpp rename source/{ => helpers}/patches.cpp (98%) rename source/{ => helpers}/utils.cpp (98%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ff907973..c0dfbdbc6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,9 +27,11 @@ SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") add_executable(ImHex source/main.cpp source/window.cpp - source/utils.cpp - source/crypto.cpp - source/patches.cpp + + source/helpers/utils.cpp + source/helpers/crypto.cpp + source/helpers/patches.cpp + source/helpers/math_evaluator.cpp source/lang/preprocessor.cpp source/lang/lexer.cpp diff --git a/include/crypto.hpp b/include/helpers/crypto.hpp similarity index 100% rename from include/crypto.hpp rename to include/helpers/crypto.hpp diff --git a/include/event.hpp b/include/helpers/event.hpp similarity index 100% rename from include/event.hpp rename to include/helpers/event.hpp diff --git a/include/helpers/math_evaluator.hpp b/include/helpers/math_evaluator.hpp new file mode 100644 index 000000000..eb7c081e5 --- /dev/null +++ b/include/helpers/math_evaluator.hpp @@ -0,0 +1,88 @@ +#pragma once + +#include <hex.hpp> + +#include <string> +#include <vector> +#include <queue> +#include <unordered_map> +#include <functional> +#include <optional> + +namespace hex { + + enum class TokenType { + Number, + Variable, + Function, + Operator, + Bracket + }; + + enum class Operator : u16 { + Invalid = 0x000, + Assign = 0x010, + Or = 0x020, + Xor = 0x030, + And = 0x040, + BitwiseOr = 0x050, + BitwiseXor = 0x060, + BitwiseAnd = 0x070, + Equals = 0x080, + NotEquals = 0x081, + GreaterThan = 0x090, + LessThan = 0x091, + GreaterThanOrEquals = 0x092, + LessThanOrEquals = 0x093, + ShiftLeft = 0x0A0, + ShiftRight = 0x0A1, + Addition = 0x0B0, + Subtraction = 0x0B1, + Multiplication = 0x0C0, + Division = 0x0C1, + Modulus = 0x0C2, + Exponentiation = 0x1D0, + Combine = 0x0E0, + BitwiseNot = 0x0F0, + Not = 0x0F1 + }; + + enum class BracketType : std::uint8_t { + Left, + Right + }; + + struct Token { + TokenType type; + + long double number; + Operator op; + BracketType bracketType; + std::string name; + std::vector<long double> arguments; + }; + + class MathEvaluator { + public: + MathEvaluator() = default; + + std::optional<long double> evaluate(std::string input); + + void registerStandardVariables(); + void registerStandardFunctions(); + + void setVariable(std::string name, long double value); + void setFunction(std::string name, std::function<std::optional<long double>(std::vector<long double>)> function, size_t minNumArgs, size_t maxNumArgs); + + const std::unordered_map<std::string, long double>& getVariables() { return this->m_variables; } + + private: + std::queue<Token> parseInput(const char *input); + std::queue<Token> toPostfix(std::queue<Token> inputQueue); + std::optional<long double> evaluate(std::queue<Token> postfixTokens); + + std::unordered_map<std::string, long double> m_variables; + std::unordered_map<std::string, std::function<std::optional<long double>(std::vector<long double>)>> m_functions; + }; + +} \ No newline at end of file diff --git a/include/patches.hpp b/include/helpers/patches.hpp similarity index 100% rename from include/patches.hpp rename to include/helpers/patches.hpp diff --git a/include/utils.hpp b/include/helpers/utils.hpp similarity index 100% rename from include/utils.hpp rename to include/helpers/utils.hpp diff --git a/include/lang/pattern_data.hpp b/include/lang/pattern_data.hpp index 9fa0bd02a..cf74a65e2 100644 --- a/include/lang/pattern_data.hpp +++ b/include/lang/pattern_data.hpp @@ -7,7 +7,7 @@ #include "imgui_memory_editor.h" #include "providers/provider.hpp" -#include "utils.hpp" +#include "helpers/utils.hpp" #include <random> diff --git a/include/views/view.hpp b/include/views/view.hpp index 2b9892e48..d41d1446c 100644 --- a/include/views/view.hpp +++ b/include/views/view.hpp @@ -4,7 +4,7 @@ #include "imgui.h" -#include "event.hpp" +#include "helpers/event.hpp" #include <functional> #include <string> diff --git a/include/views/view_bookmarks.hpp b/include/views/view_bookmarks.hpp index dba11a356..c1ac6864b 100644 --- a/include/views/view_bookmarks.hpp +++ b/include/views/view_bookmarks.hpp @@ -5,7 +5,7 @@ #include <vector> #include <list> -#include "utils.hpp" +#include "helpers/utils.hpp" namespace hex { diff --git a/include/views/view_hexeditor.hpp b/include/views/view_hexeditor.hpp index 2f034e6e2..a0226bceb 100644 --- a/include/views/view_hexeditor.hpp +++ b/include/views/view_hexeditor.hpp @@ -1,6 +1,6 @@ #pragma once -#include "utils.hpp" +#include "helpers/utils.hpp" #include "views/view.hpp" #include "imgui_memory_editor.h" diff --git a/include/views/view_tools.hpp b/include/views/view_tools.hpp index 22f888ad4..09468ba40 100644 --- a/include/views/view_tools.hpp +++ b/include/views/view_tools.hpp @@ -4,6 +4,7 @@ #include "imgui.h" #include "views/view.hpp" +#include "helpers/math_evaluator.hpp" #include <array> #include <string> @@ -33,10 +34,16 @@ namespace hex { std::array<float, 4> m_pickedColor; + MathEvaluator m_mathEvaluator; + std::vector<long double> m_mathHistory; + std::string m_lastMathError; + char *m_mathInput = nullptr; + void drawDemangler(); void drawASCIITable(); void drawRegexReplacer(); void drawColorPicker(); + void drawMathEvaluator(); }; } \ No newline at end of file diff --git a/libs/ImGui/include/imgui_memory_editor.h b/libs/ImGui/include/imgui_memory_editor.h index cff430f35..809bfb2e2 100644 --- a/libs/ImGui/include/imgui_memory_editor.h +++ b/libs/ImGui/include/imgui_memory_editor.h @@ -46,7 +46,7 @@ #include <stdio.h> // sprintf, scanf #include <stdint.h> // uint8_t, etc. -#include "utils.hpp" +#include "helpers/utils.hpp" #include "views/view.hpp" diff --git a/source/crypto.cpp b/source/helpers/crypto.cpp similarity index 99% rename from source/crypto.cpp rename to source/helpers/crypto.cpp index f854f251a..fa234287d 100644 --- a/source/crypto.cpp +++ b/source/helpers/crypto.cpp @@ -1,4 +1,4 @@ -#include "crypto.hpp" +#include "helpers/crypto.hpp" #include "providers/provider.hpp" diff --git a/source/helpers/math_evaluator.cpp b/source/helpers/math_evaluator.cpp new file mode 100644 index 000000000..df7776033 --- /dev/null +++ b/source/helpers/math_evaluator.cpp @@ -0,0 +1,390 @@ +#include "helpers/math_evaluator.hpp" + +#include <string> +#include <queue> +#include <stack> +#include <stdexcept> +#include <cmath> +#include <cstdint> +#include <optional> +#include <numbers> + +namespace hex { + + s16 comparePrecedence(const Operator& a, const Operator& b) { + return (static_cast<s8>(a) & 0x0F0) - (static_cast<s8>(b) & 0x0F0); + } + + bool isLeftAssociative(const Operator op) { + return (static_cast<u32>(op) & 0xF00) == 0; + } + + std::pair<Operator, size_t> toOperator(std::string input) { + if (input.starts_with("##")) return { Operator::Combine, 2 }; + if (input.starts_with("==")) return { Operator::Equals, 2 }; + if (input.starts_with("!=")) return { Operator::NotEquals, 2 }; + if (input.starts_with(">=")) return { Operator::GreaterThanOrEquals, 2 }; + if (input.starts_with("<=")) return { Operator::LessThanOrEquals, 2 }; + if (input.starts_with(">>")) return { Operator::ShiftRight, 2 }; + if (input.starts_with("<<")) return { Operator::ShiftLeft, 2 }; + if (input.starts_with("||")) return { Operator::Or, 2 }; + if (input.starts_with("^^")) return { Operator::Xor, 2 }; + if (input.starts_with("&&")) return { Operator::And, 2 }; + if (input.starts_with("**")) return { Operator::Exponentiation, 2 }; + if (input.starts_with(">")) return { Operator::GreaterThan, 1 }; + if (input.starts_with("<")) return { Operator::LessThan, 1 }; + if (input.starts_with("!")) return { Operator::Not, 1 }; + if (input.starts_with("|")) return { Operator::BitwiseOr, 1 }; + if (input.starts_with("^")) return { Operator::BitwiseXor, 1 }; + if (input.starts_with("&")) return { Operator::BitwiseAnd, 1 }; + if (input.starts_with("~")) return { Operator::BitwiseNot, 1 }; + if (input.starts_with("+")) return { Operator::Addition, 1 }; + if (input.starts_with("-")) return { Operator::Subtraction, 1 }; + if (input.starts_with("*")) return { Operator::Multiplication, 1 }; + if (input.starts_with("/")) return { Operator::Division, 1 }; + if (input.starts_with("%")) return { Operator::Modulus, 1 }; + if (input.starts_with("=")) return { Operator::Assign, 1 }; + + return { Operator::Invalid, 0 }; + } + + std::queue<Token> MathEvaluator::parseInput(const char *input) { + std::queue<Token> inputQueue; + + char *prevPos = const_cast<char*>(input); + for (char *pos = prevPos; *pos != 0x00;) { + if (std::isdigit(*pos) || *pos == '.') { + auto number = std::strtold(pos, &pos); + + if (*pos == 'x') { + pos--; + number = std::strtoull(pos, &pos, 0); + } + + inputQueue.push(Token{ .type = TokenType::Number, .number = number }); + } else if (*pos == '(') { + inputQueue.push(Token{ .type = TokenType::Bracket, .bracketType = BracketType::Left}); + pos++; + } else if (*pos == ')') { + inputQueue.push(Token{ .type = TokenType::Bracket, .bracketType = BracketType::Right}); + pos++; + } else if (std::isspace(*pos)) { + pos++; + } else { + auto [op, width] = toOperator(pos); + + if (op != Operator::Invalid) { + inputQueue.push(Token{ .type = TokenType::Operator, .op = op }); + pos += width; + } else { + Token token; + + while (std::isalpha(*pos) || *pos == '_') { + token.name += *pos; + pos++; + } + + if (*pos == '(') { + pos++; + + u32 depth = 1; + std::vector<std::string> expressions; + expressions.emplace_back(); + + while (*pos != 0x00) { + if (*pos == '(') depth++; + else if (*pos == ')') depth--; + + if (depth == 0) + break; + + if (depth == 1 && *pos == ',') { + expressions.emplace_back(); + pos++; + } + + expressions.back() += *pos; + + pos++; + } + + pos++; + + for (const auto &expression : expressions) { + if (expression == "" && expressions.size() > 1) + throw std::invalid_argument("Invalid function call syntax!"); + else if (expression == "") + break; + + auto inputQueue = parseInput(expression.c_str()); + auto postfixTokens = toPostfix(inputQueue); + auto result = evaluate(postfixTokens); + + if (!result.has_value()) + throw std::invalid_argument("Invalid argument for function!"); + + token.arguments.push_back(result.value()); + } + + token.type = TokenType::Function; + inputQueue.push(token); + + } else { + token.type = TokenType::Variable; + inputQueue.push(token); + } + } + + + } + + if (prevPos == pos) + throw std::invalid_argument("Invalid syntax!"); + + prevPos = pos; + } + + return inputQueue; + } + + std::queue<Token> MathEvaluator::toPostfix(std::queue<Token> inputQueue) { + std::queue<Token> outputQueue; + std::stack<Token> operatorStack; + + while (!inputQueue.empty()) { + Token currToken = inputQueue.front(); + inputQueue.pop(); + + if (currToken.type == TokenType::Number || currToken.type == TokenType::Variable || currToken.type == TokenType::Function) + outputQueue.push(currToken); + else if (currToken.type == TokenType::Operator) { + while ((!operatorStack.empty()) + && (operatorStack.top().type == TokenType::Operator && currToken.type == TokenType::Operator && (comparePrecedence(operatorStack.top().op, currToken.op) > 0) || (comparePrecedence(operatorStack.top().op, currToken.op) == 0 && isLeftAssociative(currToken.op))) + && operatorStack.top().type != TokenType::Bracket) { + outputQueue.push(operatorStack.top()); + operatorStack.pop(); + } + operatorStack.push(currToken); + } else if (currToken.type == TokenType::Bracket) { + if (currToken.bracketType == BracketType::Left) + operatorStack.push(currToken); + else { + if (operatorStack.empty()) + throw std::invalid_argument("Mismatching parenthesis!"); + + while (operatorStack.top().type != TokenType::Bracket || (operatorStack.top().type == TokenType::Bracket && operatorStack.top().bracketType != BracketType::Left)) { + if (operatorStack.empty()) + throw std::invalid_argument("Mismatching parenthesis!"); + + outputQueue.push(operatorStack.top()); + operatorStack.pop(); + } + + operatorStack.pop(); + } + } + } + + while (!operatorStack.empty()) { + auto top = operatorStack.top(); + + if (top.type == TokenType::Bracket) + throw std::invalid_argument("Mismatching parenthesis!"); + + outputQueue.push(top); + operatorStack.pop(); + } + + return outputQueue; + } + + std::optional<long double> MathEvaluator::evaluate(std::queue<Token> postfixTokens) { + std::stack<long double> evaluationStack; + + while (!postfixTokens.empty()) { + auto front = postfixTokens.front(); + postfixTokens.pop(); + + if (front.type == TokenType::Number) + evaluationStack.push(front.number); + else if (front.type == TokenType::Operator) { + long double rightOperand, leftOperand; + if (evaluationStack.size() < 2) { + if ((front.op == Operator::Addition || front.op == Operator::Subtraction || front.op == Operator::Not || front.op == Operator::BitwiseNot) && evaluationStack.size() == 1) { + rightOperand = evaluationStack.top(); evaluationStack.pop(); + leftOperand = 0; + } + else throw std::invalid_argument("Not enough operands for operator!"); + } else { + rightOperand = evaluationStack.top(); evaluationStack.pop(); + leftOperand = evaluationStack.top(); evaluationStack.pop(); + } + + long double result = std::numeric_limits<long double>::quiet_NaN(); + switch (front.op) { + default: + case Operator::Invalid: + throw std::invalid_argument("Invalid operator!"); + case Operator::And: + result = static_cast<s64>(leftOperand) && static_cast<s64>(rightOperand); + break; + case Operator::Or: + result = static_cast<s64>(leftOperand) && static_cast<s64>(rightOperand); + break; + case Operator::Xor: + result = (static_cast<s64>(leftOperand) ^ static_cast<s64>(rightOperand)) > 0; + break; + case Operator::GreaterThan: + result = leftOperand > rightOperand; + break; + case Operator::LessThan: + result = leftOperand < rightOperand; + break; + case Operator::GreaterThanOrEquals: + result = leftOperand >= rightOperand; + break; + case Operator::LessThanOrEquals: + result = leftOperand <= rightOperand; + break; + case Operator::Equals: + result = leftOperand == rightOperand; + break; + case Operator::NotEquals: + result = leftOperand != rightOperand; + break; + case Operator::Not: + result = !static_cast<s64>(rightOperand); + break; + case Operator::BitwiseOr: + result = static_cast<s64>(leftOperand) | static_cast<s64>(rightOperand); + break; + case Operator::BitwiseXor: + result = static_cast<s64>(leftOperand) ^ static_cast<s64>(rightOperand); + break; + case Operator::BitwiseAnd: + result = static_cast<s64>(leftOperand) & static_cast<s64>(rightOperand); + break; + case Operator::BitwiseNot: + result = ~static_cast<s64>(rightOperand); + break; + case Operator::ShiftLeft: + result = static_cast<s64>(leftOperand) << static_cast<s64>(rightOperand); + break; + case Operator::ShiftRight: + result = static_cast<s64>(leftOperand) >> static_cast<s64>(rightOperand); + break; + case Operator::Addition: + result = leftOperand + rightOperand; + break; + case Operator::Subtraction: + result = leftOperand - rightOperand; + break; + case Operator::Multiplication: + result = leftOperand * rightOperand; + break; + case Operator::Division: + result = leftOperand / rightOperand; + break; + case Operator::Modulus: + result = std::fmod(leftOperand, rightOperand); + break; + case Operator::Exponentiation: + result = std::pow(leftOperand, rightOperand); + break; + case Operator::Combine: + result = (static_cast<u64>(leftOperand) << (64 - __builtin_clzll(static_cast<u64>(rightOperand)))) | static_cast<u64>(rightOperand); + break; + } + + evaluationStack.push(result); + } else if (front.type == TokenType::Variable) { + if (this->m_variables.contains(front.name)) + evaluationStack.push(this->m_variables.at(front.name)); + else + throw std::invalid_argument("Unknown variable!"); + } else if (front.type == TokenType::Function) { + if (!this->m_functions[front.name]) + throw std::invalid_argument("Unknown function called!"); + + auto result = this->m_functions[front.name](front.arguments); + + if (result.has_value()) + evaluationStack.push(result.value()); + } else + throw std::invalid_argument("Parenthesis in postfix expression!"); + + } + + if (evaluationStack.empty()) + return { }; + else if (evaluationStack.size() > 1) + throw std::invalid_argument("Undigested input left!"); + else + return evaluationStack.top(); + } + + + std::optional<long double> MathEvaluator::evaluate(std::string input) { + auto inputQueue = parseInput(input.c_str()); + + std::string resultVariable = "ans"; + + { + std::queue<Token> queueCopy = inputQueue; + if (queueCopy.front().type == TokenType::Variable) { + resultVariable = queueCopy.front().name; + queueCopy.pop(); + if (queueCopy.front().type != TokenType::Operator || queueCopy.front().op != Operator::Assign) + resultVariable = "ans"; + else { + inputQueue.pop(); + inputQueue.pop(); + } + } + } + + auto postfixTokens = toPostfix(inputQueue); + + auto result = evaluate(postfixTokens); + + if (result.has_value()) { + this->setVariable(resultVariable, result.value()); + } + + return result; + } + + void MathEvaluator::setVariable(std::string name, long double value) { + this->m_variables[name] = value; + } + + void MathEvaluator::setFunction(std::string name, std::function<std::optional<long double>(std::vector<long double>)> function, size_t minNumArgs, size_t maxNumArgs) { + this->m_functions[name] = [minNumArgs, maxNumArgs, function](auto args) { + if (args.size() < minNumArgs || args.size() > maxNumArgs) + throw std::invalid_argument("Invalid number of function arguments!"); + + return function(args); + }; + } + + + void MathEvaluator::registerStandardVariables() { + this->setVariable("ans", 0); + } + + void MathEvaluator::registerStandardFunctions() { + this->setFunction("sin", [](auto args){ return std::sin(args[0]); }, 1, 1); + this->setFunction("cos", [](auto args){ return std::cos(args[0]); }, 1, 1); + this->setFunction("tan", [](auto args){ return std::tan(args[0]); }, 1, 1); + this->setFunction("sqrt", [](auto args){ return std::sqrt(args[0]); }, 1, 1); + this->setFunction("ceil", [](auto args){ return std::ceil(args[0]); }, 1, 1); + this->setFunction("floor", [](auto args){ return std::floor(args[0]); }, 1, 1); + this->setFunction("sign", [](auto args){ return (args[0] > 0) ? 1 : (args[0] == 0) ? 0 : -1; }, 1, 1); + this->setFunction("abs", [](auto args){ return std::abs(args[0]); }, 1, 1); + this->setFunction("ln", [](auto args){ return std::log(args[0]); }, 1, 1); + this->setFunction("lb", [](auto args){ return std::log2(args[0]); }, 1, 1); + this->setFunction("log", [](auto args){ return args.size() == 1 ? std::log10(args[0]) : std::log(args[1]) / std::log(args[0]); }, 1, 2); + } + +} + diff --git a/source/patches.cpp b/source/helpers/patches.cpp similarity index 98% rename from source/patches.cpp rename to source/helpers/patches.cpp index 708bc0a1d..258a1f97e 100644 --- a/source/patches.cpp +++ b/source/helpers/patches.cpp @@ -1,11 +1,11 @@ -#include "patches.hpp" +#include "helpers/patches.hpp" #include <concepts> #include <cstring> #include <string_view> #include <type_traits> -#include "utils.hpp" +#include "helpers/utils.hpp" namespace hex { diff --git a/source/utils.cpp b/source/helpers/utils.cpp similarity index 98% rename from source/utils.cpp rename to source/helpers/utils.cpp index 83468946b..7a0460176 100644 --- a/source/utils.cpp +++ b/source/helpers/utils.cpp @@ -1,4 +1,4 @@ -#include "utils.hpp" +#include "helpers/utils.hpp" #include <codecvt> #include <locale> diff --git a/source/lang/parser.cpp b/source/lang/parser.cpp index 16e891975..a3e3562a8 100644 --- a/source/lang/parser.cpp +++ b/source/lang/parser.cpp @@ -1,6 +1,6 @@ #include "lang/parser.hpp" -#include "utils.hpp" +#include "helpers/utils.hpp" #include <optional> diff --git a/source/lang/validator.cpp b/source/lang/validator.cpp index 06e5208eb..5763741af 100644 --- a/source/lang/validator.cpp +++ b/source/lang/validator.cpp @@ -3,7 +3,7 @@ #include <unordered_set> #include <string> -#include "utils.hpp" +#include "helpers/utils.hpp" namespace hex::lang { diff --git a/source/providers/file_provider.cpp b/source/providers/file_provider.cpp index c58709a21..2e5839c9e 100644 --- a/source/providers/file_provider.cpp +++ b/source/providers/file_provider.cpp @@ -6,7 +6,7 @@ #include <sys/stat.h> #include <time.h> -#include "utils.hpp" +#include "helpers/utils.hpp" namespace hex::prv { diff --git a/source/views/view_data_inspector.cpp b/source/views/view_data_inspector.cpp index 0c239b5cc..4f23f8be7 100644 --- a/source/views/view_data_inspector.cpp +++ b/source/views/view_data_inspector.cpp @@ -1,7 +1,7 @@ #include "views/view_data_inspector.hpp" #include "providers/provider.hpp" -#include "utils.hpp" +#include "helpers/utils.hpp" #include <cstring> diff --git a/source/views/view_disassembler.cpp b/source/views/view_disassembler.cpp index 016176d1a..b329091d8 100644 --- a/source/views/view_disassembler.cpp +++ b/source/views/view_disassembler.cpp @@ -1,7 +1,7 @@ #include "views/view_disassembler.hpp" #include "providers/provider.hpp" -#include "utils.hpp" +#include "helpers/utils.hpp" #include <cstring> diff --git a/source/views/view_hashes.cpp b/source/views/view_hashes.cpp index 6ecc25d22..763b09dc5 100644 --- a/source/views/view_hashes.cpp +++ b/source/views/view_hashes.cpp @@ -2,11 +2,11 @@ #include "providers/provider.hpp" -#include "crypto.hpp" +#include "helpers/crypto.hpp" #include <vector> -#include "utils.hpp" +#include "helpers/utils.hpp" namespace hex { diff --git a/source/views/view_hexeditor.cpp b/source/views/view_hexeditor.cpp index 294914b7e..d21559ff4 100644 --- a/source/views/view_hexeditor.cpp +++ b/source/views/view_hexeditor.cpp @@ -5,8 +5,8 @@ #include <GLFW/glfw3.h> -#include "crypto.hpp" -#include "patches.hpp" +#include "helpers/crypto.hpp" +#include "helpers/patches.hpp" #undef __STRICT_ANSI__ #include <cstdio> diff --git a/source/views/view_information.cpp b/source/views/view_information.cpp index 755f8d460..0386b2e76 100644 --- a/source/views/view_information.cpp +++ b/source/views/view_information.cpp @@ -2,7 +2,7 @@ #include "providers/provider.hpp" -#include "utils.hpp" +#include "helpers/utils.hpp" #include <cstring> #include <cmath> diff --git a/source/views/view_pattern.cpp b/source/views/view_pattern.cpp index c4fa0b660..acf73b612 100644 --- a/source/views/view_pattern.cpp +++ b/source/views/view_pattern.cpp @@ -5,7 +5,7 @@ #include "lang/lexer.hpp" #include "lang/validator.hpp" #include "lang/evaluator.hpp" -#include "utils.hpp" +#include "helpers/utils.hpp" #include <magic.h> diff --git a/source/views/view_strings.cpp b/source/views/view_strings.cpp index 7dbddc505..0e09cfc09 100644 --- a/source/views/view_strings.cpp +++ b/source/views/view_strings.cpp @@ -1,7 +1,7 @@ #include "views/view_strings.hpp" #include "providers/provider.hpp" -#include "utils.hpp" +#include "helpers/utils.hpp" #include <cstring> diff --git a/source/views/view_tools.cpp b/source/views/view_tools.cpp index 9db06836d..150d0a00b 100644 --- a/source/views/view_tools.cpp +++ b/source/views/view_tools.cpp @@ -2,8 +2,9 @@ #include <cstring> #include <regex> +#include <optional> -#include "utils.hpp" +#include "helpers/utils.hpp" #include <llvm/Demangle/Demangle.h> @@ -11,7 +12,6 @@ namespace hex { ViewTools::ViewTools() : View("Tools") { this->m_mangledBuffer = new char[0xF'FFFF]; - std::memset(this->m_mangledBuffer, 0x00, 0xF'FFFF); this->m_regexInput = new char[0xF'FFFF]; @@ -20,6 +20,19 @@ namespace hex { std::memset(this->m_regexInput, 0x00, 0xF'FFFF); std::memset(this->m_regexPattern, 0x00, 0xF'FFFF); std::memset(this->m_replacePattern, 0x00, 0xF'FFFF); + + + this->m_mathInput = new char[0xFFFF]; + std::memset(this->m_mathInput, 0x00, 0xFFFF); + this->m_mathEvaluator.registerStandardVariables(); + this->m_mathEvaluator.registerStandardFunctions(); + this->m_mathEvaluator.setFunction("clear", [this](auto args) -> std::optional<long double> { + this->m_mathHistory.clear(); + this->m_lastMathError.clear(); + std::memset(this->m_mathInput, 0x00, 0xFFFF); + + return { }; + }, 0, 0); } ViewTools::~ViewTools() { @@ -28,6 +41,8 @@ namespace hex { delete[] this->m_regexInput; delete[] this->m_regexPattern; delete[] this->m_replacePattern; + + delete[] this->m_mathInput; } void ViewTools::drawDemangler() { @@ -124,12 +139,131 @@ namespace hex { } } + void ViewTools::drawMathEvaluator() { + if (ImGui::CollapsingHeader("Calculator")) { + if (ImGui::InputText("Input", this->m_mathInput, 0xFFFF, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) { + ImGui::SetKeyboardFocusHere(); + std::optional<long double> result; + + try { + result = this->m_mathEvaluator.evaluate(this->m_mathInput); + } catch (std::invalid_argument &e) { + this->m_lastMathError = e.what(); + } + + if (result.has_value()) { + this->m_mathHistory.push_back(result.value()); + std::memset(this->m_mathInput, 0x00, 0xFFFF); + this->m_lastMathError.clear(); + } + + } + + if (!this->m_lastMathError.empty()) + ImGui::TextColored(ImColor(0xA00040FF), "Last Error: %s", this->m_lastMathError.c_str()); + else + ImGui::NewLine(); + + enum class MathDisplayType { Standard, Scientific, Programmer } mathDisplayType; + if (ImGui::BeginTabBar("##mathFormatTabBar")) { + if (ImGui::BeginTabItem("Standard")) { + mathDisplayType = MathDisplayType::Standard; + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Scientific")) { + mathDisplayType = MathDisplayType::Scientific; + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Programmer")) { + mathDisplayType = MathDisplayType::Programmer; + ImGui::EndTabItem(); + } + + ImGui::EndTabBar(); + } + + if (ImGui::BeginTable("##mathWrapper", 2)) { + ImGui::TableSetupColumn("##results"); + ImGui::TableSetupColumn("##variables", ImGuiTableColumnFlags_WidthStretch, 0.7); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (ImGui::BeginTable("##mathHistory", 1, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(0, 400))) { + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableSetupColumn("History"); + + ImGuiListClipper clipper; + clipper.Begin(this->m_mathHistory.size()); + + ImGui::TableHeadersRow(); + while (clipper.Step()) { + for (u64 i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + switch (mathDisplayType) { + case MathDisplayType::Standard: + ImGui::Text("%.3Lf", this->m_mathHistory[(this->m_mathHistory.size() - 1) - i]); + break; + case MathDisplayType::Scientific: + ImGui::Text("%.6Le", this->m_mathHistory[(this->m_mathHistory.size() - 1) - i]); + break; + case MathDisplayType::Programmer: + ImGui::Text("0x%llX (%llu)", + u64(this->m_mathHistory[(this->m_mathHistory.size() - 1) - i]), + u64(this->m_mathHistory[(this->m_mathHistory.size() - 1) - i])); + break; + } + } + } + + clipper.End(); + + ImGui::EndTable(); + } + + ImGui::TableNextColumn(); + if (ImGui::BeginTable("##mathVariables", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(0, 400))) { + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableSetupColumn("Name"); + ImGui::TableSetupColumn("Value"); + + ImGui::TableHeadersRow(); + for (const auto &[name, value] : this->m_mathEvaluator.getVariables()) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::TextUnformatted(name.c_str()); + + ImGui::TableNextColumn(); + switch (mathDisplayType) { + case MathDisplayType::Standard: + ImGui::Text("%.3Lf", value); + break; + case MathDisplayType::Scientific: + ImGui::Text("%.6Le", value); + break; + case MathDisplayType::Programmer: + ImGui::Text("0x%llX (%llu)", u64(value), u64(value)); + break; + } + } + + ImGui::EndTable(); + } + + ImGui::EndTable(); + } + + } + } + void ViewTools::createView() { if (ImGui::Begin("Tools", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) { this->drawDemangler(); this->drawASCIITable(); this->drawRegexReplacer(); + this->drawMathEvaluator(); this->drawColorPicker(); }