1) Added special subspecifier "h" for specifier "X" (hex values print), added corresponding menu option and application setting to UEFITool.

2) Fixed QHexView misalignment in Windows and macOS.
3) Added some more analysis to raw files and sections: raw files can (or can not) contain sections, raw sections can contain NVAR storage or PE/TE.
4) Improved CPU base address detection and propagation.
5) Improved FIT recognition.
7) ME region not displayed if it is in unknown format, fixed this because we still want to operate with it.
8) Small changes to Flash Descriptor parsing to get more cases for valid "Intel image". To get rid of cases when "Intel image" is already in tree but with parse error because of which "UEFI image" appears.
9) Added parsing of individual UEFI-files (these can be trimmed from UEFI-volume), displaying it as "UEFI volume part".
10) Added possibility to view/save contents of elements "Free volume space", "Free space" and such, because these can be non-empty.
11) Added info about blocks number and block size (with preliminary and stupid validity check) to volume info.
12) Added storage in settings of the following paths: open image file, save image file, open GUIDs file.
13) Added last opened files list.
14) Added permanent opened file name string to the end of the status bar.
15) Added opened file changes tracking: if the file was modified in other program while it is opened in UEFITool, there are 3 ways to act: a) ignore changes (but mark file path displayed in the status bar with italic font); b) ask user to reopen or ignore (if ignore, mark as in a); c) auto reopen changed file in UEFITool. If changes were in some way ignored, file path displayed in the right of the status bar will be marked with italic font and then become clickable: on click request to reopen appears again.
16) Switched to offset/size instead of byte array storing in each tree item.
17) For clarity - added icons to key tree items (compressed and with contents, contents now must be in root item only).
18) For usability - added expanding tree on open image (to depth level 1) and by menu command (expand all).
This commit is contained in:
Nikolaj Schlej 2025-04-14 19:19:45 +07:00 committed by bogdanov
parent 7cea8ee512
commit 936b09dbf4
27 changed files with 3699 additions and 859 deletions

View file

@ -92,7 +92,7 @@ USTATUS UEFIFind::findFileRecursive(const UModelIndex index, const UString & hex
// For patterns that cross header|body boundary, skip patterns entirely located in body, since
// children search above has already found them.
if (hasChildren && mode == SEARCH_MODE_ALL && offset >= model->header(index).size()) {
if (hasChildren && mode == SEARCH_MODE_ALL && offset >= model->headerSize(index)) {
offset = -1;
}

View file

@ -788,6 +788,7 @@ void QHexView::drawDocument(QTextCursor& c) const {
else if(m_options.linebackground.isValid() && !(line % 2))
bf.setBackground(m_options.linebackground);
bf.setLineHeight(this->lineHeight(), QTextBlockFormat::FixedHeight);
c.setBlockFormat(bf);
c.insertBlock({});
if(m_hexdocument->isEmpty())

View file

@ -12,6 +12,7 @@
*/
#include "ffsfinder.h"
#include "../common/utility.h"
#if QT_VERSION_MAJOR >= 6
#include <QRegularExpression>
@ -36,7 +37,8 @@ USTATUS FfsFinder::findHexPattern(const UModelIndex & index, const UByteArray &
return U_INVALID_PARAMETER;
// Check for "all substrings" pattern
if (hexPattern.count('.') == hexPattern.length())
auto c = checkSingle(hexPattern);
if (c == '.')
return U_SUCCESS;
USTATUS ret = U_ITEM_NOT_FOUND;
@ -78,7 +80,7 @@ USTATUS FfsFinder::findHexPattern(const UModelIndex & index, const UByteArray &
if (offset % 2 == 0) {
// For patterns that cross header|body boundary, skip patterns entirely located in body, since
// children search above has already found them.
if (!(hasChildren && mode == SEARCH_MODE_ALL && offset/2 >= model->header(index).size())) {
if (!(hasChildren && mode == SEARCH_MODE_ALL && offset/2 >= model->headerSize(index))) {
UModelIndex parentFileIndex = model->findParentOfType(index, Types::File);
UString name = model->name(index);
if (model->parent(index) == parentFileIndex) {
@ -165,7 +167,8 @@ USTATUS FfsFinder::findGuidPattern(const UModelIndex & index, const UByteArray &
hexPattern.append(list.at(3)).append(list.at(4));
// Check for "all substrings" pattern
if (hexPattern.count('.') == hexPattern.length())
auto c = checkSingle(hexPattern);
if (c == '.')
return U_SUCCESS;
#if QT_VERSION_MAJOR >= 6

View file

@ -23,12 +23,22 @@ UEFITool::UEFITool(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::UEFITool),
version(tr(PROGRAM_VERSION)),
markingEnabled(true)
changedFileFlag(false),
markingEnabled(true),
cStyleHexEnabled(true)
{
clipboard = QApplication::clipboard();
// Create UI
ui->setupUi(this);
openedFileLabel.setToolButtonStyle(Qt::ToolButtonTextOnly);
openedFileLabel.setStyleSheet("QToolButton { border: none; background-color: transparent; }");
openedFileLabel.setFocusPolicy(Qt::NoFocus);
ui->statusBar->addPermanentWidget(&openedFileLabel);
QFont font = ui->statusBar->font();
font.setBold(true);
openedFileLabel.setFont(font);
connect(&openedFileLabel, SIGNAL(clicked()), this, SLOT(fileChangedResume()));
searchDialog = new SearchDialog(this);
hexViewDialog = new HexViewDialog(this);
goToAddressDialog = new GoToAddressDialog(this);
@ -73,7 +83,10 @@ markingEnabled(true)
connect(ui->actionExportDiscoveredGuids, SIGNAL(triggered()), this, SLOT(exportDiscoveredGuids()));
connect(ui->actionGenerateReport, SIGNAL(triggered()), this, SLOT(generateReport()));
connect(ui->actionToggleBootGuardMarking, SIGNAL(toggled(bool)), this, SLOT(toggleBootGuardMarking(bool)));
connect(ui->actionToggleCStyleHexValues, SIGNAL(toggled(bool)), this, SLOT(toggleCStyleHexValues(bool)));
connect(ui->actionExpandAll, SIGNAL(triggered()), this, SLOT(expandTree()));
connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(writeSettings()));
connect(&watcher, SIGNAL(fileChanged(QString)), this, SLOT(fileChanged(QString)));
// Enable Drag-and-Drop actions
setAcceptDrops(true);
@ -92,6 +105,24 @@ markingEnabled(true)
// Read stored settings
readSettings();
// Update recent files list in menu
updateRecentFilesMenu();
// Create a QActionGroup
QActionGroup* actionGroup = new QActionGroup(this);
actionGroup->setExclusive(true); // Ensure only one action is checked at a time
// Add actions from the menu to the QActionGroup
ui->actionTrackAndIgnore->setData(TRACK_IGNORE);
ui->actionTrackAndIgnore->setChecked(ui->actionTrackAndIgnore->data() == fileTrackingState);
actionGroup->addAction(ui->actionTrackAndIgnore);
ui->actionTrackAndAsk->setData(TRACK_ASK);
ui->actionTrackAndAsk->setChecked(ui->actionTrackAndAsk->data() == fileTrackingState);
actionGroup->addAction(ui->actionTrackAndAsk);
ui->actionTrackAndReopen->setData(TRACK_REOPEN);
ui->actionTrackAndReopen->setChecked(ui->actionTrackAndReopen->data() == fileTrackingState);
actionGroup->addAction(ui->actionTrackAndReopen);
connect(actionGroup, SIGNAL(triggered(QAction*)), this, SLOT(onTrackingAction(QAction*)));
}
UEFITool::~UEFITool()
@ -125,6 +156,9 @@ void UEFITool::init()
// Set window title
setWindowTitle(tr("UEFITool %1").arg(version));
// Some font hint
setFont(ui->infoEdit->font());
// Disable menus
ui->actionSearch->setEnabled(false);
ui->actionGoToBase->setEnabled(false);
@ -139,6 +173,7 @@ void UEFITool::init()
ui->menuStoreActions->setEnabled(false);
ui->menuEntryActions->setEnabled(false);
ui->menuMessageActions->setEnabled(false);
ui->actionExpandAll->setEnabled(false);
// Create new model ...
delete model;
@ -152,11 +187,17 @@ void UEFITool::init()
model->setMarkingEnabled(markingEnabled);
ui->actionToggleBootGuardMarking->setChecked(markingEnabled);
// Set proper C-Style hex values view state
model->setCStyleHexEnabled(cStyleHexEnabled);
ui->actionToggleCStyleHexValues->setChecked(cStyleHexEnabled);
// Connect signals to slots
connect(ui->structureTreeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
this, SLOT(populateUi(const QModelIndex &)));
connect(ui->structureTreeView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
this, SLOT(populateUi(const QItemSelection &)));
connect(ui->structureTreeView, SIGNAL(expanded(const QModelIndex &)), this, SLOT(setExpandAll()));
connect(ui->structureTreeView, SIGNAL(collapsed(const QModelIndex&)), this, SLOT(setExpandAll()));
connect(ui->parserMessagesListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(scrollTreeView(QListWidgetItem*)));
connect(ui->parserMessagesListWidget, SIGNAL(itemEntered(QListWidgetItem*)), this, SLOT(enableMessagesCopyActions(QListWidgetItem*)));
connect(ui->finderMessagesListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(scrollTreeView(QListWidgetItem*)));
@ -211,6 +252,38 @@ void UEFITool::updateUiForNewColorScheme(Qt::ColorScheme scheme)
}
#endif
void UEFITool::updateRecentFilesMenu(const QString& fileName)
{
// Update list
if (!fileName.isEmpty()) {
recentFiles.removeAll(fileName);
recentFiles.prepend(fileName);
while (recentFiles.size() > 21) {
recentFiles.removeLast();
}
}
// Delete old actions
for (QAction* action : recentFileActions) {
ui->menuFile->removeAction(action);
delete action;
}
recentFileActions.clear();
if (!recentFiles.isEmpty()) {
// Insert new actions before "Quit"
for (const QString& path : recentFiles) {
QAction* action = new QAction(QDir::toNativeSeparators(path), this);
connect(action, SIGNAL(triggered()), this, SLOT(openRecentImageFile()));
action->setData(path);
ui->menuFile->insertAction(ui->actionQuit, action);
recentFileActions.append(action);
}
// Finally, insert a separator after the list and before "Quit"
recentFileActions.append(ui->menuFile->insertSeparator(ui->actionQuit));
}
}
void UEFITool::populateUi(const QItemSelection &selected)
{
if (selected.isEmpty()) {
@ -235,7 +308,10 @@ void UEFITool::populateUi(const QModelIndex &current)
// Enable menus
ui->menuCapsuleActions->setEnabled(type == Types::Capsule);
ui->menuImageActions->setEnabled(type == Types::Image);
ui->menuImageActions->setEnabled(type == Types::Image
|| (type == Types::Volume && model->hasEmptyHeader(current))
|| type == Types::FreeSpace
);
ui->menuRegionActions->setEnabled(type == Types::Region);
ui->menuPaddingActions->setEnabled(type == Types::Padding);
ui->menuVolumeActions->setEnabled(type == Types::Volume);
@ -300,6 +376,8 @@ void UEFITool::populateUi(const QModelIndex &current)
//ui->actionReplaceBody->setEnabled(type == Types::Volume || type == Types::File || type == Types::Section);
ui->menuMessageActions->setEnabled(false);
setExpandAll();
}
void UEFITool::search()
@ -310,7 +388,7 @@ void UEFITool::search()
int index = searchDialog->ui->tabWidget->currentIndex();
if (index == 0) { // Hex pattern
searchDialog->ui->hexEdit->setFocus();
QByteArray pattern = searchDialog->ui->hexEdit->text().toLatin1().replace(" ", "");
UByteArray pattern = searchDialog->ui->hexEdit->text().toLatin1().replace(" ", "");
if (pattern.isEmpty())
return;
UINT8 mode;
@ -326,7 +404,7 @@ void UEFITool::search()
else if (index == 1) { // GUID
searchDialog->ui->guidEdit->setFocus();
searchDialog->ui->guidEdit->setCursorPosition(0);
QByteArray pattern = searchDialog->ui->guidEdit->text().toLatin1();
UByteArray pattern = searchDialog->ui->guidEdit->text().toLatin1();
if (pattern.isEmpty())
return;
UINT8 mode;
@ -508,7 +586,7 @@ void UEFITool::extract(const UINT8 mode)
if (!index.isValid())
return;
QByteArray extracted;
UByteArray extracted;
QString name;
USTATUS result = ffsOps->extract(index, name, extracted, mode);
if (result) {
@ -516,7 +594,7 @@ void UEFITool::extract(const UINT8 mode)
return;
}
name = QDir::toNativeSeparators(currentDir + QDir::separator() + name);
name = QDir::toNativeSeparators(extractDir + QDir::separator() + name);
//ui->statusBar->showMessage(name);
@ -566,6 +644,8 @@ void UEFITool::extract(const UINT8 mode)
outputFile.resize(0);
outputFile.write(extracted);
outputFile.close();
extractDir = QFileInfo(path).absolutePath();
}
void UEFITool::rebuild()
@ -617,20 +697,185 @@ void UEFITool::saveImageFile()
}
void UEFITool::askReopenImageFile()
{
QMessageBox msgBox(QMessageBox::Question, tr("Confirmation"),
tr("Image file was changed by external program, do you want to reopen it?"),
QMessageBox::Yes | QMessageBox::No);
QCheckBox checkBox(tr("Do not ask again"));
checkBox.setChecked(TRACK_IGNORE == fileTrackingState);
msgBox.setDefaultButton(QMessageBox::Yes);
msgBox.setCheckBox(&checkBox);
int ret = msgBox.exec();
if (checkBox.isChecked()) {
if (ret == QMessageBox::Yes) {
ui->actionTrackAndReopen->setChecked(true);
fileTrackingState = TRACK_REOPEN;
}
else if (ret == QMessageBox::No) {
ui->actionTrackAndIgnore->setChecked(true);
fileTrackingState = TRACK_IGNORE;
}
}
if (ret == QMessageBox::Yes)
reopenImageFile();
}
void UEFITool::onTrackingAction(QAction* action)
{
if (action) {
int newTrackingState = action->data().toInt();
if (newTrackingState != fileTrackingState) {
fileTrackingState = newTrackingState;
}
}
}
void UEFITool::saveTreeState(const QModelIndex& index, QHash<QString, bool>& states)
{
if (!index.isValid())
return;
// Save the expanded state using the item's data as the key
QString key = model->data(index, Qt::DisplayRole).toString();
states[key] = ui->structureTreeView->isExpanded(index);
// Recursively save child states
for (int row = 0; row < model->rowCount(index); row++) {
saveTreeState(model->index(row, 0, index), states);
}
}
void UEFITool::restoreTreeState(const QModelIndex& index, const QHash<QString, bool>& states)
{
if (!index.isValid())
return;
// Restore the expanded state using the item's data as the key
QString key = model->data(index, Qt::DisplayRole).toString();
if (states.contains(key)) {
ui->structureTreeView->setExpanded(index, states[key]);
}
// Recursively restore child states
for (int row = 0; row < model->rowCount(index); row++) {
restoreTreeState(model->index(row, 0, index), states);
}
}
bool UEFITool::isAllExpanded()
{
QAbstractItemModel* model = ui->structureTreeView->model();
if (!model)
return false;
QStack<QModelIndex> stack;
stack.push(QModelIndex());
while (!stack.isEmpty()) {
QModelIndex current = stack.pop();
if (current.isValid() && ui->structureTreeView->isExpanded(current) == false) {
return false;
}
int rowCount = model->rowCount(current);
for (int i = 0; i < rowCount; ++i) {
QModelIndex child = model->index(i, 0, current);
if (child.isValid()) {
stack.push(child);
}
}
}
return true;
}
void UEFITool::setExpandAll()
{
ui->actionExpandAll->setEnabled(!isAllExpanded());
}
void UEFITool::expandTree()
{
ui->structureTreeView->expandAll();
}
void UEFITool::fileChangedResume()
{
if (changedFileFlag) {
askReopenImageFile();
}
}
void UEFITool::fileChanged(const QString& path)
{
setChangedFileFlag(true);
switch (fileTrackingState) {
case TRACK_REOPEN :
reopenImageFile();
case TRACK_IGNORE :
return;
}
askReopenImageFile();
}
void UEFITool::setChangedFileFlag(const bool flag)
{
QFont font(openedFileLabel.font());
font.setItalic(flag);
openedFileLabel.setFont(font);
changedFileFlag = flag;
}
void UEFITool::openImageFile()
{
QString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file"), currentDir, tr("BIOS image files (*.rom *.bin *.cap *scap *.bio *.fd *.wph *.dec);;All files (*)"));
QString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file"), openImageDir, tr("BIOS image files (*.rom *.bin *.cap *scap *.bio *.fd *.wph *.dec);;All files (*)"));
openImageFile(path);
}
void UEFITool::openImageFileInNewWindow()
{
QString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file in new window"), currentDir, tr("BIOS image files (*.rom *.bin *.cap *scap *.bio *.fd *.wph *.dec);;All files (*)"));
QString path = QFileDialog::getOpenFileName(this, tr("Open BIOS image file in new window"), openImageDir, tr("BIOS image files (*.rom *.bin *.cap *scap *.bio *.fd *.wph *.dec);;All files (*)"));
if (path.trimmed().isEmpty())
return;
QProcess::startDetached(currentProgramPath, QStringList(path));
}
void UEFITool::openRecentImageFile()
{
QAction* action = qobject_cast<QAction*>(sender());
if (action) {
QString fileName = action->data().toString();
if (!fileName.isEmpty()) {
openImageFile(fileName);
}
}
}
void UEFITool::reopenImageFile()
{
if (!currentPath.isEmpty() && ui->structureTreeView->model()->hasChildren(UModelIndex())) {
QHash<QString, bool> states;
saveTreeState(model->index(0, 0), states);
QList <UModelIndex> selected = ui->structureTreeView->selectionModel()->selectedIndexes();
UINT64 base = selected.isEmpty() ? UEFI_UPPER_INVALID_ADDRESS : model->base(selected.first());
openImageFile(currentPath);
restoreTreeState(model->index(0, 0), states);
if (base < UEFI_UPPER_INVALID_ADDRESS) {
UModelIndex index = model->findByBase(base);
ui->structureTreeView->selectionModel()->select(index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
ui->structureTreeView->scrollTo(index);
}
}
}
void UEFITool::openImageFile(QString path)
{
if (path.trimmed().isEmpty())
@ -645,18 +890,23 @@ void UEFITool::openImageFile(QString path)
QFile inputFile;
inputFile.setFileName(path);
QElapsedTimer timer;
timer.start();
if (!inputFile.open(QFile::ReadOnly)) {
QMessageBox::critical(this, tr("Image parsing failed"), tr("Can't open input file for reading"), QMessageBox::Ok);
return;
}
watcher.removePaths(watcher.files());
setChangedFileFlag(false);
watcher.addPath(path);
QByteArray buffer = inputFile.readAll();
inputFile.close();
init();
setWindowTitle(tr("UEFITool %1 - %2").arg(version).arg(fileInfo.fileName()));
// Parse the image
USTATUS result = ffsParser->parse(buffer);
showParserMessages();
@ -665,7 +915,10 @@ void UEFITool::openImageFile(QString path)
return;
}
else {
ui->statusBar->showMessage(tr("Opened: %1").arg(fileInfo.fileName()));
ui->statusBar->showMessage(QString("Image file %1 ").arg(fileInfo.fileName())
+ (path == currentPath ? tr("reopened") : tr("opened"))
+ QString(" in %1 sec").arg(timer.elapsed() / 1000.0));
openedFileLabel.setText(QDir::toNativeSeparators(fileInfo.absoluteFilePath()));
}
ffsParser->outputInfo();
@ -688,7 +941,7 @@ void UEFITool::openImageFile(QString path)
// Enable goToBase and goToAddress
ui->actionGoToBase->setEnabled(true);
if (ffsParser->getAddressDiff() <= 0xFFFFFFFFUL)
if (ffsParser->getAddressDiff() < UEFI_UPPER_INVALID_ADDRESS)
ui->actionGoToAddress->setEnabled(true);
// Enable generateReport
@ -699,9 +952,15 @@ void UEFITool::openImageFile(QString path)
// Set current directory
currentDir = fileInfo.absolutePath();
openImageDir = currentDir;
// Set current path
currentPath = path;
// Update menu
updateRecentFilesMenu(currentPath);
ui->structureTreeView->expandToDepth(1);
}
void UEFITool::enableMessagesCopyActions(QListWidgetItem* item)
@ -771,6 +1030,12 @@ void UEFITool::toggleBootGuardMarking(bool enabled)
markingEnabled = enabled;
}
void UEFITool::toggleCStyleHexValues(bool enabled)
{
model->setCStyleHexEnabled(enabled);
cStyleHexEnabled = enabled;
}
// Emit double click signal of QListWidget on enter/return key pressed
bool UEFITool::eventFilter(QObject* obj, QEvent* event)
{
@ -898,14 +1163,21 @@ void UEFITool::contextMenuEvent(QContextMenuEvent* event)
return;
}
QMenu* menu = nullptr;
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;
case Types::Capsule: menu = ui->menuCapsuleActions; break;
case Types::Image: menu = ui->menuImageActions; break;
case Types::Region: menu = ui->menuRegionActions; break;
case Types::Padding: menu = ui->menuPaddingActions; break;
case Types::Volume:
if (model->hasEmptyHeader(index))
menu = ui->menuImageActions;
else
menu = ui->menuVolumeActions;
break;
case Types::File: menu = ui->menuFileActions; break;
case Types::Section: menu = ui->menuSectionActions; break;
case Types::VssStore:
case Types::Vss2Store:
case Types::FdcStore:
@ -918,9 +1190,16 @@ void UEFITool::contextMenuEvent(QContextMenuEvent* event)
case Types::CmdbStore:
case Types::FptStore:
case Types::CpdStore:
case Types::BpdtStore: ui->menuStoreActions->exec(event->globalPos()); break;
case Types::FreeSpace: break; // No menu needed for FreeSpace item
default: ui->menuEntryActions->exec(event->globalPos()); break;
case Types::BpdtStore: menu = ui->menuStoreActions; break;
case Types::FreeSpace: menu = ui->menuImageActions; break;
default: menu = ui->menuEntryActions; break;
}
if (menu != nullptr) {
QList<QAction*> actions = menu->actions();
QAction s = QAction(nullptr);
s.setSeparator(true);
QMenu::exec(actions << &s << ui->actionExpandAll, event->globalPos());
}
}
@ -942,11 +1221,20 @@ void UEFITool::readSettings()
ui->structureTreeView->setColumnWidth(3, settings.value("tree/columnWidth3", ui->structureTreeView->columnWidth(3)).toInt());
markingEnabled = settings.value("tree/markingEnabled", true).toBool();
ui->actionToggleBootGuardMarking->setChecked(markingEnabled);
cStyleHexEnabled = settings.value("tree/cStyleHexEnabled", true).toBool();
ui->actionToggleCStyleHexValues->setChecked(cStyleHexEnabled);
openImageDir = settings.value("paths/openImageDir", ".").toString();
openGuidDatabaseDir = settings.value("paths/openGuidDatabaseDir", ".").toString();
extractDir = settings.value("paths/extractDir", ".").toString();
recentFiles = settings.value("paths/recentFiles").toStringList();
fileTrackingState = settings.value("options/fileTracking", TRACK_IGNORE).toInt();
if (fileTrackingState < TRACK_MIN || fileTrackingState > TRACK_MAX)
fileTrackingState = TRACK_IGNORE;
// Set monospace font
QString fontName;
int fontSize;
#if defined Q_OS_MACOS
#if defined Q_OS_OSX
fontName = settings.value("mainWindow/fontName", QString("Menlo")).toString();
fontSize = settings.value("mainWindow/fontSize", 10).toInt();
#elif defined Q_OS_WIN
@ -975,8 +1263,14 @@ void UEFITool::writeSettings()
settings.setValue("tree/columnWidth2", ui->structureTreeView->columnWidth(2));
settings.setValue("tree/columnWidth3", ui->structureTreeView->columnWidth(3));
settings.setValue("tree/markingEnabled", markingEnabled);
settings.setValue("tree/cStyleHexEnabled", cStyleHexEnabled);
settings.setValue("mainWindow/fontName", currentFont.family());
settings.setValue("mainWindow/fontSize", currentFont.pointSize());
settings.setValue("paths/openImageDir", openImageDir);
settings.setValue("paths/openGuidDatabaseDir", openGuidDatabaseDir);
settings.setValue("paths/extractDir", extractDir);
settings.setValue("paths/recentFiles", recentFiles);
settings.setValue("options/fileTracking", fileTrackingState);
}
void UEFITool::showFitTable()
@ -1041,11 +1335,12 @@ void UEFITool::currentTabChanged(int index)
void UEFITool::loadGuidDatabase()
{
QString path = QFileDialog::getOpenFileName(this, tr("Select GUID database file to load"), currentDir, tr("Comma-separated values files (*.csv);;All files (*)"));
QString path = QFileDialog::getOpenFileName(this, tr("Select GUID database file to load"), openGuidDatabaseDir, tr("Comma-separated values files (*.csv);;All files (*)"));
if (!path.isEmpty()) {
initGuidDatabase(path);
if (!currentPath.isEmpty() && QMessageBox::Yes == QMessageBox::information(this, tr("New GUID database loaded"), tr("Apply new GUID database on the opened file?\nUnsaved changes and tree position will be lost."), QMessageBox::Yes, QMessageBox::No))
openImageFile(currentPath);
openGuidDatabaseDir = QFileInfo(path).absolutePath();
}
}

View file

@ -15,6 +15,7 @@
#define UEFITOOL_H
#include <QMainWindow>
#include <QActionGroup>
#include <QByteArray>
#include <QClipboard>
#include <QDragEnterEvent>
@ -22,6 +23,7 @@
#include <QFile>
#include <QFileDialog>
#include <QFileInfo>
#include <QFileSystemWatcher>
#include <QFont>
#include <QListWidget>
#include <QMenu>
@ -32,10 +34,12 @@
#include <QProcess>
#include <QSettings>
#include <QSplitter>
#include <QStack>
#include <QStyleFactory>
#include <QString>
#include <QTableWidget>
#include <QTreeView>
#include <QToolButton>
#include <QUrl>
#include "../common/basetypes.h"
@ -75,13 +79,19 @@ private slots:
void scrollTreeView(QListWidgetItem* item); // For messages
void scrollTreeView(QTableWidgetItem* item); // For FIT table entries
void onTrackingAction(QAction* action);
void fileChangedResume();
void fileChanged(const QString& path);
void setChangedFileFlag(const bool flag);
void openImageFile();
void openImageFileInNewWindow();
void openRecentImageFile();
void saveImageFile();
void search();
void goToBase();
void goToAddress();
void expandTree();
void hexView();
void bodyHexView();
@ -112,6 +122,8 @@ private slots:
void clearMessages();
void toggleBootGuardMarking(bool enabled);
void toggleCStyleHexValues(bool enabled);
void setExpandAll();
void about();
void aboutQt();
@ -144,23 +156,39 @@ private:
GoToBaseDialog* goToBaseDialog;
GoToAddressDialog* goToAddressDialog;
QClipboard* clipboard;
QList<QAction*> recentFileActions;
QFileSystemWatcher watcher;
QToolButton openedFileLabel;
QStringList recentFiles;
QString currentDir;
QString currentPath;
QString currentProgramPath;
QString openImageDir;
QString openGuidDatabaseDir;
QString extractDir;
QFont currentFont;
const QString version;
int fileTrackingState;
bool changedFileFlag;
bool markingEnabled;
bool cStyleHexEnabled;
bool eventFilter(QObject* obj, QEvent* event);
void dragEnterEvent(QDragEnterEvent* event);
void dropEvent(QDropEvent* event);
void contextMenuEvent(QContextMenuEvent* event);
void updateRecentFilesMenu(const QString& fileName = QString());
void readSettings();
void askReopenImageFile();
void reopenImageFile();
void showParserMessages();
void showFinderMessages();
void showFitTable();
void showSecurityInfo();
void showBuilderMessages();
bool isAllExpanded();
void saveTreeState(const QModelIndex& index, QHash<QString, bool>& states);
void restoreTreeState(const QModelIndex& index, const QHash<QString, bool>& states);
enum {
TAB_PARSER,
@ -169,6 +197,14 @@ private:
TAB_SEARCH,
TAB_BUILDER
};
enum {
TRACK_MIN = 0,
TRACK_IGNORE = TRACK_MIN,
TRACK_ASK,
TRACK_REOPEN,
TRACK_MAX = TRACK_REOPEN
};
};
#endif // UEFITOOL_H

View file

@ -48,11 +48,11 @@
<item>
<widget class="QSplitter" name="messagesSplitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Orientation::Vertical</enum>
</property>
<widget class="QSplitter" name="infoSplitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<widget class="QGroupBox" name="structureGroupBox">
<property name="title">
@ -196,7 +196,7 @@
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<widget class="QTableWidget" name="fitTableWidget"/>
</widget>
@ -311,17 +311,27 @@
<x>0</x>
<y>0</y>
<width>851</width>
<height>31</height>
<height>33</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>&amp;File</string>
</property>
<widget class="QMenu" name="menuOpenedImageFileTracking">
<property name="title">
<string>Opened image file tracking</string>
</property>
<addaction name="actionTrackAndIgnore"/>
<addaction name="actionTrackAndAsk"/>
<addaction name="actionTrackAndReopen"/>
</widget>
<addaction name="actionOpenImageFile"/>
<addaction name="actionOpenImageFileInNewWindow"/>
<addaction name="actionSaveImageFile"/>
<addaction name="separator"/>
<addaction name="menuOpenedImageFileTracking"/>
<addaction name="separator"/>
<addaction name="actionGenerateReport"/>
<addaction name="separator"/>
<addaction name="actionLoadGuidDatabase"/>
@ -329,6 +339,7 @@
<addaction name="actionUnloadGuidDatabase"/>
<addaction name="actionExportDiscoveredGuids"/>
<addaction name="separator"/>
<addaction name="separator"/>
<addaction name="actionQuit"/>
</widget>
<widget class="QMenu" name="menuHelp">
@ -548,6 +559,9 @@
<string>&amp;View</string>
</property>
<addaction name="actionToggleBootGuardMarking"/>
<addaction name="actionToggleCStyleHexValues"/>
<addaction name="separator"/>
<addaction name="actionExpandAll"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuAction"/>
@ -699,7 +713,7 @@
<string>F1</string>
</property>
<property name="menuRole">
<enum>QAction::AboutRole</enum>
<enum>QAction::MenuRole::AboutRole</enum>
</property>
</action>
<action name="actionAboutQt">
@ -710,7 +724,7 @@
<string>Shift+F1</string>
</property>
<property name="menuRole">
<enum>QAction::AboutQtRole</enum>
<enum>QAction::MenuRole::AboutQtRole</enum>
</property>
</action>
<action name="actionQuit">
@ -721,7 +735,7 @@
<string>Alt+X</string>
</property>
<property name="menuRole">
<enum>QAction::QuitRole</enum>
<enum>QAction::MenuRole::QuitRole</enum>
</property>
</action>
<action name="actionSearch">
@ -936,6 +950,52 @@
<string>Ctrl+Alt+E</string>
</property>
</action>
<action name="actionToggleCStyleHexValues">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>C-style hex values</string>
</property>
<property name="toolTip">
<string>C-style hex values</string>
</property>
<property name="shortcut">
<string>Ctrl+Shift+C</string>
</property>
</action>
<action name="actionTrackAndIgnore">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Ignore changes</string>
</property>
</action>
<action name="actionTrackAndAsk">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Ask if changed</string>
</property>
</action>
<action name="actionTrackAndReopen">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Auto reopen if changed</string>
</property>
</action>
<action name="actionExpandAll">
<property name="text">
<string>Expand all</string>
</property>
<property name="toolTip">
<string>Expand all tree items</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>

View file

@ -78,6 +78,8 @@ typedef size_t USTATUS;
#define U_INVALID_SYMBOL 55
#define U_ZLIB_DECOMPRESSION_FAILED 56
#define U_INVALID_STORE 57
#define U_INVALID_PE_HEADER 58
#define U_INVALID_TE_HEADER 59
#define U_INVALID_MANIFEST 251
#define U_UNKNOWN_MANIFEST_HEADER_VERSION 252
@ -221,6 +223,10 @@ typedef struct EFI_TIME_ {
#define TCG_HASH_ALGORITHM_ID_NULL 0x0010
#define TCG_HASH_ALGORITHM_ID_SM3 0x0012
// Some whole image-related notional defines
#define UEFI_UPPER_INVALID_ADDRESS 0x100000000ULL
#define UEFI_LOWER_INVALID_ADDRESS (UEFI_UPPER_INVALID_ADDRESS >> 1)
// A workaround for compilers not supporting c++11 and c11
// for using PRIX64.
#define __STDC_FORMAT_MACROS

View file

@ -39,7 +39,7 @@ USTATUS FfsBuilder::erase(const UModelIndex & index, UByteArray & erased)
}
}
erased = UByteArray(model->header(index).size() + model->body(index).size() + model->tail(index).size(), emptyByte);
erased = UByteArray(model->headerSize(index) + model->bodySize(index) + model->tailSize(index), emptyByte);
return U_SUCCESS;
}
@ -124,7 +124,7 @@ USTATUS FfsBuilder::buildCapsule(const UModelIndex & index, UByteArray & capsule
// Check size of reconstructed capsule body, it must remain the same
UINT32 newSize = (UINT32)capsule.size();
UINT32 oldSize = (UINT32)model->body(index).size();
UINT32 oldSize = (UINT32)model->bodySize(index);
if (newSize > oldSize) {
msg(usprintf("buildCapsule: new capsule size %Xh (%u) is bigger than the original %Xh (%u)", newSize, newSize, oldSize, oldSize), index);
return U_INVALID_CAPSULE;
@ -223,7 +223,7 @@ USTATUS FfsBuilder::buildIntelImage(const UModelIndex & index, UByteArray & inte
// Check size of new image, it must be same as old one
UINT32 newSize = (UINT32)intelImage.size();
UINT32 oldSize = (UINT32)model->body(index).size();
UINT32 oldSize = (UINT32)model->bodySize(index);
if (newSize > oldSize) {
msg(usprintf("buildIntelImage: new image size %Xh (%u) is bigger than the original %Xh (%u)", newSize, newSize, oldSize, oldSize), index);
return U_INVALID_IMAGE;
@ -294,7 +294,7 @@ USTATUS FfsBuilder::buildRawArea(const UModelIndex & index, UByteArray & rawArea
// Check size of new raw area, it must be same as original one
UINT32 newSize = (UINT32)rawArea.size();
UINT32 oldSize = (UINT32)model->body(index).size();
UINT32 oldSize = (UINT32)model->bodySize(index);
if (newSize > oldSize) {
msg(usprintf("buildRawArea: new area size %Xh (%u) is bigger than the original %Xh (%u)", newSize, newSize, oldSize, oldSize), index);
return U_INVALID_RAW_AREA;

File diff suppressed because it is too large Load diff

View file

@ -14,6 +14,7 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#define FFSPARSER_H
#include <vector>
#include <unordered_map>
#include "basetypes.h"
#include "ustring.h"
@ -115,6 +116,8 @@ private:
UModelIndex lastVtf;
UINT32 imageBase;
UINT64 addressDiff;
REGION_INFO biosRegionInfo;
std::unordered_map<UINT64, int> baseAddressesMap;
UString securityInfo;
@ -126,6 +129,7 @@ private:
USTATUS performFirstPass(const UByteArray & imageFile, UModelIndex & index);
USTATUS parseCapsule(const UByteArray & capsule, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index);
USTATUS parseImage(const UByteArray& buffer, const UINT32 localOffset, const UModelIndex& parent, UModelIndex& index);
USTATUS parseIntelImage(const UByteArray & intelImage, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index);
USTATUS parseGenericImage(const UByteArray & intelImage, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index);
@ -136,9 +140,9 @@ private:
USTATUS parseRawArea(const UModelIndex & index);
USTATUS parseVolumeHeader(const UByteArray & volume, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index);
USTATUS parseVolumeBody(const UModelIndex & index);
USTATUS parseVolumeBody(const UModelIndex & index, const bool probe = false);
USTATUS parseMicrocodeVolumeBody(const UModelIndex & index);
USTATUS parseFileHeader(const UByteArray & file, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index);
USTATUS parseFileHeader(const UByteArray & file, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool probe = false);
USTATUS parseFileBody(const UModelIndex & index);
USTATUS parseSectionHeader(const UByteArray & section, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index, const bool insertIntoTree);
USTATUS parseSectionBody(const UModelIndex & index);
@ -167,8 +171,8 @@ private:
USTATUS parseDepexSectionBody(const UModelIndex & index);
USTATUS parseUiSectionBody(const UModelIndex & index);
USTATUS parseRawSectionBody(const UModelIndex & index);
USTATUS parsePeImageSectionBody(const UModelIndex & index);
USTATUS parseTeImageSectionBody(const UModelIndex & index);
USTATUS parsePeImageSectionBody(const UModelIndex & index, const bool probe = false);
USTATUS parseTeImageSectionBody(const UModelIndex & index, const bool probe = false);
USTATUS parseAprioriRawSection(const UByteArray & body, UString & parsed);
USTATUS findNextRawAreaItem(const UModelIndex & index, const UINT32 localOffset, UINT8 & nextItemType, UINT32 & nextItemOffset, UINT32 & nextItemSize, UINT32 & nextItemAlternativeSize);

View file

@ -200,7 +200,7 @@ USTATUS FitParser::parseFit(const UModelIndex & index)
return U_SUCCESS;
}
void FitParser::findFitRecursive(const UModelIndex & index, UModelIndex & found, UINT32 & fitOffset)
void FitParser::findFitRecursive(const UModelIndex& index, UModelIndex & found, UINT32 & fitOffset)
{
// Sanity check
if (!index.isValid()) {
@ -217,28 +217,30 @@ void FitParser::findFitRecursive(const UModelIndex & index, UModelIndex & found,
}
}
// Check for all FIT signatures in item body
UByteArray lastVtfBody = model->body(ffsParser->lastVtf);
// Check for all FIT signatures in item body - we want to log 'em all
UByteArray lastVtfBody = model->header(ffsParser->lastVtf) + model->body(ffsParser->lastVtf) + model->tail(ffsParser->lastVtf);
UINT64 fitSignatureValue = INTEL_FIT_SIGNATURE;
UByteArray fitSignature((const char*)&fitSignatureValue, sizeof(fitSignatureValue));
UINT32 storedFitAddress = *(const UINT32*)(lastVtfBody.constData() + lastVtfBody.size() - INTEL_FIT_POINTER_OFFSET);
UINT32 storedFitAddress = *(const UINT32*)(lastVtfBody.constData() + UEFI_UPPER_INVALID_ADDRESS - INTEL_FIT_POINTER_OFFSET
- model->base(ffsParser->lastVtf) - ffsParser->addressDiff);
for (INT32 offset = (INT32)model->body(index).indexOf(fitSignature);
offset >= 0;
offset = (INT32)model->body(index).indexOf(fitSignature, offset + 1)) {
// FIT candidate found, calculate its physical address
UINT32 fitAddress = (UINT32)(model->base(index) + (UINT32)ffsParser->addressDiff + model->header(index).size() + (UINT32)offset);
UINT32 fitAddress = (UINT32)(model->base(index) + (UINT32)ffsParser->addressDiff + model->headerSize(index) + (UINT32)offset);
// Check FIT address to be stored in the last VTF
if (fitAddress == storedFitAddress) {
// Valid FIT table must have at least two entries
if ((UINT32)model->body(index).size() < offset + 2*sizeof(INTEL_FIT_ENTRY)) {
if ((UINT32)model->bodySize(index) < offset + 2*sizeof(INTEL_FIT_ENTRY)) {
msg(usprintf("%s: FIT table candidate found, too small to contain real FIT", __FUNCTION__), index);
}
else {
// Real FIT found
found = index;
fitOffset = offset;
msg(usprintf("%s: real FIT table found at physical address %08Xh", __FUNCTION__, fitAddress), found);
msg(usprintf("%s: real FIT table found at physical address %08Xh (offset %Xh)", __FUNCTION__,
fitAddress, fitOffset), found);
break;
}
}
@ -709,8 +711,8 @@ USTATUS FitParser::parseFitEntryBootGuardBootPolicy(const UByteArray & bootPolic
else {
// Add postIbbHash protected range
UByteArray postIbbHash(ibbs_body->post_ibb_hash()->hash().data(), ibbs_body->post_ibb_hash()->len_hash());
if (postIbbHash.count('\x00') != postIbbHash.size()
&& postIbbHash.count('\xFF') != postIbbHash.size()) {
auto c = checkSingle(postIbbHash);
if (c != 0 && c != 0xFF) {
PROTECTED_RANGE range = {};
range.Type = PROTECTED_RANGE_INTEL_BOOT_GUARD_POST_IBB;
range.AlgorithmId = ibbs_body->post_ibb_hash()->hash_algorithm_id();
@ -990,8 +992,8 @@ USTATUS FitParser::parseFitEntryBootGuardBootPolicy(const UByteArray & bootPolic
else {
// Add postIbbHash protected range
UByteArray postIbbHash(ibbs_body->post_ibb_digest()->hash().data(), ibbs_body->post_ibb_digest()->len_hash());
if (postIbbHash.count('\x00') != postIbbHash.size()
&& postIbbHash.count('\xFF') != postIbbHash.size()) {
auto c = checkSingle(postIbbHash);
if (c != 0 && c != 0xFF) {
PROTECTED_RANGE range = {};
range.Type = PROTECTED_RANGE_INTEL_BOOT_GUARD_POST_IBB;
range.AlgorithmId = ibbs_body->post_ibb_digest()->hash_algorithm_id();
@ -1021,8 +1023,8 @@ USTATUS FitParser::parseFitEntryBootGuardBootPolicy(const UByteArray & bootPolic
// Add ObbHash protected range
UByteArray obbHash(ibbs_body->obb_digest()->hash().data(), ibbs_body->obb_digest()->len_hash());
if (obbHash.count('\x00') != obbHash.size()
&& obbHash.count('\xFF') != obbHash.size()) {
auto c = checkSingle(obbHash);
if (c != 0 && c != 0xFF) {
PROTECTED_RANGE range = {};
range.Type = PROTECTED_RANGE_INTEL_BOOT_GUARD_OBB;
range.AlgorithmId = ibbs_body->obb_digest()->hash_algorithm_id();

View file

@ -21,7 +21,7 @@ WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
// This structure is described in Section 9.11.1 of the Intel Software Developer manual Volume 3A Part 1
typedef struct INTEL_MICROCODE_HEADER_ {
UINT32 HeaderVersion; // 0x00000001
UINT32 HeaderType; // 0x00000001 for Microcode (we do not need to support IFS yet)
UINT32 UpdateRevision;
UINT16 DateYear; // BCD
UINT8 DateDay; // BCD
@ -31,8 +31,7 @@ typedef struct INTEL_MICROCODE_HEADER_ {
// Checksum is correct when the summation of all the DWORDs (including the extended Processor Signature Table)
// that comprise the microcode update result in 00000000H.
UINT32 LoaderRevision; // 0x00000001
UINT8 ProcessorFlags;
UINT8 ProcessorFlagsReserved[3]; // Zeroes
UINT32 PlatformIds; // Platform Ids
UINT32 DataSize; // Specifies the size of the encrypted data in bytes, and must be a multiple of DWORDs.
// If this value is 00000000H, then the microcode update encrypted data is 2000 bytes (or 500 DWORDs).
// Sane values are less than 0x1000000
@ -40,7 +39,9 @@ typedef struct INTEL_MICROCODE_HEADER_ {
// It is the summation of the header size, the encrypted data size and the size of the optional extended signature table.
// This value is always a multiple of 1024 according to the spec, but Intel already breached it several times.
// Sane values are less than 0x1000000
UINT8 Reserved[12]; // Zeroes
UINT32 MetadataSize; // Reserved in Microcode headers
UINT32 UpdateRevisionMin; // Minimum required version for OS Kernel Late Loading
UINT32 Reserved; // Zeroes
} INTEL_MICROCODE_HEADER;
#define INTEL_MICROCODE_REAL_DATA_SIZE_ON_ZERO 2000
@ -57,8 +58,8 @@ typedef struct INTEL_MICROCODE_EXTENDED_HEADER_ {
typedef struct INTEL_MICROCODE_EXTENDED_HEADER_ENTRY_ {
UINT32 ProcessorSignature;
UINT32 ProcessorFlags;
UINT32 Checksum; // To calculate the Checksum, substitute the Primary Processor Signature entry and the Processor Flags entry with the corresponding Extended Patch entry.
UINT32 PlatformIds;
UINT32 Checksum; // To calculate the Checksum, substitute the Primary Processor Signature entry and the Platform Ids entry with the corresponding Extended Patch entry.
// Delete the Extended Processor Signature Table entries.
// Checksum is correct when the summation of all DWORDs that comprise the created Extended Processor Patch results in 00000000H.
} INTEL_MICROCODE_EXTENDED_HEADER_ENTRY;

View file

@ -126,8 +126,7 @@ USTATUS MeParser::parseFptRegion(const UByteArray & region, const UModelIndex &
}
// Get info
UByteArray header = region.left(romBypassVectorSize + sizeof(FPT_HEADER));
UByteArray body = region.mid(header.size(), ptBodySize);
UINT32 headerSize = romBypassVectorSize + sizeof(FPT_HEADER);
UString name = UString("FPT partition table");
UString info;
@ -139,7 +138,7 @@ USTATUS MeParser::parseFptRegion(const UByteArray & region, const UModelIndex &
info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nROM bypass vector: %s\nNumber of entries: %u\nHeader version: %02Xh\nEntry version: %02Xh\n"
"Header length: %02Xh\nFlags: %Xh\nTicks to add: %04Xh\nTokens to add: %04Xh\nSPS Flags: %Xh\nFITC version: %u.%u.%u.%u\nCRC32 Checksum: %08Xh",
ptSize, ptSize,
(UINT32)header.size(), (UINT32)header.size(),
headerSize, headerSize,
ptBodySize, ptBodySize,
(romBypassVectorSize ? "present" : "absent"),
ptHeader21->NumEntries,
@ -159,7 +158,7 @@ USTATUS MeParser::parseFptRegion(const UByteArray & region, const UModelIndex &
info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nROM bypass vector: %s\nNumber of entries: %u\nHeader version: %02Xh\nEntry version: %02Xh\n"
"Header length: %02Xh\nFlash cycle life: %04Xh\nFlash cycle limit: %04Xh\nUMA size: %Xh\nFlags: %Xh\nFITC version: %u.%u.%u.%u\nChecksum: %02Xh",
ptSize, ptSize,
(UINT32)header.size(), (UINT32)header.size(),
headerSize, headerSize,
ptBodySize, ptBodySize,
(romBypassVectorSize ? "present" : "absent"),
ptHeader->NumEntries,
@ -176,11 +175,15 @@ USTATUS MeParser::parseFptRegion(const UByteArray & region, const UModelIndex &
}
// Add tree item
index = model->addItem(0, Types::FptStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent);
index = model->addItem(
0, Types::FptStore, 0,
name, UString(), info,
headerSize, ptBodySize, 0,
Fixed, parent);
// Add partition table entries
std::vector<FPT_PARTITION_INFO> partitions;
UINT32 offset = (UINT32)header.size();
UINT32 offset = headerSize;
UINT32 numEntries = ptHeader->NumEntries;
const FPT_HEADER_ENTRY* firstPtEntry = (const FPT_HEADER_ENTRY*)(region.constData() + offset);
for (UINT32 i = 0; i < numEntries; i++) {
@ -197,7 +200,11 @@ USTATUS MeParser::parseFptRegion(const UByteArray & region, const UModelIndex &
// Add tree item
const UINT8 type = (ptEntry->Offset != 0 && ptEntry->Offset != 0xFFFFFFFF && ptEntry->Size != 0 && ptEntry->EntryValid != 0xFF) ? Subtypes::ValidFptEntry : Subtypes::InvalidFptEntry;
UModelIndex entryIndex = model->addItem(offset, Types::FptEntry, type, name, UString(), info, UByteArray(), UByteArray((const char*)ptEntry, sizeof(FPT_HEADER_ENTRY)), UByteArray(), Fixed, index);
UModelIndex entryIndex = model->addItem(
offset, Types::FptEntry, type,
name, UString(), info,
0, sizeof(FPT_HEADER_ENTRY), 0,
Fixed, index);
// Adjust offset
offset += sizeof(FPT_HEADER_ENTRY);
@ -311,7 +318,11 @@ make_partition_table_consistent:
// Add tree item
UINT8 type = Subtypes::CodeFptPartition + partitions[i].ptEntry.Type;
partitionIndex = model->addItem(partitions[i].ptEntry.Offset, Types::FptPartition, type, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
partitionIndex = model->addItem(
partitions[i].ptEntry.Offset, Types::FptPartition, type,
name, UString(), info,
0, partition.size(), 0,
Fixed, parent);
if (type == Subtypes::CodeFptPartition && partition.size() >= (int) sizeof(UINT32) && readUnaligned((const UINT32*)partition.constData()) == CPD_SIGNATURE) {
// Parse code partition contents
UModelIndex cpdIndex;
@ -324,7 +335,11 @@ make_partition_table_consistent:
info = usprintf("Full size: %Xh (%u)", (UINT32)partition.size(), (UINT32)partition.size());
// Add tree item
model->addItem(partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
model->addItem(
partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(partition),
name, UString(), info,
0, partition.size(), 0,
Fixed, parent);
}
}
@ -343,7 +358,7 @@ USTATUS MeParser::parseIfwi16Region(const UByteArray & region, const UModelIndex
// Add header
UINT32 ptSize = sizeof(IFWI_16_LAYOUT_HEADER);
UByteArray header = region.left(ptSize);
UINT32 headerSize = ptSize > region.size() ? region.size() : ptSize;
UString name = UString("IFWI 1.6 header");
UString info = usprintf("Full size: %Xh (%u)\n"
@ -354,7 +369,7 @@ USTATUS MeParser::parseIfwi16Region(const UByteArray & region, const UModelIndex
"Boot4 partition offset: %Xh\nBoot4 partition size: %Xh\n"
"Boot5 partition offset: %Xh\nBoot5 partition size: %Xh\n"
"Checksum: %" PRIX64 "h",
(UINT32)header.size(), (UINT32)header.size(),
headerSize, headerSize,
ifwiHeader->DataPartition.Offset, ifwiHeader->DataPartition.Size,
ifwiHeader->BootPartition[0].Offset, ifwiHeader->BootPartition[0].Size,
ifwiHeader->BootPartition[1].Offset, ifwiHeader->BootPartition[1].Size,
@ -363,7 +378,11 @@ USTATUS MeParser::parseIfwi16Region(const UByteArray & region, const UModelIndex
ifwiHeader->BootPartition[4].Offset, ifwiHeader->BootPartition[4].Size,
ifwiHeader->Checksum);
// Add tree item
index = model->addItem(0, Types::IfwiHeader, 0, name, UString(), info, UByteArray(), header, UByteArray(), Fixed, parent);
index = model->addItem(
0, Types::IfwiHeader, 0,
name, UString(), info,
0, headerSize, 0,
Fixed, parent);
std::vector<IFWI_PARTITION_INFO> partitions;
// Add data partition
@ -474,7 +493,11 @@ make_partition_table_consistent:
info = usprintf("Full size: %Xh (%u)\n", (UINT32)partition.size(), (UINT32)partition.size());
// Add tree item
partitionIndex = model->addItem(partitions[i].ptEntry.Offset, partitions[i].type, partitions[i].subtype, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
partitionIndex = model->addItem(
partitions[i].ptEntry.Offset, partitions[i].type, partitions[i].subtype,
name, UString(), info,
0, partition.size(), 0,
Fixed, parent);
// Parse partition further
if (partitions[i].subtype == Subtypes::DataIfwiPartition) {
@ -493,7 +516,11 @@ make_partition_table_consistent:
info = usprintf("Full size: %Xh (%u)", (UINT32)partition.size(), (UINT32)partition.size());
// Add tree item
model->addItem(partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
model->addItem(
partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(partition),
name, UString(), info,
0, partition.size(), 0,
Fixed, parent);
}
}
@ -513,7 +540,7 @@ USTATUS MeParser::parseIfwi17Region(const UByteArray & region, const UModelIndex
// Add header
UINT32 ptSize = sizeof(IFWI_17_LAYOUT_HEADER);
UByteArray header = region.left(ptSize);
UINT32 headerSize = ptSize > region.size() ? region.size() : ptSize;
UString name = UString("IFWI 1.7 header");
UString info = usprintf("Full size: %Xh (%u)\n"
@ -527,7 +554,7 @@ USTATUS MeParser::parseIfwi17Region(const UByteArray & region, const UModelIndex
"Boot4 partition offset: %Xh\nBoot4 partition size: %Xh\n"
"Boot5 partition offset: %Xh\nBoot5 partition size: %Xh\n"
"Temp page offset: %Xh\nTemp page size: %Xh\n",
(UINT32)header.size(), (UINT32)header.size(),
headerSize, headerSize,
ifwiHeader->Flags,
ifwiHeader->Reserved,
ifwiHeader->Checksum,
@ -539,7 +566,11 @@ USTATUS MeParser::parseIfwi17Region(const UByteArray & region, const UModelIndex
ifwiHeader->BootPartition[4].Offset, ifwiHeader->BootPartition[4].Size,
ifwiHeader->TempPage.Offset, ifwiHeader->TempPage.Size);
// Add tree item
index = model->addItem(0, Types::IfwiHeader, 0, name, UString(), info, UByteArray(), header, UByteArray(), Fixed, parent);
index = model->addItem(
0, Types::IfwiHeader, 0,
name, UString(), info,
0, headerSize, 0,
Fixed, parent);
std::vector<IFWI_PARTITION_INFO> partitions;
// Add data partition
@ -662,7 +693,11 @@ make_partition_table_consistent:
info = usprintf("Full size: %Xh (%u)\n", (UINT32)partition.size(), (UINT32)partition.size());
// Add tree item
partitionIndex = model->addItem(partitions[i].ptEntry.Offset, partitions[i].type, partitions[i].subtype, name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
partitionIndex = model->addItem(
partitions[i].ptEntry.Offset, partitions[i].type, partitions[i].subtype,
name, UString(), info,
0, partition.size(), 0,
Fixed, parent);
// Parse partition further
if (partitions[i].subtype == Subtypes::DataIfwiPartition) {
@ -688,7 +723,11 @@ make_partition_table_consistent:
info = usprintf("Full size: %Xh (%u)", (UINT32)partition.size(), (UINT32)partition.size());
// Add tree item
model->addItem(partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(partition), name, UString(), info, UByteArray(), partition, UByteArray(), Fixed, parent);
model->addItem(
partitions[i].ptEntry.Offset, Types::Padding, getPaddingType(partition),
name, UString(), info,
0, partition.size(), 0,
Fixed, parent);
}
}

File diff suppressed because it is too large Load diff

View file

@ -37,8 +37,8 @@ public:
void clearMessages() { messagesVector.clear(); }
// NVRAM parsing
USTATUS parseNvramVolumeBody(const UModelIndex & index, const UINT32 fdcStoreSizeOverride = 0);
USTATUS parseNvarStore(const UModelIndex & index);
USTATUS parseNvramVolumeBody(const UModelIndex& index, const UINT32 fdcStoreSizeOverride = 0);
USTATUS parseNvarStore(const UModelIndex & index, const bool probe = false);
private:
TreeModel *model;
@ -63,7 +63,7 @@ public:
// NVRAM parsing
USTATUS parseNvramVolumeBody(const UModelIndex &) { return U_SUCCESS; }
USTATUS parseNvarStore(const UModelIndex &) { return U_SUCCESS; }
USTATUS parseNvarStore(const UModelIndex &, const bool probe = false) { return U_SUCCESS; }
};
#endif // U_ENABLE_NVRAM_PARSING_SUPPORT
#endif // NVRAMPARSER_H

1509
common/printf/printf.c Normal file

File diff suppressed because it is too large Load diff

240
common/printf/printf.h Normal file
View file

@ -0,0 +1,240 @@
/**
* @author (c) Eyal Rozenberg <eyalroz1@gmx.com>
* 2021-2023, Haifa, Palestine/Israel
* @author (c) Marco Paland (info@paland.com)
* 2014-2019, PALANDesign Hannover, Germany
*
* @note Others have made smaller contributions to this file: see the
* contributors page at https://github.com/eyalroz/printf/graphs/contributors
* or ask one of the authors.
*
* @brief Small stand-alone implementation of the printf family of functions
* (`(v)printf`, `(v)s(n)printf` etc., geared towards use on embedded systems
* with a very limited resources.
*
* @note the implementations are thread-safe; re-entrant; use no functions from
* the standard library; and do not dynamically allocate any memory.
*
* @license The MIT License (MIT)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef PRINTF_H_
#define PRINTF_H_
#ifdef __cplusplus
# include <cstdarg>
# include <cstddef>
extern "C" {
#else
# include <stdarg.h>
# include <stddef.h>
#endif
#ifdef __GNUC__
# if ((__GNUC__ == 4 && __GNUC_MINOR__>= 4) || __GNUC__ > 4)
# define ATTR_PRINTF(one_based_format_index, first_arg) \
__attribute__((format(gnu_printf, (one_based_format_index), (first_arg))))
# else
# define ATTR_PRINTF(one_based_format_index, first_arg) \
__attribute__((format(printf, (one_based_format_index), (first_arg))))
# endif
# define ATTR_VPRINTF(one_based_format_index) \
ATTR_PRINTF((one_based_format_index), 0)
#else
# define ATTR_PRINTF(one_based_format_index, first_arg)
# define ATTR_VPRINTF(one_based_format_index)
#endif
#ifndef PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT
#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT 0
#endif
#ifndef PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD
#define PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD 0
#endif
#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD
# define printf_ printf
# define sprintf_ sprintf
# define vsprintf_ vsprintf
# define snprintf_ snprintf
# define vsnprintf_ vsnprintf
# define vprintf_ vprintf
#endif
// If you want to include this implementation file directly rather than
// link against it, this will let you control the functions' visibility,
// e.g. make them static so as not to clash with other objects also
// using them.
#ifndef PRINTF_VISIBILITY
#define PRINTF_VISIBILITY
#endif
/**
* Prints/send a single character to some opaque output entity
*
* @note This function is not implemented by the library, only declared; you
* must provide an implementation if you wish to use the @ref printf / @ref
* vprintf function (and possibly for linking against the library, if your
* toolchain does not support discarding unused functions)
*
* @note The output could be as simple as a wrapper for the `write()` system
* call on a Unix-like * system, or even libc's @ref putchar , for replicating
* actual functionality of libc's @ref printf * function; but on an embedded
* system it may involve interaction with a special output device, like a UART,
* etc.
*
* @note in libc's @ref putchar, the parameter type is an int; this was intended
* to support the representation of either a proper character or EOF in a
* variable - but this is really not meaningful to pass into @ref putchar and is
* discouraged today. See further discussion in:
* @link https://stackoverflow.com/q/17452847/1593077
*
* @param c the single character to print
*/
PRINTF_VISIBILITY
void putchar_(char c);
/**
* An implementation of the C standard's printf/vprintf
*
* @note you must implement a @ref putchar_ function for using this function -
* it invokes @ref putchar_ * rather than directly performing any I/O (which
* insulates it from any dependence on the operating system * and external
* libraries).
*
* @param format A string specifying the format of the output, with %-marked
* specifiers of how to interpret additional arguments.
* @param arg Additional arguments to the function, one for each %-specifier in
* @p format
* @return The number of characters written into @p s, not counting the
* terminating null character
*/
///@{
PRINTF_VISIBILITY
int printf_(const char* format, ...) ATTR_PRINTF(1, 2);
PRINTF_VISIBILITY
int vprintf_(const char* format, va_list arg) ATTR_VPRINTF(1);
///@}
/**
* An implementation of the C standard's sprintf/vsprintf
*
* @note For security considerations (the potential for exceeding the buffer
* bounds), please consider using the size-constrained variant, @ref snprintf /
* @ref vsnprintf, instead.
*
* @param s An array in which to store the formatted string. It must be large
* enough to fit the formatted output!
* @param format A string specifying the format of the output, with %-marked
* specifiers of how to interpret additional arguments
* @param arg Additional arguments to the function, one for each specifier in
* @p format
* @return The number of characters written into @p s, not counting the
* terminating null character
*/
///@{
PRINTF_VISIBILITY
int sprintf_(char* s, const char* format, ...) ATTR_PRINTF(2, 3);
PRINTF_VISIBILITY
int vsprintf_(char* s, const char* format, va_list arg) ATTR_VPRINTF(2);
///@}
/**
* An implementation of the C standard's snprintf/vsnprintf
*
* @param s An array in which to store the formatted string. It must be large
* enough to fit either the entire formatted output, or at least @p n
* characters. Alternatively, it can be NULL, in which case nothing will
* be printed, and only the number of characters which _could_ have been
* printed is tallied and returned.
* @param n The maximum number of characters to write to the array, including
* a terminating null character
* @param format A string specifying the format of the output, with %-marked
* specifiers of how to interpret additional arguments.
* @param arg Additional arguments to the function, one for each specifier in
* @p format
* @return The number of characters that COULD have been written into @p s, not
* counting the terminating null character. A value equal or larger than
* @p n indicates truncation. Only when the returned value is non-negative
* and less than @p n, the null-terminated string has been fully and
* successfully printed.
*/
///@{
PRINTF_VISIBILITY
int snprintf_(char* s, size_t count, const char* format, ...) ATTR_PRINTF(3, 4);
PRINTF_VISIBILITY
int vsnprintf_(char* s, size_t count, const char* format, va_list arg) ATTR_VPRINTF(3);
PRINTF_VISIBILITY
int vosnprintf_(char* s, size_t count, const char* format, va_list arg) ATTR_VPRINTF(3);
///@}
/**
* printf/vprintf with user-specified output function
*
* An alternative to @ref printf_, in which the output function is specified
* dynamically (rather than @ref putchar_ being used)
*
* @param out An output function which takes one character and a type-erased
* additional parameters
* @param extra_arg The type-erased argument to pass to the output function @p
* out with each call
* @param format A string specifying the format of the output, with %-marked
* specifiers of how to interpret additional arguments.
* @param arg Additional arguments to the function, one for each specifier in
* @p format
* @return The number of characters for which the output f unction was invoked,
* not counting the terminating null character
*
*/
PRINTF_VISIBILITY
int fctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, ...) ATTR_PRINTF(3, 4);
PRINTF_VISIBILITY
int vfctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, va_list arg) ATTR_VPRINTF(3);
PRINTF_VISIBILITY
int vofctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, va_list arg) ATTR_VPRINTF(3);
#ifdef __cplusplus
} // extern "C"
#endif
#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_HARD
# undef printf_
# undef sprintf_
# undef vsprintf_
# undef snprintf_
# undef vsnprintf_
# undef vprintf_
#else
#if PRINTF_ALIAS_STANDARD_FUNCTION_NAMES_SOFT
# define printf printf_
# define sprintf sprintf_
# define vsprintf vsprintf_
# define snprintf snprintf_
# define vsnprintf vsnprintf_
# define vprintf vprintf_
#endif
#endif
#endif // PRINTF_H_

View file

@ -16,7 +16,7 @@
TreeItem::TreeItem(const UINT32 offset, const UINT8 type, const UINT8 subtype,
const UString & name, const UString & text, const UString & info,
const UByteArray & header, const UByteArray & body, const UByteArray & tail,
const UINT32 headerSize, const UINT32 bodySize, const UINT32 tailSize,
const bool fixed, const bool compressed,
TreeItem *parent) :
itemOffset(offset),
@ -27,9 +27,9 @@ itemMarking(0),
itemName(name),
itemText(text),
itemInfo(info),
itemHeader(header),
itemBody(body),
itemTail(tail),
itemHeaderSize(headerSize),
itemBodySize(bodySize),
itemTailSize(tailSize),
itemFixed(fixed),
itemCompressed(compressed),
parentItem(parent)
@ -37,16 +37,39 @@ parentItem(parent)
}
TreeItem::~TreeItem() {
std::list<TreeItem*>::iterator begin = childItems.begin();
auto begin = childItems.begin();
while (begin != childItems.end()) {
delete *begin;
++begin;
}
}
const char * TreeItem::content(UINT32 dataOffset) const
{
if (!itemContent.isEmpty()) {
return itemContent.constData() + dataOffset;
}
auto o = itemOffset;
auto p = parentItem;
while (p && ((!p->itemCompressed && p->itemContent.isEmpty()) || (p->itemCompressed && p->itemUncompressedData.isEmpty()))) {
o += p->itemOffset;
p = p->parentItem;
}
if (!p)
return nullptr;
return !p->itemCompressed ? p->content(o + dataOffset) : p->itemUncompressedData.constData() + o + dataOffset - p->itemHeaderSize;
}
bool TreeItem::compressedInherited() const {
auto p = this;
while (p && p->itemType != Types::Root && !p->itemCompressed)
p = p->parentItem;
return p ? p->itemCompressed : false;
}
UINT8 TreeItem::insertChildBefore(TreeItem *item, TreeItem *newItem)
{
std::list<TreeItem*>::iterator found = std::find(childItems.begin(), childItems.end(), item);
auto found = std::find(childItems.begin(), childItems.end(), item);
if (found == childItems.end())
return U_ITEM_NOT_FOUND;
childItems.insert(found, newItem);
@ -55,7 +78,7 @@ UINT8 TreeItem::insertChildBefore(TreeItem *item, TreeItem *newItem)
UINT8 TreeItem::insertChildAfter(TreeItem *item, TreeItem *newItem)
{
std::list<TreeItem*>::iterator found = std::find(childItems.begin(), childItems.end(), item);
auto found = std::find(childItems.begin(), childItems.end(), item);
if (found == childItems.end())
return U_ITEM_NOT_FOUND;
childItems.insert(++found, newItem);
@ -84,7 +107,7 @@ UString TreeItem::data(int column) const
int TreeItem::row() const
{
if (parentItem) {
std::list<TreeItem*>::const_iterator iter = parentItem->childItems.begin();
auto iter = parentItem->childItems.begin();
for (int i = 0; i < (int)parentItem->childItems.size(); ++i, ++iter) {
if (const_cast<TreeItem*>(this) == *iter)
return i;
@ -95,7 +118,7 @@ int TreeItem::row() const
TreeItem* TreeItem::child(int row)
{
std::list<TreeItem*>::iterator child = childItems.begin();
auto child = childItems.begin();
std::advance(child, row);
return *child;
}

View file

@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#ifndef TREEITEM_H
#define TREEITEM_H
#include <list>
#include <vector>
#include <iterator>
#include "basetypes.h"
@ -25,20 +25,20 @@ class TreeItem
{
public:
TreeItem(const UINT32 offset, const UINT8 type, const UINT8 subtype, const UString &name, const UString &text, const UString &info,
const UByteArray & header, const UByteArray & body, const UByteArray & tail,
const UINT32 headerSize, const UINT32 bodySize, const UINT32 tailSize,
const bool fixed, const bool compressed,
TreeItem *parent = 0);
~TreeItem(); // Non-trivial implementation in CPP file
// Operations with items
void appendChild(TreeItem *item) { childItems.push_back(item); }
void prependChild(TreeItem *item) { childItems.push_front(item); };
void prependChild(TreeItem *item) { childItems.insert(childItems.begin(), item); };
UINT8 insertChildBefore(TreeItem *item, TreeItem *newItem); // Non-trivial implementation in CPP file
UINT8 insertChildAfter(TreeItem *item, TreeItem *newItem); // Non-trivial implementation in CPP file
// Model support operations
TreeItem *child(int row); // Non-trivial implementation in CPP file
int childCount() const {return (int)childItems.size(); }
int childCount() const { return (int)childItems.size(); }
int columnCount() const { return 5; }
UString data(int column) const; // Non-trivial implementation in CPP file
int row() const; // Non-trivial implementation in CPP file
@ -60,14 +60,17 @@ public:
UString text() const { return itemText; }
void setText(const UString &text) { itemText = text; }
UByteArray header() const { return itemHeader; }
bool hasEmptyHeader() const { return itemHeader.isEmpty(); }
UByteArray header() const { return UByteArray(content(0), itemHeaderSize); }
UINT32 headerSize() const { return itemHeaderSize; }
bool hasEmptyHeader() const { return itemHeaderSize == 0; }
UByteArray body() const { return itemBody; };
bool hasEmptyBody() const { return itemBody.isEmpty(); }
UByteArray body() const { return UByteArray(content(itemHeaderSize), itemBodySize); }
UINT32 bodySize() const { return itemBodySize; }
bool hasEmptyBody() const { return itemBodySize == 0; }
UByteArray tail() const { return itemTail; };
bool hasEmptyTail() const { return itemTail.isEmpty(); }
UByteArray tail() const { return UByteArray(content(itemHeaderSize + itemBodySize), itemTailSize); }
UINT32 tailSize() const { return itemTailSize; }
bool hasEmptyTail() const { return itemTailSize == 0; }
UString info() const { return itemInfo; }
void addInfo(const UString &info, const bool append) { if (append) itemInfo += info; else itemInfo = info + itemInfo; }
@ -79,6 +82,10 @@ public:
bool fixed() const { return itemFixed; }
void setFixed(const bool fixed) { itemFixed = fixed; }
bool hasContent() const { return !itemContent.isEmpty(); }
void setContent(const UByteArray& c) { itemContent = c; }
bool compressedInherited() const;
bool compressed() const { return itemCompressed; }
void setCompressed(const bool compressed) { itemCompressed = compressed; }
@ -94,8 +101,13 @@ public:
void setMarking(const UINT8 marking) { itemMarking = marking; }
private:
std::list<TreeItem*> childItems;
const char* content(UINT32 dataOffset) const;
std::vector<TreeItem*> childItems;
UINT32 itemOffset;
UINT32 itemHeaderSize;
UINT32 itemBodySize;
UINT32 itemTailSize;
UINT8 itemAction;
UINT8 itemType;
UINT8 itemSubtype;
@ -103,9 +115,7 @@ private:
UString itemName;
UString itemText;
UString itemInfo;
UByteArray itemHeader;
UByteArray itemBody;
UByteArray itemTail;
UByteArray itemContent;
bool itemFixed;
bool itemCompressed;
UByteArray itemParsingData;

View file

@ -11,6 +11,12 @@
*/
#if defined (QT_GUI_LIB)
#include <QtWidgets/QApplication>
#include <QIcon>
#include <QtWidgets/QStyle>
#endif
#include "treemodel.h"
#include "stack"
@ -36,6 +42,16 @@ QVariant TreeModel::data(const UModelIndex &index, int role) const
}
}
}
else if (role == Qt::DecorationRole) {
if ((item->hasContent() || (item->parent()->hasContent() && item->parent()->type() == Types::Root)) && index.column() == 0) {
if (item->compressed())
return QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning);
else
return QApplication::style()->standardIcon(QStyle::SP_FileDialogContentsView);
}
else if (item->compressed() && index.column() == 0)
return QApplication::style()->standardIcon(QStyle::SP_FileDialogStart);
}
#endif
else if (role == Qt::UserRole) {
return item->info().toLocal8Bit();
@ -214,6 +230,14 @@ UByteArray TreeModel::header(const UModelIndex &index) const
return item->header();
}
UINT32 TreeModel::headerSize(const UModelIndex& index) const
{
if (!index.isValid())
return 0;
TreeItem* item = static_cast<TreeItem*>(index.internalPointer());
return item->headerSize();
}
bool TreeModel::hasEmptyHeader(const UModelIndex &index) const
{
if (!index.isValid())
@ -230,6 +254,14 @@ UByteArray TreeModel::body(const UModelIndex &index) const
return item->body();
}
UINT32 TreeModel::bodySize(const UModelIndex& index) const
{
if (!index.isValid())
return 0;
TreeItem* item = static_cast<TreeItem*>(index.internalPointer());
return item->bodySize();
}
bool TreeModel::hasEmptyBody(const UModelIndex &index) const
{
if (!index.isValid())
@ -246,6 +278,14 @@ UByteArray TreeModel::tail(const UModelIndex &index) const
return item->tail();
}
UINT32 TreeModel::tailSize(const UModelIndex& index) const
{
if (!index.isValid())
return 0;
TreeItem* item = static_cast<TreeItem*>(index.internalPointer());
return item->tailSize();
}
bool TreeModel::hasEmptyTail(const UModelIndex &index) const
{
if (!index.isValid())
@ -299,7 +339,7 @@ bool TreeModel::compressed(const UModelIndex &index) const
if (!index.isValid())
return false;
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
return item->compressed();
return item->compressedInherited();
}
void TreeModel::setFixed(const UModelIndex &index, const bool fixed)
@ -315,7 +355,7 @@ void TreeModel::setFixed(const UModelIndex &index, const bool fixed)
if (fixed) {
// Special handling for uncompressed to compressed boundary
if (item->compressed() && item->parent()->compressed() == FALSE) {
if (item->compressedInherited() && item->parent()->compressedInherited() == FALSE) {
item->setFixed(item->parent()->fixed());
return;
}
@ -352,6 +392,14 @@ void TreeModel::TreeModel::setMarkingDarkMode(const bool enabled)
emit dataChanged(UModelIndex(), UModelIndex());
}
void TreeModel::TreeModel::setCStyleHexEnabled(const bool enabled)
{
cStyleHexEnabledFlag = enabled;
setCStyleHexView(enabled);
emit dataChanged(UModelIndex(), UModelIndex());
}
void TreeModel::setMarking(const UModelIndex &index, const UINT8 marking)
{
if (!index.isValid())
@ -501,7 +549,7 @@ void TreeModel::setUncompressedData(const UModelIndex &index, const UByteArray &
UModelIndex TreeModel::addItem(const UINT32 offset, const UINT8 type, const UINT8 subtype,
const UString & name, const UString & text, const UString & info,
const UByteArray & header, const UByteArray & body, const UByteArray & tail,
const UINT32 headerSize, const UINT32 bodySize, const UINT32 tailSize,
const ItemFixedState fixed,
const UModelIndex & parent, const UINT8 mode)
{
@ -524,8 +572,8 @@ UModelIndex TreeModel::addItem(const UINT32 offset, const UINT8 type, const UINT
}
}
TreeItem *newItem = new TreeItem(offset, type, subtype, name, text, info, header, body, tail, Movable, this->compressed(parent), parentItem);
TreeItem* newItem = new TreeItem(offset, type, subtype, name, text, info, headerSize, bodySize, tailSize, Movable, false, parentItem);
if (mode == CREATE_MODE_APPEND) {
emit layoutAboutToBeChanged();
parentItem->appendChild(newItem);
@ -551,6 +599,7 @@ UModelIndex TreeModel::addItem(const UINT32 offset, const UINT8 type, const UINT
UModelIndex created = createIndex(newItem->row(), parentColumn, newItem);
setFixed(created, (bool)fixed); // Non-trivial logic requires additional call
return created;
}
@ -601,7 +650,7 @@ goDeeper:
UModelIndex currentIndex = parentIndex.model()->index(i, 0, parentIndex);
UINT32 currentBase = this->base(currentIndex);
UINT32 fullSize = (UINT32)(header(currentIndex).size() + body(currentIndex).size() + tail(currentIndex).size());
UINT32 fullSize = (UINT32)(headerSize(currentIndex) + bodySize(currentIndex) + tailSize(currentIndex));
if ((compressed(currentIndex) == false || (compressed(currentIndex) == true && compressed(currentIndex.parent()) == false)) // Base is meaningful only for true uncompressed items
&& currentBase <= base && base < currentBase + fullSize) { // Base must be in range [currentBase, currentBase + fullSize)
// Found a better candidate

View file

@ -97,14 +97,15 @@ private:
TreeItem *rootItem;
bool markingEnabledFlag;
bool markingDarkModeFlag;
bool cStyleHexEnabledFlag;
public:
QVariant data(const UModelIndex &index, int role) const;
Qt::ItemFlags flags(const UModelIndex &index) const;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const;
TreeModel(QObject *parent = 0) : QAbstractItemModel(parent), markingEnabledFlag(true), markingDarkModeFlag(false) {
rootItem = new TreeItem(0, Types::Root, 0, UString(), UString(), UString(), UByteArray(), UByteArray(), UByteArray(), true, false);
TreeModel(QObject *parent = 0) : QAbstractItemModel(parent), markingEnabledFlag(true), markingDarkModeFlag(false), cStyleHexEnabledFlag (true) {
rootItem = new TreeItem(0, Types::Root, 0, UString(), UString(), UString(), 0, 0, 0, true, false);
}
#else
@ -116,6 +117,7 @@ private:
TreeItem *rootItem;
bool markingEnabledFlag;
bool markingDarkModeFlag;
bool cStyleHexEnabledFlag;
void dataChanged(const UModelIndex &, const UModelIndex &) {}
void layoutAboutToBeChanged() {}
@ -126,7 +128,7 @@ public:
UString headerData(int section, int orientation, int role = 0) const;
TreeModel() : markingEnabledFlag(false), markingDarkModeFlag(false) {
rootItem = new TreeItem(0, Types::Root, 0, UString(), UString(), UString(), UByteArray(), UByteArray(), UByteArray(), true, false);
rootItem = new TreeItem(0, Types::Root, 0, UString(), UString(), UString(), 0, 0, 0, true, false);
}
bool hasIndex(int row, int column, const UModelIndex &parent = UModelIndex()) const {
@ -142,12 +144,17 @@ public:
delete rootItem;
}
void setImage(const UByteArray& image) { rootItem->setContent(image); }
bool markingEnabled() { return markingEnabledFlag; }
void setMarkingEnabled(const bool enabled);
bool markingDarkMode() { return markingDarkModeFlag; }
void setMarkingDarkMode(const bool enabled);
bool cStyleHexEnabled() { return cStyleHexEnabledFlag; }
void setCStyleHexEnabled(const bool enabled);
UModelIndex index(int row, int column, const UModelIndex &parent = UModelIndex()) const;
UModelIndex parent(const UModelIndex &index) const;
int rowCount(const UModelIndex &parent = UModelIndex()) const;
@ -190,12 +197,15 @@ public:
void setMarking(const UModelIndex &index, const UINT8 marking);
UByteArray header(const UModelIndex &index) const;
UINT32 headerSize(const UModelIndex& index) const;
bool hasEmptyHeader(const UModelIndex &index) const;
UByteArray body(const UModelIndex &index) const;
UINT32 bodySize(const UModelIndex& index) const;
bool hasEmptyBody(const UModelIndex &index) const;
UByteArray tail(const UModelIndex &index) const;
UINT32 tailSize(const UModelIndex& index) const;
bool hasEmptyTail(const UModelIndex &index) const;
UByteArray parsingData(const UModelIndex &index) const;
@ -204,7 +214,7 @@ public:
UModelIndex addItem(const UINT32 offset, const UINT8 type, const UINT8 subtype,
const UString & name, const UString & text, const UString & info,
const UByteArray & header, const UByteArray & body, const UByteArray & tail,
const UINT32 headerSize, const UINT32 bodySize, const UINT32 tailSize,
const ItemFixedState fixed,
const UModelIndex & parent = UModelIndex(), const UINT8 mode = CREATE_MODE_APPEND);

View file

@ -116,6 +116,7 @@ namespace Subtypes {
Reserved1Region,
Reserved2Region,
PttRegion,
InvalidRegion,
};
enum PaddingSubtypes {

View file

@ -46,7 +46,6 @@ public:
uint32_t toUInt(bool* ok = NULL, const uint8_t base = 10) { return (uint32_t)strtoul(d.c_str(), NULL, base); }
int32_t size() const { return (int32_t)d.size(); }
int32_t count(char ch) const { return (int32_t)std::count(d.begin(), d.end(), ch); }
char at(uint32_t i) const { return d.at(i); }
char operator[](uint32_t i) const { return d[i]; }
char& operator[](uint32_t i) { return d[i]; }

View file

@ -14,19 +14,42 @@
#include <cstddef>
#include <cstdint>
#include <stdarg.h>
#include "printf/printf.c"
static bool cStyleHexEnabled = false;
void putchar_(char c) {}
void setCStyleHexView(const bool enable)
{
cStyleHexEnabled = enable;
}
#if defined(QT_CORE_LIB)
typedef struct {
char flag_cstyle_Xh;
UString* string;
} qstring_opt_t;
void qstring_out(char c, void* extra_arg)
{
((qstring_opt_t*)extra_arg)->string->append(c);
}
UString usprintf(const char* fmt, ...)
{
UString msg;
qstring_opt_t opt = { cStyleHexEnabled ? '\1' : '\0', &msg };
va_list vl;
va_start(vl, fmt);
msg = msg.vasprintf(fmt, vl);
int n = vofctprintf(qstring_out, &opt, fmt, vl);
va_end(vl);
return msg;
};
}
UString urepeated(char c, int len)
{
@ -84,7 +107,8 @@ UString usprintf(const char* fmt, ...)
}
va_start(arglist, fmt);
exvsnprintf(r, (char *)b->data, n + 1, fmt, arglist);
*(char*)b->data = cStyleHexEnabled ? '\1' : '\0';
r = vosnprintf_((char*)b->data, n + 1, fmt, arglist);
va_end(arglist);
b->data[n] = '\0';

View file

@ -31,6 +31,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#define ATTRIBUTE_FORMAT_(t,f,a)
#endif
void setCStyleHexView(const bool enable);
UString usprintf(const char* fmt, ...) ATTRIBUTE_FORMAT_(printf, 1, 2);
UString urepeated(char c, int len);
UString uFromUcs2(const char* str, size_t max_len = 0);

View file

@ -429,16 +429,6 @@ UINT32 calculateChecksum32(const UINT32* buffer, UINT32 bufferSize)
return (UINT32)(0x100000000ULL - counter);
}
// Get padding type for a given padding
UINT8 getPaddingType(const UByteArray & padding)
{
if (padding.count('\x00') == padding.size())
return Subtypes::ZeroPadding;
if (padding.count('\xFF') == padding.size())
return Subtypes::OnePadding;
return Subtypes::DataPadding;
}
static inline int char2hex(char c)
{
if (c >= '0' && c <= '9')

View file

@ -59,8 +59,33 @@ UINT16 calculateChecksum16(const UINT16* buffer, UINT32 bufferSize);
// 32bit checksum calculation routine
UINT32 calculateChecksum32(const UINT32* buffer, UINT32 bufferSize);
// Return padding type from it's contents
UINT8 getPaddingType(const UByteArray & padding);
// Check if an array is filled in by a single repeated char
inline signed long checkSingle(const UByteArray& a, signed long defaultRc = -1)
{
size_t s = a.size();
if (!s)
return defaultRc;
if (s == 1 || memcmp(a.constData(), a.constData() + 1, s - 1) == 0)
return (unsigned char)a.at(0);
return -1;
}
// Get padding type for a given padding
inline UINT8 getPaddingType(const UByteArray& a)
{
size_t s = a.size();
if (s) {
if (s == 1 || memcmp(a.constData(), a.constData() + 1, s - 1) == 0) {
switch (a.at(0)) {
case 0:
return Subtypes::ZeroPadding;
case 0xFF:
return Subtypes::OnePadding;
}
}
}
return Subtypes::DataPadding;
}
// Make pattern from a hexstring with an assumption of . being any char
bool makePattern(const CHAR8 *textPattern, std::vector<UINT8> &pattern, std::vector<UINT8> &patternMask);