From 2e788a8a1a5d54944525dfced6987b3c8751fa7e Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej <schlej@live.de> Date: Thu, 2 Apr 2015 10:04:37 +0200 Subject: [PATCH] Big structure update - files split into common and app-specific ones - messages from parser and finder separated - ffsEngine split into multiple classes to reduce complexity - still no image rebuild --- README.rst | 44 -- UEFIExtract/ffsdumper.cpp | 91 +++ UEFIExtract/ffsdumper.h | 43 + UEFIExtract/uefiextract.pro | 37 + UEFIExtract/uefiextract_main.cpp | 65 ++ {LZMA => common/LZMA}/LzmaCompress.c | 0 {LZMA => common/LZMA}/LzmaCompress.h | 0 {LZMA => common/LZMA}/LzmaDecompress.c | 0 {LZMA => common/LZMA}/LzmaDecompress.h | 0 {LZMA => common/LZMA}/SDK/C/7zVersion.h | 0 {LZMA => common/LZMA}/SDK/C/Bra.h | 0 {LZMA => common/LZMA}/SDK/C/Bra86.c | 0 {LZMA => common/LZMA}/SDK/C/CpuArch.h | 0 {LZMA => common/LZMA}/SDK/C/LzFind.c | 0 {LZMA => common/LZMA}/SDK/C/LzFind.h | 0 {LZMA => common/LZMA}/SDK/C/LzHash.h | 0 {LZMA => common/LZMA}/SDK/C/LzmaDec.c | 0 {LZMA => common/LZMA}/SDK/C/LzmaDec.h | 0 {LZMA => common/LZMA}/SDK/C/LzmaEnc.c | 0 {LZMA => common/LZMA}/SDK/C/LzmaEnc.h | 0 {LZMA => common/LZMA}/SDK/C/Types.h | 0 {LZMA => common/LZMA}/UefiLzma.h | 0 {Tiano => common/Tiano}/EfiTianoCompress.c | 0 {Tiano => common/Tiano}/EfiTianoCompress.h | 4 +- .../Tiano}/EfiTianoCompressLegacy.c | 0 {Tiano => common/Tiano}/EfiTianoDecompress.c | 0 {Tiano => common/Tiano}/EfiTianoDecompress.h | 4 +- basetypes.h => common/basetypes.h | 19 +- descriptor.cpp => common/descriptor.cpp | 0 descriptor.h => common/descriptor.h | 0 ffs.cpp => common/ffs.cpp | 0 ffs.h => common/ffs.h | 0 common/ffsbuilder.cpp | 13 + common/ffsbuilder.h | 17 + ffsparser.cpp => common/ffsparser.cpp | 358 ++------- ffsparser.h => common/ffsparser.h | 114 +-- gbe.h => common/gbe.h | 0 me.h => common/me.h | 0 common/parsingdata.h | 90 +++ peimage.cpp => common/peimage.cpp | 0 peimage.h => common/peimage.h | 0 treeitem.cpp => common/treeitem.cpp | 0 treeitem.h => common/treeitem.h | 0 treemodel.cpp => common/treemodel.cpp | 0 treemodel.h => common/treemodel.h | 0 types.cpp => common/types.cpp | 2 +- types.h => common/types.h | 0 utility.cpp => common/utility.cpp | 45 +- utility.h => common/utility.h | 14 + guidlineedit.cpp | 61 -- guidlineedit.h | 36 - messagelistitem.cpp | 46 -- messagelistitem.h | 37 - searchdialog.cpp | 49 -- searchdialog.h | 38 - searchdialog.ui | 249 ------ uefitool.cpp | 744 ------------------ uefitool.h | 110 --- uefitool.icns | Bin 99242 -> 0 bytes uefitool.ico | Bin 32038 -> 0 bytes uefitool.pro | 55 -- uefitool.rc | 1 - uefitool.ui | 529 ------------- uefitool_main.cpp | 31 - 64 files changed, 477 insertions(+), 2469 deletions(-) delete mode 100644 README.rst create mode 100644 UEFIExtract/ffsdumper.cpp create mode 100644 UEFIExtract/ffsdumper.h create mode 100644 UEFIExtract/uefiextract.pro create mode 100644 UEFIExtract/uefiextract_main.cpp rename {LZMA => common/LZMA}/LzmaCompress.c (100%) rename {LZMA => common/LZMA}/LzmaCompress.h (100%) rename {LZMA => common/LZMA}/LzmaDecompress.c (100%) rename {LZMA => common/LZMA}/LzmaDecompress.h (100%) rename {LZMA => common/LZMA}/SDK/C/7zVersion.h (100%) rename {LZMA => common/LZMA}/SDK/C/Bra.h (100%) rename {LZMA => common/LZMA}/SDK/C/Bra86.c (100%) rename {LZMA => common/LZMA}/SDK/C/CpuArch.h (100%) rename {LZMA => common/LZMA}/SDK/C/LzFind.c (100%) rename {LZMA => common/LZMA}/SDK/C/LzFind.h (100%) rename {LZMA => common/LZMA}/SDK/C/LzHash.h (100%) rename {LZMA => common/LZMA}/SDK/C/LzmaDec.c (100%) rename {LZMA => common/LZMA}/SDK/C/LzmaDec.h (100%) rename {LZMA => common/LZMA}/SDK/C/LzmaEnc.c (100%) rename {LZMA => common/LZMA}/SDK/C/LzmaEnc.h (100%) rename {LZMA => common/LZMA}/SDK/C/Types.h (100%) rename {LZMA => common/LZMA}/UefiLzma.h (100%) rename {Tiano => common/Tiano}/EfiTianoCompress.c (100%) rename {Tiano => common/Tiano}/EfiTianoCompress.h (97%) rename {Tiano => common/Tiano}/EfiTianoCompressLegacy.c (100%) rename {Tiano => common/Tiano}/EfiTianoDecompress.c (100%) rename {Tiano => common/Tiano}/EfiTianoDecompress.h (98%) rename basetypes.h => common/basetypes.h (86%) rename descriptor.cpp => common/descriptor.cpp (100%) rename descriptor.h => common/descriptor.h (100%) rename ffs.cpp => common/ffs.cpp (100%) rename ffs.h => common/ffs.h (100%) create mode 100644 common/ffsbuilder.cpp create mode 100644 common/ffsbuilder.h rename ffsparser.cpp => common/ffsparser.cpp (90%) rename ffsparser.h => common/ffsparser.h (58%) rename gbe.h => common/gbe.h (100%) rename me.h => common/me.h (100%) create mode 100644 common/parsingdata.h rename peimage.cpp => common/peimage.cpp (100%) rename peimage.h => common/peimage.h (100%) rename treeitem.cpp => common/treeitem.cpp (100%) rename treeitem.h => common/treeitem.h (100%) rename treemodel.cpp => common/treemodel.cpp (100%) rename treemodel.h => common/treemodel.h (100%) rename types.cpp => common/types.cpp (99%) rename types.h => common/types.h (100%) rename utility.cpp => common/utility.cpp (87%) rename utility.h => common/utility.h (74%) delete mode 100644 guidlineedit.cpp delete mode 100644 guidlineedit.h delete mode 100644 messagelistitem.cpp delete mode 100644 messagelistitem.h delete mode 100644 searchdialog.cpp delete mode 100644 searchdialog.h delete mode 100644 searchdialog.ui delete mode 100644 uefitool.cpp delete mode 100644 uefitool.h delete mode 100644 uefitool.icns delete mode 100644 uefitool.ico delete mode 100644 uefitool.pro delete mode 100644 uefitool.rc delete mode 100644 uefitool.ui delete mode 100644 uefitool_main.cpp diff --git a/README.rst b/README.rst deleted file mode 100644 index 6c48723..0000000 --- a/README.rst +++ /dev/null @@ -1,44 +0,0 @@ -UEFITool -======== -.. image:: https://raw.githubusercontent.com/LongSoft/UEFITool/master/uefitool.ico -.. image:: https://scan.coverity.com/projects/1812/badge.svg?flat=1 - :target: https://scan.coverity.com/projects/1812/ - -| -| UEFITool is a cross-platform C++/Qt program for parsing, extracting and modifying UEFI firmware images. -| It supports parsing of full BIOS images starting with the flash descriptor or any binary files containing UEFI volumes. -| Original development was started `here <http://forums.mydigitallife.info/threads/48979-UEFITool-UEFI-firmware-image-viewer-and-editor>`_ at MDL forums as a cross-platform analog to `PhoenixTool <http://forums.mydigitallife.info/threads/13194-Tool-to-Insert-Replace-SLIC-in-Phoenix-Insyde-Dell-EFI-BIOSes>`_'s structure mode with some additional features, but the program's engine was proven to be usefull for another projects like `UEFIPatch <http://www.insanelymac.com/forum/topic/285444-uefipatch-uefi-patching-utility/>`_, `UBU <http://www.win-raid.com/t154f16-Tool-quot-UEFI-BIOS-Updater-quot-UBU.html>`_ and `OZMTool <http://www.insanelymac.com/forum/topic/299711-ozmtool-an-ozmosis-toolbox/>`_. - -Installation ------------- - -| You can either use `pre-built binaries for Windows and OSX <https://github.com/LongSoft/UEFITool/releases/latest>`_ or build a binary yourself. -| To build a binary you need a C++ compiler and an instance of Qt4/Qt5 library for it. -| Install both of them, get the sources, generate makefiles using qmake (*qmake UEFITool.pro*) and use your make command on that generated files (i.e. *nmake release*, *make release* and so on). - -Usage ------ - -| The program can be started directly without any arguments or supplied with a single argument - a path to the UEFI image file to open after start. -| -| The program window is divided into three panels: **Structure**, **Information** and **Messages**. -| Structure of the image is represented as a tree of elements with different names, types and subtypes. If you select an element, **Information** panel will show the available information about the selected element based on it's type and contents. -| **Messages** panel show all messages from the engine, including structure warnings and search results. Most of messages can be double-clicked to select the element that causes the message. -| -| You can open a menu on each tree element to see what operations are possible for the selected element. This can include various types of **Extract**, **Insert** and **Replace** operations, as well as **Remove** and **Rebuild**. -| **Extract** has two variants: **Extract as is** and **Extract body**. The difference is that **Extract as is** extracts the element with it's header (GUID, size, attributes and other structure-related information are located there), and **Extract body** extracts the element data only. -| **Replace** has the same two variants as **Extract** with the same meaning. -| **Insert** has the three different variants: **Insert before**, **Insert after** and **Insert into**, which is only available for UEFI volumes and encapsulation sections. -| **Remove** marks an element for removal on image reconstuction. -| **Rebuild** marks an element for rebuilding on image reconstruction. Normally, all elements that aren't marked for rebuild won't be changed at all and if you need to correct some structure error (i.e. invalid data checksum) you must mark an element for rebuild manually. If you change an element all it's parents up to the tree root will be marked for rebuild automatically. If UEFI volume is marked for rebuild all uncompressed PEI files in it will also be marked for rebuild because they must be rebased in the reconstructed image to maintain the executable-in-place constraint. -| -| There is also a search function available from the *File* menu, you can search all tree elements for a specified hexadecimal pattern (spaces are not counted, dot symbol (.) is used as placeholder for a single hex digit), a specified GUID (rules are the same as for hex except for spaces) and a specified text (either Unicode or ASCII, case sensitive or not). Search results will be added into **Messages** panel, if anything is found. -| -| After you've finished the modifications, you need to initiate image reconstruction using *Save image file* command from the *File* menu. If anything goes wrong on the reconstruction, an error will pop up, otherwise the program will prompt if you need to open the reconstructed file. Don't rush it, because reconstruction process can also generate some usefull messages, which will be lost if you open the reconstructed file immediatelly. - -Known issues ------------- -* Some images has non-standard calculation of base address of TE images, so the program can rebase them incorrectly after modifications. Will be solved ASAP. -* Some images may not work after modification because of no FIT table support implemented yet. It's on my high priority features list, so I hope it will be corrected soon. -* The program is meant to work with BIOS images, not some vendor-specific BIOS update files, that is why some of that update file either can\t be opened at all or return errors on reconstruction. If someone wants to write an unpacker for such crappy files - I will be glad to use it. -* AMI-specific features like NCBs, ROM_AREA structure and other things like that can't be implemented by me because of the NDA I have. diff --git a/UEFIExtract/ffsdumper.cpp b/UEFIExtract/ffsdumper.cpp new file mode 100644 index 0000000..4639d01 --- /dev/null +++ b/UEFIExtract/ffsdumper.cpp @@ -0,0 +1,91 @@ +/* ffsdumper.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 "ffsdumper.h" + +FfsDumper::FfsDumper(TreeModel* treeModel, QObject *parent) + : model(treeModel), QObject(parent), dumped(false) +{ +} + +FfsDumper::~FfsDumper() +{ +} + +STATUS FfsDumper::dump(const QModelIndex & root, const QString & path) +{ + dumped = false; + UINT8 result = recursiveDump(root, path); + if (result) + return result; + else if (!dumped) + return ERR_ITEM_NOT_FOUND; + return ERR_SUCCESS; +} + +STATUS FfsDumper::recursiveDump(const QModelIndex & index, const QString & path) +{ + if (!index.isValid()) + return ERR_INVALID_PARAMETER; + + QDir dir; + + if (dir.cd(path)) + return ERR_DIR_ALREADY_EXIST; + + if (!dir.mkpath(path)) + return ERR_DIR_CREATE; + + QFile file; + if (!model->header(index).isEmpty()) { + file.setFileName(tr("%1/header.bin").arg(path)); + if (!file.open(QFile::WriteOnly)) + return ERR_FILE_OPEN; + + file.write(model->header(index)); + file.close(); + } + + if (!model->body(index).isEmpty()) { + file.setFileName(tr("%1/body.bin").arg(path)); + if (!file.open(QFile::WriteOnly)) + return ERR_FILE_OPEN; + + file.write(model->body(index)); + file.close(); + } + + QString info = tr("Type: %1\nSubtype: %2\n%3%4") + .arg(itemTypeToQString(model->type(index))) + .arg(itemSubtypeToQString(model->type(index), model->subtype(index))) + .arg(model->text(index).isEmpty() ? "" : tr("Text: %1\n").arg(model->text(index))) + .arg(model->info(index)); + file.setFileName(tr("%1/info.txt").arg(path)); + if (!file.open(QFile::Text | QFile::WriteOnly)) + return ERR_FILE_OPEN; + + file.write(info.toLatin1()); + file.close(); + dumped = true; + + UINT8 result; + for (int i = 0; i < model->rowCount(index); i++) { + QModelIndex childIndex = index.child(i, 0); + QString childPath = QString("%1/%2 %3").arg(path).arg(i).arg(model->text(childIndex).isEmpty() ? model->name(childIndex) : model->text(childIndex)); + result = recursiveDump(childIndex, childPath); + if (result) + return result; + } + + return ERR_SUCCESS; +} \ No newline at end of file diff --git a/UEFIExtract/ffsdumper.h b/UEFIExtract/ffsdumper.h new file mode 100644 index 0000000..9c7d17d --- /dev/null +++ b/UEFIExtract/ffsdumper.h @@ -0,0 +1,43 @@ +/* ffsdumper.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 __FFSDUMPER_H__ +#define __FFSDUMPER_H__ + +#include <QObject> +#include <QDir> +#include <QByteArray> +#include <QString> +#include <QModelIndex> +#include <QFileInfo> + +#include "..\common\basetypes.h" +#include "..\common\treemodel.h" + +class FfsDumper : public QObject +{ + Q_OBJECT + +public: + explicit FfsDumper(TreeModel * treeModel, QObject *parent = 0); + ~FfsDumper(); + + STATUS dump(const QModelIndex & root, const QString & path); + +private: + STATUS recursiveDump(const QModelIndex & root, const QString & path); + TreeModel* model; + bool dumped; +}; + +#endif diff --git a/UEFIExtract/uefiextract.pro b/UEFIExtract/uefiextract.pro new file mode 100644 index 0000000..8805044 --- /dev/null +++ b/UEFIExtract/uefiextract.pro @@ -0,0 +1,37 @@ +QT += core +QT -= gui + +TARGET = UEFIExtract +TEMPLATE = app +CONFIG += console +CONFIG -= app_bundle + +SOURCES += uefiextract_main.cpp \ + ffsdumper.cpp \ + ../common/types.cpp \ + ../common/descriptor.cpp \ + ../common/ffs.cpp \ + ../common/ffsparser.cpp \ + ../common/peimage.cpp \ + ../common/treeitem.cpp \ + ../common/treemodel.cpp \ + ../common/utility.cpp \ + ../common/LZMA/LzmaDecompress.c \ + ../common/LZMA/SDK/C/LzmaDec.c \ + ../common/Tiano/EfiTianoDecompress.c \ + +HEADERS += ffsdumper.h \ + ../common/basetypes.h \ + ../common/descriptor.h \ + ../common/gbe.h \ + ../common/me.h \ + ../common/ffs.h \ + ../common/ffsparser.h \ + ../common/peimage.h \ + ../common/types.h \ + ../common/treeitem.h \ + ../common/treemodel.h \ + ../common/utility.h \ + ../common/LZMA/LzmaDecompress.h \ + ../common/Tiano/EfiTianoDecompress.h + diff --git a/UEFIExtract/uefiextract_main.cpp b/UEFIExtract/uefiextract_main.cpp new file mode 100644 index 0000000..6f011b8 --- /dev/null +++ b/UEFIExtract/uefiextract_main.cpp @@ -0,0 +1,65 @@ +/* uefiextract_main.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 <QCoreApplication> +#include <QVector> +#include <QPair> +#include <QString> +#include <QFileInfo> + +#include <iostream> + +#include "..\common\ffsparser.h" +#include "ffsdumper.h" + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + a.setOrganizationName("CodeRush"); + a.setOrganizationDomain("coderush.me"); + a.setApplicationName("UEFIExtract"); + + if (a.arguments().length() > 1) { + QString path = a.arguments().at(1); + QFileInfo fileInfo(path); + if (!fileInfo.exists()) + return ERR_FILE_OPEN; + + QFile inputFile; + inputFile.setFileName(path); + if (!inputFile.open(QFile::ReadOnly)) + return ERR_FILE_OPEN; + + QByteArray buffer = inputFile.readAll(); + inputFile.close(); + + TreeModel model; + FfsParser ffsParser(&model); + STATUS result = ffsParser.parseImageFile(buffer, model.index(0, 0)); + if (result) + return result; + + QVector<QPair<QString, QModelIndex> > messages = ffsParser.getMessages(); + QPair<QString, QModelIndex> msg; + foreach(msg, messages) { + std::cout << msg.first.toLatin1().constData() << std::endl; + } + + FfsDumper ffsDumper(&model); + return ffsDumper.dump(model.index(0, 0), fileInfo.fileName().append(".dump")); + } + else { + std::cout << "UEFIExtract 0.10.0" << std::endl << std::endl + << "Usage: uefiextract imagefile" << std::endl; + return 1; + } +} diff --git a/LZMA/LzmaCompress.c b/common/LZMA/LzmaCompress.c similarity index 100% rename from LZMA/LzmaCompress.c rename to common/LZMA/LzmaCompress.c diff --git a/LZMA/LzmaCompress.h b/common/LZMA/LzmaCompress.h similarity index 100% rename from LZMA/LzmaCompress.h rename to common/LZMA/LzmaCompress.h diff --git a/LZMA/LzmaDecompress.c b/common/LZMA/LzmaDecompress.c similarity index 100% rename from LZMA/LzmaDecompress.c rename to common/LZMA/LzmaDecompress.c diff --git a/LZMA/LzmaDecompress.h b/common/LZMA/LzmaDecompress.h similarity index 100% rename from LZMA/LzmaDecompress.h rename to common/LZMA/LzmaDecompress.h diff --git a/LZMA/SDK/C/7zVersion.h b/common/LZMA/SDK/C/7zVersion.h similarity index 100% rename from LZMA/SDK/C/7zVersion.h rename to common/LZMA/SDK/C/7zVersion.h diff --git a/LZMA/SDK/C/Bra.h b/common/LZMA/SDK/C/Bra.h similarity index 100% rename from LZMA/SDK/C/Bra.h rename to common/LZMA/SDK/C/Bra.h diff --git a/LZMA/SDK/C/Bra86.c b/common/LZMA/SDK/C/Bra86.c similarity index 100% rename from LZMA/SDK/C/Bra86.c rename to common/LZMA/SDK/C/Bra86.c diff --git a/LZMA/SDK/C/CpuArch.h b/common/LZMA/SDK/C/CpuArch.h similarity index 100% rename from LZMA/SDK/C/CpuArch.h rename to common/LZMA/SDK/C/CpuArch.h diff --git a/LZMA/SDK/C/LzFind.c b/common/LZMA/SDK/C/LzFind.c similarity index 100% rename from LZMA/SDK/C/LzFind.c rename to common/LZMA/SDK/C/LzFind.c diff --git a/LZMA/SDK/C/LzFind.h b/common/LZMA/SDK/C/LzFind.h similarity index 100% rename from LZMA/SDK/C/LzFind.h rename to common/LZMA/SDK/C/LzFind.h diff --git a/LZMA/SDK/C/LzHash.h b/common/LZMA/SDK/C/LzHash.h similarity index 100% rename from LZMA/SDK/C/LzHash.h rename to common/LZMA/SDK/C/LzHash.h diff --git a/LZMA/SDK/C/LzmaDec.c b/common/LZMA/SDK/C/LzmaDec.c similarity index 100% rename from LZMA/SDK/C/LzmaDec.c rename to common/LZMA/SDK/C/LzmaDec.c diff --git a/LZMA/SDK/C/LzmaDec.h b/common/LZMA/SDK/C/LzmaDec.h similarity index 100% rename from LZMA/SDK/C/LzmaDec.h rename to common/LZMA/SDK/C/LzmaDec.h diff --git a/LZMA/SDK/C/LzmaEnc.c b/common/LZMA/SDK/C/LzmaEnc.c similarity index 100% rename from LZMA/SDK/C/LzmaEnc.c rename to common/LZMA/SDK/C/LzmaEnc.c diff --git a/LZMA/SDK/C/LzmaEnc.h b/common/LZMA/SDK/C/LzmaEnc.h similarity index 100% rename from LZMA/SDK/C/LzmaEnc.h rename to common/LZMA/SDK/C/LzmaEnc.h diff --git a/LZMA/SDK/C/Types.h b/common/LZMA/SDK/C/Types.h similarity index 100% rename from LZMA/SDK/C/Types.h rename to common/LZMA/SDK/C/Types.h diff --git a/LZMA/UefiLzma.h b/common/LZMA/UefiLzma.h similarity index 100% rename from LZMA/UefiLzma.h rename to common/LZMA/UefiLzma.h diff --git a/Tiano/EfiTianoCompress.c b/common/Tiano/EfiTianoCompress.c similarity index 100% rename from Tiano/EfiTianoCompress.c rename to common/Tiano/EfiTianoCompress.c diff --git a/Tiano/EfiTianoCompress.h b/common/Tiano/EfiTianoCompress.h similarity index 97% rename from Tiano/EfiTianoCompress.h rename to common/Tiano/EfiTianoCompress.h index e5c2fb9..bb887bf 100644 --- a/Tiano/EfiTianoCompress.h +++ b/common/Tiano/EfiTianoCompress.h @@ -20,8 +20,8 @@ Header file for compression routine. */ -#ifndef _EFITIANOCOMPRESS_H_ -#define _EFITIANOCOMPRESS_H_ +#ifndef __EFITIANOCOMPRESS_H__ +#define __EFITIANOCOMPRESS_H__ #include <string.h> #include <stdlib.h> diff --git a/Tiano/EfiTianoCompressLegacy.c b/common/Tiano/EfiTianoCompressLegacy.c similarity index 100% rename from Tiano/EfiTianoCompressLegacy.c rename to common/Tiano/EfiTianoCompressLegacy.c diff --git a/Tiano/EfiTianoDecompress.c b/common/Tiano/EfiTianoDecompress.c similarity index 100% rename from Tiano/EfiTianoDecompress.c rename to common/Tiano/EfiTianoDecompress.c diff --git a/Tiano/EfiTianoDecompress.h b/common/Tiano/EfiTianoDecompress.h similarity index 98% rename from Tiano/EfiTianoDecompress.h rename to common/Tiano/EfiTianoDecompress.h index 8fa3dcf..8c5e2e6 100644 --- a/Tiano/EfiTianoDecompress.h +++ b/common/Tiano/EfiTianoDecompress.h @@ -21,8 +21,8 @@ Providing both EFI and Tiano decompress algorithms. --*/ -#ifndef _EFITIANODECOMPRESS_H_ -#define _EFITIANODECOMPRESS_H_ +#ifndef __EFITIANODECOMPRESS_H__ +#define __EFITIANODECOMPRESS_H__ #include <string.h> #include <stdlib.h> diff --git a/basetypes.h b/common/basetypes.h similarity index 86% rename from basetypes.h rename to common/basetypes.h index c675e0a..9adef10 100644 --- a/basetypes.h +++ b/common/basetypes.h @@ -60,7 +60,7 @@ typedef UINT8 STATUS; #define ERR_VOLUMES_NOT_FOUND 14 #define ERR_INVALID_VOLUME 15 #define ERR_VOLUME_REVISION_NOT_SUPPORTED 16 -#define ERR_VOLUME_GROW_FAILED 17 +#define ERR_COMPLEX_BLOCK_MAP 17 #define ERR_UNKNOWN_FFS 18 #define ERR_INVALID_FILE 19 #define ERR_INVALID_SECTION 20 @@ -70,22 +70,13 @@ typedef UINT8 STATUS; #define ERR_STANDARD_DECOMPRESSION_FAILED 24 #define ERR_CUSTOMIZED_DECOMPRESSION_FAILED 25 #define ERR_UNKNOWN_COMPRESSION_TYPE 26 -#define ERR_UNKNOWN_EXTRACT_MODE 27 -#define ERR_UNKNOWN_INSERT_MODE 28 +#define ERR_DEPEX_PARSE_FAILED 27 +#define ERR_UNKNOWN_EXTRACT_MODE 28 #define ERR_UNKNOWN_IMAGE_TYPE 29 #define ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE 30 #define ERR_UNKNOWN_RELOCATION_TYPE 31 -#define ERR_GENERIC_CALL_NOT_SUPPORTED 32 -#define ERR_VOLUME_BASE_NOT_FOUND 33 -#define ERR_PEI_CORE_ENTRY_POINT_NOT_FOUND 34 -#define ERR_COMPLEX_BLOCK_MAP 35 -#define ERR_DIR_ALREADY_EXIST 36 -#define ERR_DIR_CREATE 37 -#define ERR_UNKNOWN_PATCH_TYPE 38 -#define ERR_PATCH_OFFSET_OUT_OF_BOUNDS 39 -#define ERR_INVALID_SYMBOL 40 -#define ERR_NOTHING_TO_PATCH 41 -#define ERR_DEPEX_PARSE_FAILED 42 +#define ERR_DIR_ALREADY_EXIST 32 +#define ERR_DIR_CREATE 33 #define ERR_NOT_IMPLEMENTED 0xFF // UDK porting definitions diff --git a/descriptor.cpp b/common/descriptor.cpp similarity index 100% rename from descriptor.cpp rename to common/descriptor.cpp diff --git a/descriptor.h b/common/descriptor.h similarity index 100% rename from descriptor.h rename to common/descriptor.h diff --git a/ffs.cpp b/common/ffs.cpp similarity index 100% rename from ffs.cpp rename to common/ffs.cpp diff --git a/ffs.h b/common/ffs.h similarity index 100% rename from ffs.h rename to common/ffs.h diff --git a/common/ffsbuilder.cpp b/common/ffsbuilder.cpp new file mode 100644 index 0000000..3707071 --- /dev/null +++ b/common/ffsbuilder.cpp @@ -0,0 +1,13 @@ +/* fssbuilder.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. + +*/ + diff --git a/common/ffsbuilder.h b/common/ffsbuilder.h new file mode 100644 index 0000000..cda9b95 --- /dev/null +++ b/common/ffsbuilder.h @@ -0,0 +1,17 @@ +/* fssbuilder.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 __FFSBUILDER_H__ +#define __FFSBUILDER_H__ + +#endif diff --git a/ffsparser.cpp b/common/ffsparser.cpp similarity index 90% rename from ffsparser.cpp rename to common/ffsparser.cpp index 91e33bc..f56643c 100644 --- a/ffsparser.cpp +++ b/common/ffsparser.cpp @@ -20,78 +20,28 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "gbe.h" #include "me.h" - -#ifdef _CONSOLE -#include <iostream> -#endif - -FfsParser::FfsParser(QObject *parent) - : QObject(parent) +FfsParser::FfsParser(TreeModel* treeModel, QObject *parent) + : model(treeModel), QObject(parent) { - model = new TreeModel(); - //oldPeiCoreEntryPoint = 0; - //newPeiCoreEntryPoint = 0; - //dumped = false; } -FfsParser::~FfsParser(void) +FfsParser::~FfsParser() { - delete model; -} - -TreeModel* FfsParser::treeModel() const -{ - return model; } void FfsParser::msg(const QString & message, const QModelIndex & index) { -#ifndef _DISABLE_ENGINE_MESSAGES -#ifndef _CONSOLE - messageItems.enqueue(MessageListItem(message, NULL, 0, index)); -#else - (void) index; - std::cout << message.toLatin1().constData() << std::endl; -#endif -#else - (void)message; - (void)index; -#endif + messagesVector.push_back(QPair<QString, QModelIndex>(message, index)); } -#ifndef _CONSOLE -QQueue<MessageListItem> FfsParser::messages() const +QVector<QPair<QString, QModelIndex> > FfsParser::getMessages() const { - return messageItems; + return messagesVector; } void FfsParser::clearMessages() { - messageItems.clear(); -} -#endif - -// Utility functions -PARSING_DATA FfsParser::getParsingData(const QModelIndex & index) -{ - if (index.isValid()) { - return *(PARSING_DATA*)model->parsingData(index).data(); - } - - PARSING_DATA data; - data.fixed = FALSE; // Item is not fixed by default - data.isOnFlash = TRUE; // Data is on flash by default - data.offset = 0; - data.address = 0; - data.ffsVersion = 0; // Unknown by default - - // Type-specific parts remain unitialized - return data; -} - -QByteArray FfsParser::convertParsingData(const PARSING_DATA & pdata) -{ - return QByteArray((const char*)&pdata, sizeof(PARSING_DATA)); + messagesVector.clear(); } BOOLEAN FfsParser::hasIntersection(const UINT32 begin1, const UINT32 end1, const UINT32 begin2, const UINT32 end2) @@ -107,178 +57,8 @@ BOOLEAN FfsParser::hasIntersection(const UINT32 begin1, const UINT32 end1, const return FALSE; } -// Search routines -STATUS FfsParser::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 FfsParser::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<QByteArray> 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 FfsParser::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; -} - -STATUS FfsParser::parseAprioriRawSection(const QByteArray & body, QString & parsed) -{ - parsed.clear(); - - UINT32 count = body.size() / sizeof(EFI_GUID); - if (count > 0) { - for (UINT32 i = 0; i < count; i++) { - const EFI_GUID* guid = (const EFI_GUID*)body.constData() + i; - parsed += tr("\n%1").arg(guidToQString(*guid)); - } - } - - return ERR_SUCCESS; -} - // Firmware image parsing functions -STATUS FfsParser::parseImageFile(const QByteArray & buffer) +STATUS FfsParser::parseImageFile(const QByteArray & buffer, const QModelIndex & root) { // Check buffer size to be more then or equal to size of EFI_CAPSULE_HEADER if ((UINT32)buffer.size() <= sizeof(EFI_CAPSULE_HEADER)) { @@ -304,15 +84,14 @@ STATUS FfsParser::parseImageFile(const QByteArray & buffer) .hexarg2(capsuleHeader->Flags, 8); // Construct parsing data - PARSING_DATA pdata = getParsingData(); + PARSING_DATA pdata = getParsingData(QModelIndex()); pdata.fixed = TRUE; // Add tree item - index = model->addItem(Types::Capsule, Subtypes::UefiCapsule, name, QString(), info, header, body, convertParsingData(pdata)); + index = model->addItem(Types::Capsule, Subtypes::UefiCapsule, name, QString(), info, header, body, convertParsingData(pdata), root); } // Check buffer for being extended Aptio signed capsule header else if (buffer.startsWith(APTIO_SIGNED_CAPSULE_GUID) || buffer.startsWith(APTIO_UNSIGNED_CAPSULE_GUID)) { - bool signedCapsule = buffer.startsWith(APTIO_SIGNED_CAPSULE_GUID); // Get info const APTIO_CAPSULE_HEADER* capsuleHeader = (const APTIO_CAPSULE_HEADER*)buffer.constData(); @@ -328,17 +107,21 @@ STATUS FfsParser::parseImageFile(const QByteArray & buffer) .hexarg2(capsuleHeader->CapsuleHeader.Flags, 8); // Construct parsing data - PARSING_DATA pdata = getParsingData(); + PARSING_DATA pdata = getParsingData(QModelIndex()); pdata.fixed = TRUE; // Add tree item - index = model->addItem(Types::Capsule, signedCapsule ? Subtypes::AptioSignedCapsule : Subtypes::AptioUnsignedCapsule, name, QString(), info, header, body, convertParsingData(pdata)); + index = model->addItem(Types::Capsule, signedCapsule ? Subtypes::AptioSignedCapsule : Subtypes::AptioUnsignedCapsule, name, QString(), info, header, body, convertParsingData(pdata), root); // Show message about possible Aptio signature break if (signedCapsule) { msg(tr("parseImageFile: Aptio capsule signature may become invalid after image modifications"), index); } } + // Other cases + else { + index = root; + } // Skip capsule header to have flash chip image QByteArray flashImage = buffer.mid(capsuleHeaderSize); @@ -368,6 +151,8 @@ STATUS FfsParser::parseImageFile(const QByteArray & buffer) // Add tree item QModelIndex biosIndex = model->addItem(Types::Image, Subtypes::UefiImage, name, QString(), info, QByteArray(), flashImage, convertParsingData(pdata), index); + + // Parse the image return parseRawArea(flashImage, biosIndex); } @@ -660,7 +445,7 @@ STATUS FfsParser::parseMeRegion(const QByteArray & me, const UINT32 parentOffset PARSING_DATA pdata = getParsingData(parent); // Get info - QString name = tr("ME/TXE region"); + QString name = tr("ME region"); QString info = tr("Full size: %1h (%2)"). hexarg(me.size()).arg(me.size()); @@ -706,10 +491,10 @@ STATUS FfsParser::parseMeRegion(const QByteArray & me, const UINT32 parentOffset // Show messages if (emptyRegion) { - msg(tr("parseMeRegion: ME/TXE region is empty"), index); + msg(tr("parseMeRegion: ME region is empty"), index); } else if (!versionFound) { - msg(tr("parseMeRegion: ME/TXE region version is unknown, it can be damaged"), index); + msg(tr("parseMeRegion: ME version is unknown, it can be damaged"), index); } return ERR_SUCCESS; @@ -779,7 +564,7 @@ UINT8 FfsParser::getPaddingType(const QByteArray & padding) return Subtypes::DataPadding; } -STATUS FfsParser::parseRawArea(const QByteArray & bios, const QModelIndex & index) +STATUS FfsParser::parseRawArea(const QByteArray & data, const QModelIndex & index) { // Sanity check if (!index.isValid()) @@ -794,7 +579,7 @@ STATUS FfsParser::parseRawArea(const QByteArray & bios, const QModelIndex & inde STATUS result; UINT32 prevVolumeOffset; - result = findNextVolume(bios, 0, prevVolumeOffset); + result = findNextVolume(data, 0, prevVolumeOffset); if (result) return result; @@ -803,7 +588,7 @@ STATUS FfsParser::parseRawArea(const QByteArray & bios, const QModelIndex & inde QString info; if (prevVolumeOffset > 0) { // Get info - QByteArray padding = bios.left(prevVolumeOffset); + QByteArray padding = data.left(prevVolumeOffset); name = tr("Padding"); info = tr("Full size: %1h (%2)") .hexarg(padding.size()).arg(padding.size()); @@ -826,7 +611,7 @@ STATUS FfsParser::parseRawArea(const QByteArray & bios, const QModelIndex & inde if (volumeOffset > prevVolumeOffset + prevVolumeSize) { UINT32 paddingOffset = prevVolumeOffset + prevVolumeSize; UINT32 paddingSize = volumeOffset - paddingOffset; - QByteArray padding = bios.mid(paddingOffset, paddingSize); + QByteArray padding = data.mid(paddingOffset, paddingSize); // Get info name = tr("Padding"); @@ -845,15 +630,15 @@ STATUS FfsParser::parseRawArea(const QByteArray & bios, const QModelIndex & inde // Get volume size UINT32 volumeSize = 0; UINT32 bmVolumeSize = 0; - result = getVolumeSize(bios, volumeOffset, volumeSize, bmVolumeSize); + result = getVolumeSize(data, volumeOffset, volumeSize, bmVolumeSize); if (result) return result; // Check that volume is fully present in input - QByteArray volume = bios.mid(volumeOffset, volumeSize); + QByteArray volume = data.mid(volumeOffset, volumeSize); if (volumeSize > (UINT32)volume.size()) { // Mark the rest as padding and finish the parsing - QByteArray padding = bios.right(volume.size()); + QByteArray padding = data.right(volume.size()); // Get info name = tr("Padding"); @@ -892,13 +677,13 @@ STATUS FfsParser::parseRawArea(const QByteArray & bios, const QModelIndex & inde // Go to next volume prevVolumeOffset = volumeOffset; prevVolumeSize = volumeSize; - result = findNextVolume(bios, volumeOffset + prevVolumeSize, volumeOffset); + result = findNextVolume(data, volumeOffset + prevVolumeSize, volumeOffset); } // Padding at the end of BIOS space volumeOffset = prevVolumeOffset + prevVolumeSize; - if ((UINT32)bios.size() > volumeOffset) { - QByteArray padding = bios.mid(volumeOffset); + if ((UINT32)data.size() > volumeOffset) { + QByteArray padding = data.mid(volumeOffset); // Get info name = tr("Padding"); @@ -1268,7 +1053,7 @@ STATUS FfsParser::parseVolumeBody(const QModelIndex & index) QModelIndex fileIndex; STATUS result = parseFileHeader(file, volumeHeaderSize + fileOffset, index, fileIndex); if (result) - msg(tr("parseVolumeBody: file header parsing failed with error %1").arg(errorCodeToQString(result)), index); + msg(tr("parseVolumeBody: file header parsing failed with error \"%1\"").arg(errorCodeToQString(result)), index); // Move to next file fileOffset += fileSize; @@ -1608,7 +1393,7 @@ STATUS FfsParser::parseSections(QByteArray sections, const QModelIndex & index) QModelIndex sectionIndex; STATUS result = parseSectionHeader(sections.mid(sectionOffset, sectionSize), headerSize + sectionOffset, index, sectionIndex); if (result) - msg(tr("parseSections: section header parsing failed with error %1").arg(errorCodeToQString(result)), index); + msg(tr("parseSections: section header parsing failed with error \"%1\"").arg(errorCodeToQString(result)), index); // Move to next section sectionOffset += sectionSize; @@ -2271,6 +2056,21 @@ STATUS FfsParser::parseUiSectionBody(const QModelIndex & index) return ERR_SUCCESS; } +STATUS FfsParser::parseAprioriRawSection(const QByteArray & body, QString & parsed) +{ + parsed.clear(); + + UINT32 count = body.size() / sizeof(EFI_GUID); + if (count > 0) { + for (UINT32 i = 0; i < count; i++) { + const EFI_GUID* guid = (const EFI_GUID*)body.constData() + i; + parsed += tr("\n%1").arg(guidToQString(*guid)); + } + } + + return ERR_SUCCESS; +} + STATUS FfsParser::parseRawSectionBody(const QModelIndex & index) { // Sanity check @@ -2485,65 +2285,3 @@ STATUS FfsEngine::parseFirmwareVolumeImageSectionHeader(const QByteArray & secti } */ -STATUS FfsParser::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/ffsparser.h b/common/ffsparser.h similarity index 58% rename from ffsparser.h rename to common/ffsparser.h index e1503a4..4bfd89b 100644 --- a/ffsparser.h +++ b/common/ffsparser.h @@ -19,112 +19,33 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include <QObject> #include <QModelIndex> #include <QByteArray> -#include <QQueue> #include <QVector> #include "basetypes.h" #include "treemodel.h" #include "utility.h" #include "peimage.h" - -#ifndef _CONSOLE -#include "messagelistitem.h" -#endif +#include "parsingdata.h" class TreeModel; -//The whole parsing data is an information needed for each level of image reconstruction -//routines without the need of backward traversal - -//typedef struct _CAPSULE_PARSING_DATA { -//} CAPSULE_PARSING_DATA; - -//typedef struct _IMAGE_PARSING_DATA { -//} IMAGE_PARSING_DATA; - -//typedef struct _PADDING_PARSING_DATA { -//} PADDING_PARSING_DATA; - -typedef struct _VOLUME_PARSING_DATA { - EFI_GUID extendedHeaderGuid; - UINT32 alignment; - UINT8 revision; - BOOLEAN hasExtendedHeader; - BOOLEAN hasZeroVectorCRC32; - BOOLEAN isWeakAligned; -} VOLUME_PARSING_DATA; - -//typedef struct _FREE_SPACE_PARSING_DATA { -//} FREE_SPACE_PARSING_DATA; - -typedef struct _FILE_PARSING_DATA { - UINT16 tail; - BOOLEAN hasTail; -} FILE_PARSING_DATA; - -typedef struct _COMPRESSED_SECTION_PARSING_DATA { - UINT32 uncompressedSize; - UINT8 compressionType; - UINT8 algorithm; -} COMPRESSED_SECTION_PARSING_DATA; - -typedef struct _GUIDED_SECTION_PARSING_DATA { - EFI_GUID guid; - UINT32 attributes; -} GUIDED_SECTION_PARSING_DATA; - -typedef struct _FREEFORM_GUIDED_SECTION_PARSING_DATA { - EFI_GUID guid; -} FREEFORM_GUIDED_SECTION_PARSING_DATA; - -typedef struct _SECTION_PARSING_DATA { - union { - COMPRESSED_SECTION_PARSING_DATA compressed; - GUIDED_SECTION_PARSING_DATA guidDefined; - FREEFORM_GUIDED_SECTION_PARSING_DATA freeformSubtypeGuid; - }; -} SECTION_PARSING_DATA; - -typedef struct _PARSING_DATA { - BOOLEAN fixed; - BOOLEAN isOnFlash; - UINT8 emptyByte; - UINT8 ffsVersion; - UINT32 offset; - UINT64 address; - union { - //CAPSULE_PARSING_DATA capsule; - //IMAGE_PARSING_DATA image; - //PADDING_PARSING_DATA padding; - VOLUME_PARSING_DATA volume; - //FREE_SPACE_PARSING_DATA freeSpace; - FILE_PARSING_DATA file; - SECTION_PARSING_DATA section; - }; -} PARSING_DATA; - class FfsParser : public QObject { Q_OBJECT public: // Default constructor and destructor - FfsParser(QObject *parent = 0); - ~FfsParser(void); + FfsParser(TreeModel* treeModel, QObject *parent = 0); + ~FfsParser(); - // Returns model for Qt view classes - TreeModel* treeModel() const; - -#ifndef _CONSOLE - // Returns message items queue - QQueue<MessageListItem> messages() const; - // Clears message items queue + // Returns messages + QVector<QPair<QString, QModelIndex> > getMessages() const; + // Clears messages void clearMessages(); -#endif // Firmware image parsing - STATUS parseImageFile(const QByteArray & imageFile); - STATUS parseRawArea(const QByteArray & bios, const QModelIndex & index); + STATUS parseImageFile(const QByteArray & imageFile, const QModelIndex & index); + STATUS parseRawArea(const QByteArray & data, const QModelIndex & index); STATUS parseVolumeHeader(const QByteArray & volume, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); STATUS parseVolumeBody(const QModelIndex & index); STATUS parseFileHeader(const QByteArray & file, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); @@ -132,17 +53,15 @@ public: STATUS parseSectionHeader(const QByteArray & section, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); STATUS parseSectionBody(const QModelIndex & index); - // Search routines TODO: move to another class - 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); - - STATUS extract(const QModelIndex & index, QString & name, QByteArray & extracted, const UINT8 mode); + /*// Search routines TODO: move to another class + // Extract routine TODO: move to another class + STATUS extract(const QModelIndex & index, QString & name, QByteArray & extracted, const UINT8 mode);*/ private: TreeModel *model; + QVector<QPair<QString, QModelIndex> > messagesVector; - STATUS parseIntelImage(const QByteArray & intelImage, const QModelIndex & parent, QModelIndex & index); + STATUS parseIntelImage(const QByteArray & intelImage, const QModelIndex & parent, QModelIndex & root = QModelIndex()); STATUS parseGbeRegion(const QByteArray & gbe, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); STATUS parseMeRegion(const QByteArray & me, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); STATUS parseBiosRegion(const QByteArray & bios, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index); @@ -165,7 +84,7 @@ private: STATUS parseUiSectionBody(const QModelIndex & index); STATUS parseRawSectionBody(const QModelIndex & index); - UINT8 getPaddingType(const QByteArray & padding); + UINT8 getPaddingType(const QByteArray & padding); STATUS parseAprioriRawSection(const QByteArray & body, QString & parsed); STATUS findNextVolume(const QByteArray & bios, const UINT32 volumeOffset, UINT32 & nextVolumeOffset); STATUS getVolumeSize(const QByteArray & bios, const UINT32 volumeOffset, UINT32 & volumeSize, UINT32 & bmVolumeSize); @@ -174,12 +93,7 @@ private: // Internal operations BOOLEAN hasIntersection(const UINT32 begin1, const UINT32 end1, const UINT32 begin2, const UINT32 end2); - PARSING_DATA getParsingData(const QModelIndex & index = QModelIndex()); - QByteArray convertParsingData(const PARSING_DATA & pdata); -#ifndef _CONSOLE - QQueue<MessageListItem> messageItems; -#endif // Message helper void msg(const QString & message, const QModelIndex &index = QModelIndex()); diff --git a/gbe.h b/common/gbe.h similarity index 100% rename from gbe.h rename to common/gbe.h diff --git a/me.h b/common/me.h similarity index 100% rename from me.h rename to common/me.h diff --git a/common/parsingdata.h b/common/parsingdata.h new file mode 100644 index 0000000..8feec51 --- /dev/null +++ b/common/parsingdata.h @@ -0,0 +1,90 @@ +/* ffsparser.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, +WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +Parsing data is an information needed for each level of image reconstruction +routines without the need of backward traversal + +*/ + +#ifndef __PARSINGDATA_H__ +#define __PARSINGDATA_H__ + +#include "basetypes.h" + +//typedef struct _CAPSULE_PARSING_DATA { +//} CAPSULE_PARSING_DATA; + +//typedef struct _IMAGE_PARSING_DATA { +//} IMAGE_PARSING_DATA; + +//typedef struct _PADDING_PARSING_DATA { +//} PADDING_PARSING_DATA; + +typedef struct _VOLUME_PARSING_DATA { + EFI_GUID extendedHeaderGuid; + UINT32 alignment; + UINT8 revision; + BOOLEAN hasExtendedHeader; + BOOLEAN hasZeroVectorCRC32; + BOOLEAN isWeakAligned; +} VOLUME_PARSING_DATA; + +//typedef struct _FREE_SPACE_PARSING_DATA { +//} FREE_SPACE_PARSING_DATA; + +typedef struct _FILE_PARSING_DATA { + UINT16 tail; + BOOLEAN hasTail; +} FILE_PARSING_DATA; + +typedef struct _COMPRESSED_SECTION_PARSING_DATA { + UINT32 uncompressedSize; + UINT8 compressionType; + UINT8 algorithm; +} COMPRESSED_SECTION_PARSING_DATA; + +typedef struct _GUIDED_SECTION_PARSING_DATA { + EFI_GUID guid; + UINT32 attributes; +} GUIDED_SECTION_PARSING_DATA; + +typedef struct _FREEFORM_GUIDED_SECTION_PARSING_DATA { + EFI_GUID guid; +} FREEFORM_GUIDED_SECTION_PARSING_DATA; + +typedef struct _SECTION_PARSING_DATA { + union { + COMPRESSED_SECTION_PARSING_DATA compressed; + GUIDED_SECTION_PARSING_DATA guidDefined; + FREEFORM_GUIDED_SECTION_PARSING_DATA freeformSubtypeGuid; + }; +} SECTION_PARSING_DATA; + +typedef struct _PARSING_DATA { + BOOLEAN fixed; + BOOLEAN isOnFlash; + UINT8 emptyByte; + UINT8 ffsVersion; + UINT32 offset; + UINT64 address; + union { + //CAPSULE_PARSING_DATA capsule; + //IMAGE_PARSING_DATA image; + //PADDING_PARSING_DATA padding; + VOLUME_PARSING_DATA volume; + //FREE_SPACE_PARSING_DATA freeSpace; + FILE_PARSING_DATA file; + SECTION_PARSING_DATA section; + }; +} PARSING_DATA; + +#endif diff --git a/peimage.cpp b/common/peimage.cpp similarity index 100% rename from peimage.cpp rename to common/peimage.cpp diff --git a/peimage.h b/common/peimage.h similarity index 100% rename from peimage.h rename to common/peimage.h diff --git a/treeitem.cpp b/common/treeitem.cpp similarity index 100% rename from treeitem.cpp rename to common/treeitem.cpp diff --git a/treeitem.h b/common/treeitem.h similarity index 100% rename from treeitem.h rename to common/treeitem.h diff --git a/treemodel.cpp b/common/treemodel.cpp similarity index 100% rename from treemodel.cpp rename to common/treemodel.cpp diff --git a/treemodel.h b/common/treemodel.h similarity index 100% rename from treemodel.h rename to common/treemodel.h diff --git a/types.cpp b/common/types.cpp similarity index 99% rename from types.cpp rename to common/types.cpp index 5904e9e..a8517aa 100644 --- a/types.cpp +++ b/common/types.cpp @@ -24,7 +24,7 @@ QString regionTypeToQString(const UINT8 type) case Subtypes::GbeRegion: return QObject::tr("GbE"); case Subtypes::MeRegion: - return QObject::tr("ME/TXE"); + return QObject::tr("ME"); case Subtypes::BiosRegion: return QObject::tr("BIOS"); case Subtypes::PdrRegion: diff --git a/types.h b/common/types.h similarity index 100% rename from types.h rename to common/types.h diff --git a/utility.cpp b/common/utility.cpp similarity index 87% rename from utility.cpp rename to common/utility.cpp index 57f9ed5..6489519 100644 --- a/utility.cpp +++ b/common/utility.cpp @@ -11,6 +11,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. */ #include <QObject> +#include "treemodel.h" #include "utility.h" #include "ffs.h" #include "Tiano/EfiTianoCompress.h" @@ -18,6 +19,31 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "LZMA/LzmaCompress.h" #include "LZMA/LzmaDecompress.h" +// Returns either new parsing data instance or obtains it from index +PARSING_DATA getParsingData(const QModelIndex & index) +{ + if (index.isValid()) { + TreeModel* model = (TreeModel*)index.model(); + return *(PARSING_DATA*)model->parsingData(index).data(); + } + + PARSING_DATA data; + data.fixed = FALSE; // Item is not fixed by default + data.isOnFlash = TRUE; // Data is on flash by default + data.offset = 0; + data.address = 0; + data.ffsVersion = 0; // Unknown by default + + // Type-specific parts remain unitialized + return data; +} + +// Converts parsing data to byte array +QByteArray convertParsingData(const PARSING_DATA & pdata) +{ + return QByteArray((const char*)&pdata, sizeof(PARSING_DATA)); +} + // Returns text representation of error code QString errorCodeToQString(UINT8 errorCode) { @@ -40,7 +66,7 @@ QString errorCodeToQString(UINT8 errorCode) case ERR_VOLUMES_NOT_FOUND: return QObject::tr("UEFI volumes not found"); case ERR_INVALID_VOLUME: return QObject::tr("Invalid UEFI volume"); case ERR_VOLUME_REVISION_NOT_SUPPORTED: return QObject::tr("Volume revision not supported"); - case ERR_VOLUME_GROW_FAILED: return QObject::tr("Volume grow failed"); + //case ERR_VOLUME_GROW_FAILED: return QObject::tr("Volume grow failed"); case ERR_UNKNOWN_FFS: return QObject::tr("Unknown file system"); case ERR_INVALID_FILE: return QObject::tr("Invalid file"); case ERR_INVALID_SECTION: return QObject::tr("Invalid section"); @@ -51,20 +77,20 @@ QString errorCodeToQString(UINT8 errorCode) case ERR_CUSTOMIZED_DECOMPRESSION_FAILED: return QObject::tr("Customized compression failed"); case ERR_UNKNOWN_COMPRESSION_TYPE: return QObject::tr("Unknown compression type"); case ERR_UNKNOWN_EXTRACT_MODE: return QObject::tr("Unknown extract mode"); - case ERR_UNKNOWN_INSERT_MODE: return QObject::tr("Unknown insert mode"); + //case ERR_UNKNOWN_INSERT_MODE: return QObject::tr("Unknown insert mode"); case ERR_UNKNOWN_IMAGE_TYPE: return QObject::tr("Unknown executable image type"); case ERR_UNKNOWN_PE_OPTIONAL_HEADER_TYPE: return QObject::tr("Unknown PE optional header type"); case ERR_UNKNOWN_RELOCATION_TYPE: return QObject::tr("Unknown relocation type"); - case ERR_GENERIC_CALL_NOT_SUPPORTED: return QObject::tr("Generic call not supported"); - case ERR_VOLUME_BASE_NOT_FOUND: return QObject::tr("Volume base address not found"); - case ERR_PEI_CORE_ENTRY_POINT_NOT_FOUND: return QObject::tr("PEI core entry point not found"); + //case ERR_GENERIC_CALL_NOT_SUPPORTED: return QObject::tr("Generic call not supported"); + //case ERR_VOLUME_BASE_NOT_FOUND: return QObject::tr("Volume base address not found"); + //case ERR_PEI_CORE_ENTRY_POINT_NOT_FOUND: return QObject::tr("PEI core entry point not found"); case ERR_COMPLEX_BLOCK_MAP: return QObject::tr("Block map structure too complex for correct analysis"); case ERR_DIR_ALREADY_EXIST: return QObject::tr("Directory already exists"); case ERR_DIR_CREATE: return QObject::tr("Directory can't be created"); - case ERR_UNKNOWN_PATCH_TYPE: return QObject::tr("Unknown patch type"); - case ERR_PATCH_OFFSET_OUT_OF_BOUNDS: return QObject::tr("Patch offset out of bounds"); - case ERR_INVALID_SYMBOL: return QObject::tr("Invalid symbol"); - case ERR_NOTHING_TO_PATCH: return QObject::tr("Nothing to patch"); + //case ERR_UNKNOWN_PATCH_TYPE: return QObject::tr("Unknown patch type"); + //case ERR_PATCH_OFFSET_OUT_OF_BOUNDS: return QObject::tr("Patch offset out of bounds"); + //case ERR_INVALID_SYMBOL: return QObject::tr("Invalid symbol"); + //case ERR_NOTHING_TO_PATCH: return QObject::tr("Nothing to patch"); case ERR_DEPEX_PARSE_FAILED: return QObject::tr("Dependency expression parsing failed"); default: return QObject::tr("Unknown error %1").arg(errorCode); } @@ -198,7 +224,6 @@ STATUS decompress(const QByteArray & compressedData, UINT8 & algorithm, QByteArr // Decompress section data if (ERR_SUCCESS != LzmaDecompress(data, dataSize, decompressed)) { - //TODO: implement it again, if needed // Intel modified LZMA workaround // Decompress section data once again data += sizeof(UINT32); diff --git a/utility.h b/common/utility.h similarity index 74% rename from utility.h rename to common/utility.h index 5b47362..07fcf6c 100644 --- a/utility.h +++ b/common/utility.h @@ -10,8 +10,20 @@ 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 __UTILITY_H__ +#define __UTILITY_H__ + #include <QString> +#include <QModelIndex> #include "basetypes.h" +#include "parsingdata.h" + +// Returns either new parsing data instance or obtains it from index +PARSING_DATA getParsingData(const QModelIndex & index); + +// Converts parsing data to byte array +QByteArray convertParsingData(const PARSING_DATA & pdata); // Converts error code to QString extern QString errorCodeToQString(UINT8 errorCode); @@ -24,3 +36,5 @@ extern STATUS decompress(const QByteArray & compressed, UINT8 & algorithm, QByte // CRC32 extern UINT32 crc32(UINT32 initial, const UINT8* buffer, UINT32 length); + +#endif \ No newline at end of file diff --git a/guidlineedit.cpp b/guidlineedit.cpp deleted file mode 100644 index bf706f5..0000000 --- a/guidlineedit.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* 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/guidlineedit.h b/guidlineedit.h deleted file mode 100644 index af92125..0000000 --- a/guidlineedit.h +++ /dev/null @@ -1,36 +0,0 @@ -/* 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 <QLineEdit> -#include <QKeyEvent> -#include <QKeySequence> -#include <QString> - -#include "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/messagelistitem.cpp b/messagelistitem.cpp deleted file mode 100644 index 1333413..0000000 --- a/messagelistitem.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* 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/messagelistitem.h b/messagelistitem.h deleted file mode 100644 index 20cbef8..0000000 --- a/messagelistitem.h +++ /dev/null @@ -1,37 +0,0 @@ -/* 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 <QModelIndex> -#include <QListWidgetItem> - -#include "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/searchdialog.cpp b/searchdialog.cpp deleted file mode 100644 index c9b716d..0000000 --- a/searchdialog.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* 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/searchdialog.h b/searchdialog.h deleted file mode 100644 index 83a08f7..0000000 --- a/searchdialog.h +++ /dev/null @@ -1,38 +0,0 @@ -/* 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 <QDialog> -#include <QRegExpValidator> -#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/searchdialog.ui b/searchdialog.ui deleted file mode 100644 index 6442853..0000000 --- a/searchdialog.ui +++ /dev/null @@ -1,249 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>SearchDialog</class> - <widget class="QDialog" name="SearchDialog"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>400</width> - <height>237</height> - </rect> - </property> - <property name="windowTitle"> - <string>Search</string> - </property> - <property name="modal"> - <bool>false</bool> - </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="QTabWidget" name="tabWidget"> - <property name="currentIndex"> - <number>0</number> - </property> - <widget class="QWidget" name="hexTab"> - <attribute name="title"> - <string>Hex pattern</string> - </attribute> - <layout class="QGridLayout" name="gridLayout_2"> - <item row="0" column="0"> - <widget class="QLabel" name="hexLabel"> - <property name="text"> - <string>Hex pattern:</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QLineEdit" name="hexEdit"> - <property name="inputMask"> - <string/> - </property> - </widget> - </item> - <item row="1" column="0" colspan="2"> - <widget class="QGroupBox" name="hexGroupBox"> - <property name="title"> - <string>Search scope</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_3"> - <item> - <widget class="QRadioButton" name="hexScopeFullRadioButton"> - <property name="text"> - <string>Header and body</string> - </property> - <property name="checked"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QRadioButton" name="hexScopeHeaderRadioButton"> - <property name="text"> - <string>Header only</string> - </property> - </widget> - </item> - <item> - <widget class="QRadioButton" name="hexScopeBodyRadioButton"> - <property name="text"> - <string>Body only</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - </layout> - </widget> - <widget class="QWidget" name="guidTab"> - <attribute name="title"> - <string>GUID</string> - </attribute> - <layout class="QGridLayout" name="gridLayout"> - <item row="0" column="0"> - <widget class="QLabel" name="guidLabel"> - <property name="text"> - <string>GUID:</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="GuidLineEdit" name="guidEdit"> - <property name="inputMask"> - <string>xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</string> - </property> - <property name="text"> - <string>........-....-....-....-............</string> - </property> - </widget> - </item> - <item row="1" column="0" colspan="2"> - <widget class="QGroupBox" name="guidGroupBox"> - <property name="title"> - <string>Search scope</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_5"> - <item> - <widget class="QRadioButton" name="guidScopeFullRadioButton"> - <property name="text"> - <string>Header and body</string> - </property> - <property name="checked"> - <bool>false</bool> - </property> - </widget> - </item> - <item> - <widget class="QRadioButton" name="guidScopeHeaderRadioButton"> - <property name="text"> - <string>Header only</string> - </property> - <property name="checked"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QRadioButton" name="guidScopeBodyRadioButton"> - <property name="text"> - <string>Body only</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - </layout> - </widget> - <widget class="QWidget" name="textTab"> - <attribute name="title"> - <string>Text</string> - </attribute> - <layout class="QGridLayout" name="gridLayout_3"> - <item row="0" column="0"> - <widget class="QLabel" name="textLabel"> - <property name="text"> - <string>Text:</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QLineEdit" name="textEdit"/> - </item> - <item row="1" column="0" colspan="2"> - <widget class="QGroupBox" name="textGroupBox"> - <property name="title"> - <string>Text search options</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_4"> - <item> - <widget class="QCheckBox" name="textUnicodeCheckBox"> - <property name="text"> - <string>Unicode</string> - </property> - <property name="checked"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="textCaseSensitiveCheckBox"> - <property name="text"> - <string>Case sensitive</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - </layout> - </widget> - </widget> - </item> - <item> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="standardButtons"> - <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> - </property> - </widget> - </item> - </layout> - </widget> - <customwidgets> - <customwidget> - <class>GuidLineEdit</class> - <extends>QLineEdit</extends> - <header>guidlineedit.h</header> - </customwidget> - </customwidgets> - <tabstops> - <tabstop>tabWidget</tabstop> - <tabstop>hexEdit</tabstop> - <tabstop>hexScopeFullRadioButton</tabstop> - <tabstop>hexScopeHeaderRadioButton</tabstop> - <tabstop>hexScopeBodyRadioButton</tabstop> - <tabstop>buttonBox</tabstop> - <tabstop>textEdit</tabstop> - <tabstop>textUnicodeCheckBox</tabstop> - <tabstop>textCaseSensitiveCheckBox</tabstop> - </tabstops> - <resources/> - <connections> - <connection> - <sender>buttonBox</sender> - <signal>accepted()</signal> - <receiver>SearchDialog</receiver> - <slot>accept()</slot> - <hints> - <hint type="sourcelabel"> - <x>182</x> - <y>185</y> - </hint> - <hint type="destinationlabel"> - <x>157</x> - <y>194</y> - </hint> - </hints> - </connection> - <connection> - <sender>buttonBox</sender> - <signal>rejected()</signal> - <receiver>SearchDialog</receiver> - <slot>reject()</slot> - <hints> - <hint type="sourcelabel"> - <x>182</x> - <y>185</y> - </hint> - <hint type="destinationlabel"> - <x>286</x> - <y>194</y> - </hint> - </hints> - </connection> - </connections> -</ui> diff --git a/uefitool.cpp b/uefitool.cpp deleted file mode 100644 index 20621c3..0000000 --- a/uefitool.cpp +++ /dev/null @@ -1,744 +0,0 @@ -/* 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_alpha_parser_only")) -{ - clipboard = QApplication::clipboard(); - - // Create UI - ui->setupUi(this); - searchDialog = new SearchDialog(this); - ffsParser = 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->messageListWidget->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 ui; - delete ffsParser; - delete searchDialog; -} - -void UEFITool::init() -{ - // Clear components - ui->messageListWidget->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); - - // Make new ffsEngine - if (ffsParser) - delete ffsParser; - ffsParser = new FfsParser(this); - ui->structureTreeView->setModel(ffsParser->treeModel()); - - // Connect - connect(ui->structureTreeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), - this, SLOT(populateUi(const QModelIndex &))); - connect(ui->messageListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(scrollTreeView(QListWidgetItem*))); - connect(ui->messageListWidget, SIGNAL(itemEntered(QListWidgetItem*)), this, SLOT(enableMessagesCopyActions(QListWidgetItem*))); -} - -void UEFITool::populateUi(const QModelIndex ¤t) -{ - if (!current.isValid()) - return; - - TreeModel* model = ffsParser->treeModel(); - 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 = ffsParser->treeModel()->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; - ffsParser->findHexPattern(rootIndex, pattern, mode); - showMessages(); - } - 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; - ffsParser->findGuidPattern(rootIndex, pattern, mode); - showMessages(); - } - else if (index == 2) { // Text string - searchDialog->ui->textEdit->setFocus(); - QString pattern = searchDialog->ui->textEdit->text(); - if (pattern.isEmpty()) - return; - ffsParser->findTextPattern(rootIndex, pattern, searchDialog->ui->textUnicodeCheckBox->isChecked(), - (Qt::CaseSensitivity) searchDialog->ui->textCaseSensitiveCheckBox->isChecked()); - showMessages(); - } -} - -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; - - TreeModel* model = ffsParser->treeModel(); - 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; - - TreeModel* model = ffsParser->treeModel(); - 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; - - TreeModel* model = ffsParser->treeModel(); - - QByteArray extracted; - QString name; - UINT8 result = ffsParser->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 <b>CodeRush</b>.<br>" - "Program icon made by <a href=https://www.behance.net/alzhidkov>Alexander Zhidkov</a>.<br><br>" - "The program is dedicated to <b>RevoGirl</b>. Rest in peace, young genius.<br><br>" - "The program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License.<br>" - "The full text of the license may be found at <a href=http://opensource.org/licenses/bsd-license.php>OpenSource.org</a>.<br><br>" - "<b>THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN \"AS IS\" BASIS, " - "WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, " - "EITHER EXPRESS OR IMPLIED.</b>")); -} - -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); - showMessages(); - 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 - ui->actionSearch->setEnabled(true); - - // Set current directory - currentDir = fileInfo.absolutePath(); -} - -void UEFITool::copyMessage() -{ - clipboard->clear(); - clipboard->setText(ui->messageListWidget->currentItem()->text()); -} - -void UEFITool::copyAllMessages() -{ - QString text; - clipboard->clear(); - for(INT32 i = 0; i < ui->messageListWidget->count(); i++) - text.append(ui->messageListWidget->item(i)->text()).append("\n"); - - clipboard->clear(); - clipboard->setText(text); -} - -void UEFITool::enableMessagesCopyActions(QListWidgetItem* item) -{ - ui->actionMessagesCopy->setEnabled(item != NULL); - ui->actionMessagesCopyAll->setEnabled(item != NULL); -} - -void UEFITool::clearMessages() -{ - ffsParser->clearMessages(); - messageItems.clear(); - ui->messageListWidget->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::showMessages() -{ - ui->messageListWidget->clear(); - if (!ffsParser) - return; - - messageItems = ffsParser->messages(); - for (int i = 0; i < messageItems.count(); i++) { - ui->messageListWidget->addItem(new MessageListItem(messageItems.at(i))); - } - - ui->messageListWidget->scrollToBottom(); -} - -void UEFITool::scrollTreeView(QListWidgetItem* item) -{ - MessageListItem* messageItem = static_cast<MessageListItem*>(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->messageListWidget->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; - - TreeModel* model = ffsParser->treeModel(); - 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<int> 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->messageGroupBox->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.h b/uefitool.h deleted file mode 100644 index 35ea3cb..0000000 --- a/uefitool.h +++ /dev/null @@ -1,110 +0,0 @@ -/* 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 <QMainWindow> -#include <QByteArray> -#include <QClipboard> -#include <QDragEnterEvent> -#include <QDropEvent> -#include <QFile> -#include <QFileDialog> -#include <QFileInfo> -#include <QListWidget> -#include <QMenu> -#include <QMessageBox> -#include <QMimeData> -#include <QPlainTextEdit> -#include <QSettings> -#include <QSplitter> -#include <QString> -#include <QTreeView> -#include <QUrl> - -#include "basetypes.h" -#include "utility.h" -#include "ffs.h" -#include "ffsparser.h" -#include "searchdialog.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; - FfsParser* ffsParser; - SearchDialog* searchDialog; - QClipboard* clipboard; - QString currentDir; - QQueue<MessageListItem> messageItems; - const QString version; - - void showMessages(); - - void dragEnterEvent(QDragEnterEvent* event); - void dropEvent(QDropEvent* event); - void contextMenuEvent(QContextMenuEvent* event); - void readSettings(); -}; - -#endif diff --git a/uefitool.icns b/uefitool.icns deleted file mode 100644 index a332b7039fc6a091118d25c06fa7295bc9f2bc54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 99242 zcmeFa1z1$s`#65?9ZJRS?pR~j)wOobHR$f{?r!D|Geg4=LkvTAhpvH%ih;U<Efxv} z3W`Vw68HCBkacy@jqkeu=lMUM!vk~9+b8Zh?|aU<gN2c;6T+DJ%5ppBA%x5=jGPw2 zr#O6WL<keHa9X<tA$+CNMjI=tEU{A-@BlZm2E2fXg*Am2s<j@#L)n=sFJ9n0P~Hx7 ztAS4C9DHs<Xy5fA`Je+5MhHI?em(Z}+^)G@W8qV{f`WcXpT`f`H%MD^{#?y(T<r)| zpFdxX5M&^=ov(r-0IJRp&`|lIL{U&kk50Ly%mE&RYM@BUd33B8DAxcwNv^ZALr%^E z0DwGyzLV_C%#`bp8<Nu5(c$6I=`m1l0}8p06>=VAr`-9fnl?&aW@o2|M~6q|p#JLf zRhe?0o+`i%HV*2qt{$+lnzE<8y1J$sQcX=Ya1SO5FqEf0+W<XS9_pW85Jnpjgam~& zYf3lH2|z;1WSQf={Wu#5i^W!4tjjL3VWANMS9?yaoLyR%j4{y&!CMWlofWQYrQj?i zoO<tYv1@l_7SJ!cansfNf!;EIB(NYxS>69MVj`NOo2Ros8sTDtoZQ`=k&2xk!fYK0 z2O&pC2YY)bI|9viv2}ECvVy#Wql2v_LZp?Ql}*ayD<NK%wpMfkZ85Xf<B>H5FRqzd znImLqWNa9AqPDK7syfTi*vQb>Sl^(bx7O4C@{3ct2FAuZdfIwVUUp<hJdQo34Fy7D zhVseAD;KhQZdWR+Yic5-tYX#hI5X!t*G@&5K-0F#E6JE02ns8dl~a=6jgYLYg1oG_ zgp4F1kIBdp2+0IW`JoXi2#HBaNg(v2Kcb0=Nr*G4`sEkzD}wrEN22_)vI3N!<A+A& z=I7+(=H=yP=N4o`{Sq_ta|<%Ea<cOCax*iberegMX{TP*@6S)oO3NpZ7C$}qDEYAJ zO+7z75uvzvUfeG?p1f$iTUQwuA0Nl#ak<TXPqUIAwBCq{;qfB45fLxjx=!tDK6rxz zg`m)oz-ujacPe@x-wX^62|<V*6x-B%{A9~MUJx7VM<@L0kr(odP7?&10JMt_(~sfl z<?BuOVLo02BD|0{!;9wa>Fo*CbU?`6+uIGupqegjo~}$Z^b8M<oX!2D?qCl7&Jac( zBpHKf^9Dfc9-Rz&Mv~3JM_<YUVDxSBxAb~0JhqX)70F+OfWY-bIS3*B#8)56EeZM` z3d85NF<*Q@Jony;koSi&R1^smekfOt0(*Y_P<}ES2<BhNW4t8EHfH>-^-x4;1VRBM z37s^0ph*4M(ogCQ7KY}q-%!J4XcY(vzeZ?oFGAb;2Y=}#^g4+!@&tkP(+P}pgI^fg zBpu+?FW~8&K<f;Az>7e}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`I<Ov)kwB&n(=VZq#f)qHS zre`rR42{)p>pSfbe!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 diff --git a/uefitool.ico b/uefitool.ico deleted file mode 100644 index eb7b2ca4dac5671c55511e2e21cc6891ca2b02d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32038 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 Nc^<%j%KyJc;NR|0GOGXp diff --git a/uefitool.pro b/uefitool.pro deleted file mode 100644 index 081e069..0000000 --- a/uefitool.pro +++ /dev/null @@ -1,55 +0,0 @@ -QT += core gui -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets - -TARGET = UEFITool -TEMPLATE = app - -SOURCES += uefitool_main.cpp \ - uefitool.cpp \ - searchdialog.cpp \ - types.cpp \ - descriptor.cpp \ - ffs.cpp \ - peimage.cpp \ - utility.cpp \ - ffsparser.cpp \ - treeitem.cpp \ - treemodel.cpp \ - messagelistitem.cpp \ - guidlineedit.cpp \ - LZMA/LzmaCompress.c \ - LZMA/LzmaDecompress.c \ - LZMA/SDK/C/LzFind.c \ - LZMA/SDK/C/LzmaDec.c \ - LZMA/SDK/C/LzmaEnc.c \ - Tiano/EfiTianoDecompress.c \ - Tiano/EfiTianoCompress.c \ - Tiano/EfiTianoCompressLegacy.c - -HEADERS += uefitool.h \ - searchdialog.h \ - basetypes.h \ - descriptor.h \ - gbe.h \ - me.h \ - ffs.h \ - peimage.h \ - types.h \ - utility.h \ - ffsparser.h \ - treeitem.h \ - treemodel.h \ - messagelistitem.h \ - guidlineedit.h \ - LZMA/LzmaCompress.h \ - LZMA/LzmaDecompress.h \ - Tiano/EfiTianoDecompress.h \ - Tiano/EfiTianoCompress.h - -FORMS += uefitool.ui \ - searchdialog.ui - -RC_FILE = uefitool.rc - -ICON = uefitool.icns - diff --git a/uefitool.rc b/uefitool.rc deleted file mode 100644 index 345333b..0000000 --- a/uefitool.rc +++ /dev/null @@ -1 +0,0 @@ -IDI_ICON1 ICON DISCARDABLE "uefitool.ico" \ No newline at end of file diff --git a/uefitool.ui b/uefitool.ui deleted file mode 100644 index 82868da..0000000 --- a/uefitool.ui +++ /dev/null @@ -1,529 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>UEFITool</class> - <widget class="QMainWindow" name="UEFITool"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>800</width> - <height>600</height> - </rect> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="acceptDrops"> - <bool>true</bool> - </property> - <property name="windowTitle"> - <string>UEFITool</string> - </property> - <widget class="QWidget" name="centralWidget"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <layout class="QHBoxLayout" name="horizontalLayout_7"> - <property name="spacing"> - <number>0</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QSplitter" name="messagesSplitter"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <widget class="QSplitter" name="infoSplitter"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <widget class="QGroupBox" name="structureGroupBox"> - <property name="title"> - <string>Structure</string> - </property> - <layout class="QHBoxLayout" name="horizontalLayout_4"> - <property name="spacing"> - <number>0</number> - </property> - <property name="leftMargin"> - <number>5</number> - </property> - <property name="topMargin"> - <number>5</number> - </property> - <property name="rightMargin"> - <number>5</number> - </property> - <property name="bottomMargin"> - <number>5</number> - </property> - <item> - <widget class="QTreeView" name="structureTreeView"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="indentation"> - <number>10</number> - </property> - <property name="headerHidden"> - <bool>false</bool> - </property> - <attribute name="headerCascadingSectionResizes"> - <bool>true</bool> - </attribute> - <attribute name="headerDefaultSectionSize"> - <number>200</number> - </attribute> - <attribute name="headerStretchLastSection"> - <bool>true</bool> - </attribute> - </widget> - </item> - </layout> - </widget> - <widget class="QGroupBox" name="infoGroupBox"> - <property name="title"> - <string>Information</string> - </property> - <layout class="QHBoxLayout" name="horizontalLayout_5"> - <property name="spacing"> - <number>0</number> - </property> - <property name="leftMargin"> - <number>5</number> - </property> - <property name="topMargin"> - <number>5</number> - </property> - <property name="rightMargin"> - <number>5</number> - </property> - <property name="bottomMargin"> - <number>5</number> - </property> - <item> - <widget class="QPlainTextEdit" name="infoEdit"> - <property name="acceptDrops"> - <bool>false</bool> - </property> - <property name="undoRedoEnabled"> - <bool>false</bool> - </property> - <property name="readOnly"> - <bool>true</bool> - </property> - <property name="centerOnScroll"> - <bool>false</bool> - </property> - </widget> - </item> - </layout> - </widget> - </widget> - <widget class="QGroupBox" name="messageGroupBox"> - <property name="title"> - <string>Messages</string> - </property> - <layout class="QHBoxLayout" name="horizontalLayout_6"> - <property name="spacing"> - <number>0</number> - </property> - <property name="leftMargin"> - <number>5</number> - </property> - <property name="topMargin"> - <number>5</number> - </property> - <property name="rightMargin"> - <number>5</number> - </property> - <property name="bottomMargin"> - <number>5</number> - </property> - <item> - <widget class="QListWidget" name="messageListWidget"> - <property name="mouseTracking"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> - </widget> - </widget> - </item> - </layout> - </widget> - <widget class="QStatusBar" name="statusBar"/> - <widget class="QMenuBar" name="menuBar"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>800</width> - <height>21</height> - </rect> - </property> - <widget class="QMenu" name="menuFile"> - <property name="title"> - <string>&File</string> - </property> - <addaction name="actionOpenImageFile"/> - <addaction name="actionSaveImageFile"/> - <addaction name="separator"/> - <addaction name="actionSearch"/> - <addaction name="separator"/> - <addaction name="actionQuit"/> - </widget> - <widget class="QMenu" name="menuHelp"> - <property name="title"> - <string>H&elp</string> - </property> - <addaction name="actionAbout"/> - <addaction name="actionAboutQt"/> - </widget> - <widget class="QMenu" name="menuAction"> - <property name="title"> - <string>&Action</string> - </property> - <widget class="QMenu" name="menuCapsuleActions"> - <property name="title"> - <string>&Capsule</string> - </property> - <addaction name="actionExtract"/> - <addaction name="actionExtractBody"/> - </widget> - <widget class="QMenu" name="menuImageActions"> - <property name="title"> - <string>&Image</string> - </property> - <addaction name="actionExtract"/> - </widget> - <widget class="QMenu" name="menuRegionActions"> - <property name="title"> - <string>&Region</string> - </property> - <addaction name="actionExtract"/> - <addaction name="separator"/> - <addaction name="actionReplace"/> - </widget> - <widget class="QMenu" name="menuPaddingActions"> - <property name="title"> - <string>&Padding</string> - </property> - <addaction name="actionExtract"/> - </widget> - <widget class="QMenu" name="menuVolumeActions"> - <property name="title"> - <string>&Volume</string> - </property> - <addaction name="actionExtract"/> - <addaction name="actionExtractBody"/> - <addaction name="separator"/> - <addaction name="actionRebuild"/> - <addaction name="separator"/> - <addaction name="actionInsertInto"/> - <addaction name="separator"/> - <addaction name="actionReplace"/> - <addaction name="actionReplaceBody"/> - </widget> - <widget class="QMenu" name="menuFileActions"> - <property name="title"> - <string>&File</string> - </property> - <addaction name="actionExtract"/> - <addaction name="actionExtractBody"/> - <addaction name="separator"/> - <addaction name="actionRebuild"/> - <addaction name="separator"/> - <addaction name="actionInsertInto"/> - <addaction name="actionInsertBefore"/> - <addaction name="actionInsertAfter"/> - <addaction name="separator"/> - <addaction name="actionReplace"/> - <addaction name="actionReplaceBody"/> - <addaction name="separator"/> - <addaction name="actionRemove"/> - </widget> - <widget class="QMenu" name="menuSectionActions"> - <property name="title"> - <string>&Section</string> - </property> - <addaction name="actionExtract"/> - <addaction name="actionExtractBody"/> - <addaction name="separator"/> - <addaction name="actionRebuild"/> - <addaction name="separator"/> - <addaction name="actionInsertInto"/> - <addaction name="actionInsertBefore"/> - <addaction name="actionInsertAfter"/> - <addaction name="separator"/> - <addaction name="actionReplace"/> - <addaction name="actionReplaceBody"/> - <addaction name="separator"/> - <addaction name="actionRemove"/> - <addaction name="separator"/> - </widget> - <widget class="QMenu" name="menuMessages"> - <property name="title"> - <string>&Messages</string> - </property> - <addaction name="actionMessagesCopy"/> - <addaction name="actionMessagesCopyAll"/> - <addaction name="separator"/> - <addaction name="actionMessagesClear"/> - </widget> - <addaction name="menuCapsuleActions"/> - <addaction name="menuImageActions"/> - <addaction name="menuRegionActions"/> - <addaction name="menuPaddingActions"/> - <addaction name="menuVolumeActions"/> - <addaction name="menuFileActions"/> - <addaction name="menuSectionActions"/> - <addaction name="separator"/> - <addaction name="menuMessages"/> - </widget> - <addaction name="menuFile"/> - <addaction name="menuAction"/> - <addaction name="menuHelp"/> - </widget> - <action name="actionInsertAfter"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>Insert &after...</string> - </property> - <property name="toolTip"> - <string>Insert an object from file after selected object</string> - </property> - <property name="shortcut"> - <string>Ctrl+Shift+I</string> - </property> - </action> - <action name="actionInsertBefore"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>Insert b&efore...</string> - </property> - <property name="toolTip"> - <string>Insert object from file before selected object</string> - </property> - <property name="shortcut"> - <string>Ctrl+Alt+I</string> - </property> - </action> - <action name="actionReplace"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>Rep&lace as is...</string> - </property> - <property name="toolTip"> - <string>Replace selected object as is with an object from file</string> - </property> - <property name="shortcut"> - <string>Ctrl+R</string> - </property> - </action> - <action name="actionExtract"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>E&xtract as is...</string> - </property> - <property name="toolTip"> - <string>Extract selected object as is to file</string> - </property> - <property name="shortcut"> - <string>Ctrl+E</string> - </property> - </action> - <action name="actionExtractBody"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>Extract &body...</string> - </property> - <property name="toolTip"> - <string>Extract body of selected object to file</string> - </property> - <property name="shortcut"> - <string>Ctrl+Shift+E</string> - </property> - </action> - <action name="actionRemove"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>Re&move</string> - </property> - <property name="toolTip"> - <string>Remove selected object</string> - </property> - <property name="shortcut"> - <string>Ctrl+Del</string> - </property> - </action> - <action name="actionOpenImageFile"> - <property name="text"> - <string>&Open image file...</string> - </property> - <property name="toolTip"> - <string>Open image file</string> - </property> - <property name="shortcut"> - <string>Ctrl+O</string> - </property> - </action> - <action name="actionInsertInto"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>Insert &into...</string> - </property> - <property name="toolTip"> - <string>Insert object from file into selected object</string> - </property> - <property name="shortcut"> - <string>Ctrl+I</string> - </property> - </action> - <action name="actionSaveImageFile"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>&Save image file...</string> - </property> - <property name="toolTip"> - <string>Save modified image file</string> - </property> - <property name="shortcut"> - <string>Ctrl+S</string> - </property> - </action> - <action name="actionRebuild"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>&Rebuild</string> - </property> - <property name="toolTip"> - <string>Rebuild selected object</string> - </property> - <property name="shortcut"> - <string>Ctrl+Space</string> - </property> - </action> - <action name="actionAbout"> - <property name="text"> - <string>&About UEFITool</string> - </property> - <property name="shortcut"> - <string>F1</string> - </property> - <property name="menuRole"> - <enum>QAction::AboutRole</enum> - </property> - </action> - <action name="actionAboutQt"> - <property name="text"> - <string>About &Qt</string> - </property> - <property name="menuRole"> - <enum>QAction::AboutQtRole</enum> - </property> - </action> - <action name="actionQuit"> - <property name="text"> - <string>&Quit</string> - </property> - <property name="menuRole"> - <enum>QAction::QuitRole</enum> - </property> - </action> - <action name="actionSearch"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>Sear&ch...</string> - </property> - <property name="shortcut"> - <string>Ctrl+F</string> - </property> - </action> - <action name="actionMessagesClear"> - <property name="text"> - <string>Cl&ear</string> - </property> - <property name="toolTip"> - <string>Clear messages</string> - </property> - <property name="shortcut"> - <string>Ctrl+Backspace</string> - </property> - </action> - <action name="actionReplaceBody"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>Replace b&ody...</string> - </property> - <property name="toolTip"> - <string>Replace body of selected object with a data from file</string> - </property> - <property name="shortcut"> - <string>Ctrl+Shift+R</string> - </property> - </action> - <action name="actionMessagesCopy"> - <property name="text"> - <string>&Copy</string> - </property> - <property name="shortcut"> - <string>Ctrl+Shift+C</string> - </property> - </action> - <action name="actionMessagesCopyAll"> - <property name="text"> - <string>C&opy all</string> - </property> - <property name="shortcut"> - <string>Ctrl+Alt+C</string> - </property> - </action> - </widget> - <layoutdefault spacing="6" margin="11"/> - <resources/> - <connections/> -</ui> diff --git a/uefitool_main.cpp b/uefitool_main.cpp deleted file mode 100644 index e5764c0..0000000 --- a/uefitool_main.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* 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 <QApplication> -#include <QString> -#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