From 862fc6b242c6ed8b9e97b9eedb8ce88e6647a10a Mon Sep 17 00:00:00 2001 From: Nikolaj Schlej Date: Thu, 12 Dec 2013 12:28:39 +0100 Subject: [PATCH] Version 0.12.0 - bug with wrong file tail creation solved - "Replace" and "Replace body" actions added - "Change compression" menu and actions removed, will be returned later - minor refactoring and bugfixes done --- basetypes.h | 38 ++-- ffs.h | 10 +- ffsengine.cpp | 472 ++++++++++++++++++++++++++++++++++-------------- ffsengine.h | 36 ++-- searchdialog.ui | 17 +- treeitem.cpp | 32 ++-- treeitem.h | 19 +- treemodel.cpp | 38 ++-- treemodel.h | 3 +- uefitool.cpp | 315 ++++++++++++++++++-------------- uefitool.h | 20 +- uefitool.ui | 91 +++------- 12 files changed, 652 insertions(+), 439 deletions(-) diff --git a/basetypes.h b/basetypes.h index 5a47865..eb8dd1b 100644 --- a/basetypes.h +++ b/basetypes.h @@ -45,16 +45,7 @@ typedef uint16_t CHAR16; #define NULL ((VOID *) 0) #endif -#if _MSC_EXTENSIONS - // - // Microsoft* compiler requires _EFIAPI usage, __cdecl is Microsoft* specific C. - // - #define EFIAPI __cdecl -#endif - -#if __GNUC__ - #define EFIAPI __attribute__((cdecl)) -#endif +#define EFIAPI #define ERR_SUCCESS 0 #define ERR_INVALID_PARAMETER 1 @@ -95,16 +86,19 @@ typedef uint16_t CHAR16; #define COMPRESSION_ALGORITHM_LZMA 4 #define COMPRESSION_ALGORITHM_IMLZMA 5 -// Item extract modes -#define EXTRACT_MODE_AS_IS 0 -#define EXTRACT_MODE_BODY_ONLY 1 -#define EXTRACT_MODE_UNCOMPRESSED 2 +// Item create modes +#define CREATE_MODE_APPEND 0 +#define CREATE_MODE_PREPEND 1 +#define CREATE_MODE_BEFORE 2 +#define CREATE_MODE_AFTER 3 -// Item insert modes -#define INSERT_MODE_APPEND 0 -#define INSERT_MODE_PREPEND 1 -#define INSERT_MODE_BEFORE 2 -#define INSERT_MODE_AFTER 3 +// Item extract modes +#define EXTRACT_MODE_AS_IS 0 +#define EXTRACT_MODE_BODY 1 + +// Item replace modes +#define REPLACE_MODE_AS_IS 0 +#define REPLACE_MODE_BODY 1 // Erase polarity types #define ERASE_POLARITY_FALSE 0 @@ -112,9 +106,9 @@ typedef uint16_t CHAR16; #define ERASE_POLARITY_UNKNOWN 0xFF // Search modes -#define SEARCH_MODE_HEX 0 -#define SEARCH_MODE_ASCII 1 -#define SEARCH_MODE_UNICODE 2 +#define SEARCH_MODE_HEADER 1 +#define SEARCH_MODE_BODY 2 +#define SEARCH_MODE_ALL 3 // EFI GUID typedef struct{ diff --git a/ffs.h b/ffs.h index 8ca26ea..2ddd1e3 100644 --- a/ffs.h +++ b/ffs.h @@ -245,7 +245,7 @@ typedef struct { // UINT8 Attributes; // UINT8 Size[3]; // UINT8 State; -// UINT12 ExtendedSize; +// UINT32 ExtendedSize; //} EFI_FFS_FILE_HEADER2; // Standard data checksum, used if FFS_ATTRIB_CHECKSUM is clear @@ -277,14 +277,12 @@ typedef struct { // File attributes #define FFS_ATTRIB_RESERVED 0x80 // ErasePolarity value can be obtained from that bit +#define FFS_ATTRIB_TAIL_PRESENT 0x01 #define FFS_ATTRIB_RECOVERY 0x02 +#define FFS_ATTRIB_FIXED 0x04 #define FFS_ATTRIB_DATA_ALIGNMENT 0x38 #define FFS_ATTRIB_CHECKSUM 0x40 -// Revision 1 -#define FFS_ATTRIB_TAIL_PRESENT 0x01 -// Revision 2 -#define FFS_ATTRIB_LARGE_FILE 0x01 -#define FFS_ATTRIB_FIXED 0x04 +//#define FFS_ATTRIB_LARGE_FILE 0x01 //This attribute is removed in new PI 1.3 specification, nice // FFS alignment table extern const UINT8 ffsAlignmentTable[]; diff --git a/ffsengine.cpp b/ffsengine.cpp index 944670e..c8208fc 100644 --- a/ffsengine.cpp +++ b/ffsengine.cpp @@ -28,10 +28,6 @@ FfsEngine::FfsEngine(QObject *parent) : QObject(parent) { rootItem = new TreeItem(TreeItem::Root); - rootItem->setName("Name"); - rootItem->setTypeName("Type"); - rootItem->setSubtypeName("Subtype"); - rootItem->setText("Text"); treeModel = new TreeModel(rootItem); } @@ -150,7 +146,8 @@ UINT8 FfsEngine::parseInputFile(const QByteArray & buffer) UINT8 result; if (descriptorHeader->Signature == FLASH_DESCRIPTOR_SIGNATURE) { // Parse as Intel image - result = parseIntelImage(flashImage, index); + QModelIndex imageIndex; + result = parseIntelImage(flashImage, imageIndex, index); if (result != ERR_INVALID_FLASH_DESCRIPTOR) return result; } @@ -165,7 +162,7 @@ UINT8 FfsEngine::parseInputFile(const QByteArray & buffer) return parseBios(flashImage, index); } -UINT8 FfsEngine::parseIntelImage(const QByteArray & flashImage, const QModelIndex & parent) +UINT8 FfsEngine::parseIntelImage(const QByteArray & flashImage, QModelIndex & index, const QModelIndex & parent) { FLASH_DESCRIPTOR_MAP* descriptorMap; FLASH_DESCRIPTOR_REGION_SECTION* regionSection; @@ -291,7 +288,7 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & flashImage, const QModelInde .arg(descriptorMap->NumberOfIccTableEntries); // Add Intel image tree item - QModelIndex index = treeModel->addItem(TreeItem::Image, TreeItem::IntelImage, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), flashImage, QByteArray(), parent); + index = treeModel->addItem(TreeItem::Image, TreeItem::IntelImage, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), flashImage, QByteArray(), parent); // Descriptor // Get descriptor info @@ -329,19 +326,23 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & flashImage, const QModelInde for (int i = 0; i < offsets.count(); i++) { // Parse GbE region if (offsets.at(i) == gbeBegin) { - result = parseGbeRegion(gbe, index); + QModelIndex gbeIndex; + result = parseGbeRegion(gbe, gbeIndex, index); } // Parse ME region else if (offsets.at(i) == meBegin) { - result = parseMeRegion(me, index); + QModelIndex meIndex; + result = parseMeRegion(me, meIndex, index); } // Parse BIOS region else if (offsets.at(i) == biosBegin) { - result = parseBiosRegion(bios, index); + QModelIndex biosIndex; + result = parseBiosRegion(bios, biosIndex, index); } // Parse PDR region else if (offsets.at(i) == pdrBegin) { - result = parsePdrRegion(pdr, index); + QModelIndex pdrIndex; + result = parsePdrRegion(pdr, pdrIndex, index); } if (result) return result; @@ -350,7 +351,7 @@ UINT8 FfsEngine::parseIntelImage(const QByteArray & flashImage, const QModelInde return ERR_SUCCESS; } -UINT8 FfsEngine::parseGbeRegion(const QByteArray & gbe, const QModelIndex & parent) +UINT8 FfsEngine::parseGbeRegion(const QByteArray & gbe, QModelIndex & index, const QModelIndex & parent) { if (gbe.isEmpty()) return ERR_EMPTY_REGION; @@ -371,12 +372,12 @@ UINT8 FfsEngine::parseGbeRegion(const QByteArray & gbe, const QModelIndex & pare .arg(version->minor); // Add tree item - treeModel->addItem(TreeItem::Region, TreeItem::GbeRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), gbe, QByteArray(), parent); + index = treeModel->addItem(TreeItem::Region, TreeItem::GbeRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), gbe, QByteArray(), parent); return ERR_SUCCESS; } -UINT8 FfsEngine::parseMeRegion(const QByteArray & me, const QModelIndex & parent) +UINT8 FfsEngine::parseMeRegion(const QByteArray & me, QModelIndex & index, const QModelIndex & parent) { if (me.isEmpty()) return ERR_EMPTY_REGION; @@ -402,12 +403,12 @@ UINT8 FfsEngine::parseMeRegion(const QByteArray & me, const QModelIndex & parent } // Add tree item - treeModel->addItem(TreeItem::Region, TreeItem::MeRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), me, QByteArray(), parent); + index = treeModel->addItem(TreeItem::Region, TreeItem::MeRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), me, QByteArray(), parent); return ERR_SUCCESS; } -UINT8 FfsEngine::parsePdrRegion(const QByteArray & pdr, const QModelIndex & parent) +UINT8 FfsEngine::parsePdrRegion(const QByteArray & pdr, QModelIndex & index, const QModelIndex & parent) { if (pdr.isEmpty()) return ERR_EMPTY_REGION; @@ -418,12 +419,12 @@ UINT8 FfsEngine::parsePdrRegion(const QByteArray & pdr, const QModelIndex & pare arg(pdr.size(), 8, 16, QChar('0')); // Add tree item - treeModel->addItem(TreeItem::Region, TreeItem::PdrRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), pdr, QByteArray(), parent); + index = treeModel->addItem(TreeItem::Region, TreeItem::PdrRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), pdr, QByteArray(), parent); return ERR_SUCCESS; } -UINT8 FfsEngine::parseBiosRegion(const QByteArray & bios, const QModelIndex & parent) +UINT8 FfsEngine::parseBiosRegion(const QByteArray & bios, QModelIndex & index, const QModelIndex & parent) { if (bios.isEmpty()) return ERR_EMPTY_REGION; @@ -434,14 +435,14 @@ UINT8 FfsEngine::parseBiosRegion(const QByteArray & bios, const QModelIndex & pa arg(bios.size(), 8, 16, QChar('0')); // Add tree item - QModelIndex index = treeModel->addItem(TreeItem::Region, TreeItem::BiosRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), bios, QByteArray(), parent); + index = treeModel->addItem(TreeItem::Region, TreeItem::BiosRegion, COMPRESSION_ALGORITHM_NONE, name, "", info, QByteArray(), bios, QByteArray(), parent); return parseBios(bios, index); } UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent) { - // Search for first volume + // Search for first volume UINT32 prevVolumeOffset; UINT8 result; result = findNextVolume(bios, 0, prevVolumeOffset); @@ -577,7 +578,8 @@ UINT8 FfsEngine::parseBios(const QByteArray & bios, const QModelIndex & parent) msg(tr("parseBios: Unknown volume revision (%1)").arg(volumeHeader->Revision), parent); // Parse volume - UINT8 result = parseVolume(bios.mid(volumeOffset, volumeSize), parent); + QModelIndex index; + UINT8 result = parseVolume(bios.mid(volumeOffset, volumeSize), index, parent); if (result) msg(tr("parseBios: Volume parsing failed (%1)").arg(result), parent); @@ -640,7 +642,7 @@ UINT8 FfsEngine::getVolumeSize(const QByteArray & bios, UINT32 volumeOffset, UIN return ERR_SUCCESS; } -UINT8 FfsEngine::parseVolume(const QByteArray & volume, const QModelIndex & parent, const UINT8 mode) +UINT8 FfsEngine::parseVolume(const QByteArray & volume, QModelIndex & index, const QModelIndex & parent, const UINT8 mode) { // Populate volume header EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*) (volume.constData()); @@ -707,7 +709,7 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, const QModelIndex & par // Add tree item QByteArray header = volume.left(headerSize); QByteArray body = volume.mid(headerSize, volumeSize - headerSize); - QModelIndex index = treeModel->addItem(TreeItem::Volume, 0, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); + index = treeModel->addItem(TreeItem::Volume, 0, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, QByteArray(), parent, mode); // Do not parse volumes with unknown FS if (!parseCurrentVolume) @@ -751,8 +753,9 @@ UINT8 FfsEngine::parseVolume(const QByteArray & volume, const QModelIndex & par // Add file GUID to queue files.enqueue(header.left(sizeof(EFI_GUID))); - // Parse file - result = parseFile(file, volumeHeader->Revision, empty, index); + // Parse file + QModelIndex fileIndex; + result = parseFile(file, fileIndex, empty == '\xFF' ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE, index); if (result) msg(tr("parseVolume: Parse FFS file failed (%1)").arg(result), index); @@ -775,7 +778,7 @@ UINT8 FfsEngine::getFileSize(const QByteArray & volume, const UINT32 fileOffset, return ERR_SUCCESS; } -UINT8 FfsEngine::parseFile(const QByteArray & file, UINT8 revision, const UINT8 erasePolarity, const QModelIndex & parent, const UINT8 mode) +UINT8 FfsEngine::parseFile(const QByteArray & file, QModelIndex & index, const UINT8 erasePolarity, const QModelIndex & parent, const UINT8 mode) { // Populate file header EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*) file.constData(); @@ -812,7 +815,7 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, UINT8 revision, const UINT8 if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) { UINT32 bufferSize = file.size() - sizeof(EFI_FFS_FILE_HEADER); // Exclude file tail from data checksum calculation - if(revision == 1 && fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) + if(fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) bufferSize -= sizeof(UINT16); calculated = calculateChecksum8((UINT8*)(file.constData() + sizeof(EFI_FFS_FILE_HEADER)), bufferSize); if (fileHeader->IntegrityCheck.Checksum.File != calculated) { @@ -833,16 +836,17 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, UINT8 revision, const UINT8 // Get file body QByteArray body = file.right(file.size() - sizeof(EFI_FFS_FILE_HEADER)); - // For files in Revision 1 volumes, check for file tail presence + // Check for file tail presence QByteArray tail; - if (revision == 1 && fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) + if (fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) { //Check file tail; tail = body.right(sizeof(UINT16)); - if (!fileHeader->IntegrityCheck.TailReference == *(UINT16*)tail.constData()) - msg(tr("parseFile: %1, file tail value %2 is not a bitwise not of %3 stored in file header") + UINT16 tailValue = *(UINT16*) tail.constData(); + if (fileHeader->IntegrityCheck.TailReference != (UINT16)~tailValue) + msg(tr("parseFile: %1, bitwise not of tail value %2 differs from %3 stored in file header") .arg(guidToQString(fileHeader->Name)) - .arg(*tail, 4, 16, QChar('0')) + .arg(~tailValue, 4, 16, QChar('0')) .arg(fileHeader->IntegrityCheck.TailReference, 4, 16, QChar('0')), parent); // Remove tail from file body @@ -915,7 +919,7 @@ UINT8 FfsEngine::parseFile(const QByteArray & file, UINT8 revision, const UINT8 .arg(fileHeader->State, 2, 16, QChar('0')); // Add tree item - QModelIndex index = treeModel->addItem(TreeItem::File, fileHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, tail, parent, mode); + index = treeModel->addItem(TreeItem::File, fileHeader->Type, COMPRESSION_ALGORITHM_NONE, name, "", info, header, body, tail, parent, mode); if (!parseCurrentFile) return ERR_SUCCESS; @@ -951,14 +955,16 @@ UINT8 FfsEngine::parseSections(const QByteArray & body, const QModelIndex & pare UINT32 sectionSize; UINT32 bodySize = body.size(); UINT8 result; - while (true) { + + while (true) { // Get section size result = getSectionSize(body, sectionOffset, sectionSize); if (result) return result; // Parse section - result = parseSection(body.mid(sectionOffset, sectionSize), parent); + QModelIndex sectionIndex; + result = parseSection(body.mid(sectionOffset, sectionSize), sectionIndex, parent); if (result) return result; @@ -974,7 +980,7 @@ UINT8 FfsEngine::parseSections(const QByteArray & body, const QModelIndex & pare return ERR_SUCCESS; } -UINT8 FfsEngine::parseSection(const QByteArray & section, const QModelIndex & parent, const UINT8 mode) +UINT8 FfsEngine::parseSection(const QByteArray & section, QModelIndex & index, const QModelIndex & parent, const UINT8 mode) { EFI_COMMON_SECTION_HEADER* sectionHeader = (EFI_COMMON_SECTION_HEADER*) (section.constData()); UINT32 sectionSize = uint24ToUint32(sectionHeader->Size); @@ -984,11 +990,9 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, const QModelIndex & pa QByteArray body; UINT32 headerSize; UINT8 result; - QModelIndex index; - switch (sectionHeader->Type) { - // Encapsulated sections + // Encapsulated sections case EFI_SECTION_COMPRESSION: { bool parseCurrentSection = true; @@ -1047,7 +1051,6 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, const QModelIndex & pa parseCurrentSection = false; } } - } // Get info @@ -1090,7 +1093,7 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, const QModelIndex & pa } break; - // Leaf sections + // Leaf sections case EFI_SECTION_PE32: case EFI_SECTION_PIC: case EFI_SECTION_TE: @@ -1184,6 +1187,248 @@ UINT8 FfsEngine::parseSection(const QByteArray & section, const QModelIndex & pa } // Operations on tree items +UINT8 FfsEngine::create(const QModelIndex & index, const UINT8 type, const QByteArray & header, const QByteArray & body, const UINT8 mode, const UINT8 action, const UINT8 algorithm) +{ + QByteArray created; + UINT8 result; + + if (!index.isValid() || !index.parent().isValid()) + return ERR_INVALID_PARAMETER; + + QModelIndex parent; + if (mode == CREATE_MODE_BEFORE || mode == CREATE_MODE_AFTER) + parent = index.parent(); + else + parent = index; + TreeItem * parentItem = static_cast(parent.internalPointer()); + + // Create item + if (type == TreeItem::File) { + if (parentItem->type() != TreeItem::Volume) + return ERR_INVALID_FILE; + + EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*) parentItem->header().constData(); + UINT8 revision = volumeHeader->Revision; + bool erasePolarity = volumeHeader->Attributes & EFI_FVB_ERASE_POLARITY; + + if (header.size() != sizeof(EFI_FFS_FILE_HEADER)) + return ERR_INVALID_FILE; + + QByteArray newHeader = header; + EFI_FFS_FILE_HEADER* fileHeader = (EFI_FFS_FILE_HEADER*) newHeader.data(); + + // Correct file size + UINT8 tailSize = fileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT ? sizeof(UINT16) : 0; + uint32ToUint24(sizeof(EFI_FFS_FILE_HEADER) + body.size() + tailSize, fileHeader->Size); + + // Recalculate header checksum + fileHeader->IntegrityCheck.Checksum.Header = 0; + fileHeader->IntegrityCheck.Checksum.File = 0; + fileHeader->IntegrityCheck.Checksum.Header = calculateChecksum8((UINT8*) fileHeader, sizeof(EFI_FFS_FILE_HEADER) - 1); + + // Recalculate data checksum, if needed + if (fileHeader->Attributes & FFS_ATTRIB_CHECKSUM) { + fileHeader->IntegrityCheck.Checksum.File = calculateChecksum8((UINT8*) body.constData(), body.size()); + } + else if (revision == 1) + fileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM; + else + fileHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM2; + + // Append body + created.append(body); + + // Append tail, if needed + if (tailSize) + created.append(~fileHeader->IntegrityCheck.TailReference); + + // Set file state + UINT8 state = EFI_FILE_DATA_VALID | EFI_FILE_HEADER_VALID | EFI_FILE_HEADER_CONSTRUCTION; + if (erasePolarity) + state = ~state; + fileHeader->State = state; + + // Prepend header + created.prepend(newHeader); + + // Parse file + QModelIndex fileIndex; + result = parseFile(created, fileIndex, erasePolarity ? ERASE_POLARITY_TRUE : ERASE_POLARITY_FALSE, index, mode); + if (result) + return result; + + // Set action + treeModel->setItemAction(action, fileIndex); + + } + else if (type == TreeItem::Section) { + if (parentItem->type() != TreeItem::File && parentItem->type() != TreeItem::Section) + return ERR_INVALID_SECTION; + + if (header.size() < sizeof(EFI_COMMON_SECTION_HEADER)) + return ERR_INVALID_SECTION; + + QByteArray newHeader = header; + EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*) newHeader.data(); + + switch (commonHeader->Type) + { + case EFI_SECTION_COMPRESSION: { + EFI_COMPRESSION_SECTION* sectionHeader = (EFI_COMPRESSION_SECTION*) newHeader.data(); + // Correct uncompressed size + sectionHeader->UncompressedLength = body.size(); + // Set compression type + if (algorithm == COMPRESSION_ALGORITHM_NONE) + sectionHeader->CompressionType = EFI_NOT_COMPRESSED; + else if (algorithm == COMPRESSION_ALGORITHM_EFI11 || algorithm == COMPRESSION_ALGORITHM_TIANO) + sectionHeader->CompressionType = EFI_STANDARD_COMPRESSION; + else if (algorithm == COMPRESSION_ALGORITHM_LZMA || algorithm == COMPRESSION_ALGORITHM_IMLZMA) + sectionHeader->CompressionType = EFI_CUSTOMIZED_COMPRESSION; + else + return ERR_UNKNOWN_COMPRESSION_ALGORITHM; + // Compress body + QByteArray compressed; + result = compress(body, algorithm, compressed); + if (result) + return result; + + // Correct section size + uint32ToUint24(header.size() + compressed.size(), commonHeader->Size); + + // Append header and body + created.append(newHeader).append(compressed); + + // Parse section + QModelIndex sectionIndex; + result = parseSection(created, sectionIndex, index, mode); + if (result) + return result; + + // Set create action + treeModel->setItemAction(action, sectionIndex); + } + break; + case EFI_SECTION_GUID_DEFINED:{ + // Compress body + QByteArray compressed; + result = compress(body, algorithm, compressed); + if (result) + return result; + + // Correct section size + uint32ToUint24(header.size() + compressed.size(), commonHeader->Size); + + // Append header and body + created.append(newHeader).append(compressed); + + // Parse section + QModelIndex sectionIndex; + result = parseSection(created, sectionIndex, index, mode); + if (result) + return result; + + // Set create action + treeModel->setItemAction(action, sectionIndex); + } + break; + default: + // Correct section size + uint32ToUint24(header.size() + body.size(), commonHeader->Size); + + // Append header and body + created.append(newHeader).append(body); + + // Parse section + QModelIndex sectionIndex; + result = parseSection(created, sectionIndex, index, mode); + if (result) + return result; + + // Set create action + treeModel->setItemAction(action, sectionIndex); + } + } + else + return ERR_NOT_IMPLEMENTED; + + return ERR_SUCCESS; +} + +UINT8 FfsEngine::insert(const QModelIndex & index, const QByteArray & object, const UINT8 mode) +{ + if (!index.isValid() || !index.parent().isValid()) + return ERR_INVALID_PARAMETER; + + QModelIndex parent; + if (mode == CREATE_MODE_BEFORE || mode == CREATE_MODE_AFTER) + parent = index.parent(); + else + parent = index; + TreeItem * parentItem = static_cast(parent.internalPointer()); + + // Determine type of item to insert + UINT8 type; + UINT32 headerSize; + if (parentItem->type() == TreeItem::Volume) { + type = TreeItem::File; + headerSize = sizeof(EFI_FFS_FILE_HEADER); + } + else if (parentItem->type() == TreeItem::File) { + type = TreeItem::Section; + EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*) object.constData(); + headerSize = sizeOfSectionHeaderOfType(commonHeader->Type); + } + else + return ERR_NOT_IMPLEMENTED; + + return create(index, type, object.left(headerSize), object.right(object.size() - headerSize), mode, TreeItem::Insert); +} + +UINT8 FfsEngine::replace(const QModelIndex & index, const QByteArray & object, const UINT8 mode) +{ + if (!index.isValid()) + return ERR_INVALID_PARAMETER; + + TreeItem * parentItem = static_cast(index.internalPointer()); + + // Determine type of item to replace + UINT32 headerSize; + UINT8 result; + if (parentItem->type() == TreeItem::File) { + if (mode == REPLACE_MODE_AS_IS) { + headerSize = sizeof(EFI_FFS_FILE_HEADER); + result = create(index, TreeItem::File, object.left(headerSize), object.right(object.size() - headerSize), CREATE_MODE_AFTER, TreeItem::Replace); + } + else if (mode == REPLACE_MODE_BODY) + result = create(index, TreeItem::File, parentItem->header(), object, CREATE_MODE_AFTER, TreeItem::Replace); + else + return ERR_NOT_IMPLEMENTED; + } + else if (parentItem->type() == TreeItem::Section) { + if (mode == REPLACE_MODE_AS_IS) { + EFI_COMMON_SECTION_HEADER* commonHeader = (EFI_COMMON_SECTION_HEADER*) object.constData(); + headerSize = sizeOfSectionHeaderOfType(commonHeader->Type); + result = create(index, TreeItem::Section, object.left(headerSize), object.right(object.size() - headerSize), CREATE_MODE_AFTER, TreeItem::Replace); + } + else if (mode == REPLACE_MODE_BODY) + result = create(index, TreeItem::Section, parentItem->header(), object, CREATE_MODE_AFTER, TreeItem::Replace); + else + return ERR_NOT_IMPLEMENTED; + } + else + return ERR_NOT_IMPLEMENTED; + + // Check create result + if (result) + return result; + + // Set remove action to replaced item + treeModel->setItemAction(TreeItem::Remove, index); + + return ERR_SUCCESS; +} + + UINT8 FfsEngine::extract(const QModelIndex & index, QByteArray & extracted, const UINT8 mode) { if (!index.isValid()) @@ -1198,14 +1443,40 @@ UINT8 FfsEngine::extract(const QModelIndex & index, QByteArray & extracted, cons extracted.append(item->body()); extracted.append(item->tail()); } - else if (mode == EXTRACT_MODE_BODY_ONLY) { + else if (mode == EXTRACT_MODE_BODY) { // Extract without header and tail extracted.clear(); - extracted.append(item->body()); - } - else if (mode == EXTRACT_MODE_UNCOMPRESSED) { - // Only possible for files with compressed sections - return ERR_NOT_IMPLEMENTED; + // Special case of compressed bodies + if (item->type() == TreeItem::Section) { + QByteArray decompressed; + UINT8 result; + if (item->subtype() == EFI_SECTION_COMPRESSION) { + EFI_COMPRESSION_SECTION* compressedHeader = (EFI_COMPRESSION_SECTION*) item->header().constData(); + result = decompress(item->body(), compressedHeader->CompressionType, decompressed); + if (result) + return result; + extracted.append(decompressed); + return ERR_SUCCESS; + } + else if (item->subtype() == EFI_SECTION_GUID_DEFINED) { + QByteArray decompressed; + // Check if section requires processing + EFI_GUID_DEFINED_SECTION* guidDefinedSectionHeader = (EFI_GUID_DEFINED_SECTION*) item->header().constData(); + if (guidDefinedSectionHeader->Attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) { + // Try to decompress section body using both known compression algorithms + result = decompress(item->body(), EFI_STANDARD_COMPRESSION, decompressed); + if (result) { + result = decompress(item->body(), EFI_CUSTOMIZED_COMPRESSION, decompressed); + if (result) + return result; + } + extracted.append(decompressed); + return ERR_SUCCESS; + } + } + } + + extracted.append(item->body()); } else return ERR_UNKNOWN_EXTRACT_MODE; @@ -1213,66 +1484,6 @@ UINT8 FfsEngine::extract(const QModelIndex & index, QByteArray & extracted, cons return ERR_SUCCESS; } -UINT8 FfsEngine::insert(const QModelIndex & index, const QByteArray & object, const UINT8 type, const UINT8 mode) -{ - if (!index.isValid()) - return ERR_INVALID_PARAMETER; - - // Only files and sections can now be inserted - if (type == TreeItem::File) { - QModelIndex parent; - if (mode == INSERT_MODE_BEFORE || mode == INSERT_MODE_AFTER) - parent = index.parent(); - else - parent = index; - - // Parent type must be volume - TreeItem * parentItem = static_cast(parent.internalPointer()); - if (parentItem->type() != TreeItem::Volume) { - msg(tr("insert: file can't be inserted into something that is not volume"), parent); - return ERR_INVALID_VOLUME; - } - - EFI_FIRMWARE_VOLUME_HEADER* header = (EFI_FIRMWARE_VOLUME_HEADER*) parentItem->header().constData(); - - // Parse file - UINT8 result = parseFile(object, header->Revision, ERASE_POLARITY_UNKNOWN, index, mode); - if (result) - return result; - // Set rebuild action for parent - treeModel->setItemAction(TreeItem::Rebuild, parent); - } - else if (type == TreeItem::Section) { - QModelIndex parent; - if (mode == INSERT_MODE_BEFORE || mode == INSERT_MODE_AFTER) - parent = index.parent(); - else - parent = index; - - // Parent type must be file or encapsulation section - TreeItem * parentItem = static_cast(parent.internalPointer()); - if (parentItem->type() == TreeItem::File || (parentItem->type() == TreeItem::Section && - (parentItem->subtype() == EFI_SECTION_COMPRESSION || - parentItem->subtype() == EFI_SECTION_GUID_DEFINED || - parentItem->subtype() == EFI_SECTION_DISPOSABLE))) { - // Parse section - UINT8 result = parseSection(object, index, mode); - if (result) - return result; - // Set rebuild action for parent - treeModel->setItemAction(TreeItem::Rebuild, parent); - } - else { - msg(tr("insert: section can't be inserted into something that is not file or encapsulation section"), parent); - return ERR_INVALID_FILE; - } - } - else - return ERR_NOT_IMPLEMENTED; - - return ERR_SUCCESS; -} - UINT8 FfsEngine::remove(const QModelIndex & index) { if (!index.isValid()) @@ -1295,20 +1506,6 @@ UINT8 FfsEngine::rebuild(const QModelIndex & index) return ERR_SUCCESS; } -UINT8 FfsEngine::changeCompression(const QModelIndex & index, const UINT8 algorithm) -{ - if (!index.isValid()) - return ERR_INVALID_PARAMETER; - - // Set compression for the item - treeModel->setItemCompression(algorithm, index); - - // Set action for the item - treeModel->setItemAction(TreeItem::Modify, index); - - return ERR_SUCCESS; -} - // Compression routines UINT8 FfsEngine::decompress(const QByteArray & compressedData, const UINT8 compressionType, QByteArray & decompressedData, UINT8 * algorithm) { @@ -1580,7 +1777,7 @@ UINT8 FfsEngine::reconstruct(const QModelIndex & index, QQueue & que return ERR_NOT_IMPLEMENTED; } // Reconstruct item and it's children recursive - else if (item->action() == TreeItem::Modify || item->action() == TreeItem::Rebuild) { + else if (item->action() == TreeItem::Create || item->action() == TreeItem::Insert || item->action() == TreeItem::Replace || item->action() == TreeItem::Rebuild) { QQueue childrenQueue; switch (item->type()) { @@ -1914,7 +2111,7 @@ UINT8 FfsEngine::reconstruct(const QModelIndex & index, QQueue & que } // Construct empty char for this file - char empty = erasePolarity == ERASE_POLARITY_TRUE ? '\xFF' : '\x00'; + char empty = (erasePolarity == ERASE_POLARITY_TRUE ? '\xFF' : '\x00'); // Check file state // Invert it first if erase polarity is true @@ -2015,7 +2212,7 @@ UINT8 FfsEngine::reconstruct(const QModelIndex & index, QQueue & que // Append tail, if needed if (!item->hasEmptyTail()) - reconstructed.append(!fileHeader->IntegrityCheck.TailReference); + reconstructed.append(~fileHeader->IntegrityCheck.TailReference); // Set file state state = EFI_FILE_DATA_VALID | EFI_FILE_HEADER_VALID | EFI_FILE_HEADER_CONSTRUCTION; @@ -2075,13 +2272,16 @@ UINT8 FfsEngine::reconstruct(const QModelIndex & index, QQueue & que result = compress(reconstructed, item->compression(), compressed); if (result) return result; - // Correct compression type + // Correct compression type if (item->compression() == COMPRESSION_ALGORITHM_NONE) compessionHeader->CompressionType = EFI_NOT_COMPRESSED; else if (item->compression() == COMPRESSION_ALGORITHM_LZMA || item->compression() == COMPRESSION_ALGORITHM_IMLZMA) compessionHeader->CompressionType = EFI_CUSTOMIZED_COMPRESSION; - else + else if (item->compression() == COMPRESSION_ALGORITHM_EFI11 || item->compression() == COMPRESSION_ALGORITHM_TIANO) compessionHeader->CompressionType = EFI_STANDARD_COMPRESSION; + else + return ERR_UNKNOWN_COMPRESSION_ALGORITHM; + // Replace new section body reconstructed = compressed; } @@ -2101,7 +2301,7 @@ UINT8 FfsEngine::reconstruct(const QModelIndex & index, QQueue & que reconstructed = compressed; } else if (item->compression() != COMPRESSION_ALGORITHM_NONE) { - msg(tr("reconstruct: compression required for section of type %1") + msg(tr("reconstruct: incorreclty required compression for section of type %1") .arg(item->subtype())); return ERR_INVALID_SECTION; } @@ -2168,12 +2368,12 @@ UINT8 FfsEngine::growVolume(QByteArray & header, const UINT32 size, UINT32 & new } // Search routines -UINT8 FfsEngine::findHexPattern(const QByteArray & pattern, const bool bodyOnly) +UINT8 FfsEngine::findHexPattern(const QByteArray & pattern, const UINT8 mode) { - return findHexPatternIn(treeModel->index(0,0), pattern, bodyOnly); + return findHexPatternIn(treeModel->index(0,0), pattern, mode); } -UINT8 FfsEngine::findHexPatternIn(const QModelIndex & index, const QByteArray & pattern, const bool bodyOnly) +UINT8 FfsEngine::findHexPatternIn(const QModelIndex & index, const QByteArray & pattern, const UINT8 mode) { if (pattern.isEmpty()) return ERR_INVALID_PARAMETER; @@ -2187,19 +2387,21 @@ UINT8 FfsEngine::findHexPatternIn(const QModelIndex & index, const QByteArray & bool hasChildren = (item->childCount() > 0); for (int i = 0; i < item->childCount(); i++) { - findHexPatternIn(index.child(i, index.column()), pattern, bodyOnly); + findHexPatternIn(index.child(i, index.column()), pattern, mode); } QByteArray data; if (hasChildren) { - if(!bodyOnly) + if(mode != SEARCH_MODE_BODY) data = item->header(); } else { - if (bodyOnly) - data = item->body(); + if (mode == SEARCH_MODE_HEADER) + data.append(item->header()).append(item->tail()); + else if (mode == SEARCH_MODE_BODY) + data.append(item->body()); else - data = item->header().append(item->body()).append(item->tail()); + data.append(item->header()).append(item->body()).append(item->tail()); } int offset = -1; diff --git a/ffsengine.h b/ffsengine.h index 1ba6ebc..6f2ba57 100644 --- a/ffsengine.h +++ b/ffsengine.h @@ -45,20 +45,20 @@ public: // Firmware image parsing UINT8 parseInputFile(const QByteArray & buffer); - UINT8 parseIntelImage(const QByteArray & flashImage, const QModelIndex & parent = QModelIndex()); - UINT8 parseGbeRegion(const QByteArray & gbe, const QModelIndex & parent = QModelIndex()); - UINT8 parseMeRegion(const QByteArray & me, const QModelIndex & parent = QModelIndex()); - UINT8 parseBiosRegion(const QByteArray & bios, const QModelIndex & parent = QModelIndex()); - UINT8 parsePdrRegion(const QByteArray & pdr, const QModelIndex & parent = QModelIndex()); + UINT8 parseIntelImage(const QByteArray & flashImage, QModelIndex & index, const QModelIndex & parent = QModelIndex()); + UINT8 parseGbeRegion(const QByteArray & gbe, QModelIndex & index, const QModelIndex & parent); + UINT8 parseMeRegion(const QByteArray & me, QModelIndex & index, const QModelIndex & parent); + UINT8 parseBiosRegion(const QByteArray & bios, QModelIndex & index, const QModelIndex & parent); + UINT8 parsePdrRegion(const QByteArray & pdr, QModelIndex & index, const QModelIndex & parent); UINT8 parseBios(const QByteArray & bios, const QModelIndex & parent = QModelIndex()); UINT8 findNextVolume(const QByteArray & bios, const UINT32 volumeOffset, UINT32 & nextVolumeOffset); UINT8 getVolumeSize(const QByteArray & bios, const UINT32 volumeOffset, UINT32 & volumeSize); - UINT8 parseVolume(const QByteArray & volume, const QModelIndex & parent = QModelIndex(), const UINT8 mode = INSERT_MODE_APPEND); + UINT8 parseVolume(const QByteArray & volume, QModelIndex & index, const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); UINT8 getFileSize(const QByteArray & volume, const UINT32 fileOffset, UINT32 & fileSize); - UINT8 parseFile(const QByteArray & file, const UINT8 revision, const UINT8 erasePolarity = ERASE_POLARITY_UNKNOWN, const QModelIndex & parent = QModelIndex(), const UINT8 mode = INSERT_MODE_APPEND); + UINT8 parseFile(const QByteArray & file, QModelIndex & index, const UINT8 erasePolarity = ERASE_POLARITY_UNKNOWN, const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); UINT8 getSectionSize(const QByteArray & file, const UINT32 sectionOffset, UINT32 & sectionSize); UINT8 parseSections(const QByteArray & body, const QModelIndex & parent = QModelIndex()); - UINT8 parseSection(const QByteArray & section, const QModelIndex & parent = QModelIndex(), const UINT8 mode = INSERT_MODE_APPEND); + UINT8 parseSection(const QByteArray & section, QModelIndex & index, const QModelIndex & parent = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); // Compression routines UINT8 decompress(const QByteArray & compressed, const UINT8 compressionType, QByteArray & decompressedData, UINT8 * algorithm = NULL); @@ -71,22 +71,26 @@ public: UINT8 growVolume(QByteArray & header, const UINT32 size, UINT32 & newSize); // Operations on tree items - UINT8 extract(const QModelIndex & index, QByteArray & extracted, const UINT8 mode); - UINT8 insert(const QModelIndex & index, const QByteArray & object, const UINT8 objectType, const UINT8 mode); - UINT8 remove(const QModelIndex & index); - UINT8 rebuild(const QModelIndex & index); - UINT8 changeCompression(const QModelIndex & index, const UINT8 algorithm); + UINT8 extract(const QModelIndex & index, QByteArray & extracted, const UINT8 mode); + + UINT8 create(const QModelIndex & index, const UINT8 type, const QByteArray & header, const QByteArray & body, const UINT8 mode, const UINT8 action, const UINT8 algorithm = COMPRESSION_ALGORITHM_NONE); + UINT8 insert(const QModelIndex & index, const QByteArray & object, const UINT8 mode); + UINT8 replace(const QModelIndex & index, const QByteArray & object, const UINT8 mode); + + UINT8 remove(const QModelIndex & index); + + UINT8 rebuild(const QModelIndex & index); // Search routines - UINT8 findHexPattern(const QByteArray & pattern, const bool bodyOnly); - UINT8 findHexPatternIn(const QModelIndex & index, const QByteArray & pattern, const bool bodyOnly); + UINT8 findHexPattern(const QByteArray & pattern, const UINT8 mode); + UINT8 findHexPatternIn(const QModelIndex & index, const QByteArray & pattern, const UINT8 mode); UINT8 findTextPattern(const QString & pattern, const bool unicode, const Qt::CaseSensitivity caseSensitive); UINT8 findTextPatternIn(const QModelIndex & index, const QString & pattern, const bool unicode, const Qt::CaseSensitivity caseSensitive); private: TreeItem *rootItem; TreeModel *treeModel; - // Message helper + // Message helper QQueue messageItems; void msg(const QString & message, const QModelIndex index = QModelIndex()); diff --git a/searchdialog.ui b/searchdialog.ui index 9bf9112..1ddfb47 100644 --- a/searchdialog.ui +++ b/searchdialog.ui @@ -7,7 +7,7 @@ 0 0 290 - 172 + 195 @@ -77,14 +77,21 @@ - Header, data and footer + Header and body - + - Data only + Header only + + + + + + + Body only true @@ -140,7 +147,7 @@ searchEdit dataTypeComboBox allRadioButton - dataOnlyRadioButton + bodyOnlyRadioButton unicodeCheckBox caseSensitiveCheckBox buttonBox diff --git a/treeitem.cpp b/treeitem.cpp index 6503b2c..8f2dc81 100644 --- a/treeitem.cpp +++ b/treeitem.cpp @@ -172,12 +172,16 @@ QVariant TreeItem::data(int column) const case 0: //Name return itemName; case 1: //Action - if (itemAction == TreeItem::Modify) - return "M"; + if (itemAction == TreeItem::Create) + return QObject::tr("Create"); + if (itemAction == TreeItem::Insert) + return QObject::tr("Insert"); + if (itemAction == TreeItem::Replace) + return QObject::tr("Replace"); if (itemAction == TreeItem::Remove) - return "X"; + return QObject::tr("Remove"); if (itemAction == TreeItem::Rebuild) - return "R"; + return QObject::tr("Rebuild"); return QVariant(); case 2: //Type return itemTypeName; @@ -220,11 +224,6 @@ QString TreeItem::info() const return itemInfo; } -void TreeItem::setInfo(const QString &text) -{ - itemInfo = text; -} - int TreeItem::row() const { if (parentItem) @@ -287,12 +286,13 @@ void TreeItem::setAction(const UINT8 action) { itemAction = action; - // Set rebuild action for parent - if (parentItem) - parentItem->setAction(TreeItem::Rebuild); + // On insert action, set insert action for children + if (action == TreeItem::Insert) + for(int i = 0; i < childCount(); i++) + child(i)->setAction(TreeItem::Insert); + + // Set rebuild action for parent, if it has no action now + if (parentItem && parentItem->type() != TreeItem::Root && parentItem->action() == TreeItem::NoAction) + parentItem->setAction(TreeItem::Rebuild); } -void TreeItem::setCompression(const UINT8 algorithm) -{ - itemCompression = algorithm; -} \ No newline at end of file diff --git a/treeitem.h b/treeitem.h index 3229c7f..0abd597 100644 --- a/treeitem.h +++ b/treeitem.h @@ -31,7 +31,9 @@ public: // Action types enum ActionTypes { NoAction = 50, - Modify, + Create, + Insert, + Replace, Remove, Rebuild }; @@ -104,17 +106,12 @@ public: UINT8 action() const; UINT8 compression() const; - // Action can be changed - void setAction(const UINT8 action); - // Compression can be changed - void setCompression(const UINT8 algorithm); - - // Text values can be changed after item construction - void setTypeName(const QString &text); + // Some values can be changed after item construction + void setAction(const UINT8 action); + void setTypeName(const QString &text); void setSubtypeName(const QString &text); void setName(const QString &text); void setText(const QString &text); - void setInfo(const QString &text); private: // Set default names after construction @@ -129,9 +126,9 @@ private: QByteArray itemHeader; QByteArray itemBody; QByteArray itemTail; - QString itemTypeName; + QString itemName; + QString itemTypeName; QString itemSubtypeName; - QString itemName; QString itemText; QString itemInfo; TreeItem *parentItem; diff --git a/treemodel.cpp b/treemodel.cpp index b8cf8c6..0112a61 100644 --- a/treemodel.cpp +++ b/treemodel.cpp @@ -61,8 +61,21 @@ Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const { - if (orientation == Qt::Horizontal && role == Qt::DisplayRole) - return rootItem->data(section); + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + switch(section) + { + case 0: + return tr("Name"); + case 1: + return tr("Action"); + case 2: + return tr("Type"); + case 3: + return tr("Subtype"); + case 4: + return tr("Text"); + } + } return QVariant(); } @@ -152,17 +165,6 @@ UINT8 TreeModel::setItemAction(const UINT8 action, const QModelIndex &index) return ERR_SUCCESS; } -UINT8 TreeModel::setItemCompression(const UINT8 algorithm, const QModelIndex &index) -{ - if(!index.isValid()) - return ERR_INVALID_PARAMETER; - - TreeItem *item = static_cast(index.internalPointer()); - item->setCompression(algorithm); - emit dataChanged(this->index(0,0), index); - return ERR_SUCCESS; -} - QModelIndex TreeModel::addItem(const UINT8 type, const UINT8 subtype, const UINT8 compression, const QString & name, const QString & text, const QString & info, const QByteArray & header, const QByteArray & body, const QByteArray & tail, @@ -176,7 +178,7 @@ QModelIndex TreeModel::addItem(const UINT8 type, const UINT8 subtype, const UINT parentItem = rootItem; else { - if (mode == INSERT_MODE_BEFORE || mode == INSERT_MODE_AFTER) { + if (mode == CREATE_MODE_BEFORE || mode == CREATE_MODE_AFTER) { item = static_cast(index.internalPointer()); parentItem = item->parent(); parentColumn = index.parent().column(); @@ -188,19 +190,19 @@ QModelIndex TreeModel::addItem(const UINT8 type, const UINT8 subtype, const UINT } TreeItem *newItem = new TreeItem(type, subtype, compression, name, text, info, header, body, tail, parentItem); - if (mode == INSERT_MODE_APPEND) { + if (mode == CREATE_MODE_APPEND) { emit layoutAboutToBeChanged(); parentItem->appendChild(newItem); } - else if (mode == INSERT_MODE_PREPEND) { + else if (mode == CREATE_MODE_PREPEND) { emit layoutAboutToBeChanged(); parentItem->prependChild(newItem); } - else if (mode == INSERT_MODE_BEFORE) { + else if (mode == CREATE_MODE_BEFORE) { emit layoutAboutToBeChanged(); parentItem->insertChildBefore(item, newItem); } - else if (mode == INSERT_MODE_AFTER) { + else if (mode == CREATE_MODE_AFTER) { emit layoutAboutToBeChanged(); parentItem->insertChildAfter(item, newItem); } diff --git a/treemodel.h b/treemodel.h index 7a4bca1..410c40f 100644 --- a/treemodel.h +++ b/treemodel.h @@ -44,12 +44,11 @@ public: UINT8 setItemName(const QString &data, const QModelIndex &index); UINT8 setItemText(const QString &data, const QModelIndex &index); UINT8 setItemAction(const UINT8 action, const QModelIndex &index); - UINT8 setItemCompression(const UINT8 algorithm, const QModelIndex &index); QModelIndex addItem(const UINT8 type, const UINT8 subtype = 0, const UINT8 compression = COMPRESSION_ALGORITHM_NONE, const QString & name = QString(), const QString & text = QString(), const QString & info = QString(), const QByteArray & header = QByteArray(), const QByteArray & body = QByteArray(), const QByteArray & tail = QByteArray(), - const QModelIndex & index = QModelIndex(), const UINT8 mode = INSERT_MODE_APPEND); + const QModelIndex & index = QModelIndex(), const UINT8 mode = CREATE_MODE_APPEND); private: TreeItem *rootItem; diff --git a/uefitool.cpp b/uefitool.cpp index 447e35b..bfeb9c2 100644 --- a/uefitool.cpp +++ b/uefitool.cpp @@ -29,17 +29,13 @@ UEFITool::UEFITool(QWidget *parent) : 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->actionExtractUncompressed, SIGNAL(triggered()), this, SLOT(extractUncompressed())); 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(replace())); + 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->actionChangeToNone, SIGNAL(triggered()), this, SLOT(changeToNone())); - connect(ui->actionChangeToEfi11, SIGNAL(triggered()), this, SLOT(changeToEfi11())); - connect(ui->actionChangeToTiano, SIGNAL(triggered()), this, SLOT(changeToTiano())); - connect(ui->actionChangeToLzma, SIGNAL(triggered()), this, SLOT(changeToLzma())); connect(ui->actionMessagesClear, SIGNAL(triggered()), this, SLOT(clearMessages())); connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(about())); connect(ui->actionAboutQt, SIGNAL(triggered()), this, SLOT(aboutQt())); @@ -85,8 +81,6 @@ void UEFITool::init() ui->structureTreeView->setModel(ffsEngine->model()); // Connect - //connect(ui->structureTreeView, SIGNAL(collapsed(const QModelIndex &)), this, SLOT(resizeTreeViewColums(void))); - //connect(ui->structureTreeView, SIGNAL(expanded(const QModelIndex &)), this, SLOT(resizeTreeViewColums(void))); 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*))); @@ -100,7 +94,6 @@ void UEFITool::populateUi(const QModelIndex ¤t) TreeItem* item = static_cast(current.internalPointer()); UINT8 type = item->type(); UINT8 subtype = item->subtype(); - UINT8 algorithm = item->compression(); // Set info text ui->infoEdit->setPlainText(item->info()); @@ -119,12 +112,11 @@ void UEFITool::populateUi(const QModelIndex ¤t) ui->actionRebuild->setDisabled(item->hasEmptyHeader() && item->hasEmptyBody() && item->hasEmptyTail()); ui->actionExtractBody->setDisabled(item->hasEmptyHeader()); ui->actionRemove->setEnabled(type == TreeItem::Volume || type == TreeItem::File || type == TreeItem::Section); - ui->actionInsertInto->setEnabled(type == TreeItem::Volume || (type == TreeItem::File && subtype != EFI_FV_FILETYPE_RAW && subtype != EFI_FV_FILETYPE_PAD) - || (type == TreeItem::Section && (subtype == EFI_SECTION_COMPRESSION || subtype == EFI_SECTION_GUID_DEFINED || subtype == EFI_SECTION_DISPOSABLE))); + ui->actionInsertInto->setEnabled(type == TreeItem::Volume || (type == TreeItem::File && subtype != EFI_FV_FILETYPE_ALL && subtype != EFI_FV_FILETYPE_RAW && subtype != EFI_FV_FILETYPE_PAD)); ui->actionInsertBefore->setEnabled(type == TreeItem::File || type == TreeItem::Section); - ui->actionInsertAfter->setEnabled(type == TreeItem::File || type == TreeItem::Section); - ui->menuChangeCompressionTo->setEnabled(type == TreeItem::Section && subtype == EFI_SECTION_COMPRESSION && - (algorithm == COMPRESSION_ALGORITHM_NONE || COMPRESSION_ALGORITHM_EFI11 || algorithm == COMPRESSION_ALGORITHM_TIANO || algorithm == COMPRESSION_ALGORITHM_LZMA)); + ui->actionInsertAfter->setEnabled(type == TreeItem::File || type == TreeItem::Section); + ui->actionReplace->setEnabled(type == TreeItem::File || type == TreeItem::Section); + ui->actionReplaceBody->setEnabled(type == TreeItem::File || type == TreeItem::Section); } void UEFITool::search() @@ -137,7 +129,14 @@ void UEFITool::search() QByteArray pattern = QByteArray::fromHex(searchDialog->ui->searchEdit->text().toLatin1()); if (pattern.isEmpty()) return; - ffsEngine->findHexPattern(pattern, searchDialog->ui->dataOnlyRadioButton->isChecked()); + UINT8 mode; + if (searchDialog->ui->headerOnlyRadioButton->isChecked()) + mode = SEARCH_MODE_HEADER; + else if (searchDialog->ui->bodyOnlyRadioButton->isChecked()) + mode = SEARCH_MODE_BODY; + else + mode = SEARCH_MODE_ALL; + ffsEngine->findHexPattern(pattern, mode); showMessages(); } else if (index == 1) { // Text string @@ -184,7 +183,7 @@ void UEFITool::insert(const UINT8 mode) UINT8 type; UINT8 objectType; - if (mode == INSERT_MODE_BEFORE || mode == INSERT_MODE_AFTER) + if (mode == CREATE_MODE_BEFORE || mode == CREATE_MODE_AFTER) type = item->parent()->type(); else type = item->type(); @@ -221,7 +220,7 @@ void UEFITool::insert(const UINT8 mode) QByteArray buffer = inputFile.readAll(); inputFile.close(); - UINT8 result = ffsEngine->insert(index, buffer, objectType, mode); + UINT8 result = ffsEngine->insert(index, buffer, mode); if (result) ui->statusBar->showMessage(tr("File can't be inserted (%1)").arg(result)); else @@ -230,67 +229,189 @@ void UEFITool::insert(const UINT8 mode) void UEFITool::insertInto() { - insert(INSERT_MODE_PREPEND); + insert(CREATE_MODE_PREPEND); } void UEFITool::insertBefore() { - insert(INSERT_MODE_BEFORE); + insert(CREATE_MODE_BEFORE); } void UEFITool::insertAfter() { - insert(INSERT_MODE_AFTER); + insert(CREATE_MODE_AFTER); } -void UEFITool::replace() +void UEFITool::replaceAsIs() { - + replace(REPLACE_MODE_AS_IS); } -void UEFITool::changeToEfi11() +void UEFITool::replaceBody() +{ + replace(REPLACE_MODE_BODY); +} + +void UEFITool::replace(const UINT8 mode) +{ + QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); + if (!index.isValid()) + return; + + TreeItem* item = static_cast(index.internalPointer()); + QString path; + if (item->type() == TreeItem::File) { + if (mode == REPLACE_MODE_AS_IS) { + path = QFileDialog::getOpenFileName(this, tr("Select FFS file to replace selected object"),".","FFS files (*.ffs *.bin);;All files (*.*)"); + } + else if (mode == REPLACE_MODE_BODY) { + if (item->subtype() == EFI_FV_FILETYPE_ALL || item->subtype() == EFI_FV_FILETYPE_RAW) + path = QFileDialog::getOpenFileName(this, tr("Select raw file to replace body"),".","Raw files (*.raw *.bin);;All files (*.*)"); + else if (item->subtype() == EFI_FV_FILETYPE_PAD) // Pad file body can't be replaced + return; + else + path = QFileDialog::getOpenFileName(this, tr("Select FFS file body to replace body"),".","FFS file body files (*.fbd *.bin);;All files (*.*)"); + } + else + return; + } + else if (item->type() == TreeItem::Section) { + if (mode == REPLACE_MODE_AS_IS) { + path = QFileDialog::getOpenFileName(this, tr("Select section file to replace selected object"),".","Section files (*.sec *.bin);;All files (*.*)"); + } + else if (mode == REPLACE_MODE_BODY) { + if (item->subtype() == EFI_SECTION_COMPRESSION || item->subtype() == EFI_SECTION_GUID_DEFINED || item->subtype() == EFI_SECTION_DISPOSABLE) + path = QFileDialog::getOpenFileName(this, tr("Select FFS file body file to replace body"),".","FFS file body files (*.fbd *.bin);;All files (*.*)"); + else if (item->subtype() == EFI_SECTION_FIRMWARE_VOLUME_IMAGE) + path = QFileDialog::getOpenFileName(this, tr("Select volume file to replace body"),".","Volume files (*.vol *.bin);;All files (*.*)"); + else if (item->subtype() == EFI_SECTION_RAW) + path = QFileDialog::getOpenFileName(this, tr("Select raw file to replace body"),".","Raw files (*.raw *.bin);;All files (*.*)"); + else + path = QFileDialog::getOpenFileName(this, tr("Select file to replace body"),".","Binary files (*.bin);;All files (*.*)"); + } + else + return; + } + else + 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)) { + ui->statusBar->showMessage(tr("Can't open file for reading")); + return; + } + + QByteArray buffer = inputFile.readAll(); + inputFile.close(); + + UINT8 result = ffsEngine->replace(index, buffer, mode); + if (result) + ui->statusBar->showMessage(tr("File can't be replaced (%1)").arg(result)); + else + 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; - UINT8 result = ffsEngine->changeCompression(index, COMPRESSION_ALGORITHM_EFI11); - - if (result == ERR_SUCCESS) - ui->actionSaveImageFile->setEnabled(true); -} + TreeItem* item = static_cast(index.internalPointer()); + UINT8 type = item->type(); -void UEFITool::changeToTiano() -{ - QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); - if (!index.isValid()) - return; + QString path; + if (mode == EXTRACT_MODE_AS_IS) { + switch (type) { + case TreeItem::Capsule: + path = QFileDialog::getSaveFileName(this, tr("Save capsule to file"),".","Capsule files (*.cap *.bin);;All files (*.*)"); + break; + case TreeItem::Image: + path = QFileDialog::getSaveFileName(this, tr("Save image to file"),".","Image files (*.rom *.bin);;All files (*.*)"); + break; + case TreeItem::Region: + path = QFileDialog::getSaveFileName(this, tr("Save region to file"),".","Region files (*.rgn *.bin);;All files (*.*)"); + break; + case TreeItem::Padding: + path = QFileDialog::getSaveFileName(this, tr("Save padding to file"),".","Padding files (*.pad *.bin);;All files (*.*)"); + break; + case TreeItem::Volume: + path = QFileDialog::getSaveFileName(this, tr("Save volume to file"),".","Volume files (*.vol *.bin);;All files (*.*)"); + break; + case TreeItem::File: + path = QFileDialog::getSaveFileName(this, tr("Save FFS file to file"),".","FFS files (*.ffs *.bin);;All files (*.*)"); + break; + case TreeItem::Section: + path = QFileDialog::getSaveFileName(this, tr("Save section file to file"),".","Section files (*.sct *.bin);;All files (*.*)"); + break; + default: + path = QFileDialog::getSaveFileName(this, tr("Save object to file"),".","Binary files (*.bin);;All files (*.*)"); + } + } + else if (mode == EXTRACT_MODE_BODY) { + switch (type) { + case TreeItem::Capsule: + path = QFileDialog::getSaveFileName(this, tr("Save capsule body to image file"),".","Image files (*.rom *.bin);;All files (*.*)"); + break; + case TreeItem::File: { + if (item->subtype() == EFI_FV_FILETYPE_ALL || item->subtype() == EFI_FV_FILETYPE_RAW) + path = QFileDialog::getSaveFileName(this, tr("Save FFS file body to raw file"),".","Raw files (*.raw *.bin);;All files (*.*)"); + else + path = QFileDialog::getSaveFileName(this, tr("Save FFS file body to file"),".","FFS file body files (*.fbd *.bin);;All files (*.*)"); + } + break; + case TreeItem::Section: { + if (item->subtype() == EFI_SECTION_COMPRESSION || item->subtype() == EFI_SECTION_GUID_DEFINED || item->subtype() == EFI_SECTION_DISPOSABLE) + path = QFileDialog::getSaveFileName(this, tr("Save encapsulated section body to FFS body file"),".","FFS file body files (*.fbd *.bin);;All files (*.*)"); + else if (item->subtype() == EFI_SECTION_FIRMWARE_VOLUME_IMAGE) + path = QFileDialog::getSaveFileName(this, tr("Save section body to volume file"),".","Volume files (*.vol *.bin);;All files (*.*)"); + else if (item->subtype() == EFI_SECTION_RAW) + path = QFileDialog::getOpenFileName(this, tr("Select section body to raw file"),".","Raw files (*.raw *.bin);;All files (*.*)"); + else + path = QFileDialog::getSaveFileName(this, tr("Save section body to file"),".","Binary files (*.bin);;All files (*.*)"); + } + break; + default: + path = QFileDialog::getSaveFileName(this, tr("Save object to file"),".","Binary files (*.bin);;All files (*.*)"); + } + } + else + path = QFileDialog::getSaveFileName(this, tr("Save object to file"),".","Binary files (*.bin);;All files (*.*)"); - UINT8 result = ffsEngine->changeCompression(index, COMPRESSION_ALGORITHM_TIANO); - if (result == ERR_SUCCESS) - ui->actionSaveImageFile->setEnabled(true); -} + QFile outputFile; + outputFile.setFileName(path); + if (!outputFile.open(QFile::WriteOnly)) { + ui->statusBar->showMessage(tr("Can't open file for rewriting")); + return; + } -void UEFITool::changeToLzma() -{ - QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); - if (!index.isValid()) - return; - - UINT8 result = ffsEngine->changeCompression(index, COMPRESSION_ALGORITHM_LZMA); - if (result == ERR_SUCCESS) - ui->actionSaveImageFile->setEnabled(true); -} - -void UEFITool::changeToNone() -{ - QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); - if (!index.isValid()) - return; - - UINT8 result = ffsEngine->changeCompression(index, COMPRESSION_ALGORITHM_NONE); - if (result == ERR_SUCCESS) - ui->actionSaveImageFile->setEnabled(true); + QByteArray extracted; + UINT8 result = ffsEngine->extract(index, extracted, mode); + if (result) + ui->statusBar->showMessage(tr("File can't be extracted (%1)").arg(result)); + else { + outputFile.resize(0); + outputFile.write(extracted); + outputFile.close(); + } } void UEFITool::about() @@ -317,7 +438,7 @@ void UEFITool::exit() void UEFITool::saveImageFile() { - QString path = QFileDialog::getSaveFileName(this, tr("Save BIOS image file"),".","BIOS image files (*.rom *.bin *.cap *.fd *.wph *.efi);;All files (*.*)"); + QString path = QFileDialog::getSaveFileName(this, tr("Save BIOS image file"),".","BIOS image files (*.rom *.bin *.cap *.bio *.fd *.wph *.efi);;All files (*.*)"); QFile outputFile; outputFile.setFileName(path); @@ -340,12 +461,12 @@ void UEFITool::saveImageFile() ui->statusBar->showMessage(tr("Reconstructed image written")); } -void UEFITool::resizeTreeViewColumns() +/*void UEFITool::resizeTreeViewColumns() { int count = ffsEngine->model()->columnCount(); for(int i = 0; i < count; i++) ui->structureTreeView->resizeColumnToContents(i); -} +}*/ void UEFITool::openImageFile() { @@ -391,79 +512,6 @@ void UEFITool::clearMessages() ui->messageListWidget->clear(); } -void UEFITool::extract(const UINT8 mode) -{ - QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex(); - if (!index.isValid()) - return; - - TreeItem* item = static_cast(index.internalPointer()); - UINT8 type = item->type(); - - QString path; - if(mode == EXTRACT_MODE_AS_IS) { - switch (type) { - case TreeItem::Capsule: - path = QFileDialog::getSaveFileName(this, tr("Save capsule to binary file"),".","Capsule files (*.cap *.bin);;All files (*.*)"); - break; - case TreeItem::Image: - path = QFileDialog::getSaveFileName(this, tr("Save image to binary file"),".","Image files (*.rom *.bin);;All files (*.*)"); - break; - case TreeItem::Region: - path = QFileDialog::getSaveFileName(this, tr("Save region to binary file"),".","Region files (*.rgn *.bin);;All files (*.*)"); - break; - case TreeItem::Padding: - path = QFileDialog::getSaveFileName(this, tr("Save padding to binary file"),".","Padding files (*.pad *.bin);;All files (*.*)"); - break; - case TreeItem::Volume: - path = QFileDialog::getSaveFileName(this, tr("Save volume to binary file"),".","Volume files (*.vol *.bin);;All files (*.*)"); - break; - case TreeItem::File: - path = QFileDialog::getSaveFileName(this, tr("Save FFS file to binary file"),".","FFS files (*.ffs *.bin);;All files (*.*)"); - break; - case TreeItem::Section: - path = QFileDialog::getSaveFileName(this, tr("Save section file to binary file"),".","Section files (*.sct *.bin);;All files (*.*)"); - break; - default: - return; - } - } - else - path = QFileDialog::getSaveFileName(this, tr("Save object to binary file"),".","Binary files (*.bin);;All files (*.*)"); - - QFile outputFile; - outputFile.setFileName(path); - if (!outputFile.open(QFile::WriteOnly)) { - ui->statusBar->showMessage(tr("Can't open file for rewriting")); - return; - } - - QByteArray extracted; - UINT8 result = ffsEngine->extract(index, extracted, mode); - if (result) - ui->statusBar->showMessage(tr("File can't be extracted (%1)").arg(result)); - else { - outputFile.resize(0); - outputFile.write(extracted); - outputFile.close(); - } -} - -void UEFITool::extractAsIs() -{ - extract(EXTRACT_MODE_AS_IS); -} - -void UEFITool::extractBody() -{ - extract(EXTRACT_MODE_BODY_ONLY); -} - -void UEFITool::extractUncompressed() -{ - extract(EXTRACT_MODE_UNCOMPRESSED); -} - void UEFITool::dragEnterEvent(QDragEnterEvent* event) { if (event->mimeData()->hasFormat("text/uri-list")) @@ -553,7 +601,6 @@ void UEFITool::readSettings() vertList.append(settings.value("mainWindow/messageHeight", 180).toInt()); ui->infoSplitter->setSizes(horList); ui->messagesSplitter->setSizes(vertList); - resizeTreeViewColumns(); 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()); diff --git a/uefitool.h b/uefitool.h index 522d648..06b0654 100644 --- a/uefitool.h +++ b/uefitool.h @@ -54,7 +54,7 @@ public: private slots: void init(); void populateUi(const QModelIndex ¤t); - void resizeTreeViewColumns(); + //void resizeTreeViewColumns(); void scrollTreeView(QListWidgetItem* item); void openImageFile(); @@ -64,24 +64,20 @@ private slots: void extract(const UINT8 mode); void extractAsIs(); void extractBody(); - void extractUncompressed(); void insert(const UINT8 mode); void insertInto(); void insertBefore(); void insertAfter(); - void replace(); - - void remove(); - - void rebuild(); - - void changeToNone(); - void changeToEfi11(); - void changeToTiano(); - void changeToLzma(); + void replace(const UINT8 mode); + void replaceAsIs(); + void replaceBody(); + + void rebuild(); + void remove(); + void clearMessages(); void about(); diff --git a/uefitool.ui b/uefitool.ui index 79a6eba..41257b9 100644 --- a/uefitool.ui +++ b/uefitool.ui @@ -20,7 +20,7 @@ true - UEFITool 0.11.0 + UEFITool 0.12.0 @@ -222,21 +222,15 @@ + + + &Section - - - Change compression to - - - - - - @@ -246,9 +240,11 @@ + + + - @@ -289,7 +285,7 @@ false - Insert &before... + Insert b&efore... Insert object from file before selected object @@ -303,10 +299,10 @@ false - Rep&lace + Rep&lace as is... - Replace selected object with an object from file + Replace selected object as is with an object from file Ctrl+R @@ -331,29 +327,15 @@ false - Extract &without header... + Extract &body... - Extract selected object without header to file + Extract body of selected object to file Ctrl+Shift+E - - - false - - - Extract &uncompressed... - - - Extract selected FFS file uncompressing it - - - Ctrl+Alt+E - - false @@ -421,30 +403,6 @@ Ctrl+Space - - - &Tiano - - - Ctrl+T - - - - - &EFI 1.1 - - - Ctrl+Shift+T - - - - - &LZMA - - - Ctrl+L - - &About UEFITool @@ -472,14 +430,6 @@ QAction::QuitRole - - - &Uncompressed - - - Ctrl+U - - false @@ -495,10 +445,27 @@ &Clear + + Clear messages + Ctrl+Backspace + + + false + + + Replace b&ody... + + + Replace body of selected object with a data from file + + + Ctrl+Shift+R + +