From 2e788a8a1a5d54944525dfced6987b3c8751fa7e Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej 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 `_ at MDL forums as a cross-platform analog to `PhoenixTool `_'s structure mode with some additional features, but the program's engine was proven to be usefull for another projects like `UEFIPatch `_, `UBU `_ and `OZMTool `_. - -Installation ------------- - -| You can either use `pre-built binaries for Windows and OSX `_ 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 +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include + +#include + +#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 > messages = ffsParser.getMessages(); + QPair 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 #include 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 #include 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 -#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(message, index)); } -#ifndef _CONSOLE -QQueue FfsParser::messages() const +QVector > 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 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 #include #include -#include #include #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 messages() const; - // Clears message items queue + // Returns messages + QVector > 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 > 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 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 +#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 +#include #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 -#include -#include -#include - -#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 -#include - -#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 -#include -#include "ui_searchdialog.h" - -class SearchDialog : public QDialog -{ - Q_OBJECT - -public: - SearchDialog(QWidget *parent = 0); - ~SearchDialog(); - Ui::SearchDialog* ui; - -private slots: - void setEditFocus(int index); - -private: - QRegExpValidator hexValidator; - QRegExpValidator guidValidator; -}; - -#endif diff --git a/searchdialog.ui b/searchdialog.ui deleted file mode 100644 index 6442853..0000000 --- a/searchdialog.ui +++ /dev/null @@ -1,249 +0,0 @@ - - - SearchDialog - - - - 0 - 0 - 400 - 237 - - - - Search - - - false - - - - - - 0 - - - - Hex pattern - - - - - - Hex pattern: - - - - - - - - - - - - - - Search scope - - - - - - Header and body - - - true - - - - - - - Header only - - - - - - - Body only - - - - - - - - - - - GUID - - - - - - GUID: - - - - - - - xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - - - ........-....-....-....-............ - - - - - - - Search scope - - - - - - Header and body - - - false - - - - - - - Header only - - - true - - - - - - - Body only - - - - - - - - - - - Text - - - - - - Text: - - - - - - - - - - Text search options - - - - - - Unicode - - - true - - - - - - - Case sensitive - - - - - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - GuidLineEdit - QLineEdit -
guidlineedit.h
-
-
- - tabWidget - hexEdit - hexScopeFullRadioButton - hexScopeHeaderRadioButton - hexScopeBodyRadioButton - buttonBox - textEdit - textUnicodeCheckBox - textCaseSensitiveCheckBox - - - - - buttonBox - accepted() - SearchDialog - accept() - - - 182 - 185 - - - 157 - 194 - - - - - buttonBox - rejected() - SearchDialog - reject() - - - 182 - 185 - - - 286 - 194 - - - - -
diff --git a/uefitool.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 CodeRush.
" - "Program icon made by Alexander Zhidkov.

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

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

" - "THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN \"AS IS\" BASIS, " - "WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, " - "EITHER EXPRESS OR IMPLIED.")); -} - -void UEFITool::aboutQt() -{ - QMessageBox::aboutQt(this, tr("About Qt")); -} - -void UEFITool::exit() -{ - QCoreApplication::exit(0); -} - -void UEFITool::saveImageFile() -{ - /*QString path = QFileDialog::getSaveFileName(this, tr("Save BIOS image file"), currentDir, "BIOS image files (*.rom *.bin *.cap *.bio *.fd *.wph *.efi *.dec);;All files (*)"); - - if (path.isEmpty()) - return; - - QByteArray reconstructed; - UINT8 result = ffsEngine->reconstructImageFile(reconstructed); - showMessages(); - if (result) { - QMessageBox::critical(this, tr("Image reconstruction failed"), errorMessage(result), QMessageBox::Ok); - return; - } - - QFile outputFile; - outputFile.setFileName(path); - - if (!outputFile.open(QFile::WriteOnly)) { - QMessageBox::critical(this, tr("Image reconstruction failed"), tr("Can't open output file for rewriting"), QMessageBox::Ok); - return; - } - - outputFile.resize(0); - outputFile.write(reconstructed); - outputFile.close(); - if (QMessageBox::information(this, tr("Image reconstruction successful"), tr("Open reconstructed file?"), QMessageBox::Yes, QMessageBox::No) - == QMessageBox::Yes) - openImageFile(path);*/ -} - -void UEFITool::openImageFile() -{ - QString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file"), currentDir, "BIOS image files (*.rom *.bin *.cap *.bio *.fd *.wph *.efi *.dec);;All files (*)"); - openImageFile(path); -} - -void UEFITool::openImageFile(QString path) -{ - if (path.trimmed().isEmpty()) - return; - - QFileInfo fileInfo = QFileInfo(path); - - if (!fileInfo.exists()) { - ui->statusBar->showMessage(tr("Please select existing file")); - return; - } - - QFile inputFile; - inputFile.setFileName(path); - - if (!inputFile.open(QFile::ReadOnly)) { - QMessageBox::critical(this, tr("Image parsing failed"), tr("Can't open input file for reading"), QMessageBox::Ok); - return; - } - - QByteArray buffer = inputFile.readAll(); - inputFile.close(); - - init(); - this->setWindowTitle(tr("UEFITool %1 - %2").arg(version).arg(fileInfo.fileName())); - - UINT8 result = ffsParser->parseImageFile(buffer); - 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(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 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 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=VAr`-9fnl?&aW@o2|M~6q|p#JLf zRhe?0o+`i%HV*2qt{$+lnzE<8y1J$sQcX=Ya1SO5FqEf0+W69MVj`NOo2Ros8sTDtoZQ`=k&2xk!fYK0 z2O&pC2YY)bI|9viv2}ECvVy#Wql2v_LZp?Ql}*ayDH5FRqzd znImLqWNa9AqPDK7syfTi*vQb>Sl^(bx7O4C@{3ct2FAuZdfIwVUUpFo*CbU?`6+uIGupqegjo~}$Z^b8M-^-x4;1VRBM z37s^0ph*4M(ogCQ7KY}q-%!J4XcY(vzeZ?oFGAb;2Y=}#^g4+!@&tkP(+P}pgI^fg zBpu+?FW~8&K7e}KTZFE{-HViBNMC7QPbK-ppObw1GC=X)cO%(u+Tn$9-Qeu zM%PqP)7iiTIym#eME5%i3IP+}Ap21E1HpT~A@tDa5W#2A@2A+`)DEy^!c6~L8WQlE z;-uvy`C)25KUC5FL5@s0YUIgf4o>#xKh(2r5Z^I?rKmaww7j3C2WNR80leIKj{&Ly zhC%i1@9UkJNuen03J(uhZXi|Hv=6LMR34sCSY=?f>GVkIqy!D7-%JM>Ne-|jQ@PIf z_}@MTWm9%_ka8WJ$-f~6Gns(*kRxH;gT?E7@=z5&P*H}#Zze2&E6&S(kU!LGRILXr zpp08V38E6UQne5Ck0(hcdv=1|gLqF03)LG)1C>|-E$`u>0{D*1&H?tJ?txVtQYIx{ z?!Eqx+jnpP40KQOfPEDG@A_x3k*dxotAJWLxy->~e7@?v`kM1qg9#Q|K*68EG`K`l z3+@oXd&JPkPzSfe3SI?^IJI(92{M$FAvCqDLrcKAJ(Sw0{0B7EMpQ!#Ed79?@@>CI zQvgWt&`@a@%pe{O;)jOqd*JUr1{M4cfY6W+N22G5CNOvI=y)Ww`*d6TvtW#iMlQ(e zg>Da-BOf;{5{XWzCCQ$=B{Ng{$?LNskmz`r>f@)`G?L`IpSfbe!KO>DOIs+B$*hBFrljiDcr6mIDHD*)JBll6)XgepWZ_DH;^4` z5t?+rkNf15I1a!WEo85LDR}@#p5$4y!z57vfFmD(4RvJq3Ur#>KQWTLhJ_+p3E5Rm zkRoR2CfUY2N*)5u%4E+j#b3!tbc-Z&50I(=5;{uWD=_dNuv_H)@6R3+2uIkw8^D&g zf_9TUfH?Ca5H@EoK?+F%({Usag+ws%0DYXcH)lH!VVDrrM!`tX&CAu*!zB(0xp=s^ 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 Q0Ot2d`LR-!f@RSE0fEyExc~qF 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 @@ - - - UEFITool - - - - 0 - 0 - 800 - 600 - - - - - 0 - 0 - - - - true - - - UEFITool - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Vertical - - - - Qt::Horizontal - - - - Structure - - - - 0 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - 0 - 0 - - - - 10 - - - false - - - true - - - 200 - - - true - - - - - - - - Information - - - - 0 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - false - - - false - - - true - - - false - - - - - - - - - Messages - - - - 0 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - true - - - - - - - - - - - - - - 0 - 0 - 800 - 21 - - - - - &File - - - - - - - - - - - H&elp - - - - - - - &Action - - - - &Capsule - - - - - - - &Image - - - - - - &Region - - - - - - - - &Padding - - - - - - &Volume - - - - - - - - - - - - - - &File - - - - - - - - - - - - - - - - - - &Section - - - - - - - - - - - - - - - - - - - &Messages - - - - - - - - - - - - - - - - - - - - - - - false - - - Insert &after... - - - Insert an object from file after selected object - - - Ctrl+Shift+I - - - - - false - - - Insert b&efore... - - - Insert object from file before selected object - - - Ctrl+Alt+I - - - - - false - - - Rep&lace as is... - - - Replace selected object as is with an object from file - - - Ctrl+R - - - - - false - - - E&xtract as is... - - - Extract selected object as is to file - - - Ctrl+E - - - - - false - - - Extract &body... - - - Extract body of selected object to file - - - Ctrl+Shift+E - - - - - false - - - Re&move - - - Remove selected object - - - Ctrl+Del - - - - - &Open image file... - - - Open image file - - - Ctrl+O - - - - - false - - - Insert &into... - - - Insert object from file into selected object - - - Ctrl+I - - - - - false - - - &Save image file... - - - Save modified image file - - - Ctrl+S - - - - - false - - - &Rebuild - - - Rebuild selected object - - - Ctrl+Space - - - - - &About UEFITool - - - F1 - - - QAction::AboutRole - - - - - About &Qt - - - QAction::AboutQtRole - - - - - &Quit - - - QAction::QuitRole - - - - - false - - - Sear&ch... - - - Ctrl+F - - - - - Cl&ear - - - Clear messages - - - Ctrl+Backspace - - - - - false - - - Replace b&ody... - - - Replace body of selected object with a data from file - - - Ctrl+Shift+R - - - - - &Copy - - - Ctrl+Shift+C - - - - - C&opy all - - - Ctrl+Alt+C - - - - - - - diff --git a/uefitool_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 -#include -#include "uefitool.h" - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - a.setOrganizationName("CodeRush"); - a.setOrganizationDomain("coderush.me"); - a.setApplicationName("UEFITool"); - - UEFITool w; - if (a.arguments().length() > 1) - w.openImageFile(a.arguments().at(1)); - w.show(); - - return a.exec(); -} \ No newline at end of file