diff --git a/src/CutterApplication.cpp b/src/CutterApplication.cpp new file mode 100644 index 00000000..ce9fd8c6 --- /dev/null +++ b/src/CutterApplication.cpp @@ -0,0 +1,108 @@ +#include "CutterApplication.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc, argv){ + setOrganizationName("Cutter"); + setApplicationName("Cutter"); + setApplicationVersion(APP_VERSION); + setWindowIcon(QIcon(":/img/cutter.svg")); + + // Set QString codec to UTF-8 + QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); +#if QT_VERSION < QT_VERSION_CHECK(5,0,0) + QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); + QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); +#endif + QCommandLineParser cmd_parser; + cmd_parser.setApplicationDescription(QObject::tr("A Qt and C++ GUI for radare2 reverse engineering framework")); + cmd_parser.addHelpOption(); + cmd_parser.addVersionOption(); + cmd_parser.addPositionalArgument("filename", QObject::tr("Filename to open.")); + + QCommandLineOption analOption({"A", "anal"}, + QObject::tr("Automatically open file and optionally start analysis. Needs filename to be specified. May be a value between 0 and 2: 0 = no analysis, 1 = aaa, 2 = aaaa (experimental)"), + QObject::tr("level")); + cmd_parser.addOption(analOption); + + cmd_parser.process(*this); + + QStringList args = cmd_parser.positionalArguments(); + + // Check r2 version + QString r2version = r_core_version(); + QString localVersion = "" R2_GITTAP; + if (r2version != localVersion) + { + QMessageBox msg; + msg.setIcon(QMessageBox::Critical); + msg.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msg.setWindowTitle(QObject::tr("Version mismatch!")); + msg.setText(QString(QObject::tr("The version used to compile Cutter (%1) does not match the binary version of radare2 (%2). This could result in unexpected behaviour. Are you sure you want to continue?")).arg(localVersion, r2version)); + if (msg.exec() == QMessageBox::No) + exit(1); + } + + bool analLevelSpecified = false; + int analLevel= 0; + + if (cmd_parser.isSet(analOption)) + { + analLevel = cmd_parser.value(analOption).toInt(&analLevelSpecified); + + if (!analLevelSpecified || analLevel < 0 || analLevel > 2) + { + printf("%s\n", QObject::tr("Invalid Analysis Level. May be a value between 0 and 2.").toLocal8Bit().constData()); + exit(1); + } + } + + MainWindow *main = new MainWindow(); + + setMainWindow(main); + + if (args.empty()) + { + if (analLevelSpecified) + { + printf("%s\n", QObject::tr("Filename must be specified to start analysis automatically.").toLocal8Bit().constData()); + exit(1); + } + + main->displayNewFileDialog(); + } + else // filename specified as positional argument + { + main->openNewFile(args[0], analLevelSpecified ? analLevel : -1); + } +} + +bool CutterApplication::event(QEvent *e){ + if (e->type() == QEvent::FileOpen) { + QFileOpenEvent *openEvent = static_cast(e); + if (openEvent) { + if (m_FileAlreadyDropped) { + // we already dropped a file in macOS, let's spawn another instance + // (Like the File -> Open) + QString fileName = openEvent->file(); + QProcess process(this); + process.setEnvironment(QProcess::systemEnvironment()); + QStringList args = QStringList(fileName); + process.startDetached(qApp->applicationFilePath(), args); + } else { + QString fileName = openEvent->file(); + m_FileAlreadyDropped = true; + m_MainWindow->closeNewFileDialog(); + m_MainWindow->openNewFile(fileName, -1); + } + } + } + return QApplication::event(e); +} diff --git a/src/CutterApplication.h b/src/CutterApplication.h new file mode 100644 index 00000000..f7fedd22 --- /dev/null +++ b/src/CutterApplication.h @@ -0,0 +1,34 @@ +#ifndef CUTTERAPPLICATION_H +#define CUTTERAPPLICATION_H + +#include +#include + +#include "MainWindow.h" + + +class CutterApplication : public QApplication +{ + Q_OBJECT + Q_PROPERTY(MainWindow* mainWindow READ mainWindow WRITE setMainWindow) + +public: + CutterApplication(int &argc, char **argv); + + MainWindow * mainWindow() { + return m_MainWindow; + } + + void setMainWindow(MainWindow * mw) { + m_MainWindow = mw; + } + +protected: + bool event(QEvent *e); + +private: + bool m_FileAlreadyDropped; + MainWindow *m_MainWindow; +}; + +#endif // CUTTERAPPLICATION_H diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index b16928c9..d1923577 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -283,10 +283,19 @@ void MainWindow::openNewFile(const QString &fn, int analLevel, QList ad void MainWindow::displayNewFileDialog() { NewFileDialog *n = new NewFileDialog(); + newFileDialog = n; n->setAttribute(Qt::WA_DeleteOnClose); n->show(); } +void MainWindow::closeNewFileDialog() +{ + if (newFileDialog) { + newFileDialog->close(); + } + newFileDialog = nullptr; +} + void MainWindow::displayAnalysisOptionsDialog(int analLevel, QList advancedOptions) { OptionsDialog *o = new OptionsDialog(this); diff --git a/src/MainWindow.h b/src/MainWindow.h index c51ebc44..b0ad5c7b 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -8,6 +8,7 @@ #include "widgets/SidebarWidget.h" #include "widgets/HexdumpWidget.h" #include "widgets/PseudocodeWidget.h" +#include "dialogs/NewFileDialog.h" #include "utils/Configuration.h" #include @@ -59,6 +60,7 @@ public: void openNewFile(const QString &fn, int analLevel = -1, QList advancedOptions = QList()); void displayNewFileDialog(); + void closeNewFileDialog(); void displayAnalysisOptionsDialog(int analLevel, QList advancedOptions); void openProject(const QString &project_name); @@ -187,6 +189,7 @@ private: DisassemblerGraphView *graphView = nullptr; QDockWidget *asmDock = nullptr; QDockWidget *calcDock = nullptr; + NewFileDialog *newFileDialog = nullptr; void toggleDockWidget(QDockWidget *dock_widget, bool show); diff --git a/src/apple/Info.plist b/src/apple/Info.plist new file mode 100644 index 00000000..4e9c1c60 --- /dev/null +++ b/src/apple/Info.plist @@ -0,0 +1,80 @@ + + + + + CFBundleExecutable + @EXECUTABLE@ + CFBundleGetInfoString + Created by Qt/QMake + CFBundleIconFile + @ICON@ + CFBundleIdentifier + @BUNDLEIDENTIFIER@ + CFBundlePackageType + APPL + CFBundleSignature + @TYPEINFO@ + NOTE + This file was generated by Qt/QMake. + NSPrincipalClass + NSApplication + NSSupportsAutomaticGraphicsSwitching + + CFBundleDocumentTypes + + + CFBundleTypeName + Data + CFBundleTypeRole + Viewer + LSHandlerRank + Owner + LSItemContentTypes + + public.data + + NSDocumentClass + FileDataDocument + + + CFBundleTypeName + Other + CFBundleTypeRole + Viewer + LSHandlerRank + Alternate + LSItemContentTypes + + public.executable + public.text + public.archive + public.disk-image + public.image + public.audio + public.movie + com.adobe.pdf + + NSDocumentClass + FileDataDocument + + + CFBundleTypeExtensions + + * + + CFBundleTypeName + AllTypes + CFBundleTypeOSTypes + + **** + + CFBundleTypeRole + Viewer + LSHandlerRank + Alternate + NSDocumentClass + FileDataDocument + + + + diff --git a/src/cutter.pro b/src/cutter.pro index 204a2a12..f1c04b21 100644 --- a/src/cutter.pro +++ b/src/cutter.pro @@ -24,6 +24,7 @@ win32 { macx { QMAKE_CXXFLAGS = -mmacosx-version-min=10.7 -std=gnu0x -stdlib=libc++ + QMAKE_INFO_PLIST = apple/Info.plist } @@ -87,7 +88,8 @@ SOURCES += \ dialogs/preferences/GraphOptionsWidget.cpp \ widgets/QuickFilterView.cpp \ widgets/ClassesWidget.cpp \ - widgets/ResourcesWidget.cpp + widgets/ResourcesWidget.cpp \ + CutterApplication.cpp HEADERS += \ cutter.h \ @@ -145,7 +147,8 @@ HEADERS += \ dialogs/preferences/GraphOptionsWidget.h \ widgets/QuickFilterView.h \ widgets/ClassesWidget.h \ - widgets/ResourcesWidget.h + widgets/ResourcesWidget.h \ + CutterApplication.h FORMS += \ dialogs/AboutDialog.ui \ diff --git a/src/main.cpp b/src/main.cpp index 28ea0657..ff1526b4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,11 +1,5 @@ -#include -#include -#include -#include - +#include "CutterApplication.h" #include "MainWindow.h" -#include "dialogs/NewFileDialog.h" -#include "dialogs/OptionsDialog.h" #ifdef APPIMAGE #define PREFIX "/tmp/.cutter_usr" @@ -33,79 +27,7 @@ void set_appimage_symlink() int main(int argc, char *argv[]) { - QApplication a(argc, argv); - a.setOrganizationName("cutter"); - a.setApplicationName("cutter"); - a.setApplicationVersion(APP_VERSION); - a.setWindowIcon(QIcon(":/img/cutter.svg")); - - // Set QString codec to UTF-8 - QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); -#if QT_VERSION < QT_VERSION_CHECK(5,0,0) - QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); - QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); -#endif - - QCommandLineParser cmd_parser; - cmd_parser.setApplicationDescription(QObject::tr("A Qt and C++ GUI for radare2 reverse engineering framework")); - cmd_parser.addHelpOption(); - cmd_parser.addVersionOption(); - cmd_parser.addPositionalArgument("filename", QObject::tr("Filename to open.")); - - QCommandLineOption analOption({"A", "anal"}, - QObject::tr("Automatically open file and optionally start analysis. Needs filename to be specified. May be a value between 0 and 2: 0 = no analysis, 1 = aaa, 2 = aaaa (experimental)"), - QObject::tr("level")); - cmd_parser.addOption(analOption); - - cmd_parser.process(a); - - QStringList args = cmd_parser.positionalArguments(); - - // Check r2 version - QString r2version = r_core_version(); - QString localVersion = "" R2_GITTAP; - if (r2version != localVersion) - { - QMessageBox msg; - msg.setIcon(QMessageBox::Critical); - msg.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - msg.setWindowTitle(QObject::tr("Version mismatch!")); - msg.setText(QString(QObject::tr("The version used to compile cutter (%1) does not match the binary version of radare2 (%2). This could result in unexpected behaviour. Are you sure you want to continue?")).arg(localVersion, r2version)); - if (msg.exec() == QMessageBox::No) - return 1; - } - - bool analLevelSpecified = false; - int analLevel= 0; - - if (cmd_parser.isSet(analOption)) - { - analLevel = cmd_parser.value(analOption).toInt(&analLevelSpecified); - - if (!analLevelSpecified || analLevel < 0 || analLevel > 2) - { - printf("%s\n", QObject::tr("Invalid Analysis Level. May be a value between 0 and 2.").toLocal8Bit().constData()); - return 1; - } - } - - - if (args.empty()) - { - if (analLevelSpecified) - { - printf("%s\n", QObject::tr("Filename must be specified to start analysis automatically.").toLocal8Bit().constData()); - return 1; - } - - MainWindow *main = new MainWindow(); - main->displayNewFileDialog(); - } - else // filename specified as positional argument - { - MainWindow *main = new MainWindow(); - main->openNewFile(args[0], analLevelSpecified ? analLevel : -1); - } + CutterApplication a(argc, argv); // Hack to make it work with AppImage #ifdef APPIMAGE