diff --git a/plugins/builtin/include/content/views/view_pattern_editor.hpp b/plugins/builtin/include/content/views/view_pattern_editor.hpp index 099a2ccb5..2bb9ffb59 100644 --- a/plugins/builtin/include/content/views/view_pattern_editor.hpp +++ b/plugins/builtin/include/content/views/view_pattern_editor.hpp @@ -37,6 +37,21 @@ namespace hex::plugin::builtin { TextEditor m_textEditor; std::vector> m_console; + enum class EnvVarType { + Integer, + Float, + String, + Bool + }; + + struct EnvVar { + std::string name; + pl::Token::Literal value; + EnvVarType type; + }; + + std::vector m_envVarEntries; + void loadPatternFile(const std::string &path); void clearPatternData(); void parsePattern(char *buffer); diff --git a/plugins/builtin/source/content/pl_builtin_functions.cpp b/plugins/builtin/source/content/pl_builtin_functions.cpp index 8a6a04d74..b2ce6eef4 100644 --- a/plugins/builtin/source/content/pl_builtin_functions.cpp +++ b/plugins/builtin/source/content/pl_builtin_functions.cpp @@ -51,7 +51,7 @@ namespace hex::plugin::builtin { /* assert(condition, message) */ ContentRegistry::PatternLanguageFunctions::add(nsStd, "assert", 2, [](Evaluator *ctx, auto params) -> std::optional { auto condition = Token::literalToBoolean(params[0]); - auto message = std::get(params[1]); + auto message = Token::literalToString(params[1], false); if (!condition) LogConsole::abortEvaluation(hex::format("assertion failed \"{0}\"", message)); @@ -62,7 +62,7 @@ namespace hex::plugin::builtin { /* assert_warn(condition, message) */ ContentRegistry::PatternLanguageFunctions::add(nsStd, "assert_warn", 2, [](auto *ctx, auto params) -> std::optional { auto condition = Token::literalToBoolean(params[0]); - auto message = std::get(params[1]); + auto message = Token::literalToString(params[1], false); if (!condition) ctx->getConsole().log(LogConsole::Level::Warning, hex::format("assertion failed \"{0}\"", message)); @@ -82,6 +82,13 @@ namespace hex::plugin::builtin { return format(ctx, params); }); + /* env(name) */ + ContentRegistry::PatternLanguageFunctions::add(nsStd, "env", 1, [](Evaluator *ctx, auto params) -> std::optional { + auto name = Token::literalToString(params[0], false); + + return ctx->getEnvVariable(name); + }); + } ContentRegistry::PatternLanguageFunctions::Namespace nsStdMem = { "std", "mem" }; diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index 8360dc787..6635d51a0 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -88,6 +88,8 @@ namespace hex::plugin::builtin { this->m_textEditor.SetLanguageDefinition(PatternLanguage()); this->m_textEditor.SetShowWhitespaces(false); + this->m_envVarEntries = { { "", s128(0), EnvVarType::Integer } }; + EventManager::subscribe(this, [this]() { ProjectFile::setPattern(this->m_textEditor.GetText()); }); @@ -284,6 +286,71 @@ namespace hex::plugin::builtin { this->m_patternLanguageRuntime->getMaximumPatternCount() ).c_str() ); + + ImGui::SameLine(); + + if (ImGui::IconButton(ICON_VS_GLOBE, ImGui::GetStyleColorVec4(ImGuiCol_Text))) + ImGui::OpenPopup("env_vars"); + + if (ImGui::BeginPopup("env_vars")) { + ImGui::TextUnformatted("Environment Variables"); + ImGui::SameLine(); + if (ImGui::IconButton(ICON_VS_ADD, ImGui::GetStyleColorVec4(ImGuiCol_Text))) this->m_envVarEntries.push_back({ "", s128(0), EnvVarType::Integer }); + ImGui::Separator(); + + int index = 0; + for (auto &[name, value, type] : this->m_envVarEntries) { + ImGui::PushID(index++); + + ImGui::PushItemWidth(ImGui::GetTextLineHeightWithSpacing() * 2); + constexpr const char* Types[] = { "I", "F", "S", "B" }; + if (ImGui::BeginCombo("", Types[static_cast(type)])) { + for (auto i = 0; i < IM_ARRAYSIZE(Types); i++) { + if (ImGui::Selectable(Types[i])) + type = static_cast(i); + } + + ImGui::EndCombo(); + } + ImGui::PopItemWidth(); + + ImGui::SameLine(); + + ImGui::InputText("###name", name.data(), name.capacity(), ImGuiInputTextFlags_CallbackResize, ImGui::UpdateStringSizeCallback, &name); + ImGui::SameLine(); + + switch (type) { + case EnvVarType::Integer: { + s64 displayValue = hex::get_or(value, 0); + ImGui::InputScalar("###value", ImGuiDataType_S64, &displayValue); + value = s128(displayValue); + break; + } + case EnvVarType::Float: { + auto displayValue = hex::get_or(value, 0.0); + ImGui::InputDouble("###value", &displayValue); + value = displayValue; + break; + } + case EnvVarType::Bool: { + auto displayValue = hex::get_or(value, false); + ImGui::Checkbox("###value", &displayValue); + value = displayValue; + break; + } + case EnvVarType::String: { + auto displayValue = hex::get_or(value, ""); + ImGui::InputText("###value", displayValue.data(), displayValue.capacity(), ImGuiInputTextFlags_CallbackResize, ImGui::UpdateStringSizeCallback, &displayValue); + value = displayValue; + break; + } + } + + ImGui::PopID(); + } + + ImGui::EndPopup(); + } } if (this->m_textEditor.IsTextChanged()) { @@ -418,7 +485,11 @@ namespace hex::plugin::builtin { EventManager::post(); std::thread([this, buffer = std::string(buffer)] { - auto result = this->m_patternLanguageRuntime->executeString(ImHexApi::Provider::get(), buffer); + std::map envVars; + for (const auto &[name, value, type] : this->m_envVarEntries) + envVars.insert({ name, value }); + + auto result = this->m_patternLanguageRuntime->executeString(ImHexApi::Provider::get(), buffer, envVars); auto error = this->m_patternLanguageRuntime->getError(); if (error.has_value()) { diff --git a/plugins/libimhex/include/hex/helpers/utils.hpp b/plugins/libimhex/include/hex/helpers/utils.hpp index d89e19d1c..3131b1848 100644 --- a/plugins/libimhex/include/hex/helpers/utils.hpp +++ b/plugins/libimhex/include/hex/helpers/utils.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -227,6 +228,15 @@ namespace hex { return iter != a.end(); } + template + T get_or(const std::variant &variant, T alt) { + const T *value = std::get_if(&variant); + if (value == nullptr) + return alt; + else + return *value; + } + namespace scope_guard { #define SCOPE_GUARD ::hex::scope_guard::ScopeGuardOnExit() + [&]() diff --git a/plugins/libimhex/include/hex/pattern_language/evaluator.hpp b/plugins/libimhex/include/hex/pattern_language/evaluator.hpp index 8d5e2c8d8..3039200bc 100644 --- a/plugins/libimhex/include/hex/pattern_language/evaluator.hpp +++ b/plugins/libimhex/include/hex/pattern_language/evaluator.hpp @@ -149,6 +149,18 @@ namespace hex::pl { LogConsole::abortEvaluation("evaluation aborted by user"); } + [[nodiscard]] + std::optional getEnvVariable(const std::string &name) const { + if (this->m_envVariables.contains(name)) + return this->m_envVariables.at(name); + else + return std::nullopt; + } + + void setEnvVariable(const std::string &name, const Token::Literal &value) { + this->m_envVariables[name] = value; + } + private: void patternCreated(); @@ -173,6 +185,7 @@ namespace hex::pl { std::map m_customFunctions; std::vector m_customFunctionDefinitions; std::vector m_stack; + std::map m_envVariables; friend class PatternCreationLimiter; }; diff --git a/plugins/libimhex/include/hex/pattern_language/pattern_language.hpp b/plugins/libimhex/include/hex/pattern_language/pattern_language.hpp index 167dab106..1795fc3e7 100644 --- a/plugins/libimhex/include/hex/pattern_language/pattern_language.hpp +++ b/plugins/libimhex/include/hex/pattern_language/pattern_language.hpp @@ -3,12 +3,14 @@ #include #include +#include #include #include #include #include #include +#include namespace hex::prv { class Provider; } @@ -28,8 +30,8 @@ namespace hex::pl { PatternLanguage(); ~PatternLanguage(); - std::optional> executeString(prv::Provider *provider, const std::string &string); - std::optional> executeFile(prv::Provider *provider, const std::string &path); + std::optional> executeString(prv::Provider *provider, const std::string &string, const std::map &envVars = { }); + std::optional> executeFile(prv::Provider *provider, const std::string &path, const std::map &envVars = { }); void abort(); diff --git a/plugins/libimhex/source/pattern_language/evaluator.cpp b/plugins/libimhex/source/pattern_language/evaluator.cpp index 697ec4d78..716678f01 100644 --- a/plugins/libimhex/source/pattern_language/evaluator.cpp +++ b/plugins/libimhex/source/pattern_language/evaluator.cpp @@ -123,6 +123,10 @@ namespace hex::pl { this->m_scopes.clear(); this->m_aborted = false; + ON_SCOPE_EXIT { + this->m_envVariables.clear(); + }; + this->dataOffset() = 0x00; this->m_currPatternCount = 0; diff --git a/plugins/libimhex/source/pattern_language/pattern_language.cpp b/plugins/libimhex/source/pattern_language/pattern_language.cpp index 775bd9f77..7b367d052 100644 --- a/plugins/libimhex/source/pattern_language/pattern_language.cpp +++ b/plugins/libimhex/source/pattern_language/pattern_language.cpp @@ -95,7 +95,7 @@ namespace hex::pl { } - std::optional> PatternLanguage::executeString(prv::Provider *provider, const std::string &string) { + std::optional> PatternLanguage::executeString(prv::Provider *provider, const std::string &string, const std::map &envVars) { this->m_currError.reset(); this->m_evaluator->getConsole().clear(); this->m_evaluator->setProvider(provider); @@ -105,6 +105,9 @@ namespace hex::pl { this->m_evaluator->setPatternLimit(0x2000); this->m_evaluator->setLoopLimit(0x1000); + for (const auto &[name, value] : envVars) + this->m_evaluator->setEnvVariable(name, value); + for (auto &node : this->m_currAST) delete node; this->m_currAST.clear(); @@ -138,10 +141,10 @@ namespace hex::pl { return patterns; } - std::optional> PatternLanguage::executeFile(prv::Provider *provider, const std::string &path) { + std::optional> PatternLanguage::executeFile(prv::Provider *provider, const std::string &path, const std::map &envVars) { File file(path, File::Mode::Read); - return this->executeString(provider, file.readString()); + return this->executeString(provider, file.readString(), envVars); } void PatternLanguage::abort() {