#include #include namespace hex::lang { #define LITERAL_COMPARE(literal, cond) std::visit([&, this](auto &&literal) { return (cond) != 0; }, literal) void Evaluator::registerBuiltinFunctions() { /* findSequence */ ContentRegistry::PatternLanguageFunctions::add("findSequence", ContentRegistry::PatternLanguageFunctions::MoreParametersThan | 1, [this](auto &console, auto params) { auto& occurrenceIndex = asType(params[0])->getValue(); std::vector sequence; for (u32 i = 1; i < params.size(); i++) { sequence.push_back(std::visit([&](auto &&value) -> u8 { if (value <= 0xFF) return value; else console.abortEvaluation("sequence bytes need to fit into 1 byte"); }, asType(params[i])->getValue())); } std::vector bytes(sequence.size(), 0x00); u32 occurrences = 0; for (u64 offset = 0; offset < this->m_provider->getSize() - sequence.size(); offset++) { this->m_provider->read(offset, bytes.data(), bytes.size()); if (bytes == sequence) { if (LITERAL_COMPARE(occurrenceIndex, occurrences < occurrenceIndex)) { occurrences++; continue; } return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, offset }); } } console.abortEvaluation("failed to find sequence"); }); /* assert */ ContentRegistry::PatternLanguageFunctions::add("readUnsigned", 2, [this](auto &console, auto params) { auto address = asType(params[0])->getValue(); auto size = asType(params[1])->getValue(); if (LITERAL_COMPARE(address, address >= this->m_provider->getActualSize())) console.abortEvaluation("address out of range"); return std::visit([&, this](auto &&address, auto &&size) { if (size <= 0 || size > 16) console.abortEvaluation("invalid read size"); u8 value[(u8)size]; this->m_provider->read(address, value, size); switch ((u8)size) { case 1: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned8Bit, hex::changeEndianess(*reinterpret_cast(value), 1, this->getCurrentEndian()) }); case 2: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned16Bit, hex::changeEndianess(*reinterpret_cast(value), 2, this->getCurrentEndian()) }); case 4: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned32Bit, hex::changeEndianess(*reinterpret_cast(value), 4, this->getCurrentEndian()) }); case 8: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, hex::changeEndianess(*reinterpret_cast(value), 8, this->getCurrentEndian()) }); case 16: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned128Bit, hex::changeEndianess(*reinterpret_cast(value), 16, this->getCurrentEndian()) }); default: console.abortEvaluation("invalid read size"); } }, address, size); }); ContentRegistry::PatternLanguageFunctions::add("readSigned", 2, [this](auto &console, auto params) { auto address = asType(params[0])->getValue(); auto size = asType(params[1])->getValue(); if (LITERAL_COMPARE(address, address >= this->m_provider->getActualSize())) console.abortEvaluation("address out of range"); return std::visit([&, this](auto &&address, auto &&size) { if (size <= 0 || size > 16) console.abortEvaluation("invalid read size"); u8 value[(u8)size]; this->m_provider->read(address, value, size); switch ((u8)size) { case 1: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed8Bit, hex::changeEndianess(*reinterpret_cast(value), 1, this->getCurrentEndian()) }); case 2: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed16Bit, hex::changeEndianess(*reinterpret_cast(value), 2, this->getCurrentEndian()) }); case 4: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed32Bit, hex::changeEndianess(*reinterpret_cast(value), 4, this->getCurrentEndian()) }); case 8: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed64Bit, hex::changeEndianess(*reinterpret_cast(value), 8, this->getCurrentEndian()) }); case 16: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed128Bit, hex::changeEndianess(*reinterpret_cast(value), 16, this->getCurrentEndian()) }); default: console.abortEvaluation("invalid read size"); } }, address, size); }); ContentRegistry::PatternLanguageFunctions::add("assert", 2, [this](auto &console, auto params) { auto condition = asType(params[0])->getValue(); auto message = asType(params[1])->getString(); if (LITERAL_COMPARE(condition, condition == 0)) console.abortEvaluation(hex::format("assert failed \"%s\"", message.data())); return nullptr; }); ContentRegistry::PatternLanguageFunctions::add("warnAssert", 2, [this](auto console, auto params) { auto condition = asType(params[0])->getValue(); auto message = asType(params[1])->getString(); if (LITERAL_COMPARE(condition, condition == 0)) console.log(LogConsole::Level::Warning, hex::format("assert failed \"%s\"", message.data())); return nullptr; }); ContentRegistry::PatternLanguageFunctions::add("print", ContentRegistry::PatternLanguageFunctions::MoreParametersThan | 0, [](auto &console, auto params) { std::string message; for (auto& param : params) { if (auto integerLiteral = dynamic_cast(param); integerLiteral != nullptr) { switch (integerLiteral->getType()) { case Token::ValueType::Character: message += std::get(integerLiteral->getValue()); break; case Token::ValueType::Unsigned8Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; case Token::ValueType::Signed8Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; case Token::ValueType::Unsigned16Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; case Token::ValueType::Signed16Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; case Token::ValueType::Unsigned32Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; case Token::ValueType::Signed32Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; case Token::ValueType::Unsigned64Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; case Token::ValueType::Signed64Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; case Token::ValueType::Unsigned128Bit: message += hex::to_string(std::get(integerLiteral->getValue())); break; case Token::ValueType::Signed128Bit: message += hex::to_string(std::get(integerLiteral->getValue())); break; case Token::ValueType::Float: message += std::to_string(std::get(integerLiteral->getValue())); break; case Token::ValueType::Double: message += std::to_string(std::get(integerLiteral->getValue())); break; case Token::ValueType::Boolean: message += std::get(integerLiteral->getValue()) ? "true" : "false"; break; case Token::ValueType::CustomType: message += "< Custom Type >"; break; } } else if (auto stringLiteral = dynamic_cast(param); stringLiteral != nullptr) message += stringLiteral->getString(); } console.log(LogConsole::Level::Info, message); return nullptr; }); ContentRegistry::PatternLanguageFunctions::add("addressof", 1, [this](auto &console, auto params) -> ASTNode* { auto name = asType(params[0])->getString(); std::vector path = splitString(name, "."); auto pattern = this->patternFromName(path); return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, u64(pattern->getOffset()) }); }); ContentRegistry::PatternLanguageFunctions::add("sizeof", 1, [this](auto &console, auto params) -> ASTNode* { auto name = asType(params[0])->getString(); std::vector path = splitString(name, "."); auto pattern = this->patternFromName(path); return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, u64(pattern->getSize()) }); }); ContentRegistry::PatternLanguageFunctions::add("nextAfter", 1, [this](auto &console, auto params) -> ASTNode* { auto name = asType(params[0])->getString(); std::vector path = splitString(name, "."); auto pattern = this->patternFromName(path); return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, u64(pattern->getOffset() + pattern->getSize()) }); }); ContentRegistry::PatternLanguageFunctions::add("alignTo", 2, [this](auto &console, auto params) -> ASTNode* { auto alignment = asType(params[0])->getValue(); auto value = asType(params[1])->getValue(); auto result = std::visit([](auto &&alignment, auto &&value) { u64 remainder = u64(value) % u64(alignment); return remainder != 0 ? u64(value) + (u64(alignment) - remainder) : u64(value); }, alignment, value); return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, u64(result) }); }); } }