#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace hex::plugin::builtin { class NodeNullptr : public dp::Node { public: NodeNullptr() : Node("hex.builtin.nodes.constants.nullptr.header", { dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "") }) { } void process() override { this->setBufferOnOutput(0, {}); } }; class NodeBuffer : public dp::Node { public: NodeBuffer() : Node("hex.builtin.nodes.constants.buffer.header", { dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "") }) { } void drawNode() override { constexpr static int StepSize = 1, FastStepSize = 10; ImGui::PushItemWidth(100); ImGui::InputScalar("hex.builtin.nodes.constants.buffer.size"_lang, ImGuiDataType_U32, &this->m_size, &StepSize, &FastStepSize); ImGui::PopItemWidth(); } void process() override { if (this->m_buffer.size() != this->m_size) this->m_buffer.resize(this->m_size, 0x00); this->setBufferOnOutput(0, this->m_buffer); } void store(nlohmann::json &j) override { j = nlohmann::json::object(); j["size"] = this->m_size; j["data"] = this->m_buffer; } void load(nlohmann::json &j) override { this->m_size = j["size"]; this->m_buffer = j["data"].get>(); } private: u32 m_size = 1; std::vector m_buffer; }; class NodeString : public dp::Node { public: NodeString() : Node("hex.builtin.nodes.constants.string.header", { dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "") }) { } void drawNode() override { ImGui::PushItemWidth(100); ImGui::InputTextIcon("##string", ICON_VS_SYMBOL_KEY, this->m_value); ImGui::PopItemWidth(); } void process() override { this->setBufferOnOutput(0, { this->m_value.begin(), this->m_value.end() }); } void store(nlohmann::json &j) override { j = nlohmann::json::object(); j["data"] = this->m_value; } void load(nlohmann::json &j) override { this->m_value = j["data"].get(); } private: std::string m_value; }; class NodeInteger : public dp::Node { public: NodeInteger() : Node("hex.builtin.nodes.constants.int.header", { dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "") }) { } void drawNode() override { ImGui::PushItemWidth(100); ImGui::InputHexadecimal("##integer_value", &this->m_value); ImGui::PopItemWidth(); } void process() override { this->setIntegerOnOutput(0, this->m_value); } void store(nlohmann::json &j) override { j = nlohmann::json::object(); j["data"] = this->m_value; } void load(nlohmann::json &j) override { this->m_value = j["data"]; } private: u64 m_value = 0; }; class NodeFloat : public dp::Node { public: NodeFloat() : Node("hex.builtin.nodes.constants.float.header", { dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Float, "") }) { } void drawNode() override { ImGui::PushItemWidth(100); ImGui::InputScalar("##floatValue", ImGuiDataType_Float, &this->m_value, nullptr, nullptr, "%f", ImGuiInputTextFlags_CharsDecimal); ImGui::PopItemWidth(); } void process() override { this->setFloatOnOutput(0, this->m_value); } void store(nlohmann::json &j) override { j = nlohmann::json::object(); j["data"] = this->m_value; } void load(nlohmann::json &j) override { this->m_value = j["data"]; } private: float m_value = 0; }; class NodeRGBA8 : public dp::Node { public: NodeRGBA8() : Node("hex.builtin.nodes.constants.rgba8.header", { dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "hex.builtin.nodes.constants.rgba8.output.r"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "hex.builtin.nodes.constants.rgba8.output.g"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "hex.builtin.nodes.constants.rgba8.output.b"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "hex.builtin.nodes.constants.rgba8.output.a") }) { } void drawNode() override { ImGui::PushItemWidth(200); ImGui::ColorPicker4("##colorPicker", &this->m_color.Value.x, ImGuiColorEditFlags_AlphaBar); ImGui::PopItemWidth(); } void process() override { this->setBufferOnOutput(0, hex::toBytes(this->m_color.Value.x * 0xFF)); this->setBufferOnOutput(1, hex::toBytes(this->m_color.Value.y * 0xFF)); this->setBufferOnOutput(2, hex::toBytes(this->m_color.Value.z * 0xFF)); this->setBufferOnOutput(3, hex::toBytes(this->m_color.Value.w * 0xFF)); } void store(nlohmann::json &j) override { j = nlohmann::json::object(); j["data"] = nlohmann::json::object(); j["data"]["r"] = this->m_color.Value.x; j["data"]["g"] = this->m_color.Value.y; j["data"]["b"] = this->m_color.Value.z; j["data"]["a"] = this->m_color.Value.w; } void load(nlohmann::json &j) override { this->m_color = ImVec4(j["data"]["r"], j["data"]["g"], j["data"]["b"], j["data"]["a"]); } private: ImColor m_color; }; class NodeComment : public dp::Node { public: NodeComment() : Node("hex.builtin.nodes.constants.comment.header", {}) { } void drawNode() override { ImGui::InputTextMultiline("##string", this->m_comment, scaled(ImVec2(150, 100))); } void process() override { } void store(nlohmann::json &j) override { j = nlohmann::json::object(); j["comment"] = this->m_comment; } void load(nlohmann::json &j) override { this->m_comment = j["comment"].get(); } private: std::string m_comment; }; class NodeDisplayInteger : public dp::Node { public: NodeDisplayInteger() : Node("hex.builtin.nodes.display.int.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input") }) { } void drawNode() override { ImGui::PushItemWidth(150); if (this->m_value.has_value()) ImGui::TextFormatted("0x{0:X}", this->m_value.value()); else ImGui::TextUnformatted("???"); ImGui::PopItemWidth(); } void process() override { this->m_value.reset(); auto input = this->getIntegerOnInput(0); this->m_value = input; } private: std::optional m_value; }; class NodeDisplayFloat : public dp::Node { public: NodeDisplayFloat() : Node("hex.builtin.nodes.display.float.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Float, "hex.builtin.nodes.common.input") }) { } void drawNode() override { ImGui::PushItemWidth(150); if (this->m_value.has_value()) ImGui::TextFormatted("{0}", this->m_value.value()); else ImGui::TextUnformatted("???"); ImGui::PopItemWidth(); } void process() override { this->m_value.reset(); auto input = this->getFloatOnInput(0); this->m_value = input; } private: std::optional m_value; }; class NodeBitwiseNOT : public dp::Node { public: NodeBitwiseNOT() : Node("hex.builtin.nodes.bitwise.not.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.output") }) { } void process() override { auto input = this->getBufferOnInput(0); std::vector output = input; for (auto &byte : output) byte = ~byte; this->setBufferOnOutput(1, output); } }; class NodeBitwiseAND : public dp::Node { public: NodeBitwiseAND() : Node("hex.builtin.nodes.bitwise.and.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input.a"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input.b"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.output") }) { } void process() override { auto inputA = this->getBufferOnInput(0); auto inputB = this->getBufferOnInput(1); std::vector output(std::min(inputA.size(), inputB.size()), 0x00); for (u32 i = 0; i < output.size(); i++) output[i] = inputA[i] & inputB[i]; this->setBufferOnOutput(2, output); } }; class NodeBitwiseOR : public dp::Node { public: NodeBitwiseOR() : Node("hex.builtin.nodes.bitwise.or.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input.a"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input.b"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.output") }) { } void process() override { auto inputA = this->getBufferOnInput(0); auto inputB = this->getBufferOnInput(1); std::vector output(std::min(inputA.size(), inputB.size()), 0x00); for (u32 i = 0; i < output.size(); i++) output[i] = inputA[i] | inputB[i]; this->setBufferOnOutput(2, output); } }; class NodeBitwiseXOR : public dp::Node { public: NodeBitwiseXOR() : Node("hex.builtin.nodes.bitwise.xor.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input.a"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input.b"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.output") }) { } void process() override { auto inputA = this->getBufferOnInput(0); auto inputB = this->getBufferOnInput(1); std::vector output(std::min(inputA.size(), inputB.size()), 0x00); for (u32 i = 0; i < output.size(); i++) output[i] = inputA[i] ^ inputB[i]; this->setBufferOnOutput(2, output); } }; class NodeReadData : public dp::Node { public: NodeReadData() : Node("hex.builtin.nodes.data_access.read.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.data_access.read.address"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.data_access.read.size"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "hex.builtin.nodes.data_access.read.data") }) { } void process() override { auto address = this->getIntegerOnInput(0); auto size = this->getIntegerOnInput(1); std::vector data; data.resize(size); ImHexApi::Provider::get()->readRaw(address, data.data(), size); this->setBufferOnOutput(2, data); } }; class NodeWriteData : public dp::Node { public: NodeWriteData() : Node("hex.builtin.nodes.data_access.write.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.data_access.write.address"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.data_access.write.data") }) { } void process() override { auto address = this->getIntegerOnInput(0); auto data = this->getBufferOnInput(1); this->setOverlayData(address, data); } }; class NodeDataSize : public dp::Node { public: NodeDataSize() : Node("hex.builtin.nodes.data_access.size.header", { dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "hex.builtin.nodes.data_access.size.size") }) { } void process() override { auto size = ImHexApi::Provider::get()->getActualSize(); this->setIntegerOnOutput(0, size); } }; class NodeDataSelection : public dp::Node { public: NodeDataSelection() : Node("hex.builtin.nodes.data_access.selection.header", { dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "hex.builtin.nodes.data_access.selection.address"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "hex.builtin.nodes.data_access.selection.size") }) { EventManager::subscribe(this, [this](const Region ®ion) { this->m_address = region.address; this->m_size = region.size; }); } ~NodeDataSelection() override { EventManager::unsubscribe(this); } void process() override { this->setIntegerOnOutput(0, this->m_address); this->setIntegerOnOutput(1, this->m_size); } private: u64 m_address = 0; size_t m_size = 0; }; class NodeCastIntegerToBuffer : public dp::Node { public: NodeCastIntegerToBuffer() : Node("hex.builtin.nodes.casting.int_to_buffer.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.output") }) { } void process() override { auto input = this->getIntegerOnInput(0); std::vector output(sizeof(input), 0x00); std::memcpy(output.data(), &input, sizeof(input)); this->setBufferOnOutput(1, output); } }; class NodeCastBufferToInteger : public dp::Node { public: NodeCastBufferToInteger() : Node("hex.builtin.nodes.casting.buffer_to_int.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.output") }) { } void process() override { auto input = this->getBufferOnInput(0); i64 output = 0; if (input.empty() || input.size() > sizeof(output)) throwNodeError("Buffer is empty or bigger than 64 bits"); std::memcpy(&output, input.data(), input.size()); this->setIntegerOnOutput(1, output); } }; class NodeCastFloatToBuffer : public dp::Node { public: NodeCastFloatToBuffer() : Node("hex.builtin.nodes.casting.float_to_buffer.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Float, "hex.builtin.nodes.common.input"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.output") }) { } void process() override { auto input = this->getFloatOnInput(0); std::vector output(sizeof(input), 0x00); std::memcpy(output.data(), &input, sizeof(input)); this->setBufferOnOutput(1, output); } }; class NodeCastBufferToFloat : public dp::Node { public: NodeCastBufferToFloat() : Node("hex.builtin.nodes.casting.buffer_to_float.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Float, "hex.builtin.nodes.common.output") }) { } void process() override { auto input = this->getBufferOnInput(0); float output = 0; if (input.empty() || input.size() != sizeof(output)) throwNodeError("Buffer is empty or not the right size to fit a float"); std::memcpy(&output, input.data(), input.size()); this->setFloatOnOutput(1, output); } }; class NodeArithmeticAdd : public dp::Node { public: NodeArithmeticAdd() : Node("hex.builtin.nodes.arithmetic.add.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input.a"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input.b"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.output") }) { } void process() override { auto inputA = this->getIntegerOnInput(0); auto inputB = this->getIntegerOnInput(1); auto output = inputA + inputB; this->setIntegerOnOutput(2, output); } }; class NodeArithmeticSubtract : public dp::Node { public: NodeArithmeticSubtract() : Node("hex.builtin.nodes.arithmetic.sub.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input.a"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input.b"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.output") }) { } void process() override { auto inputA = this->getIntegerOnInput(0); auto inputB = this->getIntegerOnInput(1); auto output = inputA - inputB; this->setIntegerOnOutput(2, output); } }; class NodeArithmeticMultiply : public dp::Node { public: NodeArithmeticMultiply() : Node("hex.builtin.nodes.arithmetic.mul.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input.a"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input.b"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.output") }) { } void process() override { auto inputA = this->getIntegerOnInput(0); auto inputB = this->getIntegerOnInput(1); auto output = inputA * inputB; this->setIntegerOnOutput(2, output); } }; class NodeArithmeticDivide : public dp::Node { public: NodeArithmeticDivide() : Node("hex.builtin.nodes.arithmetic.div.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input.a"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input.b"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.output") }) { } void process() override { auto inputA = this->getIntegerOnInput(0); auto inputB = this->getIntegerOnInput(1); if (inputB == 0) throwNodeError("Division by zero"); auto output = inputA / inputB; this->setIntegerOnOutput(2, output); } }; class NodeArithmeticModulus : public dp::Node { public: NodeArithmeticModulus() : Node("hex.builtin.nodes.arithmetic.mod.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input.a"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input.b"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.output") }) { } void process() override { auto inputA = this->getIntegerOnInput(0); auto inputB = this->getIntegerOnInput(1); if (inputB == 0) throwNodeError("Division by zero"); auto output = inputA % inputB; this->setIntegerOnOutput(2, output); } }; class NodeBufferCombine : public dp::Node { public: NodeBufferCombine() : Node("hex.builtin.nodes.buffer.combine.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input.a"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input.b"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.output") }) { } void process() override { auto inputA = this->getBufferOnInput(0); auto inputB = this->getBufferOnInput(1); auto &output = inputA; std::copy(inputB.begin(), inputB.end(), std::back_inserter(output)); this->setBufferOnOutput(2, output); } }; class NodeBufferSlice : public dp::Node { public: NodeBufferSlice() : Node("hex.builtin.nodes.buffer.slice.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.buffer.slice.input.buffer"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.buffer.slice.input.from"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.buffer.slice.input.to"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.output") }) { } void process() override { auto input = this->getBufferOnInput(0); auto from = this->getIntegerOnInput(1); auto to = this->getIntegerOnInput(2); if (from < 0 || static_cast(from) >= input.size()) throwNodeError("'from' input out of range"); if (to < 0 || static_cast(from) >= input.size()) throwNodeError("'to' input out of range"); if (to <= from) throwNodeError("'to' input needs to be greater than 'from' input"); this->setBufferOnOutput(3, std::vector(input.begin() + from, input.begin() + to)); } }; class NodeBufferRepeat : public dp::Node { public: NodeBufferRepeat() : Node("hex.builtin.nodes.buffer.repeat.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.buffer.repeat.input.buffer"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.buffer.repeat.input.count"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.output") }) { } void process() override { auto buffer = this->getBufferOnInput(0); auto count = this->getIntegerOnInput(1); std::vector output; output.resize(buffer.size() * count); for (u32 i = 0; i < count; i++) std::copy(buffer.begin(), buffer.end(), output.begin() + buffer.size() * i); this->setBufferOnOutput(2, output); } }; class NodeIf : public dp::Node { public: NodeIf() : Node("hex.builtin.nodes.control_flow.if.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.control_flow.if.condition"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.control_flow.if.true"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.control_flow.if.false"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.output") }) { } void process() override { auto cond = this->getIntegerOnInput(0); auto trueData = this->getBufferOnInput(1); auto falseData = this->getBufferOnInput(2); if (cond != 0) this->setBufferOnOutput(3, trueData); else this->setBufferOnOutput(3, falseData); } }; class NodeEquals : public dp::Node { public: NodeEquals() : Node("hex.builtin.nodes.control_flow.equals.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input.a"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input.b"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.output") }) { } void process() override { auto inputA = this->getIntegerOnInput(0); auto inputB = this->getIntegerOnInput(1); this->setIntegerOnOutput(2, inputA == inputB); } }; class NodeNot : public dp::Node { public: NodeNot() : Node("hex.builtin.nodes.control_flow.not.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.output") }) { } void process() override { auto input = this->getIntegerOnInput(0); this->setIntegerOnOutput(1, !input); } }; class NodeGreaterThan : public dp::Node { public: NodeGreaterThan() : Node("hex.builtin.nodes.control_flow.gt.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input.a"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input.b"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.output") }) { } void process() override { auto inputA = this->getIntegerOnInput(0); auto inputB = this->getIntegerOnInput(1); this->setIntegerOnOutput(2, inputA > inputB); } }; class NodeLessThan : public dp::Node { public: NodeLessThan() : Node("hex.builtin.nodes.control_flow.lt.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input.a"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input.b"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.output") }) { } void process() override { auto inputA = this->getIntegerOnInput(0); auto inputB = this->getIntegerOnInput(1); this->setIntegerOnOutput(2, inputA < inputB); } }; class NodeBoolAND : public dp::Node { public: NodeBoolAND() : Node("hex.builtin.nodes.control_flow.and.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input.a"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input.b"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.output") }) { } void process() override { auto inputA = this->getIntegerOnInput(0); auto inputB = this->getIntegerOnInput(1); this->setIntegerOnOutput(2, inputA && inputB); } }; class NodeBoolOR : public dp::Node { public: NodeBoolOR() : Node("hex.builtin.nodes.control_flow.or.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input.a"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.input.b"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.output") }) { } void process() override { auto inputA = this->getIntegerOnInput(0); auto inputB = this->getIntegerOnInput(1); this->setIntegerOnOutput(2, inputA || inputB); } }; class NodeCryptoAESDecrypt : public dp::Node { public: NodeCryptoAESDecrypt() : Node("hex.builtin.nodes.crypto.aes.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.crypto.aes.key"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.crypto.aes.iv"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.crypto.aes.nonce"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.output") }) { } void drawNode() override { ImGui::PushItemWidth(100); ImGui::Combo("hex.builtin.nodes.crypto.aes.mode"_lang, &this->m_mode, "ECB\0CBC\0CFB128\0CTR\0GCM\0CCM\0OFB\0"); ImGui::Combo("hex.builtin.nodes.crypto.aes.key_length"_lang, &this->m_keyLength, "128 Bits\000192 Bits\000256 Bits\000"); ImGui::PopItemWidth(); } void process() override { auto key = this->getBufferOnInput(0); auto iv = this->getBufferOnInput(1); auto nonce = this->getBufferOnInput(2); auto input = this->getBufferOnInput(3); if (key.empty()) throwNodeError("Key cannot be empty"); if (input.empty()) throwNodeError("Input cannot be empty"); std::array ivData = { 0 }, nonceData = { 0 }; std::copy(iv.begin(), iv.end(), ivData.begin()); std::copy(nonce.begin(), nonce.end(), nonceData.begin()); auto output = crypt::aesDecrypt(static_cast(this->m_mode), static_cast(this->m_keyLength), key, nonceData, ivData, input); this->setBufferOnOutput(4, output); } void store(nlohmann::json &j) override { j = nlohmann::json::object(); j["data"] = nlohmann::json::object(); j["data"]["mode"] = this->m_mode; j["data"]["key_length"] = this->m_keyLength; } void load(nlohmann::json &j) override { this->m_mode = j["data"]["mode"]; this->m_keyLength = j["data"]["key_length"]; } private: int m_mode = 0; int m_keyLength = 0; }; class NodeDecodingBase64 : public dp::Node { public: NodeDecodingBase64() : Node("hex.builtin.nodes.decoding.base64.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.output") }) { } void process() override { auto input = this->getBufferOnInput(0); auto output = crypt::decode64(input); this->setBufferOnOutput(1, output); } }; class NodeDecodingHex : public dp::Node { public: NodeDecodingHex() : Node("hex.builtin.nodes.decoding.hex.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input"), dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.output") }) { } void process() override { auto input = this->getBufferOnInput(0); if (input.size() % 2 != 0) throwNodeError("Can't decode odd number of hex characters"); std::vector output; for (u32 i = 0; i < input.size(); i += 2) { char c1 = static_cast(std::tolower(input[i])); char c2 = static_cast(std::tolower(input[i + 1])); if (!std::isxdigit(c1) || !isxdigit(c2)) throwNodeError("Can't decode non-hexadecimal character"); u8 value; if (std::isdigit(c1)) value = (c1 - '0') << 4; else value = ((c1 - 'a') + 0x0A) << 4; if (std::isdigit(c2)) value |= c2 - '0'; else value |= (c2 - 'a') + 0x0A; output.push_back(value); } this->setBufferOnOutput(1, output); } }; class NodeVisualizerDigram : public dp::Node { public: NodeVisualizerDigram() : Node("hex.builtin.nodes.visualizer.digram.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input") }) { } void drawNode() override { drawDigram(scaled({ 200, 200 })); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); drawDigram(scaled({ 600, 600 })); ImGui::EndTooltip(); } } void drawDigram(const ImVec2 &viewSize) { ImGui::PushStyleColor(ImGuiCol_ChildBg, ImU32(ImColor(0, 0, 0))); if (ImGui::BeginChild("##visualizer", viewSize, true)) { auto drawList = ImGui::GetWindowDrawList(); float xStep = (viewSize.x * 0.95F) / 0xFF; float yStep = (viewSize.y * 0.95F) / 0xFF; for (size_t i = 0; i < (this->m_buffer.empty() ? 0 : this->m_buffer.size() - 1); i++) { const auto &[x, y] = std::pair { this->m_buffer[i] * xStep, this->m_buffer[i + 1] * yStep }; auto color = ImLerp(ImColor(0xFF, 0x6D, 0x01).Value, ImColor(0x01, 0x93, 0xFF).Value, float(i) / this->m_buffer.size()); color.w = this->m_opacityBuffer[i]; auto pos = ImGui::GetWindowPos() + ImVec2(viewSize.x * 0.025F, viewSize.y * 0.025F) + ImVec2(x, y); drawList->AddRectFilled(pos, pos + ImVec2(xStep, yStep), ImColor(color)); } } ImGui::EndChild(); ImGui::PopStyleColor(); } void process() override { constexpr static auto SampleSize = 0x9000; const static size_t SequenceCount = std::ceil(std::sqrt(SampleSize)); this->m_buffer.clear(); auto buffer = this->getBufferOnInput(0); if (buffer.size() < SampleSize) this->m_buffer = buffer; else { std::random_device randomDevice; std::mt19937_64 random(randomDevice()); std::map> orderedData; for (u32 i = 0; i < SequenceCount; i++) { ssize_t offset = random() % buffer.size(); std::vector sequence; sequence.reserve(SampleSize); std::copy(buffer.begin() + offset, buffer.begin() + offset + std::min(SequenceCount, buffer.size() - offset), std::back_inserter(sequence)); orderedData.insert({ offset, sequence }); } this->m_buffer.reserve(SampleSize); u64 lastEnd = 0x00; for (const auto &[offset, sequence] : orderedData) { if (offset < lastEnd) this->m_buffer.resize(this->m_buffer.size() - (lastEnd - offset)); std::copy(sequence.begin(), sequence.end(), std::back_inserter(this->m_buffer)); lastEnd = offset + sequence.size(); } } this->m_opacityBuffer.resize(this->m_buffer.size()); std::map heatMap; for (size_t i = 0; i < (this->m_buffer.empty() ? 0 : this->m_buffer.size() - 1); i++) { auto count = ++heatMap[this->m_buffer[i] << 8 | heatMap[i + 1]]; this->m_highestCount = std::max(this->m_highestCount, count); } for (size_t i = 0; i < (this->m_buffer.empty() ? 0 : this->m_buffer.size() - 1); i++) { this->m_opacityBuffer[i] = std::min(0.2F + (float(heatMap[this->m_buffer[i] << 8 | this->m_buffer[i + 1]]) / float(this->m_highestCount / 1000)), 1.0F); } } private: std::vector m_buffer; std::vector m_opacityBuffer; size_t m_highestCount = 0; }; class NodeVisualizerLayeredDistribution : public dp::Node { public: NodeVisualizerLayeredDistribution() : Node("hex.builtin.nodes.visualizer.layered_dist.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input") }) { } void drawNode() override { drawLayeredDistribution(scaled({ 200, 200 })); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); drawLayeredDistribution(scaled({ 600, 600 })); ImGui::EndTooltip(); } } void drawLayeredDistribution(const ImVec2 &viewSize) { ImGui::PushStyleColor(ImGuiCol_ChildBg, ImU32(ImColor(0, 0, 0))); if (ImGui::BeginChild("##visualizer", viewSize, true)) { auto drawList = ImGui::GetWindowDrawList(); float xStep = (viewSize.x * 0.95F) / 0xFF; float yStep = (viewSize.y * 0.95F) / 0xFF; for (size_t i = 0; i < (this->m_buffer.empty() ? 0 : this->m_buffer.size()); i++) { const auto &[x, y] = std::pair { this->m_buffer[i] * xStep, yStep * ((float(i) / this->m_buffer.size()) * 0xFF) }; auto color = ImLerp(ImColor(0xFF, 0x6D, 0x01).Value, ImColor(0x01, 0x93, 0xFF).Value, float(i) / this->m_buffer.size()); color.w = this->m_opacityBuffer[i]; auto pos = ImGui::GetWindowPos() + ImVec2(viewSize.x * 0.025F, viewSize.y * 0.025F) + ImVec2(x, y); drawList->AddRectFilled(pos, pos + ImVec2(xStep, yStep), ImColor(color)); } } ImGui::EndChild(); ImGui::PopStyleColor(); } void process() override { constexpr static auto SampleSize = 0x9000; const static size_t SequenceCount = std::ceil(std::sqrt(SampleSize)); this->m_buffer.clear(); auto buffer = this->getBufferOnInput(0); if (buffer.size() < SampleSize) this->m_buffer = buffer; else { std::random_device randomDevice; std::mt19937_64 random(randomDevice()); std::map> orderedData; for (u32 i = 0; i < SequenceCount; i++) { ssize_t offset = random() % buffer.size(); std::vector sequence; sequence.reserve(SampleSize); std::copy(buffer.begin() + offset, buffer.begin() + offset + std::min(SequenceCount, buffer.size() - offset), std::back_inserter(sequence)); orderedData.insert({ offset, sequence }); } this->m_buffer.reserve(SampleSize); u64 lastEnd = 0x00; for (const auto &[offset, sequence] : orderedData) { if (offset < lastEnd) this->m_buffer.resize(this->m_buffer.size() - (lastEnd - offset)); std::copy(sequence.begin(), sequence.end(), std::back_inserter(this->m_buffer)); lastEnd = offset + sequence.size(); } } this->m_opacityBuffer.resize(this->m_buffer.size()); std::map heatMap; for (size_t i = 0; i < (this->m_buffer.empty() ? 0 : this->m_buffer.size() - 1); i++) { auto count = ++heatMap[this->m_buffer[i] << 8 | heatMap[i + 1]]; this->m_highestCount = std::max(this->m_highestCount, count); } for (size_t i = 0; i < (this->m_buffer.empty() ? 0 : this->m_buffer.size() - 1); i++) { this->m_opacityBuffer[i] = std::min(0.2F + (float(heatMap[this->m_buffer[i] << 8 | this->m_buffer[i + 1]]) / float(this->m_highestCount / 1000)), 1.0F); } } private: std::vector m_buffer; std::vector m_opacityBuffer; size_t m_highestCount = 0; }; class NodeVisualizerImage : public dp::Node { public: NodeVisualizerImage() : Node("hex.builtin.nodes.visualizer.image.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input") }) { } void drawNode() override { ImGui::Image(this->m_texture, scaled(ImVec2(this->m_texture.getAspectRatio() * 200, 200))); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImGui::Image(this->m_texture, scaled(ImVec2(this->m_texture.getAspectRatio() * 600, 600))); ImGui::EndTooltip(); } } void process() override { auto rawData = this->getBufferOnInput(0); this->m_texture = ImGui::Texture(rawData.data(), rawData.size()); } private: ImGui::Texture m_texture; }; class NodeVisualizerImageRGBA : public dp::Node { public: NodeVisualizerImageRGBA() : Node("hex.builtin.nodes.visualizer.image_rgba.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.width"), dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "hex.builtin.nodes.common.height") }) { } void drawNode() override { ImGui::Image(this->m_texture, scaled(ImVec2(this->m_texture.getAspectRatio() * 200, 200))); if (ImGui::IsItemHovered() && ImGui::IsKeyDown(ImGuiKey_LeftShift)) { ImGui::BeginTooltip(); ImGui::Image(this->m_texture, scaled(ImVec2(this->m_texture.getAspectRatio() * 600, 600))); ImGui::EndTooltip(); } } void process() override { this->m_texture = { }; const auto rawData = this->getBufferOnInput(0); const auto width = this->getIntegerOnInput(1); const auto height = this->getIntegerOnInput(2); const size_t requiredBytes = width * height * 4; if (requiredBytes > rawData.size()) throwNodeError(hex::format("Image requires at least {} bytes of data, but only {} bytes are available", requiredBytes, rawData.size())); this->m_texture = ImGui::Texture(rawData.data(), rawData.size(), width, height); } private: ImGui::Texture m_texture; }; class NodeVisualizerByteDistribution : public dp::Node { public: NodeVisualizerByteDistribution() : Node("hex.builtin.nodes.visualizer.byte_distribution.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input") }) { } void drawNode() override { drawPlot(scaled({ 400, 300 })); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); drawPlot(scaled({ 700, 550 })); ImGui::EndTooltip(); } } void drawPlot(const ImVec2 &viewSize) { if (ImPlot::BeginPlot("##distribution", viewSize, ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect)) { ImPlot::SetupAxes("Address", "Count", ImPlotAxisFlags_Lock, ImPlotAxisFlags_Lock | ImPlotAxisFlags_LogScale); ImPlot::SetupAxesLimits(0, 256, 1, double(*std::max_element(this->m_counts.begin(), this->m_counts.end())) * 1.1F, ImGuiCond_Always); static auto x = [] { std::array result { 0 }; std::iota(result.begin(), result.end(), 0); return result; }(); ImPlot::PlotBars("##bytes", x.data(), this->m_counts.data(), x.size(), 1); ImPlot::EndPlot(); } } void process() override { auto buffer = this->getBufferOnInput(0); this->m_counts.fill(0x00); for (const auto &byte : buffer) { this->m_counts[byte]++; } } private: std::array m_counts = { 0 }; }; class NodePatternLanguageOutVariable : public dp::Node { public: NodePatternLanguageOutVariable() : Node("hex.builtin.nodes.pattern_language.out_var.header", { dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.output") }) { } void drawNode() override { ImGui::PushItemWidth(100); ImGui::InputText("##name", this->m_name); ImGui::PopItemWidth(); } void process() override { auto &pl = ProviderExtraData::getCurrent().patternLanguage; std::scoped_lock lock(pl.runtimeMutex); const auto &outVars = pl.runtime->getOutVariables(); if (outVars.contains(this->m_name)) { std::visit(overloaded { [](const std::string &) {}, [](pl::ptrn::Pattern *) {}, [this](auto &&value) { std::vector buffer(std::min(sizeof(value), 8)); std::memcpy(buffer.data(), &value, buffer.size()); this->setBufferOnOutput(0, buffer); } }, outVars.at(this->m_name)); } else { throwNodeError(hex::format("Out variable '{}' has not been defined!", this->m_name)); } } private: std::string m_name; }; void registerDataProcessorNodes() { ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.constants", "hex.builtin.nodes.constants.int"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.constants", "hex.builtin.nodes.constants.float"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.constants", "hex.builtin.nodes.constants.nullptr"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.constants", "hex.builtin.nodes.constants.buffer"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.constants", "hex.builtin.nodes.constants.string"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.constants", "hex.builtin.nodes.constants.rgba8"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.constants", "hex.builtin.nodes.constants.comment"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.display", "hex.builtin.nodes.display.int"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.display", "hex.builtin.nodes.display.float"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.data_access", "hex.builtin.nodes.data_access.read"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.data_access", "hex.builtin.nodes.data_access.write"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.data_access", "hex.builtin.nodes.data_access.size"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.data_access", "hex.builtin.nodes.data_access.selection"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.casting", "hex.builtin.nodes.casting.int_to_buffer"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.casting", "hex.builtin.nodes.casting.buffer_to_int"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.casting", "hex.builtin.nodes.casting.float_to_buffer"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.casting", "hex.builtin.nodes.casting.buffer_to_float"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.arithmetic", "hex.builtin.nodes.arithmetic.add"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.arithmetic", "hex.builtin.nodes.arithmetic.sub"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.arithmetic", "hex.builtin.nodes.arithmetic.mul"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.arithmetic", "hex.builtin.nodes.arithmetic.div"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.arithmetic", "hex.builtin.nodes.arithmetic.mod"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.buffer", "hex.builtin.nodes.buffer.combine"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.buffer", "hex.builtin.nodes.buffer.slice"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.buffer", "hex.builtin.nodes.buffer.repeat"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.control_flow", "hex.builtin.nodes.control_flow.if"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.control_flow", "hex.builtin.nodes.control_flow.equals"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.control_flow", "hex.builtin.nodes.control_flow.not"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.control_flow", "hex.builtin.nodes.control_flow.gt"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.control_flow", "hex.builtin.nodes.control_flow.lt"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.control_flow", "hex.builtin.nodes.control_flow.and"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.control_flow", "hex.builtin.nodes.control_flow.or"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.bitwise", "hex.builtin.nodes.bitwise.and"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.bitwise", "hex.builtin.nodes.bitwise.or"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.bitwise", "hex.builtin.nodes.bitwise.xor"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.bitwise", "hex.builtin.nodes.bitwise.not"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.decoding", "hex.builtin.nodes.decoding.base64"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.decoding", "hex.builtin.nodes.decoding.hex"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.crypto", "hex.builtin.nodes.crypto.aes"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.visualizer", "hex.builtin.nodes.visualizer.digram"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.visualizer", "hex.builtin.nodes.visualizer.layered_dist"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.visualizer", "hex.builtin.nodes.visualizer.image"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.visualizer", "hex.builtin.nodes.visualizer.image_rgba"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.visualizer", "hex.builtin.nodes.visualizer.byte_distribution"); ContentRegistry::DataProcessorNode::add("hex.builtin.nodes.pattern_language", "hex.builtin.nodes.pattern_language.out_var"); } }