From 8ab85a2af169fc69da2e739dd443049de83da4bd Mon Sep 17 00:00:00 2001 From: WerWolv Date: Thu, 7 Dec 2023 16:15:00 +0100 Subject: [PATCH] feat: Added unit converter to command palette --- plugins/builtin/romfs/lang/en_US.json | 10 + .../content/command_palette_commands.cpp | 238 ++++++++++++++++++ 2 files changed, 248 insertions(+) diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index dac631bee..2e4396321 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -61,6 +61,16 @@ "hex.builtin.achievement.misc.create_hash.name": "Hash browns", "hex.builtin.achievement.misc.create_hash.desc": "Create a new hash function in the Hash view by selecting the type, giving it a name and clicking on the Plus button next to it.", "hex.builtin.command.calc.desc": "Calculator", + "hex.builtin.command.convert.desc": "Unit conversion", + "hex.builtin.command.convert.hexadecimal": "hexadecimal", + "hex.builtin.command.convert.decimal": "decimal", + "hex.builtin.command.convert.binary": "binary", + "hex.builtin.command.convert.octal": "octal", + "hex.builtin.command.convert.invalid_conversion": "Invalid conversion", + "hex.builtin.command.convert.invalid_input": "Invalid input", + "hex.builtin.command.convert.to": "to", + "hex.builtin.command.convert.in": "in", + "hex.builtin.command.convert.as": "as", "hex.builtin.command.cmd.desc": "Command", "hex.builtin.command.cmd.result": "Run command '{0}'", "hex.builtin.command.web.desc": "Website lookup", diff --git a/plugins/builtin/source/content/command_palette_commands.cpp b/plugins/builtin/source/content/command_palette_commands.cpp index e81f4daf2..e64329fee 100644 --- a/plugins/builtin/source/content/command_palette_commands.cpp +++ b/plugins/builtin/source/content/command_palette_commands.cpp @@ -11,6 +11,238 @@ namespace hex::plugin::builtin { + namespace { + + class Value { + public: + enum class Unit { + Unitless, + Decimal, + Hexadecimal, + Binary, + Octal, + Bits, + Bytes, + Invalid + }; + + explicit Value(std::string value) { + if (!value.starts_with("0x") && !value.starts_with("0b")) { + auto index = value.find_first_not_of("0123456789.,"); + if (index == std::string::npos) { + m_unit = Unit::Unitless; + } else { + std::tie(m_unit, m_multiplier) = parseUnit(value.substr(index)); + value = value.substr(0, index); + } + } else { + m_unit = Unit::Unitless; + } + + + try { + if (!value.contains('.')) { + m_value = i128(std::stoull(value, nullptr, 0) * static_cast(m_multiplier)); + } else { + m_value = std::stod(value) * m_multiplier; + } + } catch (const std::exception &e) { + m_value = 0; + m_unit = Unit::Invalid; + m_unitString.clear(); + m_multiplier = 1; + } + } + + std::string formatAs(Value other) { + return std::visit([&, this](T value) -> std::string { + + auto unit = other.getUnit(); + auto multipler = other.getMultiplier(); + + bool isInteger = std::integral && multipler == 1; + switch (m_unit) { + case Unit::Invalid: + if (this->getUnitString() != other.getUnitString()) + return "hex.builtin.command.convert.invalid_conversion"_lang; + unit = Unit::Decimal; + [[fallthrough]]; + case Unit::Unitless: { + + switch (unit) { + case Unit::Unitless: + case Unit::Decimal: + if (isInteger) + return hex::format("{0}", value / multipler); + else + return hex::format("{0:.3f}", value / multipler); + case Unit::Hexadecimal: + return hex::format("0x{0:x}", u128(value / multipler)); + case Unit::Binary: + return hex::format("0b{0:b}", u128(value / multipler)); + case Unit::Octal: + return hex::format("0o{0:o}", u128(value / multipler)); + case Unit::Bytes: + return hex::format("{0}", u128(value / multipler)); + default: + return "hex.builtin.command.convert.invalid_conversion"_lang; + } + + break; + } + case Unit::Bits: { + + switch (unit) { + case Unit::Bits: + case Unit::Decimal: + if (isInteger) + return hex::format("{0}", value / multipler); + else + return hex::format("{0:.3f}", value / multipler); + case Unit::Hexadecimal: + return hex::format("0x{0:x}", u128(value / multipler)); + case Unit::Binary: + return hex::format("0b{0:b}", u128(value / multipler)); + case Unit::Octal: + return hex::format("0o{0:o}", u128(value / multipler)); + case Unit::Bytes: + return hex::format("{0}", u128((value / multipler) / 8)); + default: + return "hex.builtin.command.convert.invalid_conversion"_lang; + } + + break; + } + case Unit::Bytes: { + + switch (unit) { + case Unit::Bytes: + case Unit::Decimal: + if (isInteger) + return hex::format("{0}", value / multipler); + else + return hex::format("{0:.3f}", value / multipler); + case Unit::Hexadecimal: + return hex::format("0x{0:x}", u128(value / multipler)); + case Unit::Binary: + return hex::format("0b{0:b}", u128(value / multipler)); + case Unit::Octal: + return hex::format("0o{0:o}", u128(value / multipler)); + case Unit::Bits: + return hex::format("{0}", u128((value / multipler) * 8)); + default: + return "hex.builtin.command.convert.invalid_conversion"_lang; + } + + break; + } + default: + return "hex.builtin.command.convert.invalid_input"_lang; + } + }, m_value); + } + + [[nodiscard]] Unit getUnit() const { return m_unit; } + [[nodiscard]] double getMultiplier() const { return m_multiplier; } + [[nodiscard]] const std::string& getUnitString() const { return m_unitString; } + + private: + std::pair parseUnit(std::string unitString, bool parseMultiplier = true) { + auto unitStringCopy = unitString; + unitString = wolv::util::trim(unitString); + + double multiplier = 1; + if (parseMultiplier && !unitString.starts_with("dec") && !unitString.starts_with("hex") && !unitString.starts_with("bin") && !unitString.starts_with("oct")) { + if (unitString.starts_with("Ki")) { multiplier = 1024ULL; unitString = unitString.substr(2); } + else if (unitString.starts_with("Mi")) { multiplier = 1024ULL * 1024ULL; unitString = unitString.substr(2); } + else if (unitString.starts_with("Gi")) { multiplier = 1024ULL * 1024ULL * 1024ULL; unitString = unitString.substr(2); } + else if (unitString.starts_with("Ti")) { multiplier = 1024ULL * 1024ULL * 1024ULL * 1024ULL; unitString = unitString.substr(2); } + else if (unitString.starts_with("Pi")) { multiplier = 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL; unitString = unitString.substr(2); } + else if (unitString.starts_with("k")) { multiplier = 1E3; unitString = unitString.substr(1); } + else if (unitString.starts_with("M")) { multiplier = 1E6; unitString = unitString.substr(1); } + else if (unitString.starts_with("G")) { multiplier = 1E9; unitString = unitString.substr(1); } + else if (unitString.starts_with("T")) { multiplier = 1E12; unitString = unitString.substr(1); } + else if (unitString.starts_with("P")) { multiplier = 1E15; unitString = unitString.substr(1); } + else if (unitString.starts_with("E")) { multiplier = 1E18; unitString = unitString.substr(1); } + else if (unitString.starts_with("Z")) { multiplier = 1E21; unitString = unitString.substr(1); } + else if (unitString.starts_with("Y")) { multiplier = 1E24; unitString = unitString.substr(1); } + else if (unitString.starts_with("d")) { multiplier = 1E-1; unitString = unitString.substr(1); } + else if (unitString.starts_with("c")) { multiplier = 1E-2; unitString = unitString.substr(1); } + else if (unitString.starts_with("m")) { multiplier = 1E-3; unitString = unitString.substr(1); } + else if (unitString.starts_with("u")) { multiplier = 1E-6; unitString = unitString.substr(1); } + else if (unitString.starts_with("n")) { multiplier = 1E-9; unitString = unitString.substr(1); } + else if (unitString.starts_with("p")) { multiplier = 1E-12; unitString = unitString.substr(1); } + else if (unitString.starts_with("f")) { multiplier = 1E-15; unitString = unitString.substr(1); } + else if (unitString.starts_with("a")) { multiplier = 1E-18; unitString = unitString.substr(1); } + else if (unitString.starts_with("z")) { multiplier = 1E-21; unitString = unitString.substr(1); } + else if (unitString.starts_with("y")) { multiplier = 1E-24; unitString = unitString.substr(1); } + else return parseUnit(unitString, false); + } + + unitString = wolv::util::trim(unitString); + this->m_unitString = unitString; + if (unitString.empty()) { + if (multiplier == 1) + return { Unit::Unitless, 1 }; + else { + this->m_unitString = unitStringCopy; + return { Unit::Unitless, 1 }; + } + + } + else if (unitString == "bit" || unitString == "bits" || unitString == "b") + return { Unit::Bits, multiplier }; + else if (unitString == "byte" || unitString == "bytes" || unitString == "B") + return { Unit::Bytes, multiplier }; + else if (unitString == "hex" || unitString == "hex.builtin.command.convert.hexadecimal"_lang.get()) + return { Unit::Hexadecimal, multiplier }; + else if (unitString == "bin" || unitString == "hex.builtin.command.convert.binary"_lang.get()) + return { Unit::Binary, multiplier }; + else if (unitString == "oct" || unitString == "hex.builtin.command.convert.octal"_lang.get()) + return { Unit::Octal, multiplier }; + else if (unitString == "dec" || unitString == "hex.builtin.command.convert.decimal"_lang.get()) + return { Unit::Decimal, multiplier }; + else + return { Unit::Invalid, multiplier }; + } + + private: + Unit m_unit; + std::string m_unitString; + double m_multiplier = 1; + std::variant m_value; + }; + + std::vector splitConversionCommandInput(const std::string &input) { + std::vector parts = wolv::util::splitString(input, " "); + std::erase_if(parts, [](auto &part) { return part.empty(); }); + + return parts; + } + + bool verifyConversionInput(const std::vector &parts) { + if (parts.size() == 3) { + return parts[1] == "hex.builtin.command.convert.to"_lang.get() || parts[1] == "hex.builtin.command.convert.in"_lang.get() || parts[1] == "hex.builtin.command.convert.as"_lang.get(); + } else { + return false; + } + } + + + std::string handleConversionCommand(const std::string &input) { + auto parts = splitConversionCommandInput(input); + + if (!verifyConversionInput(parts)) + return "hex.builtin.command.convert.invalid_input"_lang; + + auto from = Value(parts[0]); + auto to = Value("1" + parts[2]); + + return fmt::format("% {}", from.formatAs(to)); + } + + } + void registerCommandPaletteCommands() { ContentRegistry::CommandPaletteCommands::add( @@ -80,6 +312,12 @@ namespace hex::plugin::builtin { return hex::format("Menu Item: {}", input.data()); }); + ContentRegistry::CommandPaletteCommands::add( + ContentRegistry::CommandPaletteCommands::Type::SymbolCommand, + "%", + "hex.builtin.command.convert.desc", + handleConversionCommand); + } } \ No newline at end of file