diff --git a/.gitignore b/.gitignore index c7191ed..31c6800 100644 --- a/.gitignore +++ b/.gitignore @@ -229,8 +229,4 @@ pip-log.txt ## qmake / make ############# *.o -Makefile -UEFIExtract/UEFIExtract -UEFIFind/UEFIFind -UEFIPatch/UEFIPatch -UEFITool \ No newline at end of file +Makefile \ No newline at end of file diff --git a/UEFITool/ffsfinder.cpp b/UEFITool/ffsfinder.cpp new file mode 100644 index 0000000..23642c9 --- /dev/null +++ b/UEFITool/ffsfinder.cpp @@ -0,0 +1,192 @@ +/* fssfinder.cpp + +Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +*/ + +#include "ffsfinder.h" + +FfsFinder::FfsFinder(const TreeModel* treeModel, QObject *parent) + : model(treeModel), QObject(parent) +{ +} + +FfsFinder::~FfsFinder() +{ +} + +void FfsFinder::msg(const QString & message, const QModelIndex & index) +{ + messagesVector.push_back(QPair(message, index)); +} + +QVector > FfsFinder::getMessages() const +{ + return messagesVector; +} + +void FfsFinder::clearMessages() +{ + messagesVector.clear(); +} + +STATUS FfsFinder::findHexPattern(const QModelIndex & index, const QByteArray & hexPattern, const UINT8 mode) +{ + if (!index.isValid()) + return ERR_SUCCESS; + + if (hexPattern.isEmpty()) + return ERR_INVALID_PARAMETER; + + // Check for "all substrings" pattern + if (hexPattern.count('.') == hexPattern.length()) + return ERR_SUCCESS; + + bool hasChildren = (model->rowCount(index) > 0); + for (int i = 0; i < model->rowCount(index); i++) { + findHexPattern(index.child(i, index.column()), hexPattern, mode); + } + + QByteArray data; + if (hasChildren) { + if (mode != SEARCH_MODE_BODY) + data = model->header(index); + } + else { + if (mode == SEARCH_MODE_HEADER) + data.append(model->header(index)); + else if (mode == SEARCH_MODE_BODY) + data.append(model->body(index)); + else + data.append(model->header(index)).append(model->body(index)); + } + + QString hexBody = QString(data.toHex()); + QRegExp regexp = QRegExp(QString(hexPattern), Qt::CaseInsensitive); + INT32 offset = regexp.indexIn(hexBody); + while (offset >= 0) { + if (offset % 2 == 0) { + msg(tr("Hex pattern \"%1\" found as \"%2\" in %3 at %4-offset %5h") + .arg(QString(hexPattern)) + .arg(hexBody.mid(offset, hexPattern.length()).toUpper()) + .arg(model->name(index)) + .arg(mode == SEARCH_MODE_BODY ? tr("body") : tr("header")) + .hexarg(offset / 2), + index); + } + offset = regexp.indexIn(hexBody, offset + 1); + } + + return ERR_SUCCESS; +} + +STATUS FfsFinder::findGuidPattern(const QModelIndex & index, const QByteArray & guidPattern, const UINT8 mode) +{ + if (guidPattern.isEmpty()) + return ERR_INVALID_PARAMETER; + + if (!index.isValid()) + return ERR_SUCCESS; + + bool hasChildren = (model->rowCount(index) > 0); + for (int i = 0; i < model->rowCount(index); i++) { + findGuidPattern(index.child(i, index.column()), guidPattern, mode); + } + + QByteArray data; + if (hasChildren) { + if (mode != SEARCH_MODE_BODY) + data = model->header(index); + } + else { + if (mode == SEARCH_MODE_HEADER) + data.append(model->header(index)); + else if (mode == SEARCH_MODE_BODY) + data.append(model->body(index)); + else + data.append(model->header(index)).append(model->body(index)); + } + + QString hexBody = QString(data.toHex()); + QList list = guidPattern.split('-'); + if (list.count() != 5) + return ERR_INVALID_PARAMETER; + + QByteArray hexPattern; + // Reverse first GUID block + hexPattern.append(list.at(0).mid(6, 2)); + hexPattern.append(list.at(0).mid(4, 2)); + hexPattern.append(list.at(0).mid(2, 2)); + hexPattern.append(list.at(0).mid(0, 2)); + // Reverse second GUID block + hexPattern.append(list.at(1).mid(2, 2)); + hexPattern.append(list.at(1).mid(0, 2)); + // Reverse third GUID block + hexPattern.append(list.at(2).mid(2, 2)); + hexPattern.append(list.at(2).mid(0, 2)); + // Append fourth and fifth GUID blocks as is + hexPattern.append(list.at(3)).append(list.at(4)); + + // Check for "all substrings" pattern + if (hexPattern.count('.') == hexPattern.length()) + return ERR_SUCCESS; + + QRegExp regexp = QRegExp(QString(hexPattern), Qt::CaseInsensitive); + INT32 offset = regexp.indexIn(hexBody); + while (offset >= 0) { + if (offset % 2 == 0) { + msg(tr("GUID pattern \"%1\" found as \"%2\" in %3 at %4-offset %5h") + .arg(QString(guidPattern)) + .arg(hexBody.mid(offset, hexPattern.length()).toUpper()) + .arg(model->name(index)) + .arg(mode == SEARCH_MODE_BODY ? tr("body") : tr("header")) + .hexarg(offset / 2), + index); + } + offset = regexp.indexIn(hexBody, offset + 1); + } + + return ERR_SUCCESS; +} + +STATUS FfsFinder::findTextPattern(const QModelIndex & index, const QString & pattern, const bool unicode, const Qt::CaseSensitivity caseSensitive) +{ + if (pattern.isEmpty()) + return ERR_INVALID_PARAMETER; + + if (!index.isValid()) + return ERR_SUCCESS; + + bool hasChildren = (model->rowCount(index) > 0); + for (int i = 0; i < model->rowCount(index); i++) { + findTextPattern(index.child(i, index.column()), pattern, unicode, caseSensitive); + } + + if (hasChildren) + return ERR_SUCCESS; + + QString data; + if (unicode) + data = QString::fromUtf16((const ushort*)model->body(index).data(), model->body(index).length() / 2); + else + data = QString::fromLatin1((const char*)model->body(index).data(), model->body(index).length()); + + int offset = -1; + while ((offset = data.indexOf(pattern, offset + 1, caseSensitive)) >= 0) { + msg(tr("%1 text \"%2\" found in %3 at offset %4h") + .arg(unicode ? "Unicode" : "ASCII") + .arg(pattern) + .arg(model->name(index)) + .hexarg(unicode ? offset * 2 : offset), + index); + } + + return ERR_SUCCESS; +} diff --git a/UEFITool/ffsfinder.h b/UEFITool/ffsfinder.h new file mode 100644 index 0000000..3d30c8e --- /dev/null +++ b/UEFITool/ffsfinder.h @@ -0,0 +1,48 @@ +/* fssfinder.h + +Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +*/ + +#ifndef __FFSFINDER_H__ +#define __FFSFINDER_H__ + +#include +#include +#include +#include + +#include "..\common\basetypes.h" +#include "..\common\treemodel.h" + +class FfsFinder : public QObject +{ + Q_OBJECT + +public: + explicit FfsFinder(const TreeModel * treeModel, QObject *parent = 0); + ~FfsFinder(); + + QVector > getMessages() const; + void clearMessages(); + + STATUS findHexPattern(const QModelIndex & index, const QByteArray & hexPattern, const UINT8 mode); + STATUS findGuidPattern(const QModelIndex & index, const QByteArray & guidPattern, const UINT8 mode); + STATUS findTextPattern(const QModelIndex & index, const QString & pattern, const bool unicode, const Qt::CaseSensitivity caseSensitive); + +private: + const TreeModel* model; + QVector > messagesVector; + + void msg(const QString & message, const QModelIndex &index = QModelIndex()); + +}; + +#endif diff --git a/UEFITool/ffsops.cpp b/UEFITool/ffsops.cpp new file mode 100644 index 0000000..35e4a7c --- /dev/null +++ b/UEFITool/ffsops.cpp @@ -0,0 +1,102 @@ +/* fssops.cpp + +Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +*/ + +#include "ffsops.h" + +FfsOperations::FfsOperations(const TreeModel* treeModel, QObject *parent) + : model(treeModel), QObject(parent) +{ +} + +FfsOperations::~FfsOperations() +{ +} + +void FfsOperations::msg(const QString & message, const QModelIndex & index) +{ + messagesVector.push_back(QPair(message, index)); +} + +QVector > FfsOperations::getMessages() const +{ + return messagesVector; +} + +void FfsOperations::clearMessages() +{ + messagesVector.clear(); +} + +STATUS FfsOperations::extract(const QModelIndex & index, QString & name, QByteArray & extracted, const UINT8 mode) +{ + // Sanity check + if (!index.isValid()) + return ERR_INVALID_PARAMETER; + + // Get data from parsing data + PARSING_DATA pdata = getParsingData(index); + + // Construct a name for extracted data + QString itemName = model->name(index); + QString itemText = model->text(index); + switch (model->type(index)) { + case Types::Volume: { + if (pdata.volume.hasExtendedHeader) + name = guidToQString(pdata.volume.extendedHeaderGuid); + else + name = itemName; + } break; + case Types::File: { + name = itemText.isEmpty() ? itemName : itemText.replace(' ', '_'); + } break; + case Types::Section: { + // Get parent file name + QModelIndex fileIndex = model->findParentOfType(index, Types::File); + QString fileText = model->text(fileIndex); + name = fileText.isEmpty() ? model->name(fileIndex) : fileText.replace(' ', '_'); + // Append section subtype name + name += QChar('_') + itemName.replace(' ', '_'); + } break; + + case Types::Capsule: + case Types::Image: + case Types::Region: + case Types::Padding: + default: + name = itemName.replace(' ', '_').replace('/', '_'); + } + + // Get extracted data + if (mode == EXTRACT_MODE_AS_IS) { + // Extract as is, with header body and tail + extracted.clear(); + extracted.append(model->header(index)); + extracted.append(model->body(index)); + // Handle file tail + if (model->type(index) == Types::File) { + if (pdata.file.hasTail) + extracted.append(pdata.file.tail); + } + } + else if (mode == EXTRACT_MODE_BODY) { + name += tr("_body"); + // Extract without header and tail + extracted.clear(); + extracted.append(model->body(index)); + } + else + return ERR_UNKNOWN_EXTRACT_MODE; + + return ERR_SUCCESS; +} + diff --git a/UEFITool/ffsops.h b/UEFITool/ffsops.h new file mode 100644 index 0000000..c6c16c2 --- /dev/null +++ b/UEFITool/ffsops.h @@ -0,0 +1,47 @@ +/* fssops.h + +Copyright (c) 2015, Nikolaj Schlej. All rights reserved. +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +*/ + +#ifndef __FFSOPS_H__ +#define __FFSOPS_H__ + +#include +#include +#include +#include + +#include "..\common\basetypes.h" +#include "..\common\treemodel.h" +#include "..\common\ffs.h" +#include "..\common\utility.h" + +class FfsOperations : public QObject +{ + Q_OBJECT + +public: + explicit FfsOperations(const TreeModel * treeModel, QObject *parent = 0); + ~FfsOperations(); + + QVector > getMessages() const; + void clearMessages(); + + STATUS extract(const QModelIndex & index, QString & name, QByteArray & extracted, const UINT8 mode); + +private: + const TreeModel* model; + QVector > messagesVector; + + void msg(const QString & message, const QModelIndex &index = QModelIndex()); +}; + +#endif diff --git a/UEFITool/guidlineedit.cpp b/UEFITool/guidlineedit.cpp new file mode 100644 index 0000000..bf706f5 --- /dev/null +++ b/UEFITool/guidlineedit.cpp @@ -0,0 +1,61 @@ +/* guidlineedit.cpp + + Copyright (c) 2014, Nikolaj Schlej. All rights reserved. + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + */ + +#include "guidlineedit.h" + +GuidLineEdit::GuidLineEdit(QWidget * parent) + :QLineEdit(parent) +{ +} + +GuidLineEdit::GuidLineEdit(const QString & contents, QWidget * parent) + :QLineEdit(contents, parent) +{ +} + +GuidLineEdit::~GuidLineEdit() +{ +} + +void GuidLineEdit::keyPressEvent(QKeyEvent * event) +{ + if (event == QKeySequence::Delete || event->key() == Qt::Key_Backspace) + { + int pos = cursorPosition(); + if (event->key() == Qt::Key_Backspace && pos > 0) { + cursorBackward(false); + pos = cursorPosition(); + } + + QString txt = text(); + QString selected = selectedText(); + + if (!selected.isEmpty()) { + pos = QLineEdit::selectionStart(); + for (int i = pos; i < pos + selected.count(); i++) + if (txt[i] != QChar('-')) + txt[i] = QChar('.'); + } + else + txt[pos] = QChar('.'); + + setCursorPosition(0); + insert(txt); + setCursorPosition(pos); + + return; + } + + // Call original event handler + QLineEdit::keyPressEvent(event); +} \ No newline at end of file diff --git a/UEFITool/guidlineedit.h b/UEFITool/guidlineedit.h new file mode 100644 index 0000000..636cf7a --- /dev/null +++ b/UEFITool/guidlineedit.h @@ -0,0 +1,36 @@ +/* guidlineedit.h + + Copyright (c) 2014, Nikolaj Schlej. All rights reserved. + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + */ + +#ifndef __GUIDLINEEDIT_H__ +#define __GUIDLINEEDIT_H__ + +#include +#include +#include +#include + +#include "../common/basetypes.h" + +class GuidLineEdit : public QLineEdit +{ +public: + GuidLineEdit(QWidget * parent = 0); + GuidLineEdit(const QString & contents, QWidget * parent = 0); + ~GuidLineEdit(); + +protected: + void keyPressEvent(QKeyEvent * event); + +}; + +#endif diff --git a/UEFITool/messagelistitem.cpp b/UEFITool/messagelistitem.cpp new file mode 100644 index 0000000..46b3f6b --- /dev/null +++ b/UEFITool/messagelistitem.cpp @@ -0,0 +1,44 @@ +/* messagelistitem.cpp + + Copyright (c) 2013, Nikolaj Schlej. All rights reserved. + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + */ + +#include "messagelistitem.h" + +MessageListItem::MessageListItem(QListWidget * parent, int type, const QModelIndex & index) + : QListWidgetItem(parent, type) +{ + itemIndex = index; +} + +MessageListItem::MessageListItem(const QString & text, QListWidget * parent, int type, const QModelIndex & index) + : QListWidgetItem(text, parent, type), itemIndex(index) +{ +} + +MessageListItem::MessageListItem(const QIcon & icon, const QString & text, QListWidget * parent, int type, const QModelIndex & index) + : QListWidgetItem(icon, text, parent, type), itemIndex(index) +{ +} + +MessageListItem::~MessageListItem() +{ +} + +QModelIndex MessageListItem::index() const +{ + return itemIndex; +} + +void MessageListItem::setIndex(QModelIndex & index) +{ + itemIndex = index; +} \ No newline at end of file diff --git a/UEFITool/messagelistitem.h b/UEFITool/messagelistitem.h new file mode 100644 index 0000000..82d8bcf --- /dev/null +++ b/UEFITool/messagelistitem.h @@ -0,0 +1,37 @@ +/* messagelistitem.h + + Copyright (c) 2014, Nikolaj Schlej. All rights reserved. + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + */ + +#ifndef __MESSAGELISTITEM_H__ +#define __MESSAGELISTITEM_H__ + +#include +#include + +#include "../common/basetypes.h" + +class MessageListItem : public QListWidgetItem +{ +public: + MessageListItem(QListWidget * parent = 0, int type = Type, const QModelIndex & index = QModelIndex()); + MessageListItem(const QString & text, QListWidget * parent = 0, int type = Type, const QModelIndex & index = QModelIndex()); + MessageListItem(const QIcon & icon, const QString & text, QListWidget * parent = 0, int type = Type, const QModelIndex & index = QModelIndex()); + ~MessageListItem(); + + QModelIndex index() const; + void setIndex(QModelIndex & index); + +private: + QModelIndex itemIndex; +}; + +#endif diff --git a/UEFITool/searchdialog.cpp b/UEFITool/searchdialog.cpp new file mode 100644 index 0000000..c9b716d --- /dev/null +++ b/UEFITool/searchdialog.cpp @@ -0,0 +1,49 @@ +/* searchdialog.cpp + + Copyright (c) 2014, Nikolaj Schlej. All rights reserved. + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + */ + +#include "searchdialog.h" + +SearchDialog::SearchDialog(QWidget *parent) : +QDialog(parent), +ui(new Ui::SearchDialog), +hexValidator(QRegExp("([0-9a-fA-F\\. ])*")), +guidValidator(QRegExp("[0-9a-fA-F\\.]{8}-[0-9a-fA-F\\.]{4}-[0-9a-fA-F\\.]{4}-[0-9a-fA-F\\.]{4}-[0-9a-fA-F\\.]{12}")) +{ + // Create UI + ui->setupUi(this); + ui->hexEdit->setValidator(&hexValidator); + ui->guidEdit->setValidator(&guidValidator); + + // Connect + connect(ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(setEditFocus(int))); + + // Set initial focus + setEditFocus(ui->tabWidget->currentIndex()); +} + +SearchDialog::~SearchDialog() +{ + delete ui; +} + +void SearchDialog::setEditFocus(int index) +{ + if (index == 0) // Hex pattern + ui->hexEdit->setFocus(); + else if (index == 1) { // GUID + ui->guidEdit->setFocus(); + ui->guidEdit->setCursorPosition(0); + } + else if (index == 2) // Text + ui->textEdit->setFocus(); +} \ No newline at end of file diff --git a/UEFITool/searchdialog.h b/UEFITool/searchdialog.h new file mode 100644 index 0000000..c5308dc --- /dev/null +++ b/UEFITool/searchdialog.h @@ -0,0 +1,38 @@ +/* searchdialog.h + + Copyright (c) 2014, Nikolaj Schlej. All rights reserved. + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + */ + +#ifndef __SEARCHDIALOG_H__ +#define __SEARCHDIALOG_H__ + +#include +#include +#include "ui_searchdialog.h" + +class SearchDialog : public QDialog +{ + Q_OBJECT + +public: + SearchDialog(QWidget *parent = 0); + ~SearchDialog(); + Ui::SearchDialog* ui; + +private slots: + void setEditFocus(int index); + +private: + QRegExpValidator hexValidator; + QRegExpValidator guidValidator; +}; + +#endif diff --git a/UEFITool/searchdialog.ui b/UEFITool/searchdialog.ui new file mode 100644 index 0000000..6442853 --- /dev/null +++ b/UEFITool/searchdialog.ui @@ -0,0 +1,249 @@ + + + SearchDialog + + + + 0 + 0 + 400 + 237 + + + + Search + + + false + + + + + + 0 + + + + Hex pattern + + + + + + Hex pattern: + + + + + + + + + + + + + + Search scope + + + + + + Header and body + + + true + + + + + + + Header only + + + + + + + Body only + + + + + + + + + + + GUID + + + + + + GUID: + + + + + + + xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + + + ........-....-....-....-............ + + + + + + + Search scope + + + + + + Header and body + + + false + + + + + + + Header only + + + true + + + + + + + Body only + + + + + + + + + + + Text + + + + + + Text: + + + + + + + + + + Text search options + + + + + + Unicode + + + true + + + + + + + Case sensitive + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + GuidLineEdit + QLineEdit +
guidlineedit.h
+
+
+ + tabWidget + hexEdit + hexScopeFullRadioButton + hexScopeHeaderRadioButton + hexScopeBodyRadioButton + buttonBox + textEdit + textUnicodeCheckBox + textCaseSensitiveCheckBox + + + + + buttonBox + accepted() + SearchDialog + accept() + + + 182 + 185 + + + 157 + 194 + + + + + buttonBox + rejected() + SearchDialog + reject() + + + 182 + 185 + + + 286 + 194 + + + + +
diff --git a/UEFITool/uefitool.cpp b/UEFITool/uefitool.cpp new file mode 100644 index 0000000..ea07f50 --- /dev/null +++ b/UEFITool/uefitool.cpp @@ -0,0 +1,791 @@ +/* uefitool.cpp + + Copyright (c) 2015, Nikolaj Schlej. All rights reserved. + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + */ + +#include "uefitool.h" +#include "ui_uefitool.h" + +UEFITool::UEFITool(QWidget *parent) : +QMainWindow(parent), +ui(new Ui::UEFITool), +version(tr("0.30.0_alpha2")) +{ + clipboard = QApplication::clipboard(); + + // Create UI + ui->setupUi(this); + searchDialog = new SearchDialog(this); + model = NULL; + ffsParser = NULL; + ffsFinder = NULL; + ffsOps = NULL; + + // Set window title + this->setWindowTitle(tr("UEFITool %1").arg(version)); + + // Connect signals to slots + connect(ui->actionOpenImageFile, SIGNAL(triggered()), this, SLOT(openImageFile())); + connect(ui->actionSaveImageFile, SIGNAL(triggered()), this, SLOT(saveImageFile())); + connect(ui->actionSearch, SIGNAL(triggered()), this, SLOT(search())); + connect(ui->actionExtract, SIGNAL(triggered()), this, SLOT(extractAsIs())); + connect(ui->actionExtractBody, SIGNAL(triggered()), this, SLOT(extractBody())); + connect(ui->actionInsertInto, SIGNAL(triggered()), this, SLOT(insertInto())); + connect(ui->actionInsertBefore, SIGNAL(triggered()), this, SLOT(insertBefore())); + connect(ui->actionInsertAfter, SIGNAL(triggered()), this, SLOT(insertAfter())); + connect(ui->actionReplace, SIGNAL(triggered()), this, SLOT(replaceAsIs())); + connect(ui->actionReplaceBody, SIGNAL(triggered()), this, SLOT(replaceBody())); + connect(ui->actionRemove, SIGNAL(triggered()), this, SLOT(remove())); + connect(ui->actionRebuild, SIGNAL(triggered()), this, SLOT(rebuild())); + connect(ui->actionMessagesCopy, SIGNAL(triggered()), this, SLOT(copyMessage())); + connect(ui->actionMessagesCopyAll, SIGNAL(triggered()), this, SLOT(copyAllMessages())); + connect(ui->actionMessagesClear, SIGNAL(triggered()), this, SLOT(clearMessages())); + connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(about())); + connect(ui->actionAboutQt, SIGNAL(triggered()), this, SLOT(aboutQt())); + connect(ui->actionQuit, SIGNAL(triggered()), this, SLOT(exit())); + connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(writeSettings())); + + // Enable Drag-and-Drop actions + this->setAcceptDrops(true); + + // Set current directory + currentDir = "."; + + // Set monospace font for some controls + QFont font("Courier New", 10); +#if defined Q_OS_OSX + font = QFont("Menlo", 10); +#elif defined Q_OS_WIN + font = QFont("Consolas", 9); +#endif + ui->infoEdit->setFont(font); + ui->parserMessagesListWidget->setFont(font); + ui->finderMessagesListWidget->setFont(font); + ui->structureTreeView->setFont(font); + searchDialog->ui->guidEdit->setFont(font); + searchDialog->ui->hexEdit->setFont(font); + + // Initialize non-persistent data + init(); + + // Read stored settings + readSettings(); +} + +UEFITool::~UEFITool() +{ + delete ffsOps; + delete ffsFinder; + delete ffsParser; + delete model; + delete searchDialog; + delete ui; +} + +void UEFITool::init() +{ + // Clear components + ui->parserMessagesListWidget->clear(); + ui->finderMessagesListWidget->clear(); + ui->infoEdit->clear(); + + // Set window title + this->setWindowTitle(tr("UEFITool %1").arg(version)); + + // Disable menus + ui->menuCapsuleActions->setDisabled(true); + ui->menuImageActions->setDisabled(true); + ui->menuRegionActions->setDisabled(true); + ui->menuPaddingActions->setDisabled(true); + ui->menuVolumeActions->setDisabled(true); + ui->menuFileActions->setDisabled(true); + ui->menuSectionActions->setDisabled(true); + ui->actionMessagesCopy->setDisabled(true); + ui->actionMessagesCopyAll->setDisabled(true); + + // Create new model ... + if (model) + delete model; + model = new TreeModel(); + ui->structureTreeView->setModel(model); + // ... and ffsParser + if (ffsParser) + delete ffsParser; + ffsParser = new FfsParser(model); + + // Connect + connect(ui->structureTreeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(populateUi(const QModelIndex &))); + connect(ui->parserMessagesListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(scrollTreeView(QListWidgetItem*))); + connect(ui->parserMessagesListWidget, SIGNAL(itemEntered(QListWidgetItem*)), this, SLOT(enableMessagesCopyActions(QListWidgetItem*))); + connect(ui->finderMessagesListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(scrollTreeView(QListWidgetItem*))); + connect(ui->finderMessagesListWidget, SIGNAL(itemEntered(QListWidgetItem*)), this, SLOT(enableMessagesCopyActions(QListWidgetItem*))); +} + +void UEFITool::populateUi(const QModelIndex ¤t) +{ + if (!current.isValid()) + return; + + UINT8 type = model->type(current); + //UINT8 subtype = model->subtype(current); + + // Set info text + ui->infoEdit->setPlainText(model->info(current)); + + // Enable menus + ui->menuCapsuleActions->setEnabled(type == Types::Capsule); + ui->menuImageActions->setEnabled(type == Types::Image); + ui->menuRegionActions->setEnabled(type == Types::Region); + ui->menuPaddingActions->setEnabled(type == Types::Padding); + ui->menuVolumeActions->setEnabled(type == Types::Volume); + ui->menuFileActions->setEnabled(type == Types::File); + ui->menuSectionActions->setEnabled(type == Types::Section); + + // Enable actions + ui->actionExtract->setDisabled(model->hasEmptyHeader(current) && model->hasEmptyBody(current)); + //ui->actionRebuild->setEnabled(type == Types::Volume || type == Types::File || type == Types::Section); + ui->actionExtractBody->setDisabled(model->hasEmptyBody(current)); + //ui->actionRemove->setEnabled(type == Types::Volume || type == Types::File || type == Types::Section); + //ui->actionInsertInto->setEnabled((type == Types::Volume && subtype != Subtypes::UnknownVolume) || + // (type == Types::File && subtype != EFI_FV_FILETYPE_ALL && subtype != EFI_FV_FILETYPE_RAW && subtype != EFI_FV_FILETYPE_PAD) || + // (type == Types::Section && (subtype == EFI_SECTION_COMPRESSION || subtype == EFI_SECTION_GUID_DEFINED || subtype == EFI_SECTION_DISPOSABLE))); + //ui->actionInsertBefore->setEnabled(type == Types::File || type == Types::Section); + //ui->actionInsertAfter->setEnabled(type == Types::File || type == Types::Section); + //ui->actionReplace->setEnabled((type == Types::Region && subtype != Subtypes::DescriptorRegion) || type == Types::Volume || type == Types::File || type == Types::Section); + //ui->actionReplaceBody->setEnabled(type == Types::Volume || type == Types::File || type == Types::Section); + ui->actionMessagesCopy->setEnabled(false); +} + +void UEFITool::search() +{ + if (searchDialog->exec() != QDialog::Accepted) + return; + + QModelIndex rootIndex = model->index(0, 0); + + int index = searchDialog->ui->tabWidget->currentIndex(); + if (index == 0) { // Hex pattern + searchDialog->ui->hexEdit->setFocus(); + QByteArray pattern = searchDialog->ui->hexEdit->text().toLatin1().replace(" ", ""); + if (pattern.isEmpty()) + return; + UINT8 mode; + if (searchDialog->ui->hexScopeHeaderRadioButton->isChecked()) + mode = SEARCH_MODE_HEADER; + else if (searchDialog->ui->hexScopeBodyRadioButton->isChecked()) + mode = SEARCH_MODE_BODY; + else + mode = SEARCH_MODE_ALL; + ffsFinder->findHexPattern(rootIndex, pattern, mode); + showFinderMessages(); + } + else if (index == 1) { // GUID + searchDialog->ui->guidEdit->setFocus(); + searchDialog->ui->guidEdit->setCursorPosition(0); + QByteArray pattern = searchDialog->ui->guidEdit->text().toLatin1(); + if (pattern.isEmpty()) + return; + UINT8 mode; + if (searchDialog->ui->guidScopeHeaderRadioButton->isChecked()) + mode = SEARCH_MODE_HEADER; + else if (searchDialog->ui->guidScopeBodyRadioButton->isChecked()) + mode = SEARCH_MODE_BODY; + else + mode = SEARCH_MODE_ALL; + ffsFinder->findGuidPattern(rootIndex, pattern, mode); + showFinderMessages(); + } + else if (index == 2) { // Text string + searchDialog->ui->textEdit->setFocus(); + QString pattern = searchDialog->ui->textEdit->text(); + if (pattern.isEmpty()) + return; + + ffsFinder->findTextPattern(rootIndex, pattern, searchDialog->ui->textUnicodeCheckBox->isChecked(), + (Qt::CaseSensitivity) searchDialog->ui->textCaseSensitiveCheckBox->isChecked()); + showFinderMessages(); + } +} + +void UEFITool::rebuild() +{ + QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); + if (!index.isValid()) + return; + + /*UINT8 result = ffsEngine->rebuild(index); + + if (result == ERR_SUCCESS) + ui->actionSaveImageFile->setEnabled(true);*/ +} + +void UEFITool::remove() +{ + QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); + if (!index.isValid()) + return; + + /*UINT8 result = ffsEngine->remove(index); + + if (result == ERR_SUCCESS) + ui->actionSaveImageFile->setEnabled(true);*/ +} + +void UEFITool::insert(const UINT8 mode) +{ + QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); + if (!index.isValid()) + return; + + UINT8 type; + + if (mode == CREATE_MODE_BEFORE || mode == CREATE_MODE_AFTER) + type = model->type(index.parent()); + else + type = model->type(index); + + QString path; + switch (type) { + case Types::Volume: + path = QFileDialog::getOpenFileName(this, tr("Select FFS file to insert"), currentDir, "FFS files (*.ffs *.bin);;All files (*)"); + break; + case Types::File: + case Types::Section: + path = QFileDialog::getOpenFileName(this, tr("Select section file to insert"), currentDir, "Section files (*.sct *.bin);;All files (*)"); + break; + default: + return; + } + + if (path.trimmed().isEmpty()) + return; + + QFileInfo fileInfo = QFileInfo(path); + if (!fileInfo.exists()) { + ui->statusBar->showMessage(tr("Please select existing file")); + return; + } + + QFile inputFile; + inputFile.setFileName(path); + + if (!inputFile.open(QFile::ReadOnly)) { + QMessageBox::critical(this, tr("Insertion failed"), tr("Can't open output file for reading"), QMessageBox::Ok); + return; + } + + QByteArray buffer = inputFile.readAll(); + inputFile.close(); + + /*UINT8 result = ffsEngine->insert(index, buffer, mode); + if (result) { + QMessageBox::critical(this, tr("Insertion failed"), errorMessage(result), QMessageBox::Ok); + return; + } + ui->actionSaveImageFile->setEnabled(true);*/ +} + +void UEFITool::insertInto() +{ + insert(CREATE_MODE_PREPEND); +} + +void UEFITool::insertBefore() +{ + insert(CREATE_MODE_BEFORE); +} + +void UEFITool::insertAfter() +{ + insert(CREATE_MODE_AFTER); +} + +void UEFITool::replaceAsIs() +{ + replace(REPLACE_MODE_AS_IS); +} + +void UEFITool::replaceBody() +{ + replace(REPLACE_MODE_BODY); +} + +void UEFITool::replace(const UINT8 mode) +{ + QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); + if (!index.isValid()) + return; + + QString path; + if (model->type(index) == Types::Region) { + if (mode == REPLACE_MODE_AS_IS) { + path = QFileDialog::getOpenFileName(this, tr("Select region file to replace selected object"), currentDir, "Region files (*.rgn *.bin);;All files (*)"); + } + else + return; + } + else if (model->type(index) == Types::Volume) { + if (mode == REPLACE_MODE_AS_IS) { + path = QFileDialog::getOpenFileName(this, tr("Select volume file to replace selected object"), currentDir, "Volume files (*.vol *.bin);;All files (*)"); + } + else if (mode == REPLACE_MODE_BODY) { + path = QFileDialog::getOpenFileName(this, tr("Select volume body file to replace body"), currentDir, "Volume body files (*.vbd *.bin);;All files (*)"); + } + else + return; + } + else if (model->type(index) == Types::File) { + if (mode == REPLACE_MODE_AS_IS) { + path = QFileDialog::getOpenFileName(this, tr("Select FFS file to replace selected object"), currentDir, "FFS files (*.ffs *.bin);;All files (*)"); + } + else if (mode == REPLACE_MODE_BODY) { + if (model->subtype(index) == EFI_FV_FILETYPE_ALL || model->subtype(index) == EFI_FV_FILETYPE_RAW) + path = QFileDialog::getOpenFileName(this, tr("Select raw file to replace body"), currentDir, "Raw files (*.raw *.bin);;All files (*)"); + else if (model->subtype(index) == EFI_FV_FILETYPE_PAD) // Pad file body can't be replaced + //!TODO: handle non-empty pad files + return; + else + path = QFileDialog::getOpenFileName(this, tr("Select FFS file body to replace body"), currentDir, "FFS file body files (*.fbd *.bin);;All files (*)"); + } + else + return; + } + else if (model->type(index) == Types::Section) { + if (mode == REPLACE_MODE_AS_IS) { + path = QFileDialog::getOpenFileName(this, tr("Select section file to replace selected object"), currentDir, "Section files (*.sct *.bin);;All files (*)"); + } + else if (mode == REPLACE_MODE_BODY) { + if (model->subtype(index) == EFI_SECTION_COMPRESSION || model->subtype(index) == EFI_SECTION_GUID_DEFINED || model->subtype(index) == EFI_SECTION_DISPOSABLE) + path = QFileDialog::getOpenFileName(this, tr("Select FFS file body file to replace body"), currentDir, "FFS file body files (*.fbd *.bin);;All files (*)"); + else if (model->subtype(index) == EFI_SECTION_FIRMWARE_VOLUME_IMAGE) + path = QFileDialog::getOpenFileName(this, tr("Select volume file to replace body"), currentDir, "Volume files (*.vol *.bin);;All files (*)"); + else if (model->subtype(index) == EFI_SECTION_RAW) + path = QFileDialog::getOpenFileName(this, tr("Select raw file to replace body"), currentDir, "Raw files (*.raw *.bin);;All files (*)"); + else + path = QFileDialog::getOpenFileName(this, tr("Select file to replace body"), currentDir, "Binary files (*.bin);;All files (*)"); + } + else + return; + } + else + return; + + if (path.trimmed().isEmpty()) + return; + + QFileInfo fileInfo = QFileInfo(path); + if (!fileInfo.exists()) { + ui->statusBar->showMessage(tr("Please select existing file")); + return; + } + + QFile inputFile; + inputFile.setFileName(path); + + if (!inputFile.open(QFile::ReadOnly)) { + QMessageBox::critical(this, tr("Replacing failed"), tr("Can't open input file for reading"), QMessageBox::Ok); + return; + } + + QByteArray buffer = inputFile.readAll(); + inputFile.close(); + + /*UINT8 result = ffsEngine->replace(index, buffer, mode); + if (result) { + QMessageBox::critical(this, tr("Replacing failed"), errorMessage(result), QMessageBox::Ok); + return; + } + ui->actionSaveImageFile->setEnabled(true);*/ +} + +void UEFITool::extractAsIs() +{ + extract(EXTRACT_MODE_AS_IS); +} + +void UEFITool::extractBody() +{ + extract(EXTRACT_MODE_BODY); +} + +void UEFITool::extract(const UINT8 mode) +{ + QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); + if (!index.isValid()) + return; + + QByteArray extracted; + QString name; + UINT8 result = ffsOps->extract(index, name, extracted, mode); + if (result) { + QMessageBox::critical(this, tr("Extraction failed"), errorCodeToQString(result), QMessageBox::Ok); + return; + } + + name = currentDir + QDir::separator() + name; + + UINT8 type = model->type(index); + QString path; + if (mode == EXTRACT_MODE_AS_IS) { + switch (type) { + case Types::Capsule: + path = QFileDialog::getSaveFileName(this, tr("Save capsule to file"), name + ".cap", "Capsule files (*.cap *.bin);;All files (*)"); + break; + case Types::Image: + path = QFileDialog::getSaveFileName(this, tr("Save image to file"), name + ".rom", "Image files (*.rom *.bin);;All files (*)"); + break; + case Types::Region: + path = QFileDialog::getSaveFileName(this, tr("Save region to file"), name + ".rgn", "Region files (*.rgn *.bin);;All files (*)"); + break; + case Types::Padding: + path = QFileDialog::getSaveFileName(this, tr("Save padding to file"), name + ".pad", "Padding files (*.pad *.bin);;All files (*)"); + break; + case Types::Volume: + path = QFileDialog::getSaveFileName(this, tr("Save volume to file"), name + ".vol", "Volume files (*.vol *.bin);;All files (*)"); + break; + case Types::File: + path = QFileDialog::getSaveFileName(this, tr("Save FFS file to file"), name + ".ffs", "FFS files (*.ffs *.bin);;All files (*)"); + break; + case Types::Section: + path = QFileDialog::getSaveFileName(this, tr("Save section file to file"), name + ".sct", "Section files (*.sct *.bin);;All files (*)"); + break; + default: + path = QFileDialog::getSaveFileName(this, tr("Save object to file"), name + ".bin", "Binary files (*.bin);;All files (*)"); + } + } + else if (mode == EXTRACT_MODE_BODY) { + switch (type) { + case Types::Capsule: + path = QFileDialog::getSaveFileName(this, tr("Save capsule body to image file"), name + ".rom", "Image files (*.rom *.bin);;All files (*)"); + break; + case Types::Volume: + path = QFileDialog::getSaveFileName(this, tr("Save volume body to file"), name + ".vbd", "Volume body files (*.vbd *.bin);;All files (*)"); + break; + case Types::File: { + if (model->subtype(index) == EFI_FV_FILETYPE_ALL || model->subtype(index) == EFI_FV_FILETYPE_RAW) + path = QFileDialog::getSaveFileName(this, tr("Save FFS file body to raw file"), name + ".raw", "Raw files (*.raw *.bin);;All files (*)"); + else + path = QFileDialog::getSaveFileName(this, tr("Save FFS file body to file"), name + ".fbd", "FFS file body files (*.fbd *.bin);;All files (*)"); + } + break; + case Types::Section: { + if (model->subtype(index) == EFI_SECTION_COMPRESSION || model->subtype(index) == EFI_SECTION_GUID_DEFINED || model->subtype(index) == EFI_SECTION_DISPOSABLE) + path = QFileDialog::getSaveFileName(this, tr("Save encapsulation section body to FFS body file"), name + ".fbd", "FFS file body files (*.fbd *.bin);;All files (*)"); + else if (model->subtype(index) == EFI_SECTION_FIRMWARE_VOLUME_IMAGE) + path = QFileDialog::getSaveFileName(this, tr("Save section body to volume file"), name + ".vol", "Volume files (*.vol *.bin);;All files (*)"); + else if (model->subtype(index) == EFI_SECTION_RAW) + path = QFileDialog::getSaveFileName(this, tr("Save section body to raw file"), name + ".raw", "Raw files (*.raw *.bin);;All files (*)"); + else + path = QFileDialog::getSaveFileName(this, tr("Save section body to file"), name + ".bin", "Binary files (*.bin);;All files (*)"); + } + break; + default: + path = QFileDialog::getSaveFileName(this, tr("Save object to file"), name + ".bin", "Binary files (*.bin);;All files (*)"); + } + } + else + path = QFileDialog::getSaveFileName(this, tr("Save object to file"), name + ".bin", "Binary files (*.bin);;All files (*)"); + + if (path.trimmed().isEmpty()) + return; + + QFile outputFile; + outputFile.setFileName(path); + if (!outputFile.open(QFile::WriteOnly)) { + QMessageBox::critical(this, tr("Extraction failed"), tr("Can't open output file for rewriting"), QMessageBox::Ok); + return; + } + outputFile.resize(0); + outputFile.write(extracted); + outputFile.close(); +} + +void UEFITool::about() +{ + QMessageBox::about(this, tr("About UEFITool"), tr( + "Copyright (c) 2015, Nikolaj Schlej aka CodeRush.
" + "Program icon made by Alexander Zhidkov.

" + "The program is dedicated to RevoGirl. Rest in peace, young genius.

" + "The program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License.
" + "The full text of the license may be found at OpenSource.org.

" + "THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN \"AS IS\" BASIS, " + "WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, " + "EITHER EXPRESS OR IMPLIED.")); +} + +void UEFITool::aboutQt() +{ + QMessageBox::aboutQt(this, tr("About Qt")); +} + +void UEFITool::exit() +{ + QCoreApplication::exit(0); +} + +void UEFITool::saveImageFile() +{ + /*QString path = QFileDialog::getSaveFileName(this, tr("Save BIOS image file"), currentDir, "BIOS image files (*.rom *.bin *.cap *.bio *.fd *.wph *.efi *.dec);;All files (*)"); + + if (path.isEmpty()) + return; + + QByteArray reconstructed; + UINT8 result = ffsEngine->reconstructImageFile(reconstructed); + showMessages(); + if (result) { + QMessageBox::critical(this, tr("Image reconstruction failed"), errorMessage(result), QMessageBox::Ok); + return; + } + + QFile outputFile; + outputFile.setFileName(path); + + if (!outputFile.open(QFile::WriteOnly)) { + QMessageBox::critical(this, tr("Image reconstruction failed"), tr("Can't open output file for rewriting"), QMessageBox::Ok); + return; + } + + outputFile.resize(0); + outputFile.write(reconstructed); + outputFile.close(); + if (QMessageBox::information(this, tr("Image reconstruction successful"), tr("Open reconstructed file?"), QMessageBox::Yes, QMessageBox::No) + == QMessageBox::Yes) + openImageFile(path);*/ +} + +void UEFITool::openImageFile() +{ + QString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file"), currentDir, "BIOS image files (*.rom *.bin *.cap *.bio *.fd *.wph *.efi *.dec);;All files (*)"); + openImageFile(path); +} + +void UEFITool::openImageFile(QString path) +{ + if (path.trimmed().isEmpty()) + return; + + QFileInfo fileInfo = QFileInfo(path); + + if (!fileInfo.exists()) { + ui->statusBar->showMessage(tr("Please select existing file")); + return; + } + + QFile inputFile; + inputFile.setFileName(path); + + if (!inputFile.open(QFile::ReadOnly)) { + QMessageBox::critical(this, tr("Image parsing failed"), tr("Can't open input file for reading"), QMessageBox::Ok); + return; + } + + QByteArray buffer = inputFile.readAll(); + inputFile.close(); + + init(); + this->setWindowTitle(tr("UEFITool %1 - %2").arg(version).arg(fileInfo.fileName())); + + UINT8 result = ffsParser->parseImageFile(buffer, model->index(0,0)); + showParserMessages(); + if (result) + QMessageBox::critical(this, tr("Image parsing failed"), errorCodeToQString(result), QMessageBox::Ok); + else + ui->statusBar->showMessage(tr("Opened: %1").arg(fileInfo.fileName())); + + // Enable search ... + if (ffsFinder) + delete ffsFinder; + ffsFinder = new FfsFinder(model); + ui->actionSearch->setEnabled(true); + // ... and other operations + if (ffsOps) + delete ffsOps; + ffsOps = new FfsOperations(model); + + // Set current directory + currentDir = fileInfo.absolutePath(); +} + +void UEFITool::copyMessage() +{ + clipboard->clear(); + if (ui->messagesTabWidget->currentIndex() == 0) // Parser tab + clipboard->setText(ui->parserMessagesListWidget->currentItem()->text()); + else if (ui->messagesTabWidget->currentIndex() == 1) // Search tab + clipboard->setText(ui->finderMessagesListWidget->currentItem()->text()); +} + +void UEFITool::copyAllMessages() +{ + QString text; + clipboard->clear(); + if (ui->messagesTabWidget->currentIndex() == 0) { // Parser tab + for (INT32 i = 0; i < ui->parserMessagesListWidget->count(); i++) + text.append(ui->parserMessagesListWidget->item(i)->text()).append("\n"); + clipboard->setText(text); + } + else if (ui->messagesTabWidget->currentIndex() == 1) { // Search tab + for (INT32 i = 0; i < ui->finderMessagesListWidget->count(); i++) + text.append(ui->finderMessagesListWidget->item(i)->text()).append("\n"); + clipboard->setText(text); + } +} + +void UEFITool::enableMessagesCopyActions(QListWidgetItem* item) +{ + ui->actionMessagesCopy->setEnabled(item != NULL); + ui->actionMessagesCopyAll->setEnabled(item != NULL); +} + +void UEFITool::clearMessages() +{ + if (ui->messagesTabWidget->currentIndex() == 0) { // Parser tab + ffsParser->clearMessages(); + ui->parserMessagesListWidget->clear(); + } + else if (ui->messagesTabWidget->currentIndex() == 1) { // Search tab + ffsFinder->clearMessages(); + ui->finderMessagesListWidget->clear(); + } + ui->actionMessagesCopy->setEnabled(false); + ui->actionMessagesCopyAll->setEnabled(false); +} + +void UEFITool::dragEnterEvent(QDragEnterEvent* event) +{ + if (event->mimeData()->hasFormat("text/uri-list")) + event->acceptProposedAction(); +} + +void UEFITool::dropEvent(QDropEvent* event) +{ + QString path = event->mimeData()->urls().at(0).toLocalFile(); + openImageFile(path); +} + +void UEFITool::showParserMessages() +{ + ui->parserMessagesListWidget->clear(); + if (!ffsParser) + return; + + QVector > messages = ffsParser->getMessages(); + QPair msg; + foreach (msg, messages) { + ui->parserMessagesListWidget->addItem(new MessageListItem(msg.first, NULL, 0, msg.second)); + } + + ui->messagesTabWidget->setCurrentIndex(0); + ui->parserMessagesListWidget->scrollToBottom(); +} + +void UEFITool::showFinderMessages() +{ + ui->finderMessagesListWidget->clear(); + if (!ffsParser) + return; + + QVector > messages = ffsFinder->getMessages(); + QPair msg; + foreach (msg, messages) { + ui->finderMessagesListWidget->addItem(new MessageListItem(msg.first, NULL, 0, msg.second)); + } + + ui->messagesTabWidget->setCurrentIndex(1); + ui->finderMessagesListWidget->scrollToBottom(); +} + +void UEFITool::scrollTreeView(QListWidgetItem* item) +{ + MessageListItem* messageItem = static_cast(item); + QModelIndex index = messageItem->index(); + if (index.isValid()) { + ui->structureTreeView->scrollTo(index, QAbstractItemView::PositionAtCenter); + ui->structureTreeView->selectionModel()->clearSelection(); + ui->structureTreeView->selectionModel()->select(index, QItemSelectionModel::Select); + } +} + +void UEFITool::contextMenuEvent(QContextMenuEvent* event) +{ + if (ui->parserMessagesListWidget->underMouse() || ui->finderMessagesListWidget->underMouse()) { + ui->menuMessages->exec(event->globalPos()); + return; + } + + if (!ui->structureTreeView->underMouse()) + return; + + QPoint pt = event->pos(); + QModelIndex index = ui->structureTreeView->indexAt(ui->structureTreeView->viewport()->mapFrom(this, pt)); + if (!index.isValid()) + return; + + switch (model->type(index)) + { + case Types::Capsule: + ui->menuCapsuleActions->exec(event->globalPos()); + break; + case Types::Image: + ui->menuImageActions->exec(event->globalPos()); + break; + case Types::Region: + ui->menuRegionActions->exec(event->globalPos()); + break; + case Types::Padding: + ui->menuPaddingActions->exec(event->globalPos()); + break; + case Types::Volume: + ui->menuVolumeActions->exec(event->globalPos()); + break; + case Types::File: + ui->menuFileActions->exec(event->globalPos()); + break; + case Types::Section: + ui->menuSectionActions->exec(event->globalPos()); + break; + } +} + +void UEFITool::readSettings() +{ + QSettings settings(this); + resize(settings.value("mainWindow/size", QSize(800, 600)).toSize()); + move(settings.value("mainWindow/position", QPoint(0, 0)).toPoint()); + QList horList, vertList; + horList.append(settings.value("mainWindow/treeWidth", 600).toInt()); + horList.append(settings.value("mainWindow/infoWidth", 180).toInt()); + vertList.append(settings.value("mainWindow/treeHeight", 400).toInt()); + vertList.append(settings.value("mainWindow/messageHeight", 180).toInt()); + ui->infoSplitter->setSizes(horList); + ui->messagesSplitter->setSizes(vertList); + ui->structureTreeView->setColumnWidth(0, settings.value("tree/columnWidth0", ui->structureTreeView->columnWidth(0)).toInt()); + ui->structureTreeView->setColumnWidth(1, settings.value("tree/columnWidth1", ui->structureTreeView->columnWidth(1)).toInt()); + ui->structureTreeView->setColumnWidth(2, settings.value("tree/columnWidth2", ui->structureTreeView->columnWidth(2)).toInt()); + ui->structureTreeView->setColumnWidth(3, settings.value("tree/columnWidth3", ui->structureTreeView->columnWidth(3)).toInt()); +} + +void UEFITool::writeSettings() +{ + QSettings settings(this); + settings.setValue("mainWindow/size", size()); + settings.setValue("mainWindow/position", pos()); + settings.setValue("mainWindow/treeWidth", ui->structureGroupBox->width()); + settings.setValue("mainWindow/infoWidth", ui->infoGroupBox->width()); + settings.setValue("mainWindow/treeHeight", ui->structureGroupBox->height()); + settings.setValue("mainWindow/messageHeight", ui->messagesTabWidget->height()); + settings.setValue("tree/columnWidth0", ui->structureTreeView->columnWidth(0)); + settings.setValue("tree/columnWidth1", ui->structureTreeView->columnWidth(1)); + settings.setValue("tree/columnWidth2", ui->structureTreeView->columnWidth(2)); + settings.setValue("tree/columnWidth3", ui->structureTreeView->columnWidth(3)); +} diff --git a/UEFITool/uefitool.h b/UEFITool/uefitool.h new file mode 100644 index 0000000..9472051 --- /dev/null +++ b/UEFITool/uefitool.h @@ -0,0 +1,115 @@ +/* uefitool.h + + Copyright (c) 2014, Nikolaj Schlej. All rights reserved. + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + */ + +#ifndef __UEFITOOL_H__ +#define __UEFITOOL_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "..\common\basetypes.h" +#include "..\common\utility.h" +#include "..\common\ffs.h" +#include "..\common\ffsparser.h" +#include "searchdialog.h" +#include "messagelistitem.h" +#include "ffsfinder.h" +#include "ffsops.h" + +namespace Ui { + class UEFITool; +} + +class UEFITool : public QMainWindow +{ + Q_OBJECT + +public: + explicit UEFITool(QWidget *parent = 0); + ~UEFITool(); + + void openImageFile(QString path); + +private slots: + void init(); + void populateUi(const QModelIndex ¤t); + void scrollTreeView(QListWidgetItem* item); + + void openImageFile(); + void saveImageFile(); + void search(); + + void extract(const UINT8 mode); + void extractAsIs(); + void extractBody(); + + void insert(const UINT8 mode); + void insertInto(); + void insertBefore(); + void insertAfter(); + + void replace(const UINT8 mode); + void replaceAsIs(); + void replaceBody(); + + void rebuild(); + + void remove(); + + void copyMessage(); + void copyAllMessages(); + void enableMessagesCopyActions(QListWidgetItem* item); + void clearMessages(); + + void about(); + void aboutQt(); + + void exit(); + void writeSettings(); + +private: + Ui::UEFITool* ui; + TreeModel* model; + FfsParser* ffsParser; + FfsFinder* ffsFinder; + FfsOperations* ffsOps; + SearchDialog* searchDialog; + QClipboard* clipboard; + QString currentDir; + const QString version; + + void dragEnterEvent(QDragEnterEvent* event); + void dropEvent(QDropEvent* event); + void contextMenuEvent(QContextMenuEvent* event); + void readSettings(); + void showParserMessages(); + void showFinderMessages(); +}; + +#endif diff --git a/UEFITool/uefitool.icns b/UEFITool/uefitool.icns new file mode 100644 index 0000000..a332b70 Binary files /dev/null and b/UEFITool/uefitool.icns differ diff --git a/UEFITool/uefitool.ico b/UEFITool/uefitool.ico new file mode 100644 index 0000000..eb7b2ca Binary files /dev/null and b/UEFITool/uefitool.ico differ diff --git a/UEFITool/uefitool.pro b/UEFITool/uefitool.pro new file mode 100644 index 0000000..f309b74 --- /dev/null +++ b/UEFITool/uefitool.pro @@ -0,0 +1,62 @@ +QT += core gui +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = UEFITool +TEMPLATE = app + +SOURCES += uefitool_main.cpp \ + uefitool.cpp \ + searchdialog.cpp \ + messagelistitem.cpp \ + guidlineedit.cpp \ + ffsfinder.cpp \ + ffsops.cpp \ + ../common/types.cpp \ + ../common/descriptor.cpp \ + ../common/ffs.cpp \ + ../common/peimage.cpp \ + ../common/utility.cpp \ + ../common/ffsbuilder.cpp \ + ../common/ffsparser.cpp \ + ../common/treeitem.cpp \ + ../common/treemodel.cpp \ + ../common/LZMA/LzmaCompress.c \ + ../common/LZMA/LzmaDecompress.c \ + ../common/LZMA/SDK/C/LzFind.c \ + ../common/LZMA/SDK/C/LzmaDec.c \ + ../common/LZMA/SDK/C/LzmaEnc.c \ + ../common/Tiano/EfiTianoDecompress.c \ + ../common/Tiano/EfiTianoCompress.c \ + ../common/Tiano/EfiTianoCompressLegacy.c + +HEADERS += uefitool.h \ + searchdialog.h \ + messagelistitem.h \ + guidlineedit.h \ + ffsfinder.h \ + ffsops.h \ + ../common/basetypes.h \ + ../common/descriptor.h \ + ../common/gbe.h \ + ../common/me.h \ + ../common/ffs.h \ + ../common/peimage.h \ + ../common/types.h \ + ../common/utility.h \ + ../common/parsingdata.h \ + ../common/ffsbuilder.h \ + ../common/ffsparser.h \ + ../common/treeitem.h \ + ../common/treemodel.h \ + ../common/LZMA/LzmaCompress.h \ + ../common/LZMA/LzmaDecompress.h \ + ../common/Tiano/EfiTianoDecompress.h \ + ../common/Tiano/EfiTianoCompress.h + +FORMS += uefitool.ui \ + searchdialog.ui + +RC_FILE = uefitool.rc + +ICON = uefitool.icns + diff --git a/UEFITool/uefitool.rc b/UEFITool/uefitool.rc new file mode 100644 index 0000000..345333b --- /dev/null +++ b/UEFITool/uefitool.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "uefitool.ico" \ No newline at end of file diff --git a/UEFITool/uefitool.ui b/UEFITool/uefitool.ui new file mode 100644 index 0000000..1343690 --- /dev/null +++ b/UEFITool/uefitool.ui @@ -0,0 +1,563 @@ + + + UEFITool + + + + 0 + 0 + 851 + 586 + + + + + 0 + 0 + + + + true + + + UEFITool + + + + + 0 + 0 + + + + + 0 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Qt::Vertical + + + + Qt::Horizontal + + + + Structure + + + + 0 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + 0 + + + + 10 + + + false + + + true + + + 200 + + + true + + + + + + + + Information + + + + 0 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + false + + + false + + + true + + + false + + + + + + + + + 0 + + + + Parser + + + + 0 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + true + + + + + + + + Search + + + + 0 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + true + + + + + + + + + + + + + + + 0 + 0 + 851 + 21 + + + + + &File + + + + + + + + + + + H&elp + + + + + + + &Action + + + + &Capsule + + + + + + + &Image + + + + + + &Region + + + + + + + + &Padding + + + + + + &Volume + + + + + + + + + + + + + + &File + + + + + + + + + + + + + + + + + + &Section + + + + + + + + + + + + + + + + + + + &Messages + + + + + + + + + + + + + + + + + + + + + + + false + + + Insert &after... + + + Insert an object from file after selected object + + + Ctrl+Shift+I + + + + + false + + + Insert b&efore... + + + Insert object from file before selected object + + + Ctrl+Alt+I + + + + + false + + + Rep&lace as is... + + + Replace selected object as is with an object from file + + + Ctrl+R + + + + + false + + + E&xtract as is... + + + Extract selected object as is to file + + + Ctrl+E + + + + + false + + + Extract &body... + + + Extract body of selected object to file + + + Ctrl+Shift+E + + + + + false + + + Re&move + + + Remove selected object + + + Ctrl+Del + + + + + &Open image file... + + + Open image file + + + Ctrl+O + + + + + false + + + Insert &into... + + + Insert object from file into selected object + + + Ctrl+I + + + + + false + + + &Save image file... + + + Save modified image file + + + Ctrl+S + + + + + false + + + &Rebuild + + + Rebuild selected object + + + Ctrl+Space + + + + + &About UEFITool + + + F1 + + + QAction::AboutRole + + + + + About &Qt + + + QAction::AboutQtRole + + + + + &Quit + + + QAction::QuitRole + + + + + false + + + Sear&ch... + + + Ctrl+F + + + + + Cl&ear + + + Clear messages + + + Ctrl+Backspace + + + + + false + + + Replace b&ody... + + + Replace body of selected object with a data from file + + + Ctrl+Shift+R + + + + + &Copy + + + Ctrl+Shift+C + + + + + C&opy all + + + Ctrl+Alt+C + + + + + + + diff --git a/UEFITool/uefitool_main.cpp b/UEFITool/uefitool_main.cpp new file mode 100644 index 0000000..e5764c0 --- /dev/null +++ b/UEFITool/uefitool_main.cpp @@ -0,0 +1,31 @@ +/* uefitool_main.cpp + + Copyright (c) 2014, Nikolaj Schlej. All rights reserved. + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + */ + +#include +#include +#include "uefitool.h" + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + a.setOrganizationName("CodeRush"); + a.setOrganizationDomain("coderush.me"); + a.setApplicationName("UEFITool"); + + UEFITool w; + if (a.arguments().length() > 1) + w.openImageFile(a.arguments().at(1)); + w.show(); + + return a.exec(); +} \ No newline at end of file