summaryrefslogtreecommitdiffstats
path: root/heimdall-frontend/Source
diff options
context:
space:
mode:
Diffstat (limited to 'heimdall-frontend/Source')
-rwxr-xr-xheimdall-frontend/Source/FirmwareInfo.cpp188
-rwxr-xr-xheimdall-frontend/Source/FirmwareInfo.h16
-rwxr-xr-xheimdall-frontend/Source/Packaging.cpp496
-rwxr-xr-xheimdall-frontend/Source/Packaging.h16
-rw-r--r--heimdall-frontend/Source/mainwindow.cpp88
-rw-r--r--heimdall-frontend/Source/mainwindow.h2
6 files changed, 624 insertions, 182 deletions
diff --git a/heimdall-frontend/Source/FirmwareInfo.cpp b/heimdall-frontend/Source/FirmwareInfo.cpp
index 7d11605..39ec242 100755
--- a/heimdall-frontend/Source/FirmwareInfo.cpp
+++ b/heimdall-frontend/Source/FirmwareInfo.cpp
@@ -50,7 +50,7 @@ bool DeviceInfo::ParseXml(QXmlStreamReader& xml)
{
if (foundManufacturer)
{
- // TODO: "found multiple device manufacturers."
+ // TODO: "Found multiple device manufacturers."
return (false);
}
@@ -62,7 +62,7 @@ bool DeviceInfo::ParseXml(QXmlStreamReader& xml)
{
if (foundProduct)
{
- // TODO: "found multiple device product identifiers."
+ // TODO: "Found multiple device product identifiers."
return (false);
}
@@ -74,7 +74,7 @@ bool DeviceInfo::ParseXml(QXmlStreamReader& xml)
{
if (foundName)
{
- // TODO: "found multiple device names."));
+ // TODO: "Found multiple device names."));
return (false);
}
@@ -101,6 +101,25 @@ bool DeviceInfo::ParseXml(QXmlStreamReader& xml)
return (false);
}
+void DeviceInfo::WriteXml(QXmlStreamWriter& xml) const
+{
+ xml.writeStartElement("device");
+
+ xml.writeStartElement("manufacturer");
+ xml.writeCharacters(manufacturer);
+ xml.writeEndElement();
+
+ xml.writeStartElement("product");
+ xml.writeCharacters(product);
+ xml.writeEndElement();
+
+ xml.writeStartElement("name");
+ xml.writeCharacters(name);
+ xml.writeEndElement();
+
+ xml.writeEndElement();
+}
+
PlatformInfo::PlatformInfo()
@@ -135,7 +154,7 @@ bool PlatformInfo::ParseXml(QXmlStreamReader& xml)
{
if (foundName)
{
- // TODO: "found multiple platform names."
+ // TODO: "Found multiple platform names."
return (false);
}
@@ -147,7 +166,7 @@ bool PlatformInfo::ParseXml(QXmlStreamReader& xml)
{
if (foundVersion)
{
- // TODO: "found multiple platform versions."
+ // TODO: "Found multiple platform versions."
return (false);
}
@@ -179,6 +198,21 @@ bool PlatformInfo::ParseXml(QXmlStreamReader& xml)
return (false);
}
+void PlatformInfo::WriteXml(QXmlStreamWriter& xml) const
+{
+ xml.writeStartElement("platform");
+
+ xml.writeStartElement("name");
+ xml.writeCharacters(name);
+ xml.writeEndElement();
+
+ xml.writeStartElement("version");
+ xml.writeCharacters(version);
+ xml.writeEndElement();
+
+ xml.writeEndElement();
+}
+
FileInfo::FileInfo()
@@ -206,7 +240,7 @@ bool FileInfo::ParseXml(QXmlStreamReader& xml)
{
if (foundId)
{
- // TODO: "found multiple file IDs."
+ // TODO: "Found multiple file IDs."
return (false);
}
@@ -218,7 +252,7 @@ bool FileInfo::ParseXml(QXmlStreamReader& xml)
{
if (foundFilename)
{
- // TODO: "found multiple file filenames."
+ // TODO: "Found multiple file filenames."
return (false);
}
@@ -245,11 +279,34 @@ bool FileInfo::ParseXml(QXmlStreamReader& xml)
return (false);
}
+void FileInfo::WriteXml(QXmlStreamWriter& xml) const
+{
+ xml.writeStartElement("file");
+
+ xml.writeStartElement("id");
+ xml.writeCharacters(QString::number(partitionId));
+ xml.writeEndElement();
+
+ xml.writeStartElement("filename");
+
+ int lastSlash = filename.lastIndexOf('/');
+
+ if (lastSlash < 0)
+ lastSlash = filename.lastIndexOf('\\');
+
+ xml.writeCharacters(filename.mid(lastSlash + 1));
+
+ xml.writeEndElement();
+
+ xml.writeEndElement();
+}
+
FirmwareInfo::FirmwareInfo()
{
repartition = false;
+ noReboot = false;
}
void FirmwareInfo::Clear(void)
@@ -267,13 +324,15 @@ void FirmwareInfo::Clear(void)
pitFilename.clear();
repartition = false;
+ noReboot = false;
+
fileInfos.clear();
}
bool FirmwareInfo::IsCleared(void) const
{
return (name.isEmpty() && version.isEmpty() && platformInfo.IsCleared() && developers.isEmpty() && url.isEmpty() && url.isEmpty() && donateUrl.isEmpty()
- && deviceInfos.isEmpty() && pitFilename.isEmpty() && !repartition && fileInfos.isEmpty());
+ && deviceInfos.isEmpty() && pitFilename.isEmpty() && !repartition && !noReboot && fileInfos.isEmpty());
}
bool FirmwareInfo::ParseXml(QXmlStreamReader& xml)
@@ -289,6 +348,7 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml)
bool foundDevices = false;
bool foundPit = false;
bool foundRepartition = false;
+ bool foundNoReboot = false;
bool foundFiles = false;
if (!xml.readNextStartElement())
@@ -337,7 +397,7 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml)
{
if (foundName)
{
- // TODO: "found multiple firmware names."
+ // TODO: "Found multiple firmware names."
return (false);
}
@@ -348,7 +408,7 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml)
{
if (foundVersion)
{
- // TODO: "found multiple firmware versions."
+ // TODO: "Found multiple firmware versions."
return (false);
}
@@ -359,7 +419,7 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml)
{
if (foundPlatform)
{
- // TODO: "found multiple firmware platforms."
+ // TODO: "Found multiple firmware platforms."
return (false);
}
@@ -372,7 +432,7 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml)
{
if (foundDevelopers)
{
- // TODO: "found multiple sets of firmware developers."
+ // TODO: "Found multiple sets of firmware developers."
return (false);
}
@@ -406,7 +466,7 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml)
{
if (foundUrl)
{
- // TODO: "found multiple firmware URLs."
+ // TODO: "Found multiple firmware URLs."
return (false);
}
@@ -418,7 +478,7 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml)
{
if (foundDonateUrl)
{
- // TODO: "found multiple firmware donate URLs."
+ // TODO: "Found multiple firmware donate URLs."
return (false);
}
@@ -430,7 +490,7 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml)
{
if (foundDevices)
{
- // TODO: "found multiple sets of firmware devices."
+ // TODO: "Found multiple sets of firmware devices."
return (false);
}
@@ -471,7 +531,7 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml)
{
if (foundPit)
{
- // TODO: "found multiple firmware PIT files."
+ // TODO: "Found multiple firmware PIT files."
return (false);
}
@@ -483,7 +543,7 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml)
{
if (foundRepartition)
{
- // TODO: "found multiple firmware repartition values."
+ // TODO: "Found multiple firmware repartition values."
return (false);
}
@@ -491,11 +551,23 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml)
repartition = (xml.readElementText().toInt() != 0);
}
+ else if (xml.name() == "noreboot")
+ {
+ if (foundNoReboot)
+ {
+ // TODO: "Found multiple firmware noreboot values."
+ return (false);
+ }
+
+ foundNoReboot = true;
+
+ noReboot = (xml.readElementText().toInt() != 0);
+ }
else if (xml.name() == "files")
{
if (foundFiles)
{
- // TODO: "found multiple sets of firmware files."
+ // TODO: "Found multiple sets of firmware files."
return (false);
}
@@ -542,7 +614,7 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml)
{
if (xml.name() == "firmware")
{
- if (!(foundName && foundVersion && foundPlatform && foundDevelopers && foundDevices && foundPit && foundRepartition && foundFiles))
+ if (!(foundName && foundVersion && foundPlatform && foundDevelopers && foundDevices && foundPit && foundRepartition && foundNoReboot && foundFiles))
return (false);
else
break;
@@ -569,3 +641,81 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml)
return (true);
}
+
+void FirmwareInfo::WriteXml(QXmlStreamWriter& xml) const
+{
+ xml.writeStartDocument();
+ xml.writeStartElement("firmware");
+ xml.writeAttribute("version", QString::number(FirmwareInfo::kVersion));
+
+ xml.writeStartElement("name");
+ xml.writeCharacters(name);
+ xml.writeEndElement();
+
+ xml.writeStartElement("version");
+ xml.writeCharacters(version);
+ xml.writeEndElement();
+
+ platformInfo.WriteXml(xml);
+
+ xml.writeStartElement("developers");
+
+ for (int i = 0; i < developers.length(); i++)
+ {
+ xml.writeStartElement("name");
+ xml.writeCharacters(developers[i]);
+ xml.writeEndElement();
+ }
+
+ xml.writeEndElement();
+
+ if (!url.isEmpty())
+ {
+ xml.writeStartElement("url");
+ xml.writeCharacters(url);
+ xml.writeEndElement();
+ }
+
+ if (!donateUrl.isEmpty())
+ {
+ xml.writeStartElement("donateurl");
+ xml.writeCharacters(donateUrl);
+ xml.writeEndElement();
+ }
+
+ xml.writeStartElement("devices");
+
+ for (int i = 0; i < deviceInfos.length(); i++)
+ deviceInfos[i].WriteXml(xml);
+
+ xml.writeEndElement();
+
+ xml.writeStartElement("pit");
+
+ int lastSlash = pitFilename.lastIndexOf('/');
+
+ if (lastSlash < 0)
+ lastSlash = pitFilename.lastIndexOf('\\');
+
+ xml.writeCharacters(pitFilename.mid(lastSlash + 1));
+
+ xml.writeEndElement();
+
+ xml.writeStartElement("repartition");
+ xml.writeCharacters((repartition) ? "1" : "0");
+ xml.writeEndElement();
+
+ xml.writeStartElement("noreboot");
+ xml.writeCharacters((noReboot) ? "1" : "0");
+ xml.writeEndElement();
+
+ xml.writeStartElement("files");
+
+ for (int i = 0; i < fileInfos.length(); i++)
+ fileInfos[i].WriteXml(xml);
+
+ xml.writeEndElement();
+
+ xml.writeEndElement();
+ xml.writeEndDocument();
+}
diff --git a/heimdall-frontend/Source/FirmwareInfo.h b/heimdall-frontend/Source/FirmwareInfo.h
index a72dab1..3fd6341 100755
--- a/heimdall-frontend/Source/FirmwareInfo.h
+++ b/heimdall-frontend/Source/FirmwareInfo.h
@@ -42,6 +42,7 @@ namespace HeimdallFrontend
DeviceInfo(const QString& manufacturer, const QString& product, const QString& name);
bool ParseXml(QXmlStreamReader& xml);
+ void WriteXml(QXmlStreamWriter& xml) const;
const QString& GetManufacturer(void) const
{
@@ -89,6 +90,7 @@ namespace HeimdallFrontend
bool IsCleared(void) const;
bool ParseXml(QXmlStreamReader& xml);
+ void WriteXml(QXmlStreamWriter& xml) const;
const QString& GetName(void) const
{
@@ -124,6 +126,7 @@ namespace HeimdallFrontend
FileInfo(unsigned int partitionId, const QString& filename);
bool ParseXml(QXmlStreamReader& xml);
+ void WriteXml(QXmlStreamWriter& xml) const;
unsigned int GetPartitionId(void) const
{
@@ -170,6 +173,8 @@ namespace HeimdallFrontend
QString pitFilename;
bool repartition;
+ bool noReboot;
+
QList<FileInfo> fileInfos;
public:
@@ -180,6 +185,7 @@ namespace HeimdallFrontend
bool IsCleared(void) const;
bool ParseXml(QXmlStreamReader& xml);
+ void WriteXml(QXmlStreamWriter& xml) const;
const QString& GetName(void) const
{
@@ -271,6 +277,16 @@ namespace HeimdallFrontend
this->repartition = repartition;
}
+ bool GetNoReboot(void) const
+ {
+ return (noReboot);
+ }
+
+ void SetNoReboot(bool noReboot)
+ {
+ this->noReboot = noReboot;
+ }
+
const QList<FileInfo>& GetFileInfos(void) const
{
return (fileInfos);
diff --git a/heimdall-frontend/Source/Packaging.cpp b/heimdall-frontend/Source/Packaging.cpp
index cbd03a4..1034539 100755
--- a/heimdall-frontend/Source/Packaging.cpp
+++ b/heimdall-frontend/Source/Packaging.cpp
@@ -18,6 +18,10 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.*/
+#ifdef WIN32
+#pragma warning(disable : 4996)
+#endif
+
// C/C++ Standard Library
#include <stdio.h>
@@ -27,6 +31,7 @@
// Qt
#include <QDateTime>
#include <QDir>
+#include <QProgressDialog>
// Heimdall Frontend
#include "Packaging.h"
@@ -35,20 +40,44 @@ using namespace HeimdallFrontend;
const char *Packaging::ustarMagic = "ustar";
-bool Packaging::ExtractTar(QTemporaryFile& tarFile, PackageData *outputPackageData)
+bool Packaging::ExtractTar(QTemporaryFile& tarFile, PackageData *packageData)
{
TarHeader tarHeader;
- tarFile.reset();
+ if (!tarFile.open())
+ {
+ // TODO: "Error opening temporary TAR archive."
+ return (false);
+ }
bool previousEmpty = false;
+ QProgressDialog progressDialog("Extracting files...", "Cancel", 0, tarFile.size());
+ progressDialog.setWindowModality(Qt::ApplicationModal);
+ progressDialog.setWindowTitle("Heimdall Frontend");
+
while (!tarFile.atEnd())
{
qint64 dataRead = tarFile.read(tarHeader.buffer, TarHeader::kBlockLength);
if (dataRead != TarHeader::kBlockLength)
+ {
+ // TODO: "Package's TAR archive is malformed."
+ tarFile.close();
+ progressDialog.close();
+
return (false);
+ }
+
+ progressDialog.setValue(tarFile.pos());
+
+ if (progressDialog.wasCanceled())
+ {
+ tarFile.close();
+ progressDialog.close();
+
+ return (false);
+ }
bool ustarFormat = strcmp(tarHeader.fields.magic, ustarMagic) == 0;
bool empty = true;
@@ -73,7 +102,20 @@ bool Packaging::ExtractTar(QTemporaryFile& tarFile, PackageData *outputPackageDa
}
else
{
- // TODO: Check checksum
+ int checksum = 0;
+
+ for (char *bufferIndex = tarHeader.buffer; bufferIndex < tarHeader.fields.checksum; bufferIndex++)
+ checksum += static_cast<unsigned char>(*bufferIndex);
+
+ checksum += 8 * ' ';
+ checksum += static_cast<unsigned char>(tarHeader.fields.typeFlag);
+
+ // Both the TAR and USTAR formats have terrible documentation, it's not clear if the following code is required.
+ /*if (ustarFormat)
+ {
+ for (char *bufferIndex = tarHeader.fields.linkName; bufferIndex < tarHeader.fields.prefix + 155; bufferIndex++)
+ checksum += static_cast<unsigned char>(*bufferIndex);
+ }*/
bool parsed = false;
@@ -87,6 +129,7 @@ bool Packaging::ExtractTar(QTemporaryFile& tarFile, PackageData *outputPackageDa
if (!parsed)
{
// TODO: Error message?
+ tarFile.close();
return (false);
}
@@ -95,14 +138,15 @@ bool Packaging::ExtractTar(QTemporaryFile& tarFile, PackageData *outputPackageDa
// We're working with a file.
QString filename = QString::fromUtf8(tarHeader.fields.name);
- // This is slightly pointless as we don't support directories...
- if (ustarFormat)
- filename.prepend(tarHeader.fields.prefix);
-
QTemporaryFile *outputFile = new QTemporaryFile("XXXXXX-" + filename);
- outputFile->open();
+ packageData->GetFiles().append(outputFile);
- outputPackageData->GetFiles().append(outputFile);
+ if (!outputFile->open())
+ {
+ // TODO: "Failed to open output file \"%s\""
+ tarFile.close();
+ return (false);
+ }
qulonglong dataRemaining = fileSize;
char readBuffer[TarHeader::kBlockReadCount * TarHeader::kBlockLength];
@@ -116,18 +160,41 @@ bool Packaging::ExtractTar(QTemporaryFile& tarFile, PackageData *outputPackageDa
qint64 dataRead = tarFile.read(readBuffer, fileDataToRead + (TarHeader::kBlockLength - fileDataToRead % TarHeader::kBlockLength) % TarHeader::kBlockLength);
if (dataRead < fileDataToRead || dataRead % TarHeader::kBlockLength != 0)
+ {
+ // TODO: "Unexpected error extracting package files."
+ tarFile.close();
+ outputFile->close();
+
+ remove(outputFile->fileName().toStdString().c_str());
+
return (false);
+ }
outputFile->write(readBuffer, fileDataToRead);
dataRemaining -= fileDataToRead;
+
+ progressDialog.setValue(tarFile.pos());
+
+ if (progressDialog.wasCanceled())
+ {
+ tarFile.close();
+ outputFile->close();
+
+ remove(outputFile->fileName().toStdString().c_str());
+
+ progressDialog.close();
+
+ return (false);
+ }
}
outputFile->close();
}
else
{
- // We don't support links/directories.
+ // TODO: "Heimdall packages shouldn't contain links or directories."
+ tarFile.close();
return (false);
}
}
@@ -135,195 +202,320 @@ bool Packaging::ExtractTar(QTemporaryFile& tarFile, PackageData *outputPackageDa
previousEmpty = empty;
}
+ progressDialog.close();
+ tarFile.close();
+
return (true);
}
-bool Packaging::CreateTar(const PackageData& packageData, QTemporaryFile *outputTarFile)
+bool Packaging::WriteTarEntry(const QString& filename, QTemporaryFile *tarFile, bool firmwareXml)
{
- const QList<FileInfo>& fileInfos = packageData.GetFirmwareInfo().GetFileInfos();
+ TarHeader tarHeader;
+ memset(tarHeader.buffer, 0, TarHeader::kBlockLength);
+
+ QFile file(filename);
- if (!outputTarFile->open())
+ if (!file.open(QFile::ReadOnly))
{
// TODO: "Failed to open \"%s\""
return (false);
}
- bool failure = false;
-
- TarHeader tarHeader;
-
- for (int i = 0; i < fileInfos.length(); i++)
+ if (file.size() > TarHeader::kMaxFileSize)
{
- memset(tarHeader.buffer, 0, TarHeader::kBlockLength);
+ // TODO: "File is too large to packaged"
+ return (false);
+ }
- QFile file(fileInfos[i].GetFilename());
+ QFileInfo qtFileInfo(file);
+ QByteArray utfFilename;
- if (!file.open(QFile::ReadOnly))
- {
- // TODO: "Failed to open \"%s\""
- failure = true;
- break;
- }
+ if (firmwareXml)
+ {
+ utfFilename = QString("firmware.xml").toUtf8();
+ }
+ else
+ {
+ utfFilename = qtFileInfo.fileName().toUtf8();
- if (file.size() > TarHeader::kMaxFileSize)
+ if (utfFilename.length() > 100)
{
- // TODO: "File is too large to packaged"
- failure = true;
- break;
+ // TODO: "Filename is too long"
+ return (false);
}
+ }
- QFileInfo qtFileInfo(file);
- strcpy(tarHeader.fields.name, qtFileInfo.fileName().toUtf8().constData());
+ strcpy(tarHeader.fields.name, utfFilename.constData());
- unsigned int mode = 0;
-
- QFile::Permissions permissions = file.permissions();
-
- // Other
- if (permissions.testFlag(QFile::ExeOther))
- mode |= TarHeader::kModeOtherExecute;
- if (permissions.testFlag(QFile::WriteOther))
- mode |= TarHeader::kModeOtherWrite;
- if (permissions.testFlag(QFile::ReadOther))
- mode |= TarHeader::kModeOtherRead;
-
- // Group
- if (permissions.testFlag(QFile::ExeGroup))
- mode |= TarHeader::kModeGroupExecute;
- if (permissions.testFlag(QFile::WriteGroup))
- mode |= TarHeader::kModeGroupWrite;
- if (permissions.testFlag(QFile::ReadGroup))
- mode |= TarHeader::kModeGroupRead;
-
- // Owner
- if (permissions.testFlag(QFile::ExeOwner))
- mode |= TarHeader::kModeOwnerExecute;
- if (permissions.testFlag(QFile::WriteOwner))
- mode |= TarHeader::kModeOwnerWrite;
- if (permissions.testFlag(QFile::ReadOwner))
- mode |= TarHeader::kModeOwnerRead;
-
- sprintf(tarHeader.fields.mode, "%o", mode);
-
- sprintf(tarHeader.fields.userId, "%o", qtFileInfo.ownerId());
- sprintf(tarHeader.fields.groupId, "%o", qtFileInfo.groupId());
-
- // Note: We don't support base-256 encoding. Support could be added in future.
- sprintf(tarHeader.fields.size, "%o", file.size());
-
- sprintf(tarHeader.fields.modifiedTime, "%o", qtFileInfo.lastModified().toMSecsSinceEpoch());
+ unsigned int mode = 0;
+
+ QFile::Permissions permissions = file.permissions();
+
+ // Other
+ if (permissions.testFlag(QFile::ExeOther))
+ mode |= TarHeader::kModeOtherExecute;
+ if (permissions.testFlag(QFile::WriteOther))
+ mode |= TarHeader::kModeOtherWrite;
+ if (permissions.testFlag(QFile::ReadOther))
+ mode |= TarHeader::kModeOtherRead;
+
+ // Group
+ if (permissions.testFlag(QFile::ExeGroup))
+ mode |= TarHeader::kModeGroupExecute;
+ if (permissions.testFlag(QFile::WriteGroup))
+ mode |= TarHeader::kModeGroupWrite;
+ if (permissions.testFlag(QFile::ReadGroup))
+ mode |= TarHeader::kModeGroupRead;
+
+ // Owner
+ if (permissions.testFlag(QFile::ExeOwner))
+ mode |= TarHeader::kModeOwnerExecute;
+ if (permissions.testFlag(QFile::WriteOwner))
+ mode |= TarHeader::kModeOwnerWrite;
+ if (permissions.testFlag(QFile::ReadOwner))
+ mode |= TarHeader::kModeOwnerRead;
+
+ sprintf(tarHeader.fields.mode, "%07o", mode);
+
+ // Owner id
+ uint id = qtFileInfo.ownerId();
+
+ if (id < 2097151)
+ sprintf(tarHeader.fields.userId, "%07o", id);
+ else
+ sprintf(tarHeader.fields.userId, "%07o", 0);
+
+ // Group id
+ id = qtFileInfo.groupId();
+
+ if (id < 2097151)
+ sprintf(tarHeader.fields.groupId, "%07o", id);
+ else
+ sprintf(tarHeader.fields.groupId, "%07o", 0);
+
+ // Note: We don't support base-256 encoding. Support could be added later.
+ sprintf(tarHeader.fields.size, "%011o", file.size());
+ sprintf(tarHeader.fields.modifiedTime, "%011o", qtFileInfo.lastModified().toMSecsSinceEpoch() / 1000);
- // Regular File
- tarHeader.fields.typeFlag = '0';
+ // Regular File
+ tarHeader.fields.typeFlag = '0';
- // Calculate checksum
- int checksum = 0;
+ // Calculate checksum
+ int checksum = 0;
+ memset(tarHeader.fields.checksum, ' ', 8);
+
+ for (int i = 0; i < TarHeader::kTarHeaderLength; i++)
+ checksum += static_cast<unsigned char>(tarHeader.buffer[i]);
- for (int i = 0; i < TarHeader::kTarHeaderLength; i++)
- checksum += tarHeader.buffer[i];
+ sprintf(tarHeader.fields.checksum, "%07o", checksum);
- sprintf(tarHeader.fields.checksum, "%o", checksum);
+ // Write the header to the TAR file.
+ tarFile->write(tarHeader.buffer, TarHeader::kBlockLength);
- // Write the header to the TAR file.
- outputTarFile->write(tarHeader.buffer, TarHeader::kBlockLength);
+ char buffer[TarHeader::kBlockWriteCount * TarHeader::kBlockLength];
+ qint64 offset = 0;
- char buffer[TarHeader::kBlockWriteCount * TarHeader::kBlockLength];
+ while (offset < file.size())
+ {
+ qint64 dataRead = file.read(buffer, TarHeader::kBlockWriteCount * TarHeader::kBlockLength);
+
+ if (tarFile->write(buffer, dataRead) != dataRead)
+ {
+ // TODO: "Failed to write data to the temporary TAR file."
+ return (false);
+ }
- for (qint64 i = 0; i < file.size(); i++)
+ if (dataRead % TarHeader::kBlockLength != 0)
{
- qint64 dataRead = file.read(buffer, TarHeader::kBlockWriteCount * TarHeader::kBlockLength);
+ int remainingBlockLength = TarHeader::kBlockLength - dataRead % TarHeader::kBlockLength;
+ memset(buffer, 0, remainingBlockLength);
- if (outputTarFile->write(buffer, dataRead) != dataRead)
+ if (tarFile->write(buffer, remainingBlockLength) != remainingBlockLength)
{
// TODO: "Failed to write data to the temporary TAR file."
- failure = true;
- break;
+ return (false);
}
+ }
- if (dataRead % TarHeader::kBlockLength != 0)
- {
- int remainingBlockLength = TarHeader::kBlockLength - dataRead % TarHeader::kBlockLength;
- memset(buffer, 0, remainingBlockLength);
+ offset += dataRead;
+ }
- if (outputTarFile->write(buffer, remainingBlockLength) != remainingBlockLength)
- {
- // TODO: "Failed to write data to the temporary TAR file."
- failure = true;
- break;
- }
- }
+ return (true);
+}
+
+bool Packaging::CreateTar(const FirmwareInfo& firmwareInfo, QTemporaryFile *tarFile)
+{
+ const QList<FileInfo>& fileInfos = firmwareInfo.GetFileInfos();
+
+ QProgressDialog progressDialog("Packaging files...", "Cancel", 0, fileInfos.length() + 2);
+ progressDialog.setWindowModality(Qt::ApplicationModal);
+ progressDialog.setWindowTitle("Heimdall Frontend");
+
+ QTemporaryFile firmwareXmlFile("XXXXXX-firmware.xml");
+
+ if (!firmwareXmlFile.open())
+ {
+ // TODO: "Failed to create temporary file "%s"
+ return (false);
+ }
+
+ firmwareInfo.WriteXml(QXmlStreamWriter(&firmwareXmlFile));
+
+ firmwareXmlFile.close();
+
+ if (!tarFile->open())
+ {
+ // TODO: "Failed to open \"%s\""
+ return (false);
+ }
+
+ for (int i = 0; i < fileInfos.length(); i++)
+ {
+ if (!WriteTarEntry(fileInfos[i].GetFilename(), tarFile))
+ {
+ tarFile->resize(0);
+ tarFile->close();
+
+ progressDialog.close();
- i += dataRead;
+ return (false);
}
- if (failure)
- break;
+ progressDialog.setValue(i);
+
+ if (progressDialog.wasCanceled())
+ {
+ tarFile->resize(0);
+ tarFile->close();
+
+ progressDialog.close();
+
+ return (false);
+ }
}
- if (failure)
+ if (!WriteTarEntry(firmwareInfo.GetPitFilename(), tarFile))
{
- outputTarFile->resize(0);
- outputTarFile->close();
+ tarFile->resize(0);
+ tarFile->close();
+
return (false);
}
+ progressDialog.setValue(progressDialog.value() + 1);
+
+ if (progressDialog.wasCanceled())
+ {
+ tarFile->resize(0);
+ tarFile->close();
+
+ progressDialog.close();
+
+ return (false);
+ }
+
+ if (!WriteTarEntry(firmwareXmlFile.fileName(), tarFile, true))
+ {
+ tarFile->resize(0);
+ tarFile->close();
+
+ return (false);
+ }
+
+ progressDialog.setValue(progressDialog.value() + 1);
+ progressDialog.close();
+
// Write two empty blocks to signify the end of the archive.
- memset(tarHeader.buffer, 0, TarHeader::kBlockLength);
- outputTarFile->write(tarHeader.buffer, TarHeader::kBlockLength);
- outputTarFile->write(tarHeader.buffer, TarHeader::kBlockLength);
+ char emptyEntry[TarHeader::kBlockLength];
+ memset(emptyEntry, 0, TarHeader::kBlockLength);
+
+ tarFile->write(emptyEntry, TarHeader::kBlockLength);
+ tarFile->write(emptyEntry, TarHeader::kBlockLength);
- outputTarFile->close();
+ tarFile->close();
return (true);
}
-bool Packaging::ExtractPackage(const QString& packagePath, PackageData *outputPackageData)
+bool Packaging::ExtractPackage(const QString& packagePath, PackageData *packageData)
{
FILE *compressedPackageFile = fopen(packagePath.toStdString().c_str(), "rb");
+
+ if (fopen == NULL)
+ {
+ // TODO: "Failed to open package \"%s\"."
+ return (false);
+ }
+
+ fseek(compressedPackageFile, 0, SEEK_END);
+ quint64 compressedFileSize = ftell(compressedPackageFile);
+ rewind(compressedPackageFile);
+
gzFile packageFile = gzdopen(fileno(compressedPackageFile), "rb");
QTemporaryFile outputTar("XXXXXX.tar");
if (!outputTar.open())
{
+ // TODO: "Error opening temporary TAR archive."
gzclose(packageFile);
return (false);
}
- char buffer[32768];
-
+ char buffer[kExtractBufferLength];
int bytesRead;
+ quint64 totalBytesRead = 0;
+
+ QProgressDialog progressDialog("Decompressing package...", "Cancel", 0, compressedFileSize);
+ progressDialog.setWindowModality(Qt::ApplicationModal);
+ progressDialog.setWindowTitle("Heimdall Frontend");
do
{
- bytesRead = gzread(packageFile, buffer, 32768);
+ bytesRead = gzread(packageFile, buffer, kExtractBufferLength);
if (bytesRead == -1)
{
+ // TODO: "Error decompressing archive."
gzclose(packageFile);
+ progressDialog.close();
return (false);
}
outputTar.write(buffer, bytesRead);
+
+ totalBytesRead += bytesRead;
+ progressDialog.setValue(totalBytesRead);
+
+ if (progressDialog.wasCanceled())
+ {
+ gzclose(packageFile);
+ progressDialog.close();
+
+ return (false);
+ }
} while (bytesRead > 0);
+ progressDialog.close();
+
+ outputTar.close();
gzclose(packageFile); // Closes packageFile and compressedPackageFile
- if (!ExtractTar(outputTar, outputPackageData))
+ if (!ExtractTar(outputTar, packageData))
return (false);
// Find and read firmware.xml
- for (int i = 0; i < outputPackageData->GetFiles().length(); i++)
+ for (int i = 0; i < packageData->GetFiles().length(); i++)
{
- QTemporaryFile *file = outputPackageData->GetFiles()[i];
+ QTemporaryFile *file = packageData->GetFiles()[i];
if (file->fileTemplate() == "XXXXXX-firmware.xml")
{
- if (!outputPackageData->ReadFirmwareInfo(file))
+ if (!packageData->ReadFirmwareInfo(file))
{
- outputPackageData->Clear();
+ packageData->Clear();
return (false);
}
@@ -334,12 +526,82 @@ bool Packaging::ExtractPackage(const QString& packagePath, PackageData *outputPa
return (false);
}
-bool Packaging::BuildPackage(const QString& packagePath, const PackageData& packageData)
+bool Packaging::BuildPackage(const QString& packagePath, const FirmwareInfo& firmwareInfo)
{
- QTemporaryFile temporaryFile("XXXXXX.tar");
+ FILE *compressedPackageFile = fopen(packagePath.toStdString().c_str(), "wb");
- if (!CreateTar(packageData, &temporaryFile))
+ if (fopen == NULL)
+ {
+ // TODO: "Failed to open package "%s"
return (false);
+ }
+
+ QTemporaryFile tar("XXXXXX.tar");
+
+ if (!CreateTar(firmwareInfo, &tar))
+ return (false);
+
+ if (!tar.open())
+ {
+ // TODO: "Failed to open temporary file "%s"
+ fclose(compressedPackageFile);
+ remove(packagePath.toStdString().c_str());
+ return (false);
+ }
+
+ gzFile packageFile = gzdopen(fileno(compressedPackageFile), "wb");
+
+ char buffer[kCompressBufferLength];
+ qint64 totalBytesRead = 0;
+ int bytesRead;
+
+ QProgressDialog progressDialog("Compressing package...", "Cancel", 0, tar.size());
+ progressDialog.setWindowModality(Qt::ApplicationModal);
+ progressDialog.setWindowTitle("Heimdall Frontend");
+
+ do
+ {
+ bytesRead = tar.read(buffer, kCompressBufferLength);
+
+ if (bytesRead == -1)
+ {
+ // TODO: "Error reading temporary TAR file."
+ gzclose(packageFile);
+ remove(packagePath.toStdString().c_str());
+
+ progressDialog.close();
+
+ return (false);
+ }
+
+ if (gzwrite(packageFile, buffer, bytesRead) != bytesRead)
+ {
+ // TODO: "Error compressing package."
+ gzclose(packageFile);
+ remove(packagePath.toStdString().c_str());
+
+ progressDialog.close();
+
+ return (false);
+ }
+
+ totalBytesRead += bytesRead;
+ progressDialog.setValue(totalBytesRead);
+
+ if (progressDialog.wasCanceled())
+ {
+ gzclose(packageFile);
+ remove(packagePath.toStdString().c_str());
+
+ progressDialog.close();
+
+ return (false);
+ }
+ } while (bytesRead > 0);
+
+ progressDialog.close();
+
+ gzclose(packageFile); // Closes packageFile and compressedPackageFile
return (true);
}
diff --git a/heimdall-frontend/Source/Packaging.h b/heimdall-frontend/Source/Packaging.h
index c341f0f..97637de 100755
--- a/heimdall-frontend/Source/Packaging.h
+++ b/heimdall-frontend/Source/Packaging.h
@@ -93,17 +93,25 @@ namespace HeimdallFrontend
class Packaging
{
private:
+
+ enum
+ {
+ kExtractBufferLength = 262144,
+ kCompressBufferLength = 262144
+ };
// TODO: Add support for sparse files to both methods.
- static bool ExtractTar(QTemporaryFile& tarFile, PackageData *outputPackageData);
- static bool CreateTar(const PackageData& packageData, QTemporaryFile *outputTarFile); // Uses original TAR format.
+ static bool ExtractTar(QTemporaryFile& tarFile, PackageData *packageData);
+
+ static bool WriteTarEntry(const QString& filename, QTemporaryFile *tarFile, bool firmwareXml = false);
+ static bool CreateTar(const FirmwareInfo& firmwareInfo, QTemporaryFile *tarFile); // Uses original TAR format.
public:
static const char *ustarMagic;
- static bool ExtractPackage(const QString& packagePath, PackageData *outputPackageData);
- static bool BuildPackage(const QString& packagePath, const PackageData& packageData);
+ static bool ExtractPackage(const QString& packagePath, PackageData *packageData);
+ static bool BuildPackage(const QString& packagePath, const FirmwareInfo& firmwareInfo);
};
}
diff --git a/heimdall-frontend/Source/mainwindow.cpp b/heimdall-frontend/Source/mainwindow.cpp
index 8043599..b44c3de 100644
--- a/heimdall-frontend/Source/mainwindow.cpp
+++ b/heimdall-frontend/Source/mainwindow.cpp
@@ -91,6 +91,7 @@ void MainWindow::UpdatePackageUserInterface(void)
developerDonateButton->setEnabled(false);
repartitionRadioButton->setChecked(false);
+ noRebootRadioButton->setChecked(false);
loadFirmwareButton->setEnabled(false);
}
@@ -126,7 +127,7 @@ void MainWindow::UpdatePackageUserInterface(void)
for (int i = 0; i < loadedPackageData.GetFirmwareInfo().GetDeviceInfos().length(); i++)
{
const DeviceInfo& deviceInfo = loadedPackageData.GetFirmwareInfo().GetDeviceInfos()[i];
- supportedDevicesListWidget->addItem(deviceInfo.GetManufacturer() + " " + deviceInfo.GetName() + " (" + deviceInfo.GetProduct() + ")");
+ supportedDevicesListWidget->addItem(deviceInfo.GetManufacturer() + " " + deviceInfo.GetName() + ": " + deviceInfo.GetProduct());
}
for (int i = 0; i < loadedPackageData.GetFirmwareInfo().GetFileInfos().length(); i++)
@@ -136,6 +137,7 @@ void MainWindow::UpdatePackageUserInterface(void)
}
repartitionRadioButton->setChecked(loadedPackageData.GetFirmwareInfo().GetRepartition());
+ noRebootRadioButton->setChecked(loadedPackageData.GetFirmwareInfo().GetNoReboot());
loadFirmwareButton->setEnabled(true);
}
@@ -143,7 +145,7 @@ void MainWindow::UpdatePackageUserInterface(void)
bool MainWindow::IsArchive(QString path)
{
- // Not a real check but hopefully it gets the message across, don't flash archives!
+ // Not a real check but hopefully it gets the message across, don't directly flash archives!
return (path.endsWith(".tar", Qt::CaseInsensitive) || path.endsWith(".gz", Qt::CaseInsensitive) || path.endsWith(".zip", Qt::CaseInsensitive)
|| path.endsWith(".bz2", Qt::CaseInsensitive) || path.endsWith(".7z", Qt::CaseInsensitive) || path.endsWith(".rar", Qt::CaseInsensitive));
}
@@ -266,7 +268,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
QObject::connect(partitionFileBrowseButton, SIGNAL(clicked()), this, SLOT(SelectPartitionFile()));
QObject::connect(pitBrowseButton, SIGNAL(clicked()), this, SLOT(SelectPit()));
+
QObject::connect(repartitionCheckBox, SIGNAL(stateChanged(int)), this, SLOT(SetRepartition(int)));
+ QObject::connect(noRebootCheckBox, SIGNAL(stateChanged(int)), this, SLOT(SetNoReboot(int)));
QObject::connect(startFlashButton, SIGNAL(clicked()), this, SLOT(StartFlash()));
@@ -394,6 +398,7 @@ void MainWindow::LoadFirmwarePackage(void)
UpdateUnusedPartitionIds();
workingPackageData.GetFirmwareInfo().SetRepartition(loadedPackageData.GetFirmwareInfo().GetRepartition());
+ workingPackageData.GetFirmwareInfo().SetNoReboot(loadedPackageData.GetFirmwareInfo().GetNoReboot());
loadedPackageData.Clear();
UpdatePackageUserInterface();
@@ -431,6 +436,9 @@ void MainWindow::LoadFirmwarePackage(void)
repartitionCheckBox->setEnabled(true);
repartitionCheckBox->setChecked(workingPackageData.GetFirmwareInfo().GetRepartition());
+ noRebootCheckBox->setEnabled(true);
+ noRebootCheckBox->setChecked(workingPackageData.GetFirmwareInfo().GetNoReboot());
+
partitionsListWidget->setEnabled(true);
addPartitionButton->setEnabled(true);
removePartitionButton->setEnabled(true && partitionsListWidget->currentRow() >= 0);
@@ -466,7 +474,7 @@ void MainWindow::SelectPartitionFile(void)
{
QString path = PromptFileSelection();
- if (path != "")
+ if (path != "" && !IsArchive(path))
{
workingPackageData.GetFirmwareInfo().GetFileInfos()[partitionsListWidget->currentRow()].SetFilename(path);
partitionFileLineEdit->setText(path);
@@ -608,6 +616,7 @@ void MainWindow::SelectPit(void)
pitLineEdit->setText(workingPackageData.GetFirmwareInfo().GetPitFilename());
repartitionCheckBox->setEnabled(validPit);
+ noRebootCheckBox->setEnabled(validPit);
partitionsListWidget->setEnabled(validPit);
addPartitionButton->setEnabled(validPit);
@@ -620,24 +629,41 @@ void MainWindow::SetRepartition(int enabled)
{
workingPackageData.GetFirmwareInfo().SetRepartition(enabled);
}
+void MainWindow::SetNoReboot(int enabled)
+{
+ workingPackageData.GetFirmwareInfo().SetNoReboot(enabled);
+}
void MainWindow::StartFlash(void)
{
+ outputPlainTextEdit->clear();
+
heimdallRunning = true;
heimdallFailed = false;
+
+ const FirmwareInfo& firmwareInfo = workingPackageData.GetFirmwareInfo();
+ const QList<FileInfo>& fileInfos = firmwareInfo.GetFileInfos();
QStringList arguments;
arguments.append("flash");
- if (repartitionCheckBox->isChecked())
- {
+ if (firmwareInfo.GetRepartition())
arguments.append("--repartition");
- arguments.append("--pit");
- arguments.append(pitLineEdit->text());
+ arguments.append("--pit");
+ arguments.append(firmwareInfo.GetPitFilename());
+
+ for (int i = 0; i < fileInfos.length(); i++)
+ {
+ QString flag;
+ flag.sprintf("--%u", fileInfos[i].GetPartitionId());
+
+ arguments.append(flag);
+ arguments.append(fileInfos[i].GetFilename());
}
- // TODO: Loop through partitions and append them.
+ if (firmwareInfo.GetNoReboot())
+ arguments.append("--no-reboot");
flashProgressBar->setEnabled(true);
UpdateStartButton();
@@ -785,10 +811,10 @@ void MainWindow::SelectDevice(int row)
void MainWindow::AddDevice(void)
{
- workingPackageData.GetFirmwareInfo().GetDeviceInfos().append(DeviceInfo(deviceManufacturerLineEdit->text(), deviceNameLineEdit->text(),
- deviceProductCodeLineEdit->text()));
+ workingPackageData.GetFirmwareInfo().GetDeviceInfos().append(DeviceInfo(deviceManufacturerLineEdit->text(), deviceProductCodeLineEdit->text(),
+ deviceNameLineEdit->text()));
- createDevicesListWidget->addItem(deviceManufacturerLineEdit->text() + " " + deviceNameLineEdit->text() + " (" + deviceProductCodeLineEdit->text() + ")");
+ createDevicesListWidget->addItem(deviceManufacturerLineEdit->text() + " " + deviceNameLineEdit->text() + ": " + deviceProductCodeLineEdit->text());
deviceManufacturerLineEdit->clear();
deviceNameLineEdit->clear();
deviceProductCodeLineEdit->clear();
@@ -825,12 +851,12 @@ void MainWindow::BuildPackage(void)
packagePath.append(".tar.gz");
}
- Packaging::BuildPackage(packagePath, workingPackageData);
+ Packaging::BuildPackage(packagePath, workingPackageData.GetFirmwareInfo());
}
void MainWindow::HandleHeimdallStdout(void)
{
- QString output = process.read(1024);
+ QString output = process.readAll();
// We often receive multiple lots of output from Heimdall at one time. So we use regular expressions
// to ensure we don't miss out on any important information.
@@ -845,35 +871,10 @@ void MainWindow::HandleHeimdallStdout(void)
flashProgressBar->setValue(percentString.mid(1, percentString.length() - 2).toInt());
}
- /*// Handle other information
-
- int endOfLastLine = output.length() - 1;
- for (; endOfLastLine > -1; endOfLastLine--)
- {
- if (output[endOfLastLine] != '\n')
- break;
- }
-
- if (endOfLastLine < 0)
- return; // Output was blank or just a bunch of new line characters.
-
- int startOfLastLine = endOfLastLine - 1;
- for (; startOfLastLine > -1; startOfLastLine--)
- {
- if (output[startOfLastLine] == '\n')
- break;
- }
-
- startOfLastLine++;
-
- // Just look at the last line of the output
- output = output.mid(startOfLastLine, endOfLastLine - startOfLastLine + 1); // Work with the last line only
-
- percentExp.setPattern("[0-9]+%");
-
- // If the last line wasn't a uploading message or a percentage transferred then display it.
- if (output.lastIndexOf(uploadingExp) < 0 && output.lastIndexOf(percentExp) < 0)
- flashLabel->setText(output);*/
+ output.remove(QChar('\b'));
+ output.replace(QChar('%'), QString("%\n"));
+ outputPlainTextEdit->insertPlainText(output);
+ outputPlainTextEdit->ensureCursorVisible();
}
void MainWindow::HandleHeimdallReturned(int exitCode, QProcess::ExitStatus exitStatus)
@@ -894,6 +895,9 @@ void MainWindow::HandleHeimdallReturned(int exitCode, QProcess::ExitStatus exitS
{
QString error = process.readAllStandardError();
+ outputPlainTextEdit->insertPlainText(error);
+ outputPlainTextEdit->ensureCursorVisible();
+
int firstNewLineChar = error.indexOf('\n');
if (firstNewLineChar == 0)
diff --git a/heimdall-frontend/Source/mainwindow.h b/heimdall-frontend/Source/mainwindow.h
index 7525d67..6e16596 100644
--- a/heimdall-frontend/Source/mainwindow.h
+++ b/heimdall-frontend/Source/mainwindow.h
@@ -99,7 +99,9 @@ namespace HeimdallFrontend
void RemovePartition(void);
void SelectPit(void);
+
void SetRepartition(int enabled);
+ void SetNoReboot(int enabled);
void StartFlash(void);