UT NE A18

- fixed a bug in findNextVolume
- added recursive function to add offset information
- fixed and compressed attributes are now visible too
This commit is contained in:
Nikolaj Schlej 2016-01-28 00:21:51 +01:00
parent b4ff22815e
commit 61a1e98403
4 changed files with 71 additions and 39 deletions

View File

@ -17,7 +17,7 @@
UEFITool::UEFITool(QWidget *parent) : UEFITool::UEFITool(QWidget *parent) :
QMainWindow(parent), QMainWindow(parent),
ui(new Ui::UEFITool), ui(new Ui::UEFITool),
version(tr("0.30.0_alpha17")) version(tr("0.30.0_alpha18"))
{ {
clipboard = QApplication::clipboard(); clipboard = QApplication::clipboard();

View File

@ -10,7 +10,6 @@ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
*/ */
#include "ffsbuilder.h" #include "ffsbuilder.h"
FfsBuilder::FfsBuilder(const TreeModel* treeModel, QObject *parent) FfsBuilder::FfsBuilder(const TreeModel* treeModel, QObject *parent)
@ -70,7 +69,7 @@ STATUS FfsBuilder::buildCapsule(const QModelIndex & index, QByteArray & capsule)
// Right now there is only one capsule image element supported // Right now there is only one capsule image element supported
if (model->rowCount(index) != 1) { if (model->rowCount(index) != 1) {
msg(tr("buildCapsule: building of capsules with %1 elements are not supported, original item data used").arg(model->rowCount(index)), index); msg(tr("buildCapsule: building of capsules with %1 elements are not supported, original item data is used").arg(model->rowCount(index)), index);
// Use original item data // Use original item data
capsule = model->header(index).append(model->body(index)); capsule = model->header(index).append(model->body(index));
return ERR_SUCCESS; return ERR_SUCCESS;
@ -90,14 +89,14 @@ STATUS FfsBuilder::buildCapsule(const QModelIndex & index, QByteArray & capsule)
// Check build result // Check build result
if (result) { if (result) {
msg(tr("buildCapsule: building of \"%1\" failed with error \"%2\", original item data used").arg(model->name(imageIndex)).arg(errorCodeToQString(result)), imageIndex); msg(tr("buildCapsule: building of \"%1\" failed with error \"%2\", original item data is used").arg(model->name(imageIndex)).arg(errorCodeToQString(result)), imageIndex);
capsule.append(model->header(imageIndex)).append(model->body(imageIndex)); capsule.append(model->header(imageIndex)).append(model->body(imageIndex));
} }
else else
capsule.append(imageData); capsule.append(imageData);
} }
else { else {
msg(tr("buildCapsule: unexpected child item of type \"%1\" can't be processed, original item data used").arg(itemTypeToQString(model->type(imageIndex))), imageIndex); msg(tr("buildCapsule: unexpected child item of type \"%1\" can't be processed, original item data is used").arg(itemTypeToQString(model->type(imageIndex))), imageIndex);
capsule.append(model->header(imageIndex)).append(model->body(imageIndex)); capsule.append(model->header(imageIndex)).append(model->body(imageIndex));
} }
@ -129,11 +128,10 @@ STATUS FfsBuilder::buildCapsule(const QModelIndex & index, QByteArray & capsule)
STATUS FfsBuilder::buildIntelImage(const QModelIndex & index, QByteArray & intelImage) STATUS FfsBuilder::buildIntelImage(const QModelIndex & index, QByteArray & intelImage)
{ {
// Sanity check
if (!index.isValid()) if (!index.isValid())
return ERR_SUCCESS; return ERR_SUCCESS;
// No action // No action
if (model->action(index) == Actions::NoAction) { if (model->action(index) == Actions::NoAction) {
intelImage = model->header(index).append(model->body(index)); intelImage = model->header(index).append(model->body(index));
@ -146,7 +144,7 @@ STATUS FfsBuilder::buildIntelImage(const QModelIndex & index, QByteArray & intel
// First child will always be descriptor for this type of image, and it's read only // First child will always be descriptor for this type of image, and it's read only
QByteArray descriptor = model->header(index.child(0, 0)).append(model->body(index.child(0, 0))); QByteArray descriptor = model->header(index.child(0, 0)).append(model->body(index.child(0, 0)));
// Other regions can be in different order, GbE, PDR and EC my be skipped // Other regions can be in different order, GbE, PDR and EC may be skipped
QByteArray gbe; QByteArray gbe;
QByteArray me; QByteArray me;
QByteArray bios; QByteArray bios;
@ -178,9 +176,10 @@ STATUS FfsBuilder::buildIntelImage(const QModelIndex & index, QByteArray & intel
if (!gbe.isEmpty()) { if (!gbe.isEmpty()) {
msg(tr("buildIntelImage: more than one GbE region found during image rebuild, the latest one is used"), index); msg(tr("buildIntelImage: more than one GbE region found during image rebuild, the latest one is used"), index);
} }
result = buildGbeRegion(currentRegion, gbe); result = buildGbeRegion(currentRegion, gbe);
if (result) { if (result) {
msg(tr("buildIntelImage: building of GbE region failed with error \"%1\", original item data used").arg(errorCodeToQString(result)), currentRegion); msg(tr("buildIntelImage: building of GbE region failed with error \"%1\", original item data is used").arg(errorCodeToQString(result)), currentRegion);
gbe = model->header(currentRegion).append(model->body(currentRegion)); gbe = model->header(currentRegion).append(model->body(currentRegion));
} }
break; break;
@ -188,9 +187,10 @@ STATUS FfsBuilder::buildIntelImage(const QModelIndex & index, QByteArray & intel
if (!me.isEmpty()) { if (!me.isEmpty()) {
msg(tr("buildIntelImage: more than one ME region found during image rebuild, the latest one is used"), index); msg(tr("buildIntelImage: more than one ME region found during image rebuild, the latest one is used"), index);
} }
result = buildMeRegion(currentRegion, me); result = buildMeRegion(currentRegion, me);
if (result) { if (result) {
msg(tr("buildIntelImage: building of ME region failed with error \"%1\", original item data used").arg(errorCodeToQString(result)), currentRegion); msg(tr("buildIntelImage: building of ME region failed with error \"%1\", original item data is used").arg(errorCodeToQString(result)), currentRegion);
me = model->header(currentRegion).append(model->body(currentRegion)); me = model->header(currentRegion).append(model->body(currentRegion));
} }
break; break;
@ -198,9 +198,10 @@ STATUS FfsBuilder::buildIntelImage(const QModelIndex & index, QByteArray & intel
if (!bios.isEmpty()) { if (!bios.isEmpty()) {
msg(tr("buildIntelImage: more than one BIOS region found during image rebuild, the latest one is used"), index); msg(tr("buildIntelImage: more than one BIOS region found during image rebuild, the latest one is used"), index);
} }
result = buildRawArea(currentRegion, bios); result = buildRawArea(currentRegion, bios);
if (result) { if (result) {
msg(tr("buildIntelImage: building of BIOS region failed with error \"%1\", original item data used").arg(errorCodeToQString(result)), currentRegion); msg(tr("buildIntelImage: building of BIOS region failed with error \"%1\", original item data is used").arg(errorCodeToQString(result)), currentRegion);
bios = model->header(currentRegion).append(model->body(currentRegion)); bios = model->header(currentRegion).append(model->body(currentRegion));
} }
break; break;
@ -208,9 +209,10 @@ STATUS FfsBuilder::buildIntelImage(const QModelIndex & index, QByteArray & intel
if (!pdr.isEmpty()) { if (!pdr.isEmpty()) {
msg(tr("buildIntelImage: more than one PDR region found during image rebuild, the latest one is used"), index); msg(tr("buildIntelImage: more than one PDR region found during image rebuild, the latest one is used"), index);
} }
result = buildPdrRegion(currentRegion, pdr); result = buildPdrRegion(currentRegion, pdr);
if (result) { if (result) {
msg(tr("buildIntelImage: building of PDR region failed with error \"%1\", original item data used").arg(errorCodeToQString(result)), currentRegion); msg(tr("buildIntelImage: building of PDR region failed with error \"%1\", original item data is used").arg(errorCodeToQString(result)), currentRegion);
pdr = model->header(currentRegion).append(model->body(currentRegion)); pdr = model->header(currentRegion).append(model->body(currentRegion));
} }
break; break;
@ -218,9 +220,10 @@ STATUS FfsBuilder::buildIntelImage(const QModelIndex & index, QByteArray & intel
if (!ec.isEmpty()) { if (!ec.isEmpty()) {
msg(tr("buildIntelImage: more than one EC region found during image rebuild, the latest one is used"), index); msg(tr("buildIntelImage: more than one EC region found during image rebuild, the latest one is used"), index);
} }
result = buildEcRegion(currentRegion, ec); result = buildEcRegion(currentRegion, ec);
if (result) { if (result) {
msg(tr("buildIntelImage: building of EC region failed with error \"%1\", original item data used").arg(errorCodeToQString(result)), currentRegion); msg(tr("buildIntelImage: building of EC region failed with error \"%1\", original item data is used").arg(errorCodeToQString(result)), currentRegion);
ec = model->header(currentRegion).append(model->body(currentRegion)); ec = model->header(currentRegion).append(model->body(currentRegion));
} }
break; break;
@ -231,7 +234,6 @@ STATUS FfsBuilder::buildIntelImage(const QModelIndex & index, QByteArray & intel
} }
// Check size of new image, it must be same as old one // Check size of new image, it must be same as old one
UINT32 newSize = intelImage.size(); UINT32 newSize = intelImage.size();
UINT32 oldSize = model->body(index).size(); UINT32 oldSize = model->body(index).size();
@ -307,13 +309,13 @@ STATUS FfsBuilder::buildRawArea(const QModelIndex & index, QByteArray & rawArea,
result = buildPadding(currentChild, currentData); result = buildPadding(currentChild, currentData);
} }
else { else {
msg(tr("buildRawArea: unexpected child item of type \"%1\" can't be processed, original item data used").arg(itemTypeToQString(model->type(currentChild))), currentChild); msg(tr("buildRawArea: unexpected child item of type \"%1\" can't be processed, original item data is used").arg(itemTypeToQString(model->type(currentChild))), currentChild);
result = ERR_SUCCESS; result = ERR_SUCCESS;
currentData = model->header(currentChild).append(model->body(currentChild)); currentData = model->header(currentChild).append(model->body(currentChild));
} }
// Check build result // Check build result
if (result) { if (result) {
msg(tr("buildRawArea: building of \"%1\" failed with error \"%2\", original item data used").arg(model->name(currentChild)).arg(errorCodeToQString(result)), currentChild); msg(tr("buildRawArea: building of \"%1\" failed with error \"%2\", original item data is used").arg(model->name(currentChild)).arg(errorCodeToQString(result)), currentChild);
currentData = model->header(currentChild).append(model->body(currentChild)); currentData = model->header(currentChild).append(model->body(currentChild));
} }
// Append current data // Append current data
@ -363,7 +365,7 @@ STATUS FfsBuilder::buildPadding(const QModelIndex & index, QByteArray & padding)
else if (model->action(index) == Actions::Erase) { else if (model->action(index) == Actions::Erase) {
padding = model->header(index).append(model->body(index)); padding = model->header(index).append(model->body(index));
if(erase(index, padding)) if(erase(index, padding))
msg(tr("buildPadding: erase failed, original item data used"), index); msg(tr("buildPadding: erase failed, original item data is used"), index);
return ERR_SUCCESS; return ERR_SUCCESS;
} }
@ -387,7 +389,7 @@ STATUS FfsBuilder::buildNonUefiData(const QModelIndex & index, QByteArray & data
else if (model->action(index) == Actions::Erase) { else if (model->action(index) == Actions::Erase) {
data = model->header(index).append(model->body(index)); data = model->header(index).append(model->body(index));
if (erase(index, data)) if (erase(index, data))
msg(tr("buildNonUefiData: erase failed, original item data used"), index); msg(tr("buildNonUefiData: erase failed, original item data is used"), index);
return ERR_SUCCESS; return ERR_SUCCESS;
} }

View File

@ -222,6 +222,9 @@ STATUS FfsParser::parseImageFile(const QByteArray & buffer, const QModelIndex &
if (result) if (result)
return result; return result;
// Add offsets
addOffsetsRecursive(index);
// Check if the last VTF is found // Check if the last VTF is found
if (!lastVtf.isValid()) { if (!lastVtf.isValid()) {
msg(tr("parseImageFile: not a single Volume Top File is found, the image may be corrupted"), biosIndex); msg(tr("parseImageFile: not a single Volume Top File is found, the image may be corrupted"), biosIndex);
@ -627,6 +630,9 @@ STATUS FfsParser::parseIntelImage(const QByteArray & intelImage, const UINT32 pa
model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index); model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index);
} }
// Add offsets
addOffsetsRecursive(index);
// Check if the last VTF is found // Check if the last VTF is found
if (!lastVtf.isValid()) { if (!lastVtf.isValid()) {
msg(tr("parseIntelImage: not a single Volume Top File is found, the image may be corrupted"), index); msg(tr("parseIntelImage: not a single Volume Top File is found, the image may be corrupted"), index);
@ -831,14 +837,14 @@ STATUS FfsParser::parseRawArea(const QByteArray & data, const QModelIndex & inde
// Get parsing data // Get parsing data
PARSING_DATA pdata = parsingDataFromQModelIndex(index); PARSING_DATA pdata = parsingDataFromQModelIndex(index);
UINT32 offset = pdata.offset;
UINT32 headerSize = model->header(index).size(); UINT32 headerSize = model->header(index).size();
UINT32 offset = pdata.offset + headerSize;
// Search for first volume // Search for first volume
STATUS result; STATUS result;
UINT32 prevVolumeOffset; UINT32 prevVolumeOffset;
result = findNextVolume(index, data, 0, prevVolumeOffset); result = findNextVolume(index, data, offset, 0, prevVolumeOffset);
if (result) if (result)
return result; return result;
@ -853,7 +859,7 @@ STATUS FfsParser::parseRawArea(const QByteArray & data, const QModelIndex & inde
.hexarg(padding.size()).arg(padding.size()); .hexarg(padding.size()).arg(padding.size());
// Construct parsing data // Construct parsing data
pdata.offset = offset + headerSize; pdata.offset = offset;
// Add tree item // Add tree item
model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index); model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index);
@ -877,7 +883,7 @@ STATUS FfsParser::parseRawArea(const QByteArray & data, const QModelIndex & inde
.hexarg(padding.size()).arg(padding.size()); .hexarg(padding.size()).arg(padding.size());
// Construct parsing data // Construct parsing data
pdata.offset = offset + headerSize + paddingOffset; pdata.offset = offset + paddingOffset;
// Add tree item // Add tree item
model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index); model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index);
@ -909,7 +915,7 @@ STATUS FfsParser::parseRawArea(const QByteArray & data, const QModelIndex & inde
.hexarg(padding.size()).arg(padding.size()); .hexarg(padding.size()).arg(padding.size());
// Construct parsing data // Construct parsing data
pdata.offset = offset + headerSize + volumeOffset; pdata.offset = offset + volumeOffset;
// Add tree item // Add tree item
QModelIndex paddingIndex = model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index); QModelIndex paddingIndex = model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index);
@ -938,7 +944,7 @@ STATUS FfsParser::parseRawArea(const QByteArray & data, const QModelIndex & inde
// Go to next volume // Go to next volume
prevVolumeOffset = volumeOffset; prevVolumeOffset = volumeOffset;
prevVolumeSize = volumeSize; prevVolumeSize = volumeSize;
result = findNextVolume(index, data, volumeOffset + prevVolumeSize, volumeOffset); result = findNextVolume(index, data, offset, volumeOffset + prevVolumeSize, volumeOffset);
} }
// Padding at the end of BIOS space // Padding at the end of BIOS space
@ -1184,25 +1190,25 @@ STATUS FfsParser::parseVolumeHeader(const QByteArray & volume, const UINT32 pare
return ERR_SUCCESS; return ERR_SUCCESS;
} }
STATUS FfsParser::findNextVolume(const QModelIndex index, const QByteArray & bios, UINT32 volumeOffset, UINT32 & nextVolumeOffset) STATUS FfsParser::findNextVolume(const QModelIndex index, const QByteArray & bios, const UINT32 parentOffset, const UINT32 volumeOffset, UINT32 & nextVolumeOffset)
{ {
int nextIndex = bios.indexOf(EFI_FV_SIGNATURE, volumeOffset); int nextIndex = bios.indexOf(EFI_FV_SIGNATURE, volumeOffset);
if (nextIndex < EFI_FV_SIGNATURE_OFFSET) if (nextIndex < EFI_FV_SIGNATURE_OFFSET)
return ERR_VOLUMES_NOT_FOUND; return ERR_VOLUMES_NOT_FOUND;
// Check volume header to be sane // Check volume header to be sane
for (; nextIndex > 0; nextIndex = bios.indexOf(EFI_FV_SIGNATURE, volumeOffset + nextIndex + 1)) { for (; nextIndex > 0; nextIndex = bios.indexOf(EFI_FV_SIGNATURE, nextIndex + 1)) {
const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(bios.constData() + nextIndex - EFI_FV_SIGNATURE_OFFSET); const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(bios.constData() + nextIndex - EFI_FV_SIGNATURE_OFFSET);
if (volumeHeader->FvLength < sizeof(EFI_FIRMWARE_VOLUME_HEADER) + 2 * sizeof(EFI_FV_BLOCK_MAP_ENTRY) || volumeHeader->FvLength >= 0xFFFFFFFFUL) { if (volumeHeader->FvLength < sizeof(EFI_FIRMWARE_VOLUME_HEADER) + 2 * sizeof(EFI_FV_BLOCK_MAP_ENTRY) || volumeHeader->FvLength >= 0xFFFFFFFFUL) {
msg(tr("findNextVolume: volume candidate skipped, has invalid FvLength %1h").hexarg2(volumeHeader->FvLength, 16), index); msg(tr("findNextVolume: volume candidate at offset %1h skipped, has invalid FvLength %2h").hexarg(parentOffset + (nextIndex - EFI_FV_SIGNATURE_OFFSET)).hexarg2(volumeHeader->FvLength, 16), index);
continue; continue;
} }
if (volumeHeader->Reserved != 0xFF && volumeHeader->Reserved != 0x00) { if (volumeHeader->Reserved != 0xFF && volumeHeader->Reserved != 0x00) {
msg(tr("findNextVolume: volume candidate skipped, has invalid Reserved byte value %1").hexarg2(volumeHeader->Reserved, 2), index); msg(tr("findNextVolume: volume candidate at offset %1h skipped, has invalid Reserved byte value %2").hexarg(parentOffset + (nextIndex - EFI_FV_SIGNATURE_OFFSET)).hexarg2(volumeHeader->Reserved, 2), index);
continue; continue;
} }
if (volumeHeader->Revision != 1 && volumeHeader->Revision != 2) { if (volumeHeader->Revision != 1 && volumeHeader->Revision != 2) {
msg(tr("findNextVolume: volume candidate skipped, has invalid Revision byte value %1").hexarg2(volumeHeader->Revision, 2), index); msg(tr("findNextVolume: volume candidate at offset %1h skipped, has invalid Revision byte value %2").hexarg(parentOffset + (nextIndex - EFI_FV_SIGNATURE_OFFSET)).hexarg2(volumeHeader->Revision, 2), index);
continue; continue;
} }
// All checks passed, volume found // All checks passed, volume found
@ -2729,9 +2735,6 @@ STATUS FfsParser::addMemoryAddressesRecursive(const QModelIndex & index, const U
// Get parsing data for the current item // Get parsing data for the current item
PARSING_DATA pdata = parsingDataFromQModelIndex(index); PARSING_DATA pdata = parsingDataFromQModelIndex(index);
// Show offset
model->addInfo(index, tr("Offset: %1h\n").hexarg(pdata.offset), false);
// Check address sanity // Check address sanity
if ((const UINT64)diff + pdata.offset <= 0xFFFFFFFFUL) { if ((const UINT64)diff + pdata.offset <= 0xFFFFFFFFUL) {
// Update info // Update info
@ -2765,10 +2768,6 @@ STATUS FfsParser::addMemoryAddressesRecursive(const QModelIndex & index, const U
} }
} }
//TODO: debugging, don't shows FIT file fixed attribute correctly
model->addInfo(index, tr("\nCompressed: %1").arg(model->compressed(index) ? tr("Yes") : tr("No")));
model->addInfo(index, tr("\nFixed: %1").arg(model->fixed(index) ? tr("Yes") : tr("No")));
// Process child items // Process child items
for (int i = 0; i < model->rowCount(index); i++) { for (int i = 0; i < model->rowCount(index); i++) {
addMemoryAddressesRecursive(index.child(i, 0), diff); addMemoryAddressesRecursive(index.child(i, 0), diff);
@ -2776,3 +2775,33 @@ STATUS FfsParser::addMemoryAddressesRecursive(const QModelIndex & index, const U
return ERR_SUCCESS; return ERR_SUCCESS;
} }
STATUS FfsParser::addOffsetsRecursive(const QModelIndex & index)
{
// Sanity check
if (!index.isValid())
return ERR_INVALID_PARAMETER;
// Get parsing data for the current item
PARSING_DATA pdata = parsingDataFromQModelIndex(index);
// Add current offset if the element is not compressed
if (!model->compressed(index)) {
model->addInfo(index, tr("Offset: %1h\n").hexarg(pdata.offset), false);
}
// Or it's compressed, but it's parent isn't
else if (index.parent().isValid() && !model->compressed(index.parent())) {
model->addInfo(index, tr("Offset: %1h\n").hexarg(pdata.offset), false);
}
//TODO: show FIT file fixed attribute correctly
model->addInfo(index, tr("\nCompressed: %1").arg(model->compressed(index) ? tr("Yes") : tr("No")));
model->addInfo(index, tr("\nFixed: %1").arg(model->fixed(index) ? tr("Yes") : tr("No")));
// Process child items
for (int i = 0; i < model->rowCount(index); i++) {
addOffsetsRecursive(index.child(i, 0));
}
return ERR_SUCCESS;
}

View File

@ -91,12 +91,13 @@ private:
UINT8 getPaddingType(const QByteArray & padding); UINT8 getPaddingType(const QByteArray & padding);
STATUS parseAprioriRawSection(const QByteArray & body, QString & parsed); STATUS parseAprioriRawSection(const QByteArray & body, QString & parsed);
STATUS findNextVolume(const QModelIndex index, const QByteArray & bios, const UINT32 volumeOffset, UINT32 & nextVolumeOffset); STATUS findNextVolume(const QModelIndex index, const QByteArray & bios, const UINT32 parentOffset, const UINT32 volumeOffset, UINT32 & nextVolumeOffset);
STATUS getVolumeSize(const QByteArray & bios, const UINT32 volumeOffset, UINT32 & volumeSize, UINT32 & bmVolumeSize); STATUS getVolumeSize(const QByteArray & bios, const UINT32 volumeOffset, UINT32 & volumeSize, UINT32 & bmVolumeSize);
UINT32 getFileSize(const QByteArray & volume, const UINT32 fileOffset, const UINT8 ffsVersion); UINT32 getFileSize(const QByteArray & volume, const UINT32 fileOffset, const UINT8 ffsVersion);
UINT32 getSectionSize(const QByteArray & file, const UINT32 sectionOffset, const UINT8 ffsVersion); UINT32 getSectionSize(const QByteArray & file, const UINT32 sectionOffset, const UINT8 ffsVersion);
STATUS performSecondPass(const QModelIndex & index); STATUS performSecondPass(const QModelIndex & index);
STATUS addOffsetsRecursive(const QModelIndex & index);
STATUS addMemoryAddressesRecursive(const QModelIndex & index, const UINT32 diff); STATUS addMemoryAddressesRecursive(const QModelIndex & index, const UINT32 diff);
// Internal operations // Internal operations