From d94eb489785ca74eabf42bfe64a47dba71e96493 Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Thu, 2 Apr 2015 10:07:32 +0200 Subject: [PATCH] Fixed gitignore The whole UEFITool was removed because of false .gitignore settings --- .gitignore | 6 +- UEFITool/ffsfinder.cpp | 192 +++++++++ UEFITool/ffsfinder.h | 48 +++ UEFITool/ffsops.cpp | 102 +++++ UEFITool/ffsops.h | 47 +++ UEFITool/guidlineedit.cpp | 61 +++ UEFITool/guidlineedit.h | 36 ++ UEFITool/messagelistitem.cpp | 44 ++ UEFITool/messagelistitem.h | 37 ++ UEFITool/searchdialog.cpp | 49 +++ UEFITool/searchdialog.h | 38 ++ UEFITool/searchdialog.ui | 249 +++++++++++ UEFITool/uefitool.cpp | 791 +++++++++++++++++++++++++++++++++++ UEFITool/uefitool.h | 115 +++++ UEFITool/uefitool.icns | Bin 0 -> 99242 bytes UEFITool/uefitool.ico | Bin 0 -> 32038 bytes UEFITool/uefitool.pro | 62 +++ UEFITool/uefitool.rc | 1 + UEFITool/uefitool.ui | 563 +++++++++++++++++++++++++ UEFITool/uefitool_main.cpp | 31 ++ 20 files changed, 2467 insertions(+), 5 deletions(-) create mode 100644 UEFITool/ffsfinder.cpp create mode 100644 UEFITool/ffsfinder.h create mode 100644 UEFITool/ffsops.cpp create mode 100644 UEFITool/ffsops.h create mode 100644 UEFITool/guidlineedit.cpp create mode 100644 UEFITool/guidlineedit.h create mode 100644 UEFITool/messagelistitem.cpp create mode 100644 UEFITool/messagelistitem.h create mode 100644 UEFITool/searchdialog.cpp create mode 100644 UEFITool/searchdialog.h create mode 100644 UEFITool/searchdialog.ui create mode 100644 UEFITool/uefitool.cpp create mode 100644 UEFITool/uefitool.h create mode 100644 UEFITool/uefitool.icns create mode 100644 UEFITool/uefitool.ico create mode 100644 UEFITool/uefitool.pro create mode 100644 UEFITool/uefitool.rc create mode 100644 UEFITool/uefitool.ui create mode 100644 UEFITool/uefitool_main.cpp 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 0000000000000000000000000000000000000000..a332b7039fc6a091118d25c06fa7295bc9f2bc54 GIT binary patch literal 99242 zcmeFa1z1$s`#65?9ZJRS?pR~j)wOobHR$f{?r!D|Geg4=LkvTAhpvH%ih;U=VAr`-9fnl?&aW@o2|M~6q|p#JLf zRhe?0o+`i%HV*2qt{$+lnzE<8y1J$sQcX=Ya1SO5FqEf0+W69MVj`NOo2Ros8sTDtoZQ`=k&2xk!fYK0 z2O&pC2YY)bI|9viv2}ECvVy#Wql2v_LZp?Ql}*ayDH5FRqzd znImLqWNa9AqPDK7syfTi*vQb>Sl^(bx7O4C@{3ct2FAuZdfIwVUUpFo*CbU?`6+uIGupqegjo~}$Z^b8M-^-x4;1VRBM z37s^0ph*4M(ogCQ7KY}q-%!J4XcY(vzeZ?oFGAb;2Y=}#^g4+!@&tkP(+P}pgI^fg zBpu+?FW~8&K7e}KTZFE{-HViBNMC7QPbK-ppObw1GC=X)cO%(u+Tn$9-Qeu zM%PqP)7iiTIym#eME5%i3IP+}Ap21E1HpT~A@tDa5W#2A@2A+`)DEy^!c6~L8WQlE z;-uvy`C)25KUC5FL5@s0YUIgf4o>#xKh(2r5Z^I?rKmaww7j3C2WNR80leIKj{&Ly zhC%i1@9UkJNuen03J(uhZXi|Hv=6LMR34sCSY=?f>GVkIqy!D7-%JM>Ne-|jQ@PIf z_}@MTWm9%_ka8WJ$-f~6Gns(*kRxH;gT?E7@=z5&P*H}#Zze2&E6&S(kU!LGRILXr zpp08V38E6UQne5Ck0(hcdv=1|gLqF03)LG)1C>|-E$`u>0{D*1&H?tJ?txVtQYIx{ z?!Eqx+jnpP40KQOfPEDG@A_x3k*dxotAJWLxy->~e7@?v`kM1qg9#Q|K*68EG`K`l z3+@oXd&JPkPzSfe3SI?^IJI(92{M$FAvCqDLrcKAJ(Sw0{0B7EMpQ!#Ed79?@@>CI zQvgWt&`@a@%pe{O;)jOqd*JUr1{M4cfY6W+N22G5CNOvI=y)Ww`*d6TvtW#iMlQ(e zg>Da-BOf;{5{XWzCCQ$=B{Ng{$?LNskmz`r>f@)`G?L`IpSfbe!KO>DOIs+B$*hBFrljiDcr6mIDHD*)JBll6)XgepWZ_DH;^4` z5t?+rkNf15I1a!WEo85LDR}@#p5$4y!z57vfFmD(4RvJq3Ur#>KQWTLhJ_+p3E5Rm zkRoR2CfUY2N*)5u%4E+j#b3!tbc-Z&50I(=5;{uWD=_dNuv_H)@6R3+2uIkw8^D&g zf_9TUfH?Ca5H@EoK?+F%({Usag+ws%0DYXcH)lH!VVDrrM!`tX&CAu*!zB(0xp=s^ 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 Q0Ot2d`LR-!f@RSE0fEyExc~qF literal 0 HcmV?d00001 diff --git a/UEFITool/uefitool.ico b/UEFITool/uefitool.ico new file mode 100644 index 0000000000000000000000000000000000000000..eb7b2ca4dac5671c55511e2e21cc6891ca2b02d3 GIT binary patch literal 32038 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 Nc^<%j%KyJc;NR|0GOGXp literal 0 HcmV?d00001 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