1
0
mirror of synced 2024-11-27 00:50:51 +01:00
ImHex/plugins/builtin/source/content/tools_entries.cpp
paxcut 3e4c4430d5
feat: Greatly improved the IEEE754 tool (#1047)
I just realized one feature request existed about this tool and have
added a comment to it referring this pr. Errors and additions are
described in the fork commit already. I'm not sure if I should repeat
them here again. I have tested the changes thoroughly, but it is always
possible some fringe case was not tested and is incorrect. The tests
were done using the many similar online calculators for IEEE 754
floating point formats.
IEEE 745 floating point tool redesign modeled after 'float toy' web app
(http://evanw.github.io/float-toy/)

Streamlined output using colors and compact layout which can be further
simplified.
Chosen display mode (detailed or simplified) is automatically saved and
set on new sessions.
Edit the binary bits, the integer hexadecimal or the floating point
decimal values and the entire app will update with the change.
Supports the main IEEE745 standard formats (half, single and double
precision) together with custom formats of size <= 64 bits.
Each format choice uses and displays the number of significant decimal
digits defined by the mantissa size.
Added labels to identify the location of each bit box inside the binary
representation.
Satisfies round trip / idempotent (reproducing) conversion property
Added theme colors, radio buttons for display mode and a clear button
that resets the tool.
Removed previously and incorrectly added locale translation to various
labels and languages
Attempted to adhere to code style formatting using existing code as
example.
An effort was made to use preferred variable types and functions from
std namespace when appropriate.
Attempted to document code using comments. 

Not implemented / left to complete at an later time

Arbitrary width and precision formats. 
Extended precision formats.
Shortest string property.
hexadecimal floating point display and conversions.
2023-05-19 21:18:38 +02:00

1846 lines
83 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <hex/api/content_registry.hpp>
#include <hex/api/imhex_api.hpp>
#include <hex/helpers/http_requests.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/fmt.hpp>
#include <hex/helpers/literals.hpp>
#include <hex/helpers/fs.hpp>
#include <hex/api/localization.hpp>
#include <hex/ui/view.hpp>
#include <algorithm>
#include <chrono>
#include <random>
#include <regex>
#include <llvm/Demangle/Demangle.h>
#include <content/helpers/math_evaluator.hpp>
#include <imgui.h>
#include <hex/ui/imgui_imhex_extensions.h>
#include <content/popups/popup_notification.hpp>
#include <nlohmann/json.hpp>
#include <wolv/io/file.hpp>
#include <wolv/utils/guards.hpp>
#include <charconv>
namespace hex::plugin::builtin {
namespace {
using namespace std::literals::string_literals;
using namespace std::literals::chrono_literals;
using namespace hex::literals;
void drawDemangler() {
static std::string mangledName, demangledName;
if (ImGui::InputTextWithHint("hex.builtin.tools.demangler.mangled"_lang, "Itanium, MSVC, Dlang & Rust", mangledName)) {
demangledName = llvm::demangle(mangledName);
if (demangledName == mangledName) {
demangledName = "???";
}
}
ImGui::Header("hex.builtin.tools.demangler.demangled"_lang);
if (ImGui::BeginChild("demangled", ImVec2(0, 200_scaled), true)) {
ImGui::TextFormattedWrapped("{}", demangledName);
}
ImGui::EndChild();
}
void drawASCIITable() {
static bool asciiTableShowOctal = false;
ImGui::BeginTable("##asciitable", 4);
ImGui::TableSetupColumn("");
ImGui::TableSetupColumn("");
ImGui::TableSetupColumn("");
ImGui::TableSetupColumn("");
ImGui::TableNextColumn();
for (u8 tablePart = 0; tablePart < 4; tablePart++) {
ImGui::BeginTable("##asciitablepart", asciiTableShowOctal ? 4 : 3, ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg);
ImGui::TableSetupColumn("dec");
if (asciiTableShowOctal)
ImGui::TableSetupColumn("oct");
ImGui::TableSetupColumn("hex");
ImGui::TableSetupColumn("char");
ImGui::TableHeadersRow();
for (u8 i = 0; i < 0x80 / 4; i++) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextFormatted("{0:03d}", i + 32 * tablePart);
if (asciiTableShowOctal) {
ImGui::TableNextColumn();
ImGui::TextFormatted("0o{0:03o}", i + 32 * tablePart);
}
ImGui::TableNextColumn();
ImGui::TextFormatted("0x{0:02X}", i + 32 * tablePart);
ImGui::TableNextColumn();
ImGui::TextFormatted("{0}", hex::makePrintable(i + 32 * tablePart));
}
ImGui::EndTable();
ImGui::TableNextColumn();
}
ImGui::EndTable();
ImGui::Checkbox("hex.builtin.tools.ascii_table.octal"_lang, &asciiTableShowOctal);
}
void drawRegexReplacer() {
static auto regexInput = [] { std::string s; s.reserve(0xFFF); return s; }();
static auto regexPattern = [] { std::string s; s.reserve(0xFFF); return s; }();
static auto replacePattern = [] { std::string s; s.reserve(0xFFF); return s; }();
static auto regexOutput = [] { std::string s; s.reserve(0xFFF); return s; }();
bool changed1 = ImGui::InputTextIcon("hex.builtin.tools.regex_replacer.pattern"_lang, ICON_VS_REGEX, regexPattern);
bool changed2 = ImGui::InputTextIcon("hex.builtin.tools.regex_replacer.replace"_lang, ICON_VS_REGEX, replacePattern);
bool changed3 = ImGui::InputTextMultiline("hex.builtin.tools.regex_replacer.input"_lang, regexInput, ImVec2(0, 0));
if (changed1 || changed2 || changed3) {
try {
regexOutput = std::regex_replace(regexInput.data(), std::regex(regexPattern.data()), replacePattern.data());
} catch (std::regex_error &) { }
}
ImGui::InputTextMultiline("hex.builtin.tools.regex_replacer.output"_lang, regexOutput.data(), regexOutput.size(), ImVec2(0, 0), ImGuiInputTextFlags_ReadOnly);
}
void drawColorPicker() {
static std::array<float, 4> pickedColor = { 0 };
ImGui::SetNextItemWidth(300_scaled);
ImGui::ColorPicker4("hex.builtin.tools.color"_lang, pickedColor.data(), ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_DisplayHex);
}
void drawMathEvaluator() {
static std::vector<long double> mathHistory;
static std::string lastMathError;
static std::string mathInput;
bool evaluate = false;
static MathEvaluator<long double> mathEvaluator = [&] {
MathEvaluator<long double> evaluator;
evaluator.registerStandardVariables();
evaluator.registerStandardFunctions();
evaluator.setFunction(
"clear", [&](auto args) -> std::optional<long double> {
hex::unused(args);
mathHistory.clear();
lastMathError.clear();
mathEvaluator.getVariables().clear();
mathEvaluator.registerStandardVariables();
mathInput.clear();
return std::nullopt;
},
0,
0);
evaluator.setFunction(
"read", [](auto args) -> std::optional<long double> {
u8 value = 0;
auto provider = ImHexApi::Provider::get();
if (!ImHexApi::Provider::isValid() || !provider->isReadable() || args[0] >= provider->getActualSize())
return std::nullopt;
provider->read(args[0], &value, sizeof(u8));
return value;
},
1,
1);
evaluator.setFunction(
"write", [](auto args) -> std::optional<long double> {
auto provider = ImHexApi::Provider::get();
if (!ImHexApi::Provider::isValid() || !provider->isWritable() || args[0] >= provider->getActualSize())
return std::nullopt;
if (args[1] > 0xFF)
return std::nullopt;
u8 value = args[1];
provider->write(args[0], &value, sizeof(u8));
return std::nullopt;
},
2,
2);
return evaluator;
}();
enum class MathDisplayType : u8 {
Standard,
Scientific,
Engineering,
Programmer
} mathDisplayType = MathDisplayType::Standard;
if (ImGui::BeginTabBar("##mathFormatTabBar")) {
if (ImGui::BeginTabItem("hex.builtin.tools.format.standard"_lang)) {
mathDisplayType = MathDisplayType::Standard;
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("hex.builtin.tools.format.scientific"_lang)) {
mathDisplayType = MathDisplayType::Scientific;
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("hex.builtin.tools.format.engineering"_lang)) {
mathDisplayType = MathDisplayType::Engineering;
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("hex.builtin.tools.format.programmer"_lang)) {
mathDisplayType = MathDisplayType::Programmer;
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
if (ImGui::BeginTable("##mathWrapper", 3)) {
ImGui::TableSetupColumn("##keypad", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize);
ImGui::TableSetupColumn("##results", ImGuiTableColumnFlags_WidthStretch, 0.666);
ImGui::TableSetupColumn("##variables", ImGuiTableColumnFlags_WidthStretch, 0.666);
ImGui::TableNextRow();
ImGui::TableNextColumn();
auto buttonSize = ImVec2(3, 2) * ImGui::GetTextLineHeightWithSpacing();
if (ImGui::Button("Ans", buttonSize)) mathInput += "ans";
ImGui::SameLine();
if (ImGui::Button("Pi", buttonSize)) mathInput += "pi";
ImGui::SameLine();
if (ImGui::Button("e", buttonSize)) mathInput += "e";
ImGui::SameLine();
if (ImGui::Button("CE", buttonSize)) mathInput.clear();
ImGui::SameLine();
if (ImGui::Button(ICON_FA_BACKSPACE, buttonSize)) mathInput.clear();
ImGui::SameLine();
ImGui::NewLine();
switch (mathDisplayType) {
case MathDisplayType::Standard:
case MathDisplayType::Scientific:
case MathDisplayType::Engineering:
if (ImGui::Button("", buttonSize)) mathInput += "** 2";
ImGui::SameLine();
if (ImGui::Button("1/x", buttonSize)) mathInput += "1/";
ImGui::SameLine();
if (ImGui::Button("|x|", buttonSize)) mathInput += "abs";
ImGui::SameLine();
if (ImGui::Button("exp", buttonSize)) mathInput += "e ** ";
ImGui::SameLine();
if (ImGui::Button("%", buttonSize)) mathInput += "%";
ImGui::SameLine();
break;
case MathDisplayType::Programmer:
if (ImGui::Button("<<", buttonSize)) mathInput += "<<";
ImGui::SameLine();
if (ImGui::Button(">>", buttonSize)) mathInput += ">>";
ImGui::SameLine();
if (ImGui::Button("&", buttonSize)) mathInput += "&";
ImGui::SameLine();
if (ImGui::Button("|", buttonSize)) mathInput += "|";
ImGui::SameLine();
if (ImGui::Button("^", buttonSize)) mathInput += "^";
ImGui::SameLine();
break;
}
ImGui::NewLine();
if (ImGui::Button("sqrt", buttonSize)) mathInput += "sqrt";
ImGui::SameLine();
if (ImGui::Button("(", buttonSize)) mathInput += "(";
ImGui::SameLine();
if (ImGui::Button(")", buttonSize)) mathInput += ")";
ImGui::SameLine();
if (ImGui::Button("sign", buttonSize)) mathInput += "sign";
ImGui::SameLine();
if (ImGui::Button("÷", buttonSize)) mathInput += "/";
ImGui::SameLine();
ImGui::NewLine();
if (ImGui::Button("", buttonSize)) mathInput += "**";
ImGui::SameLine();
if (ImGui::Button("7", buttonSize)) mathInput += "7";
ImGui::SameLine();
if (ImGui::Button("8", buttonSize)) mathInput += "8";
ImGui::SameLine();
if (ImGui::Button("9", buttonSize)) mathInput += "9";
ImGui::SameLine();
if (ImGui::Button("×", buttonSize)) mathInput += "*";
ImGui::SameLine();
ImGui::NewLine();
if (ImGui::Button("log", buttonSize)) mathInput += "log";
ImGui::SameLine();
if (ImGui::Button("4", buttonSize)) mathInput += "4";
ImGui::SameLine();
if (ImGui::Button("5", buttonSize)) mathInput += "5";
ImGui::SameLine();
if (ImGui::Button("6", buttonSize)) mathInput += "6";
ImGui::SameLine();
if (ImGui::Button("-", buttonSize)) mathInput += "-";
ImGui::SameLine();
ImGui::NewLine();
if (ImGui::Button("ln", buttonSize)) mathInput += "ln";
ImGui::SameLine();
if (ImGui::Button("1", buttonSize)) mathInput += "1";
ImGui::SameLine();
if (ImGui::Button("2", buttonSize)) mathInput += "2";
ImGui::SameLine();
if (ImGui::Button("3", buttonSize)) mathInput += "3";
ImGui::SameLine();
if (ImGui::Button("+", buttonSize)) mathInput += "+";
ImGui::SameLine();
ImGui::NewLine();
if (ImGui::Button("lb", buttonSize)) mathInput += "lb";
ImGui::SameLine();
if (ImGui::Button("x=", buttonSize)) mathInput += "=";
ImGui::SameLine();
if (ImGui::Button("0", buttonSize)) mathInput += "0";
ImGui::SameLine();
if (ImGui::Button(".", buttonSize)) mathInput += ".";
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetCustomColorVec4(ImGuiCustomCol_DescButtonHovered));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::GetCustomColorVec4(ImGuiCustomCol_DescButton));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetCustomColorVec4(ImGuiCustomCol_DescButtonActive));
if (ImGui::Button("=", buttonSize)) evaluate = true;
ImGui::SameLine();
ImGui::PopStyleColor(3);
ImGui::NewLine();
ImGui::TableNextColumn();
if (ImGui::BeginTable("##mathHistory", 1, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(0, 300))) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("hex.builtin.tools.history"_lang);
ImGuiListClipper clipper;
clipper.Begin(mathHistory.size());
ImGui::TableHeadersRow();
while (clipper.Step()) {
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
if (i == 0)
ImGui::PushStyleColor(ImGuiCol_Text, ImU32(ImColor(0xA5, 0x45, 0x45)));
ImGui::TableNextRow();
ImGui::TableNextColumn();
switch (mathDisplayType) {
case MathDisplayType::Standard:
ImGui::TextFormatted("{0:.3Lf}", mathHistory[(mathHistory.size() - 1) - i]);
break;
case MathDisplayType::Scientific:
ImGui::TextFormatted("{0:.6Lg}", mathHistory[(mathHistory.size() - 1) - i]);
break;
case MathDisplayType::Engineering:
ImGui::TextFormatted("{0}", hex::toEngineeringString(mathHistory[(mathHistory.size() - 1) - i]).c_str());
break;
case MathDisplayType::Programmer:
ImGui::TextFormatted("0x{0:X} ({1})",
u64(mathHistory[(mathHistory.size() - 1) - i]),
u64(mathHistory[(mathHistory.size() - 1) - i]));
break;
}
if (i == 0)
ImGui::PopStyleColor();
}
}
clipper.End();
ImGui::EndTable();
}
ImGui::TableNextColumn();
if (ImGui::BeginTable("##mathVariables", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(0, 300))) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("hex.builtin.tools.name"_lang);
ImGui::TableSetupColumn("hex.builtin.tools.value"_lang);
ImGui::TableHeadersRow();
for (const auto &[name, variable] : mathEvaluator.getVariables()) {
const auto &[value, constant] = variable;
if (constant)
continue;
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted(name.c_str());
ImGui::TableNextColumn();
switch (mathDisplayType) {
case MathDisplayType::Standard:
ImGui::TextFormatted("{0:.3Lf}", value);
break;
case MathDisplayType::Scientific:
ImGui::TextFormatted("{0:.6Lg}", value);
break;
case MathDisplayType::Engineering:
ImGui::TextFormatted("{}", hex::toEngineeringString(value));
break;
case MathDisplayType::Programmer:
ImGui::TextFormatted("0x{0:X} ({1})", u64(value), u64(value));
break;
}
}
ImGui::EndTable();
}
ImGui::EndTable();
}
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::InputTextIcon("##input", ICON_VS_SYMBOL_OPERATOR, mathInput, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) {
ImGui::SetKeyboardFocusHere();
evaluate = true;
}
ImGui::PopItemWidth();
if (!lastMathError.empty())
ImGui::TextFormattedColored(ImColor(0xA00040FF), "hex.builtin.tools.error"_lang, lastMathError);
else
ImGui::NewLine();
if (evaluate) {
try {
auto result = mathEvaluator.evaluate(mathInput);
mathInput.clear();
if (result.has_value()) {
mathHistory.push_back(result.value());
lastMathError.clear();
}
} catch (std::invalid_argument &e) {
lastMathError = e.what();
}
}
}
void drawBaseConverter() {
static std::array<std::string, 4> buffers;
static auto ConvertBases = [](u8 base) {
u64 number;
switch (base) {
case 16:
number = std::strtoull(buffers[1].c_str(), nullptr, base);
break;
case 10:
number = std::strtoull(buffers[0].c_str(), nullptr, base);
break;
case 8:
number = std::strtoull(buffers[2].c_str(), nullptr, base);
break;
case 2:
number = std::strtoull(buffers[3].c_str(), nullptr, base);
break;
default:
return;
}
buffers[0] = std::to_string(number);
buffers[1] = hex::format("0x{0:X}", number);
buffers[2] = hex::format("{0:#o}", number);
buffers[3] = hex::toBinaryString(number);
};
if (ImGui::InputTextIcon("hex.builtin.tools.base_converter.dec"_lang, ICON_VS_SYMBOL_NUMERIC, buffers[0]))
ConvertBases(10);
if (ImGui::InputTextIcon("hex.builtin.tools.base_converter.hex"_lang, ICON_VS_SYMBOL_NUMERIC, buffers[1]))
ConvertBases(16);
if (ImGui::InputTextIcon("hex.builtin.tools.base_converter.oct"_lang, ICON_VS_SYMBOL_NUMERIC, buffers[2]))
ConvertBases(8);
if (ImGui::InputTextIcon("hex.builtin.tools.base_converter.bin"_lang, ICON_VS_SYMBOL_NUMERIC, buffers[3]))
ConvertBases(2);
}
void drawByteSwapper() {
static std::string input, buffer, output;
if (ImGui::InputTextIcon("hex.builtin.tools.input"_lang, ICON_VS_SYMBOL_NUMERIC, input, ImGuiInputTextFlags_CharsHexadecimal)) {
auto nextAlignedSize = std::max<size_t>(2, std::bit_ceil(input.size()));
buffer.clear();
buffer.resize(nextAlignedSize - input.size(), '0');
buffer += input;
output.clear();
for (u32 i = 0; i < buffer.size(); i += 2) {
output += buffer[buffer.size() - i - 2];
output += buffer[buffer.size() - i - 1];
}
}
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().DisabledAlpha);
ImGui::InputTextIcon("hex.builtin.tools.output"_lang, ICON_VS_SYMBOL_NUMERIC, output, ImGuiInputTextFlags_ReadOnly);
ImGui::PopStyleVar();
}
void drawPermissionsCalculator() {
static bool setuid, setgid, sticky;
static bool r[3], w[3], x[3];
ImGui::TextUnformatted("hex.builtin.tools.permissions.perm_bits"_lang);
ImGui::Separator();
if (ImGui::BeginTable("Permissions", 4, ImGuiTableFlags_Borders)) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("Special", ImGuiTableColumnFlags_NoSort);
ImGui::TableSetupColumn("User", ImGuiTableColumnFlags_NoSort);
ImGui::TableSetupColumn("Group", ImGuiTableColumnFlags_NoSort);
ImGui::TableSetupColumn("Other", ImGuiTableColumnFlags_NoSort);
ImGui::TableHeadersRow();
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Checkbox("setuid", &setuid);
ImGui::Checkbox("setgid", &setgid);
ImGui::Checkbox("Sticky bit", &sticky);
for (u8 i = 0; i < 3; i++) {
ImGui::TableNextColumn();
ImGui::PushID(i);
ImGui::Checkbox("Read", &r[i]);
ImGui::Checkbox("Write", &w[i]);
ImGui::Checkbox("Execute", &x[i]);
ImGui::PopID();
}
ImGui::EndTable();
}
ImGui::NewLine();
ImGui::TextUnformatted("hex.builtin.tools.permissions.absolute"_lang);
ImGui::Separator();
auto result = hex::format("{}{}{}{}",
(setuid << 2) | (setgid << 1) | (sticky << 0),
(r[0] << 2) | (w[0] << 1) | (x[0] << 0),
(r[1] << 2) | (w[1] << 1) | (x[1] << 0),
(r[2] << 2) | (w[2] << 1) | (x[2] << 0));
ImGui::InputText("##permissions_absolute", result.data(), result.size(), ImGuiInputTextFlags_ReadOnly);
ImGui::NewLine();
static const auto WarningColor = ImColor(0.92F, 0.25F, 0.2F, 1.0F);
if (setuid && !x[0])
ImGui::TextFormattedColored(WarningColor, "{}", "hex.builtin.tools.permissions.setuid_error"_lang);
if (setgid && !x[1])
ImGui::TextFormattedColored(WarningColor, "{}", "hex.builtin.tools.permissions.setgid_error"_lang);
if (sticky && !x[2])
ImGui::TextFormattedColored(WarningColor, "{}", "hex.builtin.tools.permissions.sticky_error"_lang);
}
void drawFileUploader() {
struct UploadedFile {
std::string fileName, link, size;
};
static HttpRequest request("POST", "https://api.anonfiles.com/upload");
static std::future<HttpRequest::Result<std::string>> uploadProcess;
static std::fs::path currFile;
static std::vector<UploadedFile> links;
bool uploading = uploadProcess.valid() && uploadProcess.wait_for(0s) != std::future_status::ready;
ImGui::Header("hex.builtin.tools.file_uploader.control"_lang, true);
if (!uploading) {
if (ImGui::Button("hex.builtin.tools.file_uploader.upload"_lang)) {
fs::openFileBrowser(fs::DialogMode::Open, {}, [&](auto path) {
uploadProcess = request.uploadFile(path);
currFile = path;
});
}
} else {
if (ImGui::Button("hex.builtin.common.cancel"_lang)) {
request.cancel();
}
}
ImGui::SameLine();
ImGui::ProgressBar(request.getProgress(), ImVec2(0, 0), uploading ? nullptr : "Done!");
ImGui::Header("hex.builtin.tools.file_uploader.recent"_lang);
if (ImGui::BeginTable("##links", 3, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg, ImVec2(0, 200))) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("hex.builtin.common.file"_lang);
ImGui::TableSetupColumn("hex.builtin.common.link"_lang);
ImGui::TableSetupColumn("hex.builtin.common.size"_lang);
ImGui::TableHeadersRow();
ImGuiListClipper clipper;
clipper.Begin(links.size());
while (clipper.Step()) {
for (i32 i = clipper.DisplayEnd - 1; i >= clipper.DisplayStart; i--) {
auto &[fileName, link, size] = links[i];
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted(fileName.c_str());
ImGui::TableNextColumn();
if (ImGui::Hyperlink(link.c_str())) {
if (ImGui::IsKeyDown(ImGuiKey_LeftCtrl))
hex::openWebpage(link);
else
ImGui::SetClipboardText(link.c_str());
}
ImGui::InfoTooltip("hex.builtin.tools.file_uploader.tooltip"_lang);
ImGui::TableNextColumn();
ImGui::TextUnformatted(size.c_str());
}
}
clipper.End();
ImGui::EndTable();
}
if (uploadProcess.valid() && uploadProcess.wait_for(0s) == std::future_status::ready) {
auto response = uploadProcess.get();
if (response.getStatusCode() == 200) {
try {
auto json = nlohmann::json::parse(response.getData());
links.push_back({
wolv::util::toUTF8String(currFile.filename()),
json.at("data").at("file").at("url").at("short"),
json.at("data").at("file").at("metadata").at("size").at("readable")
});
} catch (...) {
PopupError::open("hex.builtin.tools.file_uploader.invalid_response"_lang);
}
} else if (response.getStatusCode() == 0) {
// Canceled by user, no action needed
} else PopupError::open(hex::format("hex.builtin.tools.file_uploader.error"_lang, response.getStatusCode()));
uploadProcess = {};
currFile.clear();
}
}
std::string getWikipediaApiUrl() {
auto setting = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.wiki_explain_language", "en");
return "https://" + setting + ".wikipedia.org/w/api.php?format=json&action=query&prop=extracts&explaintext&redirects=10&formatversion=2";
}
void drawWikiExplainer() {
static HttpRequest request("GET", "");
static std::string resultTitle, resultExtract;
static std::future<HttpRequest::Result<std::string>> searchProcess;
static bool extendedSearch = false;
std::string searchString;
ImGui::Header("hex.builtin.tools.wiki_explain.control"_lang, true);
bool startSearch;
startSearch = ImGui::InputTextIcon("##search", ICON_VS_SYMBOL_KEY, searchString, ImGuiInputTextFlags_EnterReturnsTrue);
ImGui::SameLine();
ImGui::BeginDisabled((searchProcess.valid() && searchProcess.wait_for(0s) != std::future_status::ready) || searchString.empty());
startSearch = ImGui::Button("hex.builtin.tools.wiki_explain.search"_lang) || startSearch;
ImGui::EndDisabled();
if (startSearch && !searchString.empty()) {
request.setUrl(getWikipediaApiUrl() + "&exintro"s + "&titles="s + request.urlEncode(searchString));
searchProcess = request.execute();
}
ImGui::Header("hex.builtin.tools.wiki_explain.results"_lang);
if (ImGui::BeginChild("##summary", ImVec2(0, 300), true)) {
if (!resultTitle.empty() && !resultExtract.empty()) {
ImGui::HeaderColored(resultTitle.c_str(), ImGui::GetCustomColorVec4(ImGuiCustomCol_Highlight), true);
ImGui::TextFormattedWrapped("{}", resultExtract.c_str());
}
}
ImGui::EndChild();
if (searchProcess.valid() && searchProcess.wait_for(0s) == std::future_status::ready) {
try {
auto response = searchProcess.get();
if (response.getStatusCode() != 200) throw std::runtime_error("Invalid response");
auto json = nlohmann::json::parse(response.getData());
resultTitle = json.at("query").at("pages").at(0).at("title").get<std::string>();
resultExtract = json.at("query").at("pages").at(0).at("extract").get<std::string>();
if (!extendedSearch && resultExtract.ends_with(':')) {
extendedSearch = true;
request.setUrl(getWikipediaApiUrl() + "&titles="s + request.urlEncode(searchString));
searchProcess = request.execute();
resultTitle.clear();
resultExtract.clear();
} else {
extendedSearch = false;
searchString.clear();
}
} catch (...) {
resultTitle.clear();
resultExtract.clear();
extendedSearch = false;
searchProcess = {};
resultTitle = "???";
resultExtract = "hex.builtin.tools.wiki_explain.invalid_response"_lang.get();
}
}
}
void drawFileToolShredder() {
static std::u8string selectedFile;
static bool fastMode = false;
static TaskHolder shredderTask;
ImGui::TextUnformatted("hex.builtin.tools.file_tools.shredder.warning"_lang);
ImGui::NewLine();
if (ImGui::BeginChild("settings", { 0, ImGui::GetTextLineHeightWithSpacing() * 4 }, true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
ImGui::BeginDisabled(shredderTask.isRunning());
{
ImGui::TextUnformatted("hex.builtin.tools.file_tools.shredder.input"_lang);
ImGui::SameLine();
ImGui::InputText("##path", selectedFile);
ImGui::SameLine();
if (ImGui::Button("...")) {
fs::openFileBrowser(fs::DialogMode::Open, {}, [](const auto &path) {
selectedFile = path.u8string();
});
}
ImGui::Checkbox("hex.builtin.tools.file_tools.shredder.fast"_lang, &fastMode);
}
ImGui::EndDisabled();
}
ImGui::EndChild();
if (shredderTask.isRunning())
ImGui::TextSpinner("hex.builtin.tools.file_tools.shredder.shredding"_lang);
else {
ImGui::BeginDisabled(selectedFile.empty());
{
if (ImGui::Button("hex.builtin.tools.file_tools.shredder.shred"_lang)) {
shredderTask = TaskManager::createTask("hex.builtin.tools.file_tools.shredder.shredding", 0, [](auto &task) {
ON_SCOPE_EXIT {
selectedFile.clear();
};
wolv::io::File file(selectedFile, wolv::io::File::Mode::Write);
if (!file.isValid()) {
PopupError::open("hex.builtin.tools.file_tools.shredder.error.open"_lang);
return;
}
task.setMaxValue(file.getSize());
std::vector<std::array<u8, 3>> overwritePattern;
if (fastMode) {
/* Should be sufficient for modern disks */
overwritePattern.push_back({ 0x00, 0x00, 0x00 });
overwritePattern.push_back({ 0xFF, 0xFF, 0xFF });
} else {
/* Gutmann's method. Secure for magnetic storage */
std::random_device rd;
std::uniform_int_distribution<u8> dist(0x00, 0xFF);
/* Fill fixed patterns */
overwritePattern = {
{ },
{ },
{},
{},
{ 0x55, 0x55, 0x55 },
{ 0xAA, 0xAA, 0xAA },
{ 0x92, 0x49, 0x24 },
{ 0x49, 0x24, 0x92 },
{ 0x24, 0x92, 0x49 },
{ 0x00, 0x00, 0x00 },
{ 0x11, 0x11, 0x11 },
{ 0x22, 0x22, 0x22 },
{ 0x33, 0x33, 0x44 },
{ 0x55, 0x55, 0x55 },
{ 0x66, 0x66, 0x66 },
{ 0x77, 0x77, 0x77 },
{ 0x88, 0x88, 0x88 },
{ 0x99, 0x99, 0x99 },
{ 0xAA, 0xAA, 0xAA },
{ 0xBB, 0xBB, 0xBB },
{ 0xCC, 0xCC, 0xCC },
{ 0xDD, 0xDD, 0xDD },
{ 0xEE, 0xEE, 0xEE },
{ 0xFF, 0xFF, 0xFF },
{ 0x92, 0x49, 0x24 },
{ 0x49, 0x24, 0x92 },
{ 0x24, 0x92, 0x49 },
{ 0x6D, 0xB6, 0xDB },
{ 0xB6, 0xDB, 0x6D },
{ 0xBD, 0x6D, 0xB6 },
{},
{},
{},
{}
};
/* Fill random patterns */
for (u8 i = 0; i < 4; i++)
overwritePattern[i] = { dist(rd), dist(rd), dist(rd) };
for (u8 i = 0; i < 4; i++)
overwritePattern[overwritePattern.size() - 1 - i] = { dist(rd), dist(rd), dist(rd) };
}
size_t fileSize = file.getSize();
for (const auto &pattern : overwritePattern) {
for (u64 offset = 0; offset < fileSize; offset += 3) {
file.writeBuffer(pattern.data(), std::min<u64>(pattern.size(), fileSize - offset));
task.update(offset);
}
file.flush();
}
file.remove();
PopupInfo::open("hex.builtin.tools.file_tools.shredder.success"_lang);
});
}
}
ImGui::EndDisabled();
}
}
void drawFileToolSplitter() {
std::array sizeText = {
(const char *)"hex.builtin.tools.file_tools.splitter.sizes.5_75_floppy"_lang,
(const char *)"hex.builtin.tools.file_tools.splitter.sizes.3_5_floppy"_lang,
(const char *)"hex.builtin.tools.file_tools.splitter.sizes.zip100"_lang,
(const char *)"hex.builtin.tools.file_tools.splitter.sizes.zip200"_lang,
(const char *)"hex.builtin.tools.file_tools.splitter.sizes.cdrom650"_lang,
(const char *)"hex.builtin.tools.file_tools.splitter.sizes.cdrom700"_lang,
(const char *)"hex.builtin.tools.file_tools.splitter.sizes.fat32"_lang,
(const char *)"hex.builtin.tools.file_tools.splitter.sizes.custom"_lang
};
std::array<u64, sizeText.size()> sizes = {
1200_KiB,
1400_KiB,
100_MiB,
200_MiB,
650_MiB,
700_MiB,
4_GiB,
1
};
static std::u8string selectedFile;
static std::u8string baseOutputPath;
static u64 splitSize = sizes[0];
static int selectedItem = 0;
static TaskHolder splitterTask;
if (ImGui::BeginChild("split_settings", { 0, ImGui::GetTextLineHeightWithSpacing() * 7 }, true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
ImGui::BeginDisabled(splitterTask.isRunning());
{
ImGui::InputText("##path", selectedFile);
ImGui::SameLine();
if (ImGui::Button("...##input")) {
fs::openFileBrowser(fs::DialogMode::Open, {}, [](const auto &path) {
selectedFile = path.u8string();
});
}
ImGui::SameLine();
ImGui::TextUnformatted("hex.builtin.tools.file_tools.splitter.input"_lang);
ImGui::InputText("##base_path", baseOutputPath);
ImGui::SameLine();
if (ImGui::Button("...##output")) {
fs::openFileBrowser(fs::DialogMode::Save, {}, [](const auto &path) {
baseOutputPath = path.u8string();
});
}
ImGui::SameLine();
ImGui::TextUnformatted("hex.builtin.tools.file_tools.splitter.output"_lang);
ImGui::Separator();
if (ImGui::Combo("###part_size", &selectedItem, sizeText.data(), sizeText.size())) {
splitSize = sizes[selectedItem];
}
}
ImGui::EndDisabled();
ImGui::BeginDisabled(splitterTask.isRunning() || selectedItem != sizes.size() - 1);
{
ImGui::InputScalar("###custom_size", ImGuiDataType_U64, &splitSize);
ImGui::SameLine();
ImGui::TextUnformatted("Bytes");
}
ImGui::EndDisabled();
}
ImGui::EndChild();
ImGui::BeginDisabled(selectedFile.empty() || baseOutputPath.empty() || splitSize == 0);
{
if (splitterTask.isRunning())
ImGui::TextSpinner("hex.builtin.tools.file_tools.splitter.picker.splitting"_lang);
else {
if (ImGui::Button("hex.builtin.tools.file_tools.splitter.picker.split"_lang)) {
splitterTask = TaskManager::createTask("hex.builtin.tools.file_tools.splitter.picker.splitting", 0, [](auto &task) {
ON_SCOPE_EXIT {
selectedFile.clear();
baseOutputPath.clear();
};
wolv::io::File file(selectedFile, wolv::io::File::Mode::Read);
if (!file.isValid()) {
PopupError::open("hex.builtin.tools.file_tools.splitter.picker.error.open"_lang);
return;
}
if (file.getSize() < splitSize) {
PopupError::open("hex.builtin.tools.file_tools.splitter.picker.error.size"_lang);
return;
}
task.setMaxValue(file.getSize());
u32 index = 1;
for (u64 offset = 0; offset < file.getSize(); offset += splitSize) {
task.update(offset);
std::fs::path path = baseOutputPath;
path += hex::format(".{:05}", index);
wolv::io::File partFile(path, wolv::io::File::Mode::Create);
if (!partFile.isValid()) {
PopupError::open(hex::format("hex.builtin.tools.file_tools.splitter.picker.error.create"_lang, index));
return;
}
constexpr static auto BufferSize = 0xFF'FFFF;
for (u64 partOffset = 0; partOffset < splitSize; partOffset += BufferSize) {
partFile.writeVector(file.readVector(std::min<u64>(BufferSize, splitSize - partOffset)));
partFile.flush();
}
index++;
}
PopupInfo::open("hex.builtin.tools.file_tools.splitter.picker.success"_lang);
});
}
}
}
ImGui::EndDisabled();
}
void drawFileToolCombiner() {
static std::vector<std::fs::path> files;
static std::u8string outputPath;
static u32 selectedIndex;
static TaskHolder combinerTask;
if (ImGui::BeginTable("files_table", 2, ImGuiTableFlags_SizingStretchProp)) {
ImGui::TableSetupColumn("file list", ImGuiTableColumnFlags_NoHeaderLabel, 10);
ImGui::TableSetupColumn("buttons", ImGuiTableColumnFlags_NoHeaderLabel, 1);
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::BeginListBox("##files", { -FLT_MIN, 10 * ImGui::GetTextLineHeightWithSpacing() })) {
u32 index = 0;
for (auto &file : files) {
if (ImGui::Selectable(wolv::util::toUTF8String(file).c_str(), index == selectedIndex))
selectedIndex = index;
index++;
}
ImGui::EndListBox();
}
ImGui::TableNextColumn();
ImGui::BeginDisabled(selectedIndex <= 0);
{
if (ImGui::ArrowButton("move_up", ImGuiDir_Up)) {
std::iter_swap(files.begin() + selectedIndex, files.begin() + selectedIndex - 1);
selectedIndex--;
}
}
ImGui::EndDisabled();
ImGui::BeginDisabled(files.empty() || selectedIndex >= files.size() - 1);
{
if (ImGui::ArrowButton("move_down", ImGuiDir_Down)) {
std::iter_swap(files.begin() + selectedIndex, files.begin() + selectedIndex + 1);
selectedIndex++;
}
}
ImGui::EndDisabled();
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::BeginDisabled(combinerTask.isRunning());
{
if (ImGui::Button("hex.builtin.tools.file_tools.combiner.add"_lang)) {
fs::openFileBrowser(fs::DialogMode::Open, {}, [](const auto &path) {
files.push_back(path);
});
}
ImGui::SameLine();
if (ImGui::Button("hex.builtin.tools.file_tools.combiner.delete"_lang)) {
files.erase(files.begin() + selectedIndex);
selectedIndex--;
}
ImGui::SameLine();
if (ImGui::Button("hex.builtin.tools.file_tools.combiner.clear"_lang)) {
files.clear();
}
}
ImGui::EndDisabled();
ImGui::EndTable();
}
ImGui::BeginDisabled(combinerTask.isRunning());
{
ImGui::InputText("##output_path", outputPath);
ImGui::SameLine();
if (ImGui::Button("...")) {
fs::openFileBrowser(fs::DialogMode::Save, {}, [](const auto &path) {
outputPath = path.u8string();
});
}
ImGui::SameLine();
ImGui::TextUnformatted("hex.builtin.tools.file_tools.combiner.output"_lang);
}
ImGui::EndDisabled();
ImGui::BeginDisabled(files.empty() || outputPath.empty());
{
if (combinerTask.isRunning())
ImGui::TextSpinner("hex.builtin.tools.file_tools.combiner.combining"_lang);
else {
if (ImGui::Button("hex.builtin.tools.file_tools.combiner.combine"_lang)) {
combinerTask = TaskManager::createTask("hex.builtin.tools.file_tools.combiner.combining", 0, [](auto &task) {
wolv::io::File output(outputPath, wolv::io::File::Mode::Create);
if (!output.isValid()) {
PopupError::open("hex.builtin.tools.file_tools.combiner.error.open_output"_lang);
return;
}
task.setMaxValue(files.size());
u64 fileIndex = 0;
for (const auto &file : files) {
task.update(fileIndex);
fileIndex++;
wolv::io::File input(file, wolv::io::File::Mode::Read);
if (!input.isValid()) {
PopupError::open(hex::format("hex.builtin.tools.file_tools.combiner.open_input"_lang, wolv::util::toUTF8String(file)));
return;
}
constexpr static auto BufferSize = 0xFF'FFFF;
auto inputSize = input.getSize();
for (u64 inputOffset = 0; inputOffset < inputSize; inputOffset += BufferSize) {
output.writeVector(input.readVector(std::min<u64>(BufferSize, inputSize - inputOffset)));
output.flush();
}
}
files.clear();
selectedIndex = 0;
outputPath.clear();
PopupInfo::open("hex.builtin.tools.file_tools.combiner.success"_lang);
});
}
}
}
ImGui::EndDisabled();
}
void drawFileTools() {
if (ImGui::BeginTabBar("file_tools_tabs")) {
if (ImGui::BeginTabItem("hex.builtin.tools.file_tools.shredder"_lang)) {
drawFileToolShredder();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("hex.builtin.tools.file_tools.splitter"_lang)) {
drawFileToolSplitter();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("hex.builtin.tools.file_tools.combiner"_lang)) {
drawFileToolCombiner();
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
}
// Tool for converting between different number formats
// There are three places where input can be changed; the bit checkboxes, the hex input and the decimal input.
// The bit checkboxes and the hex input are directly related and can be converted between each other easily.
// The decimal input is a bit more complicated. IEEE 754 floating point numbers are represented as a sign bit,
// an exponent and a mantissa. For details see https://en.wikipedia.org/wiki/IEEE_754.
// Workflow is as follows:
// From the bit checkboxes determine the integer hex value. This is straightforward.
// From the hex value determine the binary floating point value by extracting the sign, exponent and mantissa.
// From the binary floating point value determine the decimal floating point value using third party library.
// From the decimal floating point we reconstruct the binary floating point value using internal hardware.
// If format is non-standard the reconstruction is done using properties of the format.
void drawIEEE754Decoder() {
static u128 value = 0;
static int exponentBitCount = 8, mantissaBitCount = 23;
long double exponentValue;
long double mantissaValue;
static long double resultFloat;
i64 exponentBias;
constexpr static auto flags = ImGuiInputTextFlags_EnterReturnsTrue;
enum class NumberKind {
Normal,
Zero,
Denormal,
Infinity,
NaN,
} numberKind;
enum class NumberType {
Regular,
SignalingNaN,
QuietNaN,
NegativeInfinity,
PositiveInfinity,
} numberType;
const static auto BitCheckbox = [](u8 bit) {
bool checkbox = false;
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
checkbox = (value & (u128(1) << bit)) != 0;
ImGui::BitCheckbox("##checkbox", &checkbox);
value = (value & ~(u128(1) << bit)) | (u128(checkbox) << bit);
ImGui::PopStyleVar();
};
const static auto BitCheckboxes = [](u32 startBit, u32 count) {
for (u32 i = 0; i < count; i++) {
ImGui::PushID(startBit - i);
BitCheckbox(startBit - i);
ImGui::SameLine(0, 0);
ImGui::PopID();
}
};
ImVec4 textColor = ImGui::GetStyleColorVec4(ImGuiCol_Text);
const auto totalBitCount = exponentBitCount + mantissaBitCount;
const auto signBitPosition = totalBitCount - 0;
const auto exponentBitPosition = totalBitCount - 1;
const auto mantissaBitPosition = totalBitCount - 1 - exponentBitCount;
const static auto ExtractBits = [](u32 startBit, u32 count) {
return hex::extract(startBit, startBit - (count - 1), value);
};
const static auto DisplayBitLabels = [](u32 startBit, u32 count) {
// In this case we always label one box
if (count < 4) {
std::string labelString = "xx";
auto checkBoxWidth = ImGui::CalcTextSize(labelString.c_str()).x;
auto centerBox = count == 3 || count == 2 ? 1 : 0;
auto labelIndex = startBit - centerBox;
labelString = fmt::format("{}", labelIndex);
auto labelWidth = ImGui::CalcTextSize(labelString.c_str()).x;
auto indentSize = checkBoxWidth * centerBox;
auto centeredIndentSize = indentSize + checkBoxWidth / 2 - labelWidth / 2;
// Fix for imgui reposition bug only happens if first checkbox has label
if (centeredIndentSize == 0)
centeredIndentSize -= 1;
ImGui::Indent(centeredIndentSize);
ImGui::TextUnformatted(labelString.c_str());
ImGui::Unindent(centeredIndentSize);
} else {
auto columnWidth = ImGui::GetColumnWidth();
auto checkBoxWidth = columnWidth / count;
auto labelIndex = startBit - startBit % 4;
std::string labelString = fmt::format("{}", labelIndex);
auto labelWidth = ImGui::CalcTextSize(labelString.c_str()).x;
// indent size for checkbox
auto indentSize = (startBit % 4) * checkBoxWidth;
auto centeredIndentSize = indentSize + checkBoxWidth / 2 - labelWidth / 2;
// Fix for imgui reposition bug only happens if first checkbox has label
if (centeredIndentSize == 0)
centeredIndentSize -= 1;
ImGui::Indent(centeredIndentSize);
ImGui::TextUnformatted(labelString.c_str());
ImGui::Unindent(centeredIndentSize);
auto boxesLeft = count - (startBit % 4);
auto labelsLeft = boxesLeft / 4;
// If we have a multiple of 4 boxes left the last label belongs to mantissa
if (boxesLeft % 4 == 0)
labelsLeft--;
for (i64 i = 0; i < labelsLeft; i++) {
ImGui::SameLine();
labelIndex -= 4;
labelString = fmt::format("{}", labelIndex);
labelWidth = ImGui::CalcTextSize(labelString.c_str()).x;
indentSize += 4 * checkBoxWidth;
ImGui::Indent(indentSize + checkBoxWidth / 2 - labelWidth / 2);
ImGui::TextFormatted("{}", labelIndex);
ImGui::Unindent(indentSize + checkBoxWidth / 2 - labelWidth / 2);
}
}
};
i64 signBits = ExtractBits(signBitPosition, 1);
i64 exponentBits = ExtractBits(exponentBitPosition, exponentBitCount);
i64 mantissaBits = ExtractBits(mantissaBitPosition, mantissaBitCount);
static i64 inputFieldWidth = 0;
ImGui::TextFormattedWrapped("{}", "hex.builtin.tools.ieee754.description"_lang);
ImGui::NewLine();
static i64 displayMode = ContentRegistry::Settings::read("hex.builtin.tools.ieee754.settings", "display_mode", 0);
i64 displayModeTemp = displayMode;
ImGui::RadioButton("hex.builtin.tools.ieee754.settings.display_mode.detailed"_lang, reinterpret_cast<int *>(&displayMode), 0);
ImGui::SameLine();
ImGui::RadioButton("hex.builtin.tools.ieee754.settings.display_mode.simplified"_lang, reinterpret_cast<int *>(&displayMode), 1);
if (displayModeTemp != displayMode) {
ContentRegistry::Settings::write("hex.builtin.tools.ieee754.settings", "display_mode", displayMode);
displayModeTemp = displayMode;
}
auto tableFlags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoKeepColumnsVisible | ImGuiTableFlags_ScrollX;
if (ImGui::BeginTable("##outer", 8, tableFlags, ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 5.5 ))) {
ImGui::TableSetupColumn("hex.builtin.tools.ieee754.result.title"_lang);
ImGui::TableSetupColumn("##equals");
ImGui::TableSetupColumn("hex.builtin.tools.ieee754.sign"_lang);
ImGui::TableSetupColumn("##times");
ImGui::TableSetupColumn("hex.builtin.tools.ieee754.exponent"_lang);
ImGui::TableSetupColumn("##times");
ImGui::TableSetupColumn("hex.builtin.tools.ieee754.mantissa"_lang);
ImGui::TableHeadersRow();
ImGui::TableNextRow();
// Row for bit labels. Due to font size constrains each bit cannot have its own label.
// Instead, we label each 4 bits and then use the bit position to determine the bit label.
// Result
ImGui::TableNextColumn();
// Equals
ImGui::TableNextColumn();
// Sign bit label is always shown
ImGui::TableNextColumn();
std::string labelString = fmt::format("{}",totalBitCount+1);
auto columnWidth = ImGui::GetColumnWidth();
auto checkBoxWidth = columnWidth - 20_scaled;
auto labelWidth = ImGui::CalcTextSize(labelString.c_str()).x;
auto indentSize = 20_scaled + checkBoxWidth /2 - labelWidth / 2;
ImGui::Indent(indentSize);
ImGui::TextUnformatted(labelString.c_str());
ImGui::Unindent(indentSize);
// Times
ImGui::TableNextColumn();
// Exponent
ImGui::TableNextColumn();
DisplayBitLabels(exponentBitPosition + 1, exponentBitCount);
// Times
ImGui::TableNextColumn();
// Mantissa
ImGui::TableNextColumn();
DisplayBitLabels(mantissaBitPosition + 1, mantissaBitCount);
ImGui::TableNextRow();
// Row for bit checkboxes
// Result
ImGui::TableNextColumn();
u64 mask = hex::bitmask(totalBitCount+1);
std::string maskString = hex::format("0x{:X} ", mask);
auto style = ImGui::GetStyle();
inputFieldWidth = std::fmax(inputFieldWidth, ImGui::CalcTextSize(maskString.c_str()).x + style.FramePadding.x * 2.0f);
ImGui::PushItemWidth(inputFieldWidth);
u64 newValue = value & mask;
if (ImGui::InputHexadecimal("##hex", &newValue, flags))
value = newValue;
ImGui::PopItemWidth();
// Equals
ImGui::TableNextColumn();
ImGui::Text("=");
// Sign
ImGui::TableNextColumn();
ImVec4 signColor = ImGui::GetCustomColorVec4(ImGuiCustomCol_IEEEToolSign);
ImVec4 expColor = ImGui::GetCustomColorVec4(ImGuiCustomCol_IEEEToolExp);
ImVec4 mantColor = ImGui::GetCustomColorVec4(ImGuiCustomCol_IEEEToolMantissa);
ImVec4 black = ImVec4(0.0, 0.0, 0.0, 1.0);
ImGui::Indent(20_scaled);
ImGui::PushStyleColor(ImGuiCol_FrameBg, signColor);
ImGui::PushStyleColor(ImGuiCol_Border, black);
BitCheckboxes(signBitPosition, 1);
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::Unindent(20_scaled);
// Times
ImGui::TableNextColumn();
// Exponent
ImGui::TableNextColumn();
ImGui::PushStyleColor(ImGuiCol_FrameBg, expColor);
ImGui::PushStyleColor(ImGuiCol_Border, black);
BitCheckboxes(exponentBitPosition, exponentBitCount);
ImGui::PopStyleColor();
ImGui::PopStyleColor();
// Times
ImGui::TableNextColumn();
// Mantissa
ImGui::TableNextColumn();
ImGui::PushStyleColor(ImGuiCol_FrameBg, mantColor);
ImGui::PushStyleColor(ImGuiCol_Border, black);
BitCheckboxes(mantissaBitPosition, mantissaBitCount);
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::TableNextRow();
ImGui::TableNextColumn();
exponentBias = (u128(1) << (exponentBitCount - 1)) - 1;
long double signValue = signBits == 0 ? 1.0 : -1.0;
// Zero or denormal
if (exponentBits == 0) {
// result doesn't fit in 128 bits
if ((exponentBias - 1) > 128)
exponentValue = std::pow(2.0L, static_cast<long double>(-exponentBias + 1));
else {
if (exponentBias == 0) {
// exponent is zero
if (mantissaBits == 0)
exponentValue = 1.0;
else
// exponent is one
exponentValue = 2.0;
}
else
exponentValue = 1.0 / static_cast<long double>(u128(1) << (exponentBias - 1));
}
}
// Normal
else {
// result doesn't fit in 128 bits
if (std::abs(exponentBits - exponentBias) > 128)
exponentValue = std::pow(2.0L, static_cast<long double>(exponentBits - exponentBias));
//result fits in 128 bits
else {
// exponent is positive
if (exponentBits > exponentBias)
exponentValue = static_cast<long double>(u128(1) << (exponentBits - exponentBias));
// exponent is negative
else if (exponentBits < exponentBias)
exponentValue = 1.0 / static_cast<long double>(u128(1) << (exponentBias - exponentBits));
// exponent is zero
else exponentValue = 1.0;
}
}
mantissaValue = static_cast<long double>(mantissaBits) / static_cast<long double>(u128(1) << (mantissaBitCount));
if (exponentBits != 0)
mantissaValue += 1.0;
// Check if all exponent bits are set.
if (std::popcount(static_cast<u64>(exponentBits)) == static_cast<i64>(exponentBitCount)) {
// if fraction is zero number is infinity.
if (mantissaBits == 0) {
if (signBits == 0) {
numberType = NumberType::PositiveInfinity;
resultFloat = std::numeric_limits<long double>::infinity();
} else {
numberType = NumberType::NegativeInfinity;
resultFloat = -std::numeric_limits<long double>::infinity();
}
numberKind = NumberKind::Infinity;
// otherwise number is NaN.
} else {
if (mantissaBits & (u128(1) << (mantissaBitCount - 1))) {
numberType = NumberType::QuietNaN;
resultFloat = std::numeric_limits<long double>::quiet_NaN();
} else {
numberType = NumberType::SignalingNaN;
resultFloat = std::numeric_limits<long double>::signaling_NaN();
}
numberKind = NumberKind::NaN;
}
// if all exponent bits are zero, but we have a non-zero fraction
// then the number is denormal which are smaller than regular numbers
// but not as precise.
} else if (exponentBits == 0 && mantissaBits != 0) {
numberKind = NumberKind::Denormal;
numberType = NumberType::Regular;
resultFloat = signValue * exponentValue * mantissaValue;
} else {
numberKind = NumberKind::Normal;
numberType = NumberType::Regular;
resultFloat = signValue * exponentValue * mantissaValue;
}
i64 precision;
if (numberKind == NumberKind::Denormal)
precision = std::ceil(1+mantissaBitCount * std::log10(2.0L));
else
precision = std::ceil(1+(mantissaBitCount + 1) * std::log10(2.0L));
// For C++ from_chars is better than strtold.
// the main problem is that from_chars will not process special numbers
// like inf and nan, so we handle them manually
static std::string decimalFloatingPointNumberString;
static std::string_view decimalStrView;
// use qnan for quiet NaN and snan for signaling NaN
if (numberKind == NumberKind::NaN) {
if (numberType == NumberType::QuietNaN)
decimalFloatingPointNumberString = "qnan";
else
decimalFloatingPointNumberString = "snan";
} else
decimalFloatingPointNumberString = fmt::format("{:.{}}", resultFloat, precision);
auto style1 = ImGui::GetStyle();
inputFieldWidth = std::fmax(inputFieldWidth, ImGui::CalcTextSize(decimalFloatingPointNumberString.c_str()).x + 2 * style1.FramePadding.x);
ImGui::PushItemWidth(inputFieldWidth);
enum class InputType { infinity, notANumber, quietNotANumber, signalingNotANumber, regular, invalid };
std::string specialNumbers[] = {"inf", "Inf", "INF", "nan", "Nan", "NAN", "qnan", "Qnan", "QNAN", "snan", "Snan", "SNAN"};
// We allow any input in order to accept infinities and NaNs, all invalid entries
// are detected by from_chars. You can also enter -0 or -inf.
std::from_chars_result res;
if (ImGui::InputText("##resultFloat", decimalFloatingPointNumberString, flags)) {
// Always obtain sign first.
if (decimalFloatingPointNumberString[0] == '-') {
// and remove it from the string.
signBits = 1;
decimalFloatingPointNumberString.erase(0, 1);
} else
//important to switch from - to +.
signBits = 0;
InputType inputType;
bool matchFound = false;
i32 i;
// detect and use special numbers.
for (i = 0; i < 12; i++) {
if (decimalFloatingPointNumberString == specialNumbers[i]) {
inputType = InputType(i/3);
matchFound = true;
break;
}
}
if (!matchFound)
inputType = InputType::regular;
if (inputType == InputType::regular) {
decimalStrView = decimalFloatingPointNumberString;
res = std::from_chars(decimalStrView.data(), decimalStrView.data() + decimalStrView.size(), resultFloat);
// this is why we use from_chars
if (res.ec != std::errc()) {
inputType = InputType::invalid;
}
} else if (inputType == InputType::infinity) {
resultFloat = std::numeric_limits<long double>::infinity();
resultFloat *= (signBits == 1 ? -1 : 1);
} else if (inputType == InputType::notANumber)
resultFloat = std::numeric_limits<long double>::quiet_NaN();
else if (inputType == InputType::quietNotANumber)
resultFloat = std::numeric_limits<long double>::quiet_NaN();
else if (inputType == InputType::signalingNotANumber)
resultFloat = std::numeric_limits<long double>::signaling_NaN();
long double log2Result;
if (inputType != InputType::invalid) {
// deal with zero first so we can use log2.
if (resultFloat == 0.0) {
if (signBits == 1)
resultFloat = -0.0;
else
resultFloat = 0.0;
numberKind = NumberKind::Zero;
numberType = NumberType::Regular;
exponentBits = 0;
mantissaBits = 0;
} else {
log2Result = std::log2(resultFloat);
// 2^(bias+1)-2^(bias-prec) is the largest number that can be represented.
// If the number entered is larger than this then the input is set to infinity.
if (resultFloat > (std::pow(2.0L, exponentBias + 1) - std::pow(2.0L, exponentBias - mantissaBitCount)) || inputType == InputType::infinity ) {
resultFloat = std::numeric_limits<long double>::infinity();
numberKind = NumberKind::Infinity;
numberType = signBits == 1 ? NumberType::NegativeInfinity : NumberType::PositiveInfinity;
exponentBits = (u128(1) << exponentBitCount) - 1;
mantissaBits = 0;
} else if (-std::rint(log2Result) > exponentBias + mantissaBitCount - 1) {
// 1/2^(bias-1+prec) is the smallest number that can be represented.
// If the number entered is smaller than this then the input is set to zero.
if (signBits == 1)
resultFloat = -0.0;
else
resultFloat = 0.0;
numberKind = NumberKind::Zero;
numberType = NumberType::Regular;
exponentBits = 0;
mantissaBits = 0;
} else if (inputType == InputType::signalingNotANumber) {
resultFloat = std::numeric_limits<long double>::signaling_NaN();
numberType = NumberType::SignalingNaN;
numberKind = NumberKind::NaN;
exponentBits = (u128(1) << exponentBitCount) - 1;
mantissaBits = 1;
} else if (inputType == InputType::quietNotANumber || inputType == InputType::notANumber ) {
resultFloat = std::numeric_limits<long double>::quiet_NaN();
numberType = NumberType::QuietNaN;
numberKind = NumberKind::NaN;
exponentBits = (u128(1) << exponentBitCount) - 1;
mantissaBits = (u128(1) << (mantissaBitCount - 1));
} else if (static_cast<i64>(std::floor(log2Result)) + exponentBias <= 0) {
numberKind = NumberKind::Denormal;
numberType = NumberType::Regular;
exponentBits = 0;
auto mantissaExp = log2Result + exponentBias + mantissaBitCount - 1;
mantissaBits = static_cast<i64>(std::round(std::pow(2.0L, mantissaExp)));
} else {
numberType = NumberType::Regular;
numberKind = NumberKind::Normal;
i64 unBiasedExponent = static_cast<i64>(std::floor(log2Result));
exponentBits = unBiasedExponent + exponentBias;
mantissaValue = resultFloat * std::pow(2.0L, -unBiasedExponent) - 1;
mantissaBits = static_cast<i64>(std::round( static_cast<long double>(u128(1) << (mantissaBitCount)) * mantissaValue));
}
}
// Put the bits together.
value = (signBits << (totalBitCount)) | (exponentBits << (totalBitCount - exponentBitCount)) | mantissaBits;
}
}
ImGui::PopItemWidth();
if (displayMode == 0) {
unsigned signColorU32 = ImGui::GetCustomColorU32(ImGuiCustomCol_IEEEToolSign);
unsigned expColorU32 = ImGui::GetCustomColorU32(ImGuiCustomCol_IEEEToolExp);
unsigned mantColorU32 = ImGui::GetCustomColorU32(ImGuiCustomCol_IEEEToolMantissa);
ImGui::TableNextColumn();
ImGui::Text("=");
// Sign
ImGui::TableNextColumn();
// this has the effect of dimming the color of the numbers so user doesn't try
// to interact with them.
ImGui::BeginDisabled();
ImGui::PushStyleColor(ImGuiCol_Text, textColor);
ImGui::Indent(20_scaled);
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, signColorU32);
if (signBits == 1)
ImGui::Text("-1");
else
ImGui::Text("+1");
ImGui::Unindent(20_scaled);
//times
ImGui::TableNextColumn();
ImGui::Text("x");
ImGui::TableNextColumn();
// Exponent
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, expColorU32);
ImGui::Indent(20_scaled);
if (numberKind == NumberKind::NaN) {
if (numberType == NumberType::QuietNaN) {
ImGui::Text("qNaN");
} else {
ImGui::Text("sNaN");
}
} else if (numberKind == NumberKind::Infinity) {
ImGui::Text("Inf");
} else if (numberKind == NumberKind::Zero) {
ImGui::Text("0");
} else if (numberKind == NumberKind::Denormal) {
ImGui::TextFormatted("2^{0}", 1 - exponentBias);
} else {
ImGui::TextFormatted("2^{0}", exponentBits - exponentBias);
}
ImGui::Unindent(20_scaled);
//times
ImGui::TableNextColumn();
ImGui::Text("x");
ImGui::TableNextColumn();
// Mantissa
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, mantColorU32);
ImGui::Indent(20_scaled);
ImGui::TextFormatted("{:.{}}", mantissaValue,precision);
ImGui::Unindent(20_scaled);
ImGui::PopStyleColor();
ImGui::EndDisabled();
}
ImGui::EndTable();
}
// we are done. The rest selects the format if user interacts with the widgets.
// If precision and exponent match one of the IEEE 754 formats the format is highlighted
// and remains highlighted until user changes to a different format. Matching formats occur when
// the user clicks on one of the selections or if the slider values match the format in question.
// when a new format is selected it may have a smaller number of digits than
// the previous selection. Since the largest of the hexadecimal and the decimal
// representation widths sets both field widths to the same value we need to
// reset it here when a new choice is set.
if (ImGui::SliderInt("hex.builtin.tools.ieee754.exponent_size"_lang, &exponentBitCount, 1, 63 - mantissaBitCount))
inputFieldWidth = 0;
if (ImGui::SliderInt("hex.builtin.tools.ieee754.mantissa_size"_lang, &mantissaBitCount, 1, 63 - exponentBitCount))
inputFieldWidth = 0;
ImGui::Separator();
auto color = ImGui::GetColorU32(ImGuiCol_ButtonActive);
bool needsPop = false;
if (exponentBitCount == 5 && mantissaBitCount == 10) {
ImGui::PushStyleColor(ImGuiCol_Button, color);
needsPop = true;
}
if (ImGui::Button("hex.builtin.tools.ieee754.half_precision"_lang)) {
exponentBitCount = 5;
mantissaBitCount = 10;
inputFieldWidth = 0;
}
if (needsPop) ImGui::PopStyleColor();
ImGui::SameLine();
needsPop = false;
if (exponentBitCount == 8 && mantissaBitCount == 23) {
ImGui::PushStyleColor(ImGuiCol_Button, color);
needsPop = true;
}
if (ImGui::Button("hex.builtin.tools.ieee754.single_precision"_lang)) {
exponentBitCount = 8;
mantissaBitCount = 23;
inputFieldWidth = 0;
}
if (needsPop) ImGui::PopStyleColor();
ImGui::SameLine();
needsPop = false;
if (exponentBitCount == 11 && mantissaBitCount == 52) {
ImGui::PushStyleColor(ImGuiCol_Button, color);
needsPop = true;
}
if (ImGui::Button("hex.builtin.tools.ieee754.double_precision"_lang)) {
exponentBitCount = 11;
mantissaBitCount = 52;
inputFieldWidth = 0;
}
if (needsPop) ImGui::PopStyleColor();
ImGui::SameLine();
needsPop = false;
if (ImGui::Button("hex.builtin.tools.ieee754.clear"_lang))
//this will reset all interactive widgets to zero.
value = 0;
ImGui::Separator();
ImGui::NewLine();
}
void drawInvariantMultiplicationDecoder() {
static u64 divisor = 1;
static u64 multiplier = 1;
static u64 numBits = 32;
ImGui::TextFormattedWrapped("{}", "hex.builtin.tools.invariant_multiplication.description"_lang);
ImGui::NewLine();
if (ImGui::BeginChild("##calculator", ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 5), true)) {
static const u64 min = 1, max = 64;
ImGui::SliderScalar("hex.builtin.tools.invariant_multiplication.num_bits"_lang, ImGuiDataType_U64, &numBits, &min, &max);
ImGui::NewLine();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_TableRowBgAlt));
if (ImGui::BeginChild("##calculator", ImVec2(0, ImGui::GetTextLineHeightWithSpacing() + 12_scaled), true)) {
ImGui::PushItemWidth(100_scaled);
ImGui::TextUnformatted("X /");
ImGui::SameLine();
if (ImGui::InputScalar("##divisor", ImGuiDataType_U64, &divisor)) {
if (divisor == 0)
divisor = 1;
multiplier = ((1LLU << (numBits + 1)) / divisor) + 1;
}
ImGui::SameLine();
ImGui::TextUnformatted(" <=> ");
ImGui::SameLine();
ImGui::TextUnformatted("( X *");
ImGui::SameLine();
if (ImGui::InputHexadecimal("##multiplier", &multiplier)) {
if (multiplier == 0)
multiplier = 1;
divisor = ((1LLU << (numBits + 1)) / multiplier) + 1;
}
ImGui::SameLine();
ImGui::TextFormatted(") >> {}", numBits + 1);
ImGui::PopItemWidth();
}
ImGui::EndChild();
ImGui::PopStyleColor();
ImGui::PopStyleVar();
}
ImGui::EndChild();
}
}
void registerToolEntries() {
ContentRegistry::Tools::add("hex.builtin.tools.demangler", drawDemangler);
ContentRegistry::Tools::add("hex.builtin.tools.ascii_table", drawASCIITable);
ContentRegistry::Tools::add("hex.builtin.tools.regex_replacer", drawRegexReplacer);
ContentRegistry::Tools::add("hex.builtin.tools.color", drawColorPicker);
ContentRegistry::Tools::add("hex.builtin.tools.calc", drawMathEvaluator);
ContentRegistry::Tools::add("hex.builtin.tools.base_converter", drawBaseConverter);
ContentRegistry::Tools::add("hex.builtin.tools.byte_swapper", drawByteSwapper);
ContentRegistry::Tools::add("hex.builtin.tools.permissions", drawPermissionsCalculator);
ContentRegistry::Tools::add("hex.builtin.tools.file_uploader", drawFileUploader);
ContentRegistry::Tools::add("hex.builtin.tools.wiki_explain", drawWikiExplainer);
ContentRegistry::Tools::add("hex.builtin.tools.file_tools", drawFileTools);
ContentRegistry::Tools::add("hex.builtin.tools.ieee754", drawIEEE754Decoder);
ContentRegistry::Tools::add("hex.builtin.tools.invariant_multiplication", drawInvariantMultiplicationDecoder);
}
}