diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 223d4ee7..43a8c9fe 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,7 @@ set(SOURCES core/CutterJson.cpp core/RizinCpp.cpp core/Basefind.cpp + core/BinDiff.cpp dialogs/EditStringDialog.cpp dialogs/WriteCommandsDialogs.cpp widgets/DisassemblerGraphView.cpp @@ -58,7 +59,6 @@ set(SOURCES widgets/DecompilerWidget.cpp widgets/VisualNavbar.cpp widgets/GraphView.cpp - dialogs/bindiff/BinDiffDialog.cpp dialogs/preferences/PreferencesDialog.cpp dialogs/preferences/AppearanceOptionsWidget.cpp dialogs/preferences/GraphOptionsWidget.cpp @@ -156,6 +156,7 @@ set(SOURCES tools/basefind/BaseFindDialog.cpp tools/basefind/BaseFindSearchDialog.cpp tools/basefind/BaseFindResultsDialog.cpp + tools/bindiff/DiffLoadDialog.cpp ) set(HEADER_FILES core/Cutter.h @@ -164,6 +165,7 @@ set(HEADER_FILES core/CutterJson.h core/RizinCpp.h core/Basefind.h + core/BinDiff.h dialogs/EditStringDialog.h dialogs/WriteCommandsDialogs.h widgets/DisassemblerGraphView.h @@ -214,7 +216,6 @@ set(HEADER_FILES widgets/DecompilerWidget.h widgets/VisualNavbar.h widgets/GraphView.h - dialogs/bindiff/BinDiffDialog.h dialogs/preferences/PreferencesDialog.h dialogs/preferences/AppearanceOptionsWidget.h dialogs/preferences/PreferenceCategory.h @@ -321,6 +322,7 @@ set(HEADER_FILES tools/basefind/BaseFindDialog.h tools/basefind/BaseFindSearchDialog.h tools/basefind/BaseFindResultsDialog.h + tools/bindiff/DiffLoadDialog.h ) set(UI_FILES dialogs/AboutDialog.ui @@ -347,7 +349,6 @@ set(UI_FILES widgets/GlobalsWidget.ui widgets/StringsWidget.ui widgets/HexdumpWidget.ui - dialogs/bindiff/BinDiffDialog.ui dialogs/preferences/PreferencesDialog.ui dialogs/preferences/AppearanceOptionsWidget.ui dialogs/preferences/GraphOptionsWidget.ui @@ -396,6 +397,7 @@ set(UI_FILES tools/basefind/BaseFindDialog.ui tools/basefind/BaseFindSearchDialog.ui tools/basefind/BaseFindResultsDialog.ui + tools/bindiff/DiffLoadDialog.ui ) set(QRC_FILES resources.qrc diff --git a/src/core/BinDiff.cpp b/src/core/BinDiff.cpp new file mode 100644 index 00000000..d18e0f29 --- /dev/null +++ b/src/core/BinDiff.cpp @@ -0,0 +1,120 @@ +#include "BinDiff.h" + +bool BinDiff::threadCallback(const size_t nLeft, const size_t nMatch, void *user) +{ + auto bdiff = reinterpret_cast(user); + return bdiff->updateProgress(nLeft, nMatch); +} + +BinDiff::BinDiff(CutterCore *core) + : core(core), + result(nullptr), + continue_run(true), + maxTotal(1) +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + , + mutex(QMutex::Recursive) +#endif +{ +} + +BinDiff::~BinDiff() +{ + cancel(); + wait(); + rz_analysis_match_result_free(result); +} + +void BinDiff::run(QString fileName) +{ + mutex.lock(); + rz_analysis_match_result_free(result); + result = nullptr; + continue_run = true; + maxTotal = 1; // maxTotal must be at least 1. + mutex.unlock(); + + core->coreMutex.lock(); + result = core->diffNewFile(fileName, threadCallback, this); + core->coreMutex.unlock(); + + emit complete(); +} + +void BinDiff::cancel() +{ + mutex.lock(); + continue_run = false; + mutex.unlock(); +} + +static void setFunctionDescription(FunctionDescription *desc, const RzAnalysisFunction *func) +{ + desc->offset = func->addr; + desc->linearSize = rz_analysis_function_linear_size((RzAnalysisFunction *)func); + desc->nargs = rz_analysis_arg_count((RzAnalysisFunction *)func); + desc->nlocals = rz_analysis_var_local_count((RzAnalysisFunction *)func); + desc->nbbs = rz_list_length(func->bbs); + desc->calltype = func->cc ? QString::fromUtf8(func->cc) : QString(); + desc->name = func->name ? QString::fromUtf8(func->name) : QString(); + desc->edges = rz_analysis_function_count_edges(func, nullptr); + desc->stackframe = func->maxstack; +} + +QList BinDiff::matches() +{ + QList pairs; + RzAnalysisMatchPair *pair = nullptr; + RzListIter *it = nullptr; + const RzAnalysisFunction *fcn_a = nullptr; + const RzAnalysisFunction *fcn_b = nullptr; + + CutterRzListForeach (result->matches, it, RzAnalysisMatchPair, pair) { + BinDiffMatchDescription desc; + fcn_a = static_cast(pair->pair_a); + fcn_b = static_cast(pair->pair_b); + + setFunctionDescription(&desc.original, fcn_a); + setFunctionDescription(&desc.modified, fcn_b); + + desc.simtype = RZ_ANALYSIS_SIMILARITY_TYPE_STR(pair->similarity); + desc.similarity = pair->similarity; + + pairs << desc; + } + + return pairs; +} + +QList BinDiff::mismatch(bool fileA) +{ + QList list; + RzAnalysisFunction *func = nullptr; + RzList *unmatch = fileA ? result->unmatch_a : result->unmatch_b; + RzListIter *it = nullptr; + + CutterRzListForeach (unmatch, it, RzAnalysisFunction, func) { + FunctionDescription desc; + setFunctionDescription(&desc, func); + list << desc; + } + + return list; +} + +bool BinDiff::updateProgress(const size_t nLeft, const size_t nMatch) +{ + mutex.lock(); + + if (maxTotal < nMatch) { + maxTotal = nMatch; + } + if (maxTotal < nLeft) { + maxTotal = nLeft; + } + + emit progress(maxTotal, nLeft, nMatch); + bool ret = continue_run; + mutex.unlock(); + return ret; +} diff --git a/src/core/BinDiff.h b/src/core/BinDiff.h new file mode 100644 index 00000000..9871cf89 --- /dev/null +++ b/src/core/BinDiff.h @@ -0,0 +1,47 @@ +#ifndef CUTTER_BINDIFF_CORE_H +#define CUTTER_BINDIFF_CORE_H + +#include +#include + +#include "Cutter.h" +#include "CutterDescriptions.h" +#include + +class CutterCore; + +class BinDiff : public QThread +{ + Q_OBJECT + +public: + explicit BinDiff(CutterCore *core); + virtual ~BinDiff(); + + void run(QString fileName); + QList matches(); + QList mismatch(bool fileA); + +public slots: + void cancel(); + +signals: + void progress(const size_t total, const size_t nLeft, const size_t nMatch); + void complete(); + +private: + CutterCore *const core; + RzAnalysisMatchResult *result; + bool continue_run; + size_t maxTotal; +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + QMutex mutex; +#else + QRecursiveMutex mutex; +#endif + + bool updateProgress(const size_t nLeft, const size_t nMatch); + static bool threadCallback(const size_t nLeft, const size_t nMatch, void *user); +}; + +#endif // CUTTER_BINDIFF_CORE_H diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp index 41ee1d5e..a32372fa 100644 --- a/src/core/Cutter.cpp +++ b/src/core/Cutter.cpp @@ -396,7 +396,7 @@ QString CutterCore::getFunctionExecOut(const std::function &fcn, o = rz_cons_get_buffer(); rz_cons_pop(); - rz_cons_echo(NULL); + rz_cons_echo(nullptr); clean_return: if (offset != core->offset) { @@ -487,7 +487,7 @@ QString CutterCore::cmdRaw(const char *cmd) // cleaning up rz_cons_pop(); - rz_cons_echo(NULL); + rz_cons_echo(nullptr); return res; } @@ -603,14 +603,14 @@ bool CutterCore::loadFile(QString path, ut64 baddr, ut64 mapaddr, int perms, int // load RzBin information // XXX only for sub-bins rz_core_bin_load(core, path.toUtf8(), baddr); - rz_bin_select_idx(core->bin, NULL, idx); + rz_bin_select_idx(core->bin, nullptr, idx); } #endif } else { // Not loading RzBin info coz va = false } - auto iod = core->io ? core->io->desc : NULL; + auto iod = core->io ? core->io->desc : nullptr; auto debug = core->file && iod && (core->file->fd == iod->fd) && iod->plugin && iod->plugin->isdbg; @@ -856,7 +856,7 @@ QString CutterCore::getString(RVA addr, uint64_t len, RzStrEnc encoding, bool es opt.encoding = encoding; opt.escape_nl = escape_nl; auto seek = seekTemp(addr); - return fromOwnedCharPtr(rz_str_stringify_raw_buffer(&opt, NULL)); + return fromOwnedCharPtr(rz_str_stringify_raw_buffer(&opt, nullptr)); } QString CutterCore::getMetaString(RVA addr) @@ -1093,13 +1093,13 @@ void CutterCore::createSignature(const QString &filepath) ut64 CutterCore::math(const QString &expr) { CORE_LOCK(); - return rz_num_math(core ? core->num : NULL, expr.toUtf8().constData()); + return rz_num_math(core ? core->num : nullptr, expr.toUtf8().constData()); } ut64 CutterCore::num(const QString &expr) { CORE_LOCK(); - return rz_num_get(core ? core->num : NULL, expr.toUtf8().constData()); + return rz_num_get(core ? core->num : nullptr, expr.toUtf8().constData()); } QString CutterCore::itoa(ut64 num, int rdx) @@ -1902,7 +1902,7 @@ void CutterCore::setCurrentDebugThread(int tid) if (!asyncTask( [=](RzCore *core) { rz_debug_select(core->dbg, core->dbg->pid, tid); - return (void *)NULL; + return (void *)nullptr; }, debugTask)) { return; @@ -1929,7 +1929,7 @@ void CutterCore::setCurrentDebugProcess(int pid) [=](RzCore *core) { rz_debug_select(core->dbg, pid, core->dbg->tid); core->dbg->main_pid = pid; - return (void *)NULL; + return (void *)nullptr; }, debugTask)) { return; @@ -2004,7 +2004,7 @@ void CutterCore::startEmulation() asyncTask( [&](RzCore *core) { rz_core_analysis_esil_reinit(core); - rz_core_analysis_esil_init_mem(core, NULL, UT64_MAX, UT32_MAX); + rz_core_analysis_esil_init_mem(core, nullptr, UT64_MAX, UT32_MAX); rz_core_analysis_esil_init_regs(core); return nullptr; }, @@ -2180,7 +2180,7 @@ void CutterCore::stopDebug() CORE_LOCK(); if (currentlyEmulating) { - rz_core_analysis_esil_init_mem_del(core, NULL, UT64_MAX, UT32_MAX); + rz_core_analysis_esil_init_mem_del(core, nullptr, UT64_MAX, UT32_MAX); rz_core_analysis_esil_deinit(core); resetWriteCache(); rz_core_debug_clear_register_flags(core); @@ -2218,7 +2218,7 @@ void CutterCore::continueDebug() if (currentlyEmulating) { if (!asyncTask( [](RzCore *core) { - rz_core_esil_step(core, UT64_MAX, "0", NULL, false); + rz_core_esil_step(core, UT64_MAX, "0", nullptr, false); rz_core_reg_update_flags(core); return nullptr; }, @@ -2294,7 +2294,7 @@ void CutterCore::continueUntilDebug(ut64 offset) if (currentlyEmulating) { if (!asyncTask( [=](RzCore *core) { - rz_core_esil_step(core, offset, NULL, NULL, false); + rz_core_esil_step(core, offset, nullptr, nullptr, false); rz_core_reg_update_flags(core); return nullptr; }, @@ -2380,7 +2380,7 @@ void CutterCore::continueUntilSyscall() [](void *x) { rz_debug_stop(reinterpret_cast(x)); }, core->dbg); rz_reg_arena_swap(core->dbg->reg, true); - rz_debug_continue_syscalls(core->dbg, NULL, 0); + rz_debug_continue_syscalls(core->dbg, nullptr, 0); rz_cons_break_pop(); rz_core_dbg_follow_seek_register(core); return nullptr; @@ -2410,7 +2410,7 @@ void CutterCore::stepDebug() if (currentlyEmulating) { if (!asyncTask( [](RzCore *core) { - rz_core_esil_step(core, UT64_MAX, NULL, NULL, false); + rz_core_esil_step(core, UT64_MAX, nullptr, nullptr, false); rz_core_reg_update_flags(core); return nullptr; }, @@ -2635,7 +2635,7 @@ void CutterCore::stopTraceSession() if (!asyncTask( [](RzCore *core) { rz_debug_session_free(core->dbg->session); - core->dbg->session = NULL; + core->dbg->session = nullptr; return nullptr; }, debugTask)) { @@ -3255,8 +3255,8 @@ QList CutterCore::getSignaturesDB() { CORE_LOCK(); - void *ptr = NULL; - RzListIter *iter = NULL; + void *ptr = nullptr; + RzListIter *iter = nullptr; QList sigdb; RzList *list = rz_core_analysis_sigdb_list(core, true); @@ -3451,7 +3451,7 @@ QList CutterCore::getAllSections() if (!digests) { continue; } - const char *entropy = (const char *)ht_pp_find(digests, "entropy", NULL); + const char *entropy = (const char *)ht_pp_find(digests, "entropy", nullptr); section.entropy = rz_str_get(entropy); ht_pp_free(digests); } @@ -4055,7 +4055,7 @@ void CutterCore::addGlobalVariable(RVA offset, QString name, QString typ) { name = sanitizeStringForCommand(name); CORE_LOCK(); - char *errmsg = NULL; + char *errmsg = nullptr; RzType *globType = rz_type_parse_string_single(core->analysis->typedb->parser, typ.toStdString().c_str(), &errmsg); if (errmsg) { @@ -4084,7 +4084,7 @@ void CutterCore::modifyGlobalVariable(RVA offset, QString name, QString typ) if (name.compare(glob->name)) { rz_analysis_var_global_rename(core->analysis, glob->name, name.toStdString().c_str()); } - char *errmsg = NULL; + char *errmsg = nullptr; RzType *globType = rz_type_parse_string_single(core->analysis->typedb->parser, typ.toStdString().c_str(), &errmsg); if (errmsg) { @@ -4260,7 +4260,7 @@ QList CutterCore::disassembleLines(RVA offset, int lines) rz_cons_singleton()->is_html = false; rz_cons_singleton()->was_html = true; } - rz_core_print_disasm(core, offset, core->block, core->blocksize, lines, NULL, &options); + rz_core_print_disasm(core, offset, core->block, core->blocksize, lines, nullptr, &options); } QList r; @@ -4387,7 +4387,7 @@ QString CutterCore::getVersionInformation() { "rz_syscall", &rz_syscall_version }, { "rz_util", &rz_util_version }, /* ... */ - { NULL, NULL } + { nullptr, nullptr } }; versionInfo.append(getRizinVersionReadable()); versionInfo.append("\n"); @@ -4564,9 +4564,12 @@ bool CutterCore::isWriteModeEnabled() return false; } -RzAnalysisMatchResult *CutterCore::matchFunctionsFromNewFile(const QString &filePath) { - RzList *fcns_a = NULL, *fcns_b = NULL; - RzAnalysisMatchResult *result = NULL; +RzAnalysisMatchResult *CutterCore::diffNewFile(const QString &filePath, + RzAnalysisMatchThreadInfoCb callback, void *user) +{ + RzList *fcns_a = nullptr, *fcns_b = nullptr; + RzAnalysisMatchResult *result = nullptr; + RzAnalysisMatchOpt opts; RzListIter *iter; RzConfigNode *node; void *ptr; @@ -4590,7 +4593,7 @@ RzAnalysisMatchResult *CutterCore::matchFunctionsFromNewFile(const QString &file goto fail; } - if (!rz_core_bin_load(core_b, NULL, UT64_MAX)) { + if (!rz_core_bin_load(core_b, nullptr, UT64_MAX)) { qWarning() << "cannot load bin " << filePath; goto fail; } @@ -4600,18 +4603,18 @@ RzAnalysisMatchResult *CutterCore::matchFunctionsFromNewFile(const QString &file goto fail; } - rz_list_foreach (core_a->config->nodes, iter, ptr) { + rz_list_foreach(core_a->config->nodes, iter, ptr) + { node = reinterpret_cast(ptr); - if (!strcmp(node->name, "scr.color") || - !strcmp(node->name, "scr.interactive") || - !strcmp(node->name, "cfg.debug")) { + if (!strcmp(node->name, "scr.color") || !strcmp(node->name, "scr.interactive") + || !strcmp(node->name, "cfg.debug")) { rz_config_set(core_b->config, node->name, "0"); continue; } rz_config_set(core_b->config, node->name, node->value); } - if (!rz_core_analysis_everything(core_b, false, NULL)) { + if (!rz_core_analysis_everything(core_b, false, nullptr)) { qWarning() << "cannot analyze binary " << filePath; goto fail; } @@ -4631,10 +4634,15 @@ RzAnalysisMatchResult *CutterCore::matchFunctionsFromNewFile(const QString &file rz_list_sort(fcns_a, core_a->analysis->columnSort); rz_list_sort(fcns_b, core_b->analysis->columnSort); + opts.analysis_a = core_a->analysis; + opts.analysis_b = core_b->analysis; + opts.callback = callback; + opts.user = user; + // calculate all the matches between the functions of the 2 different core files. - result = rz_analysis_match_functions_2(core_a->analysis, fcns_a, core_b->analysis, fcns_b); + result = rz_analysis_match_functions(fcns_a, fcns_b, &opts); if (!result) { - qWarning() << "failed to perform the function matching operation"; + qWarning() << "failed to perform the function matching operation or job was cancelled."; goto fail; } @@ -4647,10 +4655,9 @@ fail: rz_list_free(fcns_b); rz_core_free(this->core_b); this->core_b = nullptr; - return NULL; + return nullptr; } - /** * @brief get a compact disassembly preview for tooltips * @param address - the address from which to print the disassembly diff --git a/src/core/Cutter.h b/src/core/Cutter.h index 33530f67..485622ba 100644 --- a/src/core/Cutter.h +++ b/src/core/Cutter.h @@ -5,6 +5,7 @@ #include "core/CutterDescriptions.h" #include "core/CutterJson.h" #include "core/Basefind.h" +#include "core/BinDiff.h" #include "common/BasicInstructionHighlighter.h" #include @@ -71,6 +72,7 @@ class CUTTER_EXPORT CutterCore : public QObject friend class RzCoreLocked; friend class RizinTask; friend class Basefind; + friend class BinDiff; public: explicit CutterCore(QObject *parent = nullptr); @@ -755,7 +757,8 @@ public: void writeGraphvizGraphToFile(QString path, QString format, RzCoreGraphType type, RVA address); /* Diffing/Matching */ - RzAnalysisMatchResult *matchFunctionsFromNewFile(const QString &filePath); + RzAnalysisMatchResult *diffNewFile(const QString &filePath, + RzAnalysisMatchThreadInfoCb callback, void *user); signals: void refreshAll(); diff --git a/src/core/CutterDescriptions.h b/src/core/CutterDescriptions.h index bb04e12f..92809917 100644 --- a/src/core/CutterDescriptions.h +++ b/src/core/CutterDescriptions.h @@ -405,6 +405,14 @@ struct BasefindResultDescription ut32 score; }; +struct BinDiffMatchDescription +{ + FunctionDescription original; + FunctionDescription modified; + QString simtype; + double similarity; +}; + Q_DECLARE_METATYPE(FunctionDescription) Q_DECLARE_METATYPE(ImportDescription) Q_DECLARE_METATYPE(ExportDescription) @@ -446,5 +454,6 @@ Q_DECLARE_METATYPE(RefDescription) Q_DECLARE_METATYPE(VariableDescription) Q_DECLARE_METATYPE(BasefindCoreStatusDescription) Q_DECLARE_METATYPE(BasefindResultDescription) +Q_DECLARE_METATYPE(BinDiffMatchDescription) #endif // DESCRIPTIONS_H diff --git a/src/core/MainWindow.cpp b/src/core/MainWindow.cpp index 35cb22e1..d2868768 100644 --- a/src/core/MainWindow.cpp +++ b/src/core/MainWindow.cpp @@ -119,6 +119,7 @@ // Tools #include "tools/basefind/BaseFindDialog.h" +#include "tools/bindiff/DiffLoadDialog.h" #define PROJECT_FILE_FILTER tr("Rizin Project (*.rzdb)") @@ -1616,60 +1617,14 @@ void MainWindow::on_actionBackward_triggered() void MainWindow::on_actionDiff_triggered() { - QFileDialog dialog(this); - dialog.setWindowTitle(tr("Select a file to use for diffing")); - dialog.setNameFilters({ tr("All files (*)") }); - - if (!dialog.exec()) { - return; - } - - const QString &fileName = QDir::toNativeSeparators(dialog.selectedFiles().first()); - - if (fileName.isEmpty()) { - return; - } - - auto compareTask = new RizinFunctionTask([=](RzCore *) { - return Core()->matchFunctionsFromNewFile(fileName); - }, false); - - task = QSharedPointer(compareTask); - taskDialog = new RizinTaskDialog(task); - taskDialog->setAttribute(Qt::WA_DeleteOnClose); - taskDialog->setDesc(tr("Performing function comparison...")); - taskDialog->show(); - - connect(task.data(), &RizinTask::finished, this, [this, compareTask]() { - RzAnalysisMatchResult *result = static_cast(compareTask->getResult()); - openBinDiffDialog(result); - delete taskDialog; - taskDialog = nullptr; - }); - - compareTask->startTask(); + auto dialog = new DiffLoadDialog(this); + dialog->show(); } void MainWindow::on_actionForward_triggered() { core->seekNext(); } - -void MainWindow::openBinDiffDialog(RzAnalysisMatchResult *result) -{ - if (!result) { - messageBoxWarning(tr("Error"), tr("Failed to perform the function matching.")); - return; - } - - if (binDiffDialog) { - delete binDiffDialog; - } - - binDiffDialog = new BinDiffDialog(result, this); - binDiffDialog->show(); -} - void MainWindow::on_actionDisasAdd_comment_triggered() { CommentsDialog c(this); diff --git a/src/core/MainWindow.h b/src/core/MainWindow.h index 8a3b8289..2824f669 100644 --- a/src/core/MainWindow.h +++ b/src/core/MainWindow.h @@ -152,10 +152,10 @@ public slots: void openNewFileFailed(); - void openBinDiffDialog(RzAnalysisMatchResult *result); - void toggleOverview(bool visibility, GraphWidget *targetGraph); + private slots: + void on_actionDiff_triggered(); void on_actionBaseFind_triggered(); void on_actionAbout_triggered(); void on_actionIssue_triggered(); @@ -179,8 +179,6 @@ private slots: void on_actionBackward_triggered(); void on_actionForward_triggered(); - void on_actionDiff_triggered(); - void on_actionMap_triggered(); void on_actionTabs_on_Top_triggered(); diff --git a/src/tools/bindiff/DiffLoadDialog.cpp b/src/tools/bindiff/DiffLoadDialog.cpp new file mode 100644 index 00000000..1acc7d29 --- /dev/null +++ b/src/tools/bindiff/DiffLoadDialog.cpp @@ -0,0 +1,94 @@ +#include "DiffLoadDialog.h" +#include "ui_DiffLoadDialog.h" + +#include +#include + +#include +#include + +DiffLoadDialog::DiffLoadDialog(QWidget *parent) : QDialog(parent), ui(new Ui::DiffLoadDialog) +{ + ui->setupUi(this); + setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint)); + + ui->lineEditFileName->setReadOnly(true); + ui->lineEditFileName->setText(""); + + ui->lineEditDiffFile->setReadOnly(true); + ui->lineEditDiffFile->setText(""); +} + +DiffLoadDialog::~DiffLoadDialog() {} + +QString DiffLoadDialog::getFileToOpen() const +{ + return ui->lineEditFileName->text(); +} + +QString DiffLoadDialog::getPreviousDiffFile() const +{ + return ui->lineEditDiffFile->text(); +} + +void DiffLoadDialog::on_buttonDiffOpen_clicked() +{ + QFileDialog dialog(this); + dialog.setWindowTitle(tr("Select the previous diff to load")); + dialog.setNameFilters({ tr("JSON array (*.json)") }); + + if (!dialog.exec()) { + return; + } + + const QString &fileName = QDir::toNativeSeparators(dialog.selectedFiles().first()); + + if (fileName.isEmpty()) { + return; + } + + ui->lineEditDiffFile->setText(fileName); +} + +void DiffLoadDialog::on_buttonFileOpen_clicked() +{ + QFileDialog dialog(this); + dialog.setWindowTitle(tr("Select a file to use for diffing")); + dialog.setNameFilters({ tr("All files (*)") }); + + if (!dialog.exec()) { + return; + } + + const QString &fileName = QDir::toNativeSeparators(dialog.selectedFiles().first()); + + if (fileName.isEmpty()) { + return; + } + + ui->lineEditFileName->setText(fileName); +} + +void DiffLoadDialog::openErrorBox(QString errorMessage) +{ + QMessageBox mb(this); + mb.setIcon(QMessageBox::Warning); + mb.setStandardButtons(QMessageBox::Ok); + mb.setWindowTitle(tr("Error")); + mb.setText(errorMessage); + mb.exec(); +} + +void DiffLoadDialog::on_buttonBox_accepted() +{ + QString fileName = getFileToOpen(); + if (fileName.isEmpty()) { + openErrorBox(tr("The compare file was not selected.")); + return; + } +} + +void DiffLoadDialog::on_buttonBox_rejected() +{ + // cancel +} diff --git a/src/tools/bindiff/DiffLoadDialog.h b/src/tools/bindiff/DiffLoadDialog.h new file mode 100644 index 00000000..2d543bf0 --- /dev/null +++ b/src/tools/bindiff/DiffLoadDialog.h @@ -0,0 +1,37 @@ +#ifndef DIFF_LOAD_DIALOG_H +#define DIFF_LOAD_DIALOG_H + +#include +#include +#include + +#include + +namespace Ui { +class DiffLoadDialog; +} + +class DiffLoadDialog : public QDialog +{ + Q_OBJECT + +public: + explicit DiffLoadDialog(QWidget *parent = nullptr); + ~DiffLoadDialog(); + + QString getFileToOpen() const; + QString getPreviousDiffFile() const; + +private slots: + void on_buttonDiffOpen_clicked(); + void on_buttonFileOpen_clicked(); + void on_buttonBox_accepted(); + void on_buttonBox_rejected(); + +private: + std::unique_ptr ui; + + void openErrorBox(QString errorMessage); +}; + +#endif // DIFF_LOAD_DIALOG_H diff --git a/src/tools/bindiff/DiffLoadDialog.ui b/src/tools/bindiff/DiffLoadDialog.ui new file mode 100644 index 00000000..e4f450af --- /dev/null +++ b/src/tools/bindiff/DiffLoadDialog.ui @@ -0,0 +1,142 @@ + + + DiffLoadDialog + + + Qt::NonModal + + + + 0 + 0 + 636 + 142 + + + + + 0 + 0 + + + + Select which file to compare. + + + + QLayout::SetMinimumSize + + + + + QLayout::SetMinimumSize + + + + + + + + + + + + 0 + 0 + + + + <html><head/><body><p>File to compare to.</p></body></html> + + + File to compare (required) + + + + + + + Select + + + + + + + Select + + + + + + + + 0 + 0 + + + + <html><head/><body><p>You can load a previous saved diff instead of performing a new diff scan.</p></body></html> + + + Previous diff file (optional) + + + + + + + + + QLayout::SetMinimumSize + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + DiffLoadDialog + accept() + + + 248 + 234 + + + 157 + 274 + + + + + buttonBox + rejected() + DiffLoadDialog + reject() + + + 316 + 240 + + + 286 + 254 + + + + +