1
0
mirror of synced 2025-01-25 15:53:43 +01:00

feat: Added export disassembler results to ASM file (#1987)

### Problem description
<!-- Describe the bug that you fixed/feature request that you
implemented, or link to an existing issue describing it -->
This PR implements the feature request #1781, that suggests adding a
button to export disassembled instructions into an ASM file.

### Implementation description
This adds a button to export the current disassembled instructions to an
ASM file. Said file is suffixed by an `.asm` extension if not specified
at file creation.

*Note: the file is written to for every `Disassembly` item in the
vector, as it was the easiest and most memory-conservative way of doing
it.*

The file creation task is implemented based on IPS patch exports, so it
fits the same pattern.
A `ToastError` is raised when the ASM export could not complete
successfully.

Translations have been implemented for both `en_US` and `de_DE` for the
two new keys:
- `hex.disassembler.view.disassembler.export`: file export button
- `hex.disassembler.view.disassembler.export.popup.error`: error popup
text

### Screenshots
The button is disabled when the disassembler is working, or when the
disassembly vector is empty.

Here is a complete breakdown of the visual changes:

![image](https://github.com/user-attachments/assets/af0ce701-9d77-45f1-9a5a-90d68d00bb0d)

### Additional things
As expected, the exporter writes every item's `mnemonic` and `operators`
to the file, producing an output like this:

`example.asm`
```asm
.byte 0x7f, 0x45, 0x4c, 0x46
andeq r0, r1, r2, lsl #2
andeq r0, r0, r0
andeq r0, r0, r0
eorseq r0, lr, r3
andeq r0, r0, r1
andeq r1, r0, r0, asr #32
andeq r0, r0, r0
andeq r0, r0, r0, asr #32
```

---------

Signed-off-by: BioTheWolff <47079795+BioTheWolff@users.noreply.github.com>
This commit is contained in:
BioTheWolff 2024-12-05 23:04:38 +01:00 committed by GitHub
parent 3c73f88a52
commit 1b9f4f33de
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 46 additions and 1 deletions

View File

@ -41,6 +41,7 @@ namespace hex::plugin::disasm {
std::vector<Disassembly> m_disassembly;
void disassemble();
void exportToFile();
};
}

View File

@ -17,6 +17,8 @@
"hex.disassembler.view.disassembler.bpf.classic": "Classic",
"hex.disassembler.view.disassembler.bpf.extended": "Extended",
"hex.disassembler.view.disassembler.disassemble": "Disassemble",
"hex.disassembler.view.disassembler.export": "Exportieren",
"hex.disassembler.view.disassembler.export.popup.error": "Der Export zur ASM-Datei ist fehlgeschlagen.",
"hex.disassembler.view.disassembler.disassembling": "Disassemblen...",
"hex.disassembler.view.disassembler.disassembly.address": "Adresse",
"hex.disassembler.view.disassembler.disassembly.bytes": "Byte",

View File

@ -17,6 +17,8 @@
"hex.disassembler.view.disassembler.bpf.classic": "Classic",
"hex.disassembler.view.disassembler.bpf.extended": "Extended",
"hex.disassembler.view.disassembler.disassemble": "Disassemble",
"hex.disassembler.view.disassembler.export": "Export instructions as...",
"hex.disassembler.view.disassembler.export.popup.error": "Failed to export to ASM file!",
"hex.disassembler.view.disassembler.disassembling": "Disassembling...",
"hex.disassembler.view.disassembler.disassembly.address": "Address",
"hex.disassembler.view.disassembler.disassembly.bytes": "Byte",

View File

@ -6,6 +6,7 @@
#include <fonts/codicons_font.h>
#include <cstring>
#include <toasts/toast_notification.hpp>
using namespace std::literals::string_literals;
@ -92,6 +93,36 @@ namespace hex::plugin::disasm {
});
}
void ViewDisassembler::exportToFile() {
TaskManager::createTask("hex.ui.common.processing"_lang, TaskManager::NoProgress, [this](auto &) {
TaskManager::doLater([this] {
fs::openFileBrowser(fs::DialogMode::Save, {}, [this](const std::fs::path &path) {
auto p = path;
if (p.extension() != ".asm")
p.replace_filename(hex::format("{}{}", p.filename().string(), ".asm"));
auto file = wolv::io::File(p, wolv::io::File::Mode::Create);
if (!file.isValid()) {
ui::ToastError::open("hex.disassembler.view.disassembler.export.popup.error"_lang);
return;
}
// As disassembly code can be quite long, we prefer writing each disassembled instruction to file
for (const Disassembly& d : m_disassembly) {
// We test for a "bugged" case that should never happen - the instruction should always have a mnemonic
if (d.mnemonic.empty())
continue;
if (d.operators.empty())
file.writeString(hex::format("{}\n", d.mnemonic));
else
file.writeString(hex::format("{} {}\n", d.mnemonic, d.operators));
}
});
});
});
}
void ViewDisassembler::drawContent() {
auto provider = ImHexApi::Provider::get();
if (ImHexApi::Provider::isValid() && provider->isReadable()) {
@ -390,9 +421,18 @@ namespace hex::plugin::disasm {
}
ImGui::EndDisabled();
// Draw export to file icon button
ImGui::SameLine();
ImGui::BeginDisabled(m_disassemblerTask.isRunning() || m_disassembly.empty());
{
if (ImGuiExt::DimmedIconButton(ICON_VS_EXPORT, ImGui::GetStyleColorVec4(ImGuiCol_Text)))
this->exportToFile();
}
ImGui::EndDisabled();
ImGuiExt::InfoTooltip("hex.disassembler.view.disassembler.export"_lang);
// Draw a spinner if the disassembler is running
if (m_disassemblerTask.isRunning()) {
ImGui::SameLine();
ImGuiExt::TextSpinner("hex.disassembler.view.disassembler.disassembling"_lang);
}