diff --git a/.gitmodules b/.gitmodules index e819c17..c2dd554 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "Qt-Frameless-Window-DarkStyle"] path = Qt-Frameless-Window-DarkStyle url = https://github.com/Jorgen-VikingGod/Qt-Frameless-Window-DarkStyle +[submodule "QHexView"] + path = QHexView + url = https://github.com/Dax89/QHexView.git diff --git a/QHexView b/QHexView new file mode 160000 index 0000000..4524ca3 --- /dev/null +++ b/QHexView @@ -0,0 +1 @@ +Subproject commit 4524ca368221c2883a0743d77412fbcfe6fe457b diff --git a/README.md b/README.md index 11fd0b8..11982f6 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ If you are just willing to quickly decompile an [Android](https://android.com/) - Decompile/recompile/sign & install APKs - Built-in code editor (\*.java; \*.smali; \*.xml; \*.yml) w/ syntax highlighting - Built-in viewer for image (\*.gif; \*.jpg; \*.jpeg; \*.png) files +- Built-in hex viewer for binary files ### Downloads Please head over to [Releases](https://github.com/vaibhavpandeyvpz/apkstudio/releases) page for downloading. @@ -44,6 +45,8 @@ Information on building from source is provided in the [Wiki](https://github.com - [patrickfav](https://github.com/patrickfav) for [uber-apk-signer](https://github.com/patrickfav/uber-apk-signer) - [skylot](https://github.com/skylot) for [jadx](https://github.com/skylot/jadx) - [probonopd](https://github.com/probonopd) for [linuxdeployqt](https://github.com/probonopd/linuxdeployqt) +- [Jürgen Skrotzky](https://github.com/Jorgen-VikingGod) for [Qt-Frameless-Window-DarkStyle](https://github.com/Jorgen-VikingGod/Qt-Frameless-Window-DarkStyle) +- [Antonio Davide](https://github.com/Dax89) for [QHexView](https://github.com/Dax89/QHexView) - [p.yusukekamiyamane](https://p.yusukekamiyamane.com/) for [Fugue](https://p.yusukekamiyamane.com/) icons - [Icons8](https://icons8.com/) for various icons - [Surendrajat](https://github.com/Surendrajat) for maintaining project while I couldn't diff --git a/apkstudio.pro b/apkstudio.pro index fc864cd..b8e303d 100644 --- a/apkstudio.pro +++ b/apkstudio.pro @@ -1,3 +1,5 @@ +include(QHexView/QHexView.pri) + QT += core gui widgets TARGET = ApkStudio @@ -16,6 +18,7 @@ HEADERS += \ sources/binarysettingswidget.h \ sources/findreplacedialog.h \ sources/flickcharm.h \ + sources/hexedit.h \ sources/imageviewerwidget.h \ sources/mainwindow.h \ sources/processutils.h \ @@ -30,6 +33,7 @@ HEADERS += \ SOURCES += \ Qt-Frameless-Window-DarkStyle/DarkStyle.cpp \ sources/flickcharm.cpp \ + sources/hexedit.cpp \ sources/imageviewerwidget.cpp \ sources/main.cpp \ sources/adbinstallworker.cpp \ diff --git a/resources/all.qrc b/resources/all.qrc index 7378bf4..4c26bfc 100644 --- a/resources/all.qrc +++ b/resources/all.qrc @@ -1,10 +1,13 @@ + html.def java.def numbers.def + properties.def smali.def strings.def xml.def + yaml.def yml.def diff --git a/resources/html.def b/resources/html.def new file mode 100644 index 0000000..5ecd027 --- /dev/null +++ b/resources/html.def @@ -0,0 +1 @@ +@include html diff --git a/resources/properties.def b/resources/properties.def new file mode 100644 index 0000000..96c675b --- /dev/null +++ b/resources/properties.def @@ -0,0 +1 @@ +keywords \b[a-zA-Z0-9.]+= diff --git a/resources/yaml.def b/resources/yaml.def new file mode 100644 index 0000000..91eb9e6 --- /dev/null +++ b/resources/yaml.def @@ -0,0 +1 @@ +@include yml diff --git a/sources/hexedit.cpp b/sources/hexedit.cpp new file mode 100644 index 0000000..883d958 --- /dev/null +++ b/sources/hexedit.cpp @@ -0,0 +1,36 @@ +#include +#include +#include "document/buffer/qmemorybuffer.h" +#include "hexedit.h" + +HexEdit::HexEdit(QWidget *parent) + : QWidget(parent) +{ + auto layout = new QVBoxLayout(); + layout->addWidget(m_HexView = new QHexView(this)); + layout->setContentsMargins(0, 0, 0, 0); + setLayout(layout); +} + +QString HexEdit::filePath() +{ + return m_FilePath; +} + +void HexEdit::open(const QString &path) +{ + auto document = QHexDocument::fromFile(path); + m_HexView->setDocument(document); + m_FilePath = path; +} + +bool HexEdit::save() +{ + QFile file(m_FilePath); + if (file.open(QFile::WriteOnly)) { + m_HexView->document()->saveTo(&file); + file.close(); + return true; + } + return false; +} diff --git a/sources/hexedit.h b/sources/hexedit.h new file mode 100644 index 0000000..b7b44a5 --- /dev/null +++ b/sources/hexedit.h @@ -0,0 +1,20 @@ +#ifndef HEXEDIT_H +#define HEXEDIT_H + +#include +#include "qhexview.h" + +class HexEdit : public QWidget +{ + Q_OBJECT +private: + QString m_FilePath; + QHexView *m_HexView; +public: + explicit HexEdit(QWidget *parent = nullptr); + QString filePath(); + void open(const QString &path); + bool save(); +}; + +#endif // HEXEDIT_H diff --git a/sources/mainwindow.cpp b/sources/mainwindow.cpp index 4dafb7d..4add996 100644 --- a/sources/mainwindow.cpp +++ b/sources/mainwindow.cpp @@ -26,18 +26,20 @@ #include "apkrecompileworker.h" #include "apksignworker.h" #include "findreplacedialog.h" +#include "hexedit.h" #include "imageviewerwidget.h" +#include "mainwindow.h" #include "settingsdialog.h" #include "signingconfigdialog.h" #include "sourcecodeedit.h" -#include "mainwindow.h" #define COLOR_CODE 0x2ad2c9 #define COLOR_COMMAND 0xd0d2d3 #define COLOR_OUTPUT 0xffffff #define COLOR_ERROR 0xfb0a2a -#define IMAGE_EXTENSIONS "gif|jpg|jpeg|png" +#define IMAGE_EXTENSIONS "gif|jpeg|jpg|png" +#define TEXT_EXTENSIONS "java|html|properties|smali|txt|xml|yaml|yml" #define URL_CONTRIBUTE "https://github.com/vaibhavpandeyvpz/apkstudio" #define URL_DOCUMENTATION "https://vaibhavpandey.com/apkstudio/" @@ -328,14 +330,16 @@ int MainWindow::findTabIndex(const QString &path) int total = m_TabEditors->count(); for (int i = 0; i < total; i++) { QString path2; - auto edit = dynamic_cast(m_TabEditors->widget(i)); + auto widget = m_TabEditors->widget(i); + auto edit = dynamic_cast(widget); + auto hex = dynamic_cast(widget); + auto viewer = dynamic_cast(widget); if (edit) { path2 = edit->filePath(); - } else { - auto viewer = dynamic_cast(m_TabEditors->widget(i)); - if (viewer) { - path2 = viewer->filePath(); - } + } else if (hex) { + path2 = hex->filePath(); + } else if (viewer) { + path2 = viewer->filePath(); } if (QString::compare(path, path2) == 0) { return i; @@ -817,14 +821,16 @@ void MainWindow::handleTabChanged(const int index) qDebug() << "User changed current tab" << index; #endif QString path; - auto edit = dynamic_cast(m_TabEditors->currentWidget()); + auto widget = m_TabEditors->currentWidget(); + auto edit = dynamic_cast(widget); + auto hex = dynamic_cast(widget); + auto viewer = dynamic_cast(widget); if (edit) { path = edit->filePath(); - } else { - auto viewer = dynamic_cast(m_TabEditors->currentWidget()); - if (viewer) { - path = viewer->filePath(); - } + } else if (hex) { + path = hex->filePath(); + } else if (viewer) { + path = viewer->filePath(); } const int total = m_ModelOpenFiles->rowCount(); for (int i = 0; i < total; ++i) { @@ -843,8 +849,8 @@ void MainWindow::handleTabChanged(const int index) m_ActionUndo->setEnabled(false); m_ActionFind->setEnabled(edit); m_ActionReplace->setEnabled(edit); - m_ActionSave->setEnabled(edit); - m_ActionSaveAll->setEnabled(edit); + m_ActionSave->setEnabled(edit || hex); + m_ActionSaveAll->setEnabled(edit || hex); m_ActionGoto->setEnabled(edit); for (auto conn: m_EditorConnections) { disconnect(conn); @@ -875,14 +881,16 @@ void MainWindow::handleTabCloseRequested(const int index) qDebug() << "User requested to close tab" << index; #endif QString path; - auto edit = dynamic_cast(m_TabEditors->widget(index)); + auto widget = m_TabEditors->widget(index); + auto edit = dynamic_cast(widget); + auto hex = dynamic_cast(widget); + auto viewer = dynamic_cast(widget); if (edit) { path = edit->filePath(); - } else { - auto viewer = dynamic_cast(m_TabEditors->widget(index)); - if (viewer) { - path = viewer->filePath(); - } + } else if (hex) { + path = hex->filePath(); + } else if (viewer) { + path = viewer->filePath(); } const int total = m_ModelOpenFiles->rowCount(); for (int i = 0; i < total; ++i) { @@ -1031,10 +1039,14 @@ void MainWindow::openFile(const QString &path) viewer->open(path); viewer->zoomReset(); widget = viewer; - } else { + } else if (!extension.isEmpty() && QString(TEXT_EXTENSIONS).contains(extension, Qt::CaseInsensitive)) { auto editor = new SourceCodeEdit(this); editor->open(path); widget = editor; + } else { + auto hex = new HexEdit(this); + hex->open(path); + widget = hex; } const QIcon icon = m_FileIconProvider.icon(info); auto item = new QStandardItem(icon, info.fileName()); @@ -1116,9 +1128,15 @@ void MainWindow::reloadChildren(QTreeWidgetItem *item) bool MainWindow::saveTab(int i) { - auto edit = dynamic_cast(m_TabEditors->widget(i)); + auto widget = m_TabEditors->widget(i); + auto edit = dynamic_cast(widget); if (edit) { return edit->save(); + } else { + auto hex = dynamic_cast(widget); + if (hex) { + return hex->save(); + } } return true; }