summaryrefslogtreecommitdiffstats
path: root/heimdall-frontend/Source
diff options
context:
space:
mode:
Diffstat (limited to 'heimdall-frontend/Source')
-rwxr-xr-xheimdall-frontend/Source/FirmwareInfo.cpp571
-rwxr-xr-xheimdall-frontend/Source/FirmwareInfo.h286
-rwxr-xr-xheimdall-frontend/Source/PackageData.cpp65
-rwxr-xr-xheimdall-frontend/Source/PackageData.h77
-rwxr-xr-xheimdall-frontend/Source/Packaging.cpp345
-rwxr-xr-xheimdall-frontend/Source/Packaging.h110
-rw-r--r--heimdall-frontend/Source/aboutform.cpp2
-rw-r--r--heimdall-frontend/Source/aboutform.h6
-rw-r--r--heimdall-frontend/Source/main.cpp14
-rw-r--r--heimdall-frontend/Source/mainwindow.cpp972
-rw-r--r--heimdall-frontend/Source/mainwindow.h85
11 files changed, 2107 insertions, 426 deletions
diff --git a/heimdall-frontend/Source/FirmwareInfo.cpp b/heimdall-frontend/Source/FirmwareInfo.cpp
new file mode 100755
index 0000000..7d11605
--- /dev/null
+++ b/heimdall-frontend/Source/FirmwareInfo.cpp
@@ -0,0 +1,571 @@
+/* Copyright (c) 2010 Benjamin Dobell, Glass Echidna
+
+ 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.*/
+
+// Heimdall Frontend
+#include "FirmwareInfo.h"
+
+using namespace HeimdallFrontend;
+
+DeviceInfo::DeviceInfo()
+{
+}
+
+DeviceInfo::DeviceInfo(const QString& manufacturer, const QString& product, const QString& name)
+{
+ this->manufacturer = manufacturer;
+ this->product = product;
+ this->name = name;
+}
+
+bool DeviceInfo::ParseXml(QXmlStreamReader& xml)
+{
+ bool foundManufacturer = false;
+ bool foundProduct = false;
+ bool foundName = false;
+
+ while (!xml.atEnd())
+ {
+ QXmlStreamReader::TokenType nextToken = xml.readNext();
+
+ if (nextToken == QXmlStreamReader::StartElement)
+ {
+ if (xml.name() == "manufacturer")
+ {
+ if (foundManufacturer)
+ {
+ // TODO: "found multiple device manufacturers."
+ return (false);
+ }
+
+ foundManufacturer = true;
+
+ manufacturer = xml.readElementText();
+ }
+ else if (xml.name() == "product")
+ {
+ if (foundProduct)
+ {
+ // TODO: "found multiple device product identifiers."
+ return (false);
+ }
+
+ foundProduct = true;
+
+ product = xml.readElementText();
+ }
+ else if (xml.name() == "name")
+ {
+ if (foundName)
+ {
+ // TODO: "found multiple device names."));
+ return (false);
+ }
+
+ foundName = true;
+
+ name = xml.readElementText();
+ }
+ }
+ else if (nextToken == QXmlStreamReader::EndElement)
+ {
+ if (xml.name() == "device")
+ return (foundManufacturer && foundProduct && foundName);
+ }
+ else
+ {
+ if (!(nextToken == QXmlStreamReader::Characters && xml.isWhitespace()))
+ {
+ // TODO: "Unexpected token found in <device>"
+ return (false);
+ }
+ }
+ }
+
+ return (false);
+}
+
+
+
+PlatformInfo::PlatformInfo()
+{
+}
+
+void PlatformInfo::Clear(void)
+{
+ name.clear();
+ version.clear();
+}
+
+bool PlatformInfo::IsCleared(void) const
+{
+ return (name.isEmpty() && version.isEmpty());
+}
+
+bool PlatformInfo::ParseXml(QXmlStreamReader& xml)
+{
+ Clear();
+
+ bool foundName = false;
+ bool foundVersion = false;
+
+ while (!xml.atEnd())
+ {
+ QXmlStreamReader::TokenType nextToken = xml.readNext();
+
+ if (nextToken == QXmlStreamReader::StartElement)
+ {
+ if (xml.name() == "name")
+ {
+ if (foundName)
+ {
+ // TODO: "found multiple platform names."
+ return (false);
+ }
+
+ foundName = true;
+
+ name = xml.readElementText();
+ }
+ else if (xml.name() == "version")
+ {
+ if (foundVersion)
+ {
+ // TODO: "found multiple platform versions."
+ return (false);
+ }
+
+ foundVersion = true;
+
+ version = xml.readElementText();
+ }
+ else
+ {
+ // TODO: "found unknown <platform> sub-element <" + xml.name() + ">."
+ return (false);
+ }
+ }
+ else if (nextToken == QXmlStreamReader::EndElement)
+ {
+ if (xml.name() == "platform")
+ return (foundName && foundVersion);
+ }
+ else
+ {
+ if (!(nextToken == QXmlStreamReader::Characters && xml.isWhitespace()))
+ {
+ // TODO: "Unexpected token found in <platform>"
+ return (false);
+ }
+ }
+ }
+
+ return (false);
+}
+
+
+
+FileInfo::FileInfo()
+{
+}
+
+FileInfo::FileInfo(unsigned int partitionId, const QString& filename)
+{
+ this->partitionId = partitionId;
+ this->filename = filename;
+}
+
+bool FileInfo::ParseXml(QXmlStreamReader& xml)
+{
+ bool foundId = false;
+ bool foundFilename = false;
+
+ while (!xml.atEnd())
+ {
+ QXmlStreamReader::TokenType nextToken = xml.readNext();
+
+ if (nextToken == QXmlStreamReader::StartElement)
+ {
+ if (xml.name() == "id")
+ {
+ if (foundId)
+ {
+ // TODO: "found multiple file IDs."
+ return (false);
+ }
+
+ foundId = true;
+
+ partitionId = xml.readElementText().toInt();
+ }
+ else if (xml.name() == "filename")
+ {
+ if (foundFilename)
+ {
+ // TODO: "found multiple file filenames."
+ return (false);
+ }
+
+ foundFilename = true;
+
+ filename = xml.readElementText();
+ }
+ }
+ else if (nextToken == QXmlStreamReader::EndElement)
+ {
+ if (xml.name() == "file")
+ return (foundId && foundFilename);
+ }
+ else
+ {
+ if (!(nextToken == QXmlStreamReader::Characters && xml.isWhitespace()))
+ {
+ // TODO: "Unexpected token found in <file>"
+ return (false);
+ }
+ }
+ }
+
+ return (false);
+}
+
+
+
+FirmwareInfo::FirmwareInfo()
+{
+ repartition = false;
+}
+
+void FirmwareInfo::Clear(void)
+{
+ name = "";
+ version = "";
+ platformInfo.Clear();
+
+ developers.clear();
+ url.clear();
+ donateUrl.clear();
+
+ deviceInfos.clear();
+
+ pitFilename.clear();
+ repartition = 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());
+}
+
+bool FirmwareInfo::ParseXml(QXmlStreamReader& xml)
+{
+ Clear();
+
+ bool foundName = false;
+ bool foundVersion = false;
+ bool foundPlatform = false;
+ bool foundDevelopers = false;
+ bool foundUrl = false;
+ bool foundDonateUrl = false;
+ bool foundDevices = false;
+ bool foundPit = false;
+ bool foundRepartition = false;
+ bool foundFiles = false;
+
+ if (!xml.readNextStartElement())
+ {
+ // TODO: "Failed to find <firmware> element."
+ return (false);
+ }
+
+ if (xml.name() != "firmware")
+ {
+ // TODO: "Expected <firmware> element but found <%s>"
+ return (false);
+ }
+
+ QString formatVersionString;
+ formatVersionString += xml.attributes().value("version");
+
+ if (formatVersionString.isEmpty())
+ {
+ // TODO: <firmware> is missing a version."
+ return (false);
+ }
+
+ bool parsedVersion = false;
+ int formatVersion = formatVersionString.toInt(&parsedVersion);
+
+ if (!parsedVersion)
+ {
+ // TODO: "<firmware> contains a malformed version."
+ return (false);
+ }
+
+ if (formatVersion > kVersion)
+ {
+ // TODO: "Package is for a newer version of Heimdall Frontend. Please download the latest version of Heimdall Frontend."
+ return (false);
+ }
+
+ while (!xml.atEnd())
+ {
+ QXmlStreamReader::TokenType nextToken = xml.readNext();
+
+ if (nextToken == QXmlStreamReader::StartElement)
+ {
+ if (xml.name() == "name")
+ {
+ if (foundName)
+ {
+ // TODO: "found multiple firmware names."
+ return (false);
+ }
+
+ foundName = true;
+ name = xml.readElementText();
+ }
+ else if (xml.name() == "version")
+ {
+ if (foundVersion)
+ {
+ // TODO: "found multiple firmware versions."
+ return (false);
+ }
+
+ foundVersion = true;
+ version = xml.readElementText();
+ }
+ else if (xml.name() == "platform")
+ {
+ if (foundPlatform)
+ {
+ // TODO: "found multiple firmware platforms."
+ return (false);
+ }
+
+ foundPlatform = true;
+
+ if (!platformInfo.ParseXml(xml))
+ return (false);
+ }
+ else if (xml.name() == "developers")
+ {
+ if (foundDevelopers)
+ {
+ // TODO: "found multiple sets of firmware developers."
+ return (false);
+ }
+
+ foundDevelopers = true;
+
+ while (!xml.atEnd())
+ {
+ nextToken = xml.readNext();
+
+ if (nextToken == QXmlStreamReader::StartElement)
+ {
+ if (xml.name() == "name")
+ developers.append(xml.readElementText());
+ }
+ else if (nextToken == QXmlStreamReader::EndElement)
+ {
+ if (xml.name() == "developers")
+ break;
+ }
+ else
+ {
+ if (!(nextToken == QXmlStreamReader::Characters && xml.isWhitespace()))
+ {
+ // TODO: "Unexpected token found in <developers>"
+ return (false);
+ }
+ }
+ }
+ }
+ else if (xml.name() == "url")
+ {
+ if (foundUrl)
+ {
+ // TODO: "found multiple firmware URLs."
+ return (false);
+ }
+
+ foundUrl = true;
+
+ url = xml.readElementText();
+ }
+ else if (xml.name() == "donateurl")
+ {
+ if (foundDonateUrl)
+ {
+ // TODO: "found multiple firmware donate URLs."
+ return (false);
+ }
+
+ foundDonateUrl = true;
+
+ donateUrl = xml.readElementText();
+ }
+ else if (xml.name() == "devices")
+ {
+ if (foundDevices)
+ {
+ // TODO: "found multiple sets of firmware devices."
+ return (false);
+ }
+
+ foundDevices = true;
+
+ while (!xml.atEnd())
+ {
+ nextToken = xml.readNext();
+
+ if (nextToken == QXmlStreamReader::StartElement)
+ {
+ if (xml.name() == "device")
+ {
+ DeviceInfo deviceInfo;
+
+ if (!deviceInfo.ParseXml(xml))
+ return (false);
+
+ deviceInfos.append(deviceInfo);
+ }
+ }
+ else if (nextToken == QXmlStreamReader::EndElement)
+ {
+ if (xml.name() == "devices")
+ break;
+ }
+ else
+ {
+ if (!(nextToken == QXmlStreamReader::Characters && xml.isWhitespace()))
+ {
+ // TODO: "Unexpected token found in <devices>"
+ return (false);
+ }
+ }
+ }
+ }
+ else if (xml.name() == "pit")
+ {
+ if (foundPit)
+ {
+ // TODO: "found multiple firmware PIT files."
+ return (false);
+ }
+
+ foundPit = true;
+
+ pitFilename = xml.readElementText();
+ }
+ else if (xml.name() == "repartition")
+ {
+ if (foundRepartition)
+ {
+ // TODO: "found multiple firmware repartition values."
+ return (false);
+ }
+
+ foundRepartition = true;
+
+ repartition = (xml.readElementText().toInt() != 0);
+ }
+ else if (xml.name() == "files")
+ {
+ if (foundFiles)
+ {
+ // TODO: "found multiple sets of firmware files."
+ return (false);
+ }
+
+ foundFiles = true;
+
+ while (!xml.atEnd())
+ {
+ nextToken = xml.readNext();
+
+ if (nextToken == QXmlStreamReader::StartElement)
+ {
+ if (xml.name() == "file")
+ {
+ FileInfo fileInfo;
+
+ if (!fileInfo.ParseXml(xml))
+ return (false);
+
+ fileInfos.append(fileInfo);
+ }
+ }
+ else if (nextToken == QXmlStreamReader::EndElement)
+ {
+ if (xml.name() == "files")
+ break;
+ }
+ else
+ {
+ if (!(nextToken == QXmlStreamReader::Characters && xml.isWhitespace()))
+ {
+ // TODO: "Unexpected token found in <devices>"
+ return (false);
+ }
+ }
+ }
+ }
+ else
+ {
+ // TODO: "unknown <firmware> sub-element <" + xml.name() + ">."
+ return (false);
+ }
+ }
+ else if (nextToken == QXmlStreamReader::EndElement)
+ {
+ if (xml.name() == "firmware")
+ {
+ if (!(foundName && foundVersion && foundPlatform && foundDevelopers && foundDevices && foundPit && foundRepartition && foundFiles))
+ return (false);
+ else
+ break;
+ }
+ }
+ else
+ {
+ if (!(nextToken == QXmlStreamReader::Characters && xml.isWhitespace()))
+ {
+ // TODO: "Unexpected token found in <firmware>"
+ return (false);
+ }
+ }
+ }
+
+ // Read whitespaces at the end of the file (if there are any)
+ xml.readNext();
+
+ if (!xml.atEnd())
+ {
+ // TODO: "Found data after </firmware>"
+ return (false);
+ }
+
+ return (true);
+}
diff --git a/heimdall-frontend/Source/FirmwareInfo.h b/heimdall-frontend/Source/FirmwareInfo.h
new file mode 100755
index 0000000..a72dab1
--- /dev/null
+++ b/heimdall-frontend/Source/FirmwareInfo.h
@@ -0,0 +1,286 @@
+/* Copyright (c) 2010 Benjamin Dobell, Glass Echidna
+
+ 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 FIRMWAREINFO_H
+#define FIRMWAREINFO_H
+
+// Qt
+#include <QFile>
+#include <QString>
+#include <QXmlStreamReader>
+
+namespace HeimdallFrontend
+{
+ class DeviceInfo
+ {
+ private:
+
+ QString manufacturer;
+ QString product;
+ QString name;
+
+ public:
+
+ DeviceInfo();
+ DeviceInfo(const QString& manufacturer, const QString& product, const QString& name);
+
+ bool ParseXml(QXmlStreamReader& xml);
+
+ const QString& GetManufacturer(void) const
+ {
+ return (manufacturer);
+ }
+
+ void SetManufacturer(const QString& manufacturer)
+ {
+ this->manufacturer = manufacturer;
+ }
+
+ const QString& GetProduct(void) const
+ {
+ return (product);
+ }
+
+ void SetProduct(const QString& product)
+ {
+ this->product = product;
+ }
+
+ const QString& GetName(void) const
+ {
+ return (name);
+ }
+
+ void SetName(const QString& name)
+ {
+ this->name = name;
+ }
+ };
+
+ class PlatformInfo
+ {
+ private:
+
+ QString name;
+ QString version;
+
+ public:
+
+ PlatformInfo();
+
+ void Clear(void);
+ bool IsCleared(void) const;
+
+ bool ParseXml(QXmlStreamReader& xml);
+
+ const QString& GetName(void) const
+ {
+ return (name);
+ }
+
+ void SetName(const QString& name)
+ {
+ this->name = name;
+ }
+
+ const QString& GetVersion(void) const
+ {
+ return (version);
+ }
+
+ void SetVersion(const QString& version)
+ {
+ this->version = version;
+ }
+ };
+
+ class FileInfo
+ {
+ private:
+
+ unsigned int partitionId;
+ QString filename;
+
+ public:
+
+ FileInfo();
+ FileInfo(unsigned int partitionId, const QString& filename);
+
+ bool ParseXml(QXmlStreamReader& xml);
+
+ unsigned int GetPartitionId(void) const
+ {
+ return (partitionId);
+ }
+
+ void SetPartitionId(unsigned int partitionId)
+ {
+ this->partitionId = partitionId;
+ }
+
+ const QString& GetFilename(void) const
+ {
+ return (filename);
+ }
+
+ void SetFilename(const QString& filename)
+ {
+ this->filename = filename;
+ }
+ };
+
+ class FirmwareInfo
+ {
+ public:
+
+ enum
+ {
+ kVersion = 1
+ };
+
+ private:
+
+ QString name;
+ QString version;
+ PlatformInfo platformInfo;
+
+ QList<QString> developers;
+ QString url;
+ QString donateUrl;
+
+ QList<DeviceInfo> deviceInfos;
+
+ QString pitFilename;
+ bool repartition;
+
+ QList<FileInfo> fileInfos;
+
+ public:
+
+ FirmwareInfo();
+
+ void Clear(void);
+ bool IsCleared(void) const;
+
+ bool ParseXml(QXmlStreamReader& xml);
+
+ const QString& GetName(void) const
+ {
+ return (name);
+ }
+
+ void SetName(const QString& name)
+ {
+ this->name = name;
+ }
+
+ const QString& GetVersion(void) const
+ {
+ return (version);
+ }
+
+ void SetVersion(const QString& version)
+ {
+ this->version = version;
+ }
+
+ const PlatformInfo& GetPlatformInfo(void) const
+ {
+ return (platformInfo);
+ }
+
+ PlatformInfo& GetPlatformInfo(void)
+ {
+ return (platformInfo);
+ }
+
+ const QList<QString>& GetDevelopers(void) const
+ {
+ return (developers);
+ }
+
+ QList<QString>& GetDevelopers(void)
+ {
+ return (developers);
+ }
+
+ const QString& GetUrl(void) const
+ {
+ return (url);
+ }
+
+ void SetUrl(const QString& url)
+ {
+ this->url = url;
+ }
+
+ const QString& GetDonateUrl(void) const
+ {
+ return (donateUrl);
+ }
+
+ void SetDonateUrl(const QString& donateUrl)
+ {
+ this->donateUrl = donateUrl;
+ }
+
+ const QList<DeviceInfo>& GetDeviceInfos(void) const
+ {
+ return (deviceInfos);
+ }
+
+ QList<DeviceInfo>& GetDeviceInfos(void)
+ {
+ return (deviceInfos);
+ }
+
+ const QString& GetPitFilename(void) const
+ {
+ return (pitFilename);
+ }
+
+ void SetPitFilename(const QString& pitFilename)
+ {
+ this->pitFilename = pitFilename;
+ }
+
+ bool GetRepartition(void) const
+ {
+ return (repartition);
+ }
+
+ void SetRepartition(bool repartition)
+ {
+ this->repartition = repartition;
+ }
+
+ const QList<FileInfo>& GetFileInfos(void) const
+ {
+ return (fileInfos);
+ }
+
+ QList<FileInfo>& GetFileInfos(void)
+ {
+ return (fileInfos);
+ }
+ };
+}
+
+#endif
diff --git a/heimdall-frontend/Source/PackageData.cpp b/heimdall-frontend/Source/PackageData.cpp
new file mode 100755
index 0000000..36138cf
--- /dev/null
+++ b/heimdall-frontend/Source/PackageData.cpp
@@ -0,0 +1,65 @@
+/* Copyright (c) 2010 Benjamin Dobell, Glass Echidna
+
+ 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.*/
+
+// Heimdall Frontend
+#include "PackageData.h"
+
+using namespace HeimdallFrontend;
+
+PackageData::PackageData()
+{
+}
+
+PackageData::~PackageData()
+{
+ for (int i = 0; i < files.length(); i++)
+ delete files[i];
+}
+
+void PackageData::Clear(void)
+{
+ firmwareInfo.Clear();
+
+ for (int i = 0; i < files.length(); i++)
+ delete files[i];
+
+ files.clear();
+}
+
+bool PackageData::ReadFirmwareInfo(QFile *file)
+{
+ if (!file->open(QFile::ReadOnly))
+ {
+ // TODO: Error
+ return (false);
+ }
+
+ QXmlStreamReader xml(file);
+ bool success = firmwareInfo.ParseXml(xml);
+
+ file->close();
+
+ return (success);
+}
+
+bool PackageData::IsCleared(void) const
+{
+ return (firmwareInfo.IsCleared() && files.isEmpty());
+}
diff --git a/heimdall-frontend/Source/PackageData.h b/heimdall-frontend/Source/PackageData.h
new file mode 100755
index 0000000..9952393
--- /dev/null
+++ b/heimdall-frontend/Source/PackageData.h
@@ -0,0 +1,77 @@
+/* Copyright (c) 2010 Benjamin Dobell, Glass Echidna
+
+ 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 PACKAGEDATA_H
+#define PACKAGEDATA_H
+
+// Qt
+#include <QTemporaryFile>
+
+// Heimdall Frontend
+#include "FirmwareInfo.h"
+
+namespace HeimdallFrontend
+{
+ class PackageData
+ {
+ private:
+
+ FirmwareInfo firmwareInfo;
+ QList<QTemporaryFile *> files;
+
+ public:
+
+ PackageData();
+ ~PackageData();
+
+ void Clear(void);
+ bool ReadFirmwareInfo(QFile *file);
+
+ bool IsCleared(void) const;
+
+ const FirmwareInfo& GetFirmwareInfo(void) const
+ {
+ return (firmwareInfo);
+ }
+
+ FirmwareInfo& GetFirmwareInfo(void)
+ {
+ return (firmwareInfo);
+ }
+
+ const QList<QTemporaryFile *>& GetFiles(void) const
+ {
+ return (files);
+ }
+
+ QList<QTemporaryFile *>& GetFiles(void)
+ {
+ return (files);
+ }
+
+ // Simply clears the files list, it does delete/close any files.
+ void RemoveAllFiles(void)
+ {
+ files.clear();
+ }
+ };
+}
+
+#endif
diff --git a/heimdall-frontend/Source/Packaging.cpp b/heimdall-frontend/Source/Packaging.cpp
new file mode 100755
index 0000000..cbd03a4
--- /dev/null
+++ b/heimdall-frontend/Source/Packaging.cpp
@@ -0,0 +1,345 @@
+/* Copyright (c) 2010 Benjamin Dobell, Glass Echidna
+
+ 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.*/
+
+// C/C++ Standard Library
+#include <stdio.h>
+
+// zlib
+#include "zlib.h"
+
+// Qt
+#include <QDateTime>
+#include <QDir>
+
+// Heimdall Frontend
+#include "Packaging.h"
+
+using namespace HeimdallFrontend;
+
+const char *Packaging::ustarMagic = "ustar";
+
+bool Packaging::ExtractTar(QTemporaryFile& tarFile, PackageData *outputPackageData)
+{
+ TarHeader tarHeader;
+
+ tarFile.reset();
+
+ bool previousEmpty = false;
+
+ while (!tarFile.atEnd())
+ {
+ qint64 dataRead = tarFile.read(tarHeader.buffer, TarHeader::kBlockLength);
+
+ if (dataRead != TarHeader::kBlockLength)
+ return (false);
+
+ bool ustarFormat = strcmp(tarHeader.fields.magic, ustarMagic) == 0;
+ bool empty = true;
+
+ for (int i = 0; i < TarHeader::kBlockLength; i++)
+ {
+ if (tarHeader.buffer[i] != 0)
+ {
+ empty = false;
+ break;
+ }
+ }
+
+ if (empty)
+ {
+ if (previousEmpty)
+ {
+ // Two empty blocks in a row means we've reached the end of the archive.
+ // TODO: Make sure we're at the end of the file.
+ break;
+ }
+ }
+ else
+ {
+ // TODO: Check checksum
+
+ bool parsed = false;
+
+ // The size field is not always null terminated, so we must create a copy and null terminate it for parsing.
+ char fileSizeString[13];
+ memcpy(fileSizeString, tarHeader.fields.size, 12);
+ fileSizeString[12] = '\0';
+
+ qulonglong fileSize = QString(fileSizeString).toULongLong(&parsed, 8);
+
+ if (!parsed)
+ {
+ // TODO: Error message?
+ return (false);
+ }
+
+ if (fileSize > 0 && tarHeader.fields.typeFlag == '0')
+ {
+ // 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();
+
+ outputPackageData->GetFiles().append(outputFile);
+
+ qulonglong dataRemaining = fileSize;
+ char readBuffer[TarHeader::kBlockReadCount * TarHeader::kBlockLength];
+
+ // Copy the file contents from tarFile to outputFile
+ while (dataRemaining > 0)
+ {
+ qint64 fileDataToRead = (dataRemaining < TarHeader::kBlockReadCount * TarHeader::kBlockLength)
+ ? dataRemaining : TarHeader::kBlockReadCount * TarHeader::kBlockLength;
+
+ qint64 dataRead = tarFile.read(readBuffer, fileDataToRead + (TarHeader::kBlockLength - fileDataToRead % TarHeader::kBlockLength) % TarHeader::kBlockLength);
+
+ if (dataRead < fileDataToRead || dataRead % TarHeader::kBlockLength != 0)
+ return (false);
+
+ outputFile->write(readBuffer, fileDataToRead);
+
+ dataRemaining -= fileDataToRead;
+ }
+
+ outputFile->close();
+ }
+ else
+ {
+ // We don't support links/directories.
+ return (false);
+ }
+ }
+
+ previousEmpty = empty;
+ }
+
+ return (true);
+}
+
+bool Packaging::CreateTar(const PackageData& packageData, QTemporaryFile *outputTarFile)
+{
+ const QList<FileInfo>& fileInfos = packageData.GetFirmwareInfo().GetFileInfos();
+
+ if (!outputTarFile->open())
+ {
+ // TODO: "Failed to open \"%s\""
+ return (false);
+ }
+
+ bool failure = false;
+
+ TarHeader tarHeader;
+
+ for (int i = 0; i < fileInfos.length(); i++)
+ {
+ memset(tarHeader.buffer, 0, TarHeader::kBlockLength);
+
+ QFile file(fileInfos[i].GetFilename());
+
+ if (!file.open(QFile::ReadOnly))
+ {
+ // TODO: "Failed to open \"%s\""
+ failure = true;
+ break;
+ }
+
+ if (file.size() > TarHeader::kMaxFileSize)
+ {
+ // TODO: "File is too large to packaged"
+ failure = true;
+ break;
+ }
+
+ QFileInfo qtFileInfo(file);
+ strcpy(tarHeader.fields.name, qtFileInfo.fileName().toUtf8().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());
+
+ // Regular File
+ tarHeader.fields.typeFlag = '0';
+
+ // Calculate checksum
+ int checksum = 0;
+
+ for (int i = 0; i < TarHeader::kTarHeaderLength; i++)
+ checksum += tarHeader.buffer[i];
+
+ sprintf(tarHeader.fields.checksum, "%o", checksum);
+
+ // Write the header to the TAR file.
+ outputTarFile->write(tarHeader.buffer, TarHeader::kBlockLength);
+
+ char buffer[TarHeader::kBlockWriteCount * TarHeader::kBlockLength];
+
+ for (qint64 i = 0; i < file.size(); i++)
+ {
+ qint64 dataRead = file.read(buffer, TarHeader::kBlockWriteCount * TarHeader::kBlockLength);
+
+ if (outputTarFile->write(buffer, dataRead) != dataRead)
+ {
+ // TODO: "Failed to write data to the temporary TAR file."
+ failure = true;
+ break;
+ }
+
+ if (dataRead % TarHeader::kBlockLength != 0)
+ {
+ int remainingBlockLength = TarHeader::kBlockLength - dataRead % TarHeader::kBlockLength;
+ memset(buffer, 0, remainingBlockLength);
+
+ if (outputTarFile->write(buffer, remainingBlockLength) != remainingBlockLength)
+ {
+ // TODO: "Failed to write data to the temporary TAR file."
+ failure = true;
+ break;
+ }
+ }
+
+ i += dataRead;
+ }
+
+ if (failure)
+ break;
+ }
+
+ if (failure)
+ {
+ outputTarFile->resize(0);
+ outputTarFile->close();
+ return (false);
+ }
+
+ // 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);
+
+ outputTarFile->close();
+
+ return (true);
+}
+
+bool Packaging::ExtractPackage(const QString& packagePath, PackageData *outputPackageData)
+{
+ FILE *compressedPackageFile = fopen(packagePath.toStdString().c_str(), "rb");
+ gzFile packageFile = gzdopen(fileno(compressedPackageFile), "rb");
+
+ QTemporaryFile outputTar("XXXXXX.tar");
+
+ if (!outputTar.open())
+ {
+ gzclose(packageFile);
+
+ return (false);
+ }
+
+ char buffer[32768];
+
+ int bytesRead;
+
+ do
+ {
+ bytesRead = gzread(packageFile, buffer, 32768);
+
+ if (bytesRead == -1)
+ {
+ gzclose(packageFile);
+
+ return (false);
+ }
+
+ outputTar.write(buffer, bytesRead);
+ } while (bytesRead > 0);
+
+ gzclose(packageFile); // Closes packageFile and compressedPackageFile
+
+ if (!ExtractTar(outputTar, outputPackageData))
+ return (false);
+
+ // Find and read firmware.xml
+ for (int i = 0; i < outputPackageData->GetFiles().length(); i++)
+ {
+ QTemporaryFile *file = outputPackageData->GetFiles()[i];
+
+ if (file->fileTemplate() == "XXXXXX-firmware.xml")
+ {
+ if (!outputPackageData->ReadFirmwareInfo(file))
+ {
+ outputPackageData->Clear();
+ return (false);
+ }
+
+ return (true);
+ }
+ }
+
+ return (false);
+}
+
+bool Packaging::BuildPackage(const QString& packagePath, const PackageData& packageData)
+{
+ QTemporaryFile temporaryFile("XXXXXX.tar");
+
+ if (!CreateTar(packageData, &temporaryFile))
+ return (false);
+
+ return (true);
+}
diff --git a/heimdall-frontend/Source/Packaging.h b/heimdall-frontend/Source/Packaging.h
new file mode 100755
index 0000000..c341f0f
--- /dev/null
+++ b/heimdall-frontend/Source/Packaging.h
@@ -0,0 +1,110 @@
+/* Copyright (c) 2010 Benjamin Dobell, Glass Echidna
+
+ 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 PACKAGING_H
+#define PACKAGING_H
+
+// Qt
+#include <QList>
+#include <QString>
+#include <QTemporaryFile>
+
+// Heimdall Frontend
+#include "PackageData.h"
+
+namespace HeimdallFrontend
+{
+ union TarHeader
+ {
+ enum
+ {
+ kBlockLength = 512,
+ kBlockReadCount = 8,
+ kBlockWriteCount = 8,
+
+ kTarHeaderLength = 257,
+ kUstarHeaderLength = 500,
+ };
+
+ enum : quint64
+ {
+ kMaxFileSize = 8589934592
+ };
+
+ enum
+ {
+ kModeOtherExecute = 1,
+ kModeOtherWrite = 1 << 1,
+ kModeOtherRead = 1 << 2,
+
+ kModeGroupExecute = 1 << 3,
+ kModeGroupWrite = 1 << 4,
+ kModeGroupRead = 1 << 5,
+
+ kModeOwnerExecute = 1 << 6,
+ kModeOwnerWrite = 1 << 7,
+ kModeOwnerRead = 1 << 8,
+
+ kModeReserved = 2 << 9,
+ kModeSetGid = 2 << 10,
+ kModeSetUid = 2 << 11
+ };
+
+ struct
+ {
+ char name[100];
+ char mode[8];
+ char userId[8];
+ char groupId[8];
+ char size[12];
+ char modifiedTime[12];
+ char checksum[8];
+ char typeFlag;
+ char linkName[100];
+ char magic[6];
+ char version[2];
+ char userName[32];
+ char groupName[32];
+ char devMajor[8];
+ char devMinor[8];
+ char prefix[155];
+ } fields;
+
+ char buffer[kBlockLength];
+ };
+
+ class Packaging
+ {
+ private:
+
+ // 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.
+
+ public:
+
+ static const char *ustarMagic;
+
+ static bool ExtractPackage(const QString& packagePath, PackageData *outputPackageData);
+ static bool BuildPackage(const QString& packagePath, const PackageData& packageData);
+ };
+}
+
+#endif
diff --git a/heimdall-frontend/Source/aboutform.cpp b/heimdall-frontend/Source/aboutform.cpp
index f043c20..08b8a3e 100644
--- a/heimdall-frontend/Source/aboutform.cpp
+++ b/heimdall-frontend/Source/aboutform.cpp
@@ -18,7 +18,7 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.*/
-// Heimdall
+// Heimdall Frontend
#include "aboutform.h"
using namespace HeimdallFrontend;
diff --git a/heimdall-frontend/Source/aboutform.h b/heimdall-frontend/Source/aboutform.h
index 2a44c95..7e29136 100644
--- a/heimdall-frontend/Source/aboutform.h
+++ b/heimdall-frontend/Source/aboutform.h
@@ -18,12 +18,12 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.*/
-// Heimdall
-#include "ui_aboutform.h"
-
// Qt
#include <QWidget>
+// Heimdall Frontend
+#include "ui_aboutform.h"
+
namespace HeimdallFrontend
{
class AboutForm : public QWidget, public Ui::AboutForm
diff --git a/heimdall-frontend/Source/main.cpp b/heimdall-frontend/Source/main.cpp
index 8df987c..9db00f5 100644
--- a/heimdall-frontend/Source/main.cpp
+++ b/heimdall-frontend/Source/main.cpp
@@ -18,20 +18,20 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.*/
-// Heimdall Frontend
-#include "mainwindow.h"
-
// Qt
#include <QtGui/QApplication>
+// Heimdall Frontend
+#include "mainwindow.h"
+
using namespace HeimdallFrontend;
int main(int argc, char *argv[])
{
- QApplication a(argc, argv);
+ QApplication application(argc, argv);
- MainWindow w;
- w.show();
+ MainWindow window;
+ window.show();
- return a.exec();
+ return (application.exec());
}
diff --git a/heimdall-frontend/Source/mainwindow.cpp b/heimdall-frontend/Source/mainwindow.cpp
index b5287e2..8043599 100644
--- a/heimdall-frontend/Source/mainwindow.cpp
+++ b/heimdall-frontend/Source/mainwindow.cpp
@@ -18,9 +18,6 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.*/
-// Heimdall Frontend
-#include "mainwindow.h"
-
// Qt
#include <QCoreApplication>
#include <QDesktopServices>
@@ -30,151 +27,214 @@
#include <QRegExp>
#include <QUrl>
+// Heimdall Frontend
+#include "mainwindow.h"
+#include "Packaging.h"
+
using namespace HeimdallFrontend;
-bool MainWindow::IsArchive(QString path)
+void MainWindow::UpdateUnusedPartitionIds(void)
{
- // Not a real check but hopefully it gets the message across, don't 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));
+ unusedPartitionIds.clear();
+
+ // Initially populate unusedPartitionIds with all possible partition IDs.
+ for (unsigned int i = 0; i < currentPitData.GetEntryCount(); i++)
+ {
+ const PitEntry *pitEntry = currentPitData.GetEntry(i);
+
+ if (!pitEntry->GetUnused() && strcmp(pitEntry->GetPartitionName(), "PIT") != 0)
+ unusedPartitionIds.append(pitEntry->GetPartitionIdentifier());
+ }
+
+ // Remove any used partition IDs from unusedPartitionIds
+ QList<FileInfo>& fileList = workingPackageData.GetFirmwareInfo().GetFileInfos();
+
+ for (int i = 0; i < fileList.length(); i++)
+ unusedPartitionIds.removeOne(fileList[i].GetPartitionId());
}
-QString MainWindow::PromptFileSelection(void)
+bool MainWindow::ReadPit(QFile *file)
{
- QString path = QFileDialog::getOpenFileName(this, "Select File", lastDirectory);
+ if(!file->open(QIODevice::ReadOnly))
+ return (false);
- if (path != "")
- lastDirectory = path.left(path.lastIndexOf('/') + 1);
+ unsigned char *buffer = new unsigned char[file->size()];
- return (path);
+ file->read(reinterpret_cast<char *>(buffer), file->size());
+ file->close();
+
+ bool success = currentPitData.Unpack(buffer);
+ delete buffer;
+
+ if (!success)
+ currentPitData.Clear();
+
+ return (success);
}
-void MainWindow::UpdateStartButton(void)
+void MainWindow::UpdatePackageUserInterface(void)
{
- if (heimdallRunning)
+ supportedDevicesListWidget->clear();
+ includedFilesListWidget->clear();
+
+ if (loadedPackageData.IsCleared())
{
- startFlashButton->setEnabled(false);
- return;
- }
+ // Package Interface
+ firmwareNameLineEdit->clear();
+ versionLineEdit->clear();
- if (repartitionCheckBox->isChecked())
+ developerNamesLineEdit->clear();
+
+ platformLineEdit->clear();
+
+ developerHomepageButton->setEnabled(false);
+ developerDonateButton->setEnabled(false);
+
+ repartitionRadioButton->setChecked(false);
+
+ loadFirmwareButton->setEnabled(false);
+ }
+ else
{
- if (!IsArchive(pitLineEdit->text()) && factoryfsCheckBox->isChecked() && !IsArchive(factoryfsLineEdit->text()) && kernelCheckBox->isChecked()
- && !IsArchive(kernelLineEdit->text()) && paramCheckBox->isChecked() && !IsArchive(paramLineEdit->text())
- && primaryBootCheckBox->isChecked() && !IsArchive(primaryBootLineEdit->text()) && secondaryBootCheckBox->isChecked()
- && !IsArchive(secondaryBootLineEdit->text()) && modemCheckBox->isChecked() && !IsArchive(modemLineEdit->text()))
+ firmwareNameLineEdit->setText(loadedPackageData.GetFirmwareInfo().GetName());
+ versionLineEdit->setText(loadedPackageData.GetFirmwareInfo().GetVersion());
+
+ QString developerNames;
+
+ if (!loadedPackageData.GetFirmwareInfo().GetDevelopers().isEmpty())
{
- startFlashButton->setEnabled(true);
+ developerNames = loadedPackageData.GetFirmwareInfo().GetDevelopers()[0];
+ for (int i = 1; i < loadedPackageData.GetFirmwareInfo().GetDevelopers().length(); i++)
+ developerNames += ", " + loadedPackageData.GetFirmwareInfo().GetDevelopers()[i];
}
+
+ developerNamesLineEdit->setText(developerNames);
+
+ platformLineEdit->setText(loadedPackageData.GetFirmwareInfo().GetPlatformInfo().GetName() + " ("
+ + loadedPackageData.GetFirmwareInfo().GetPlatformInfo().GetVersion() + ")");
+
+ if (!loadedPackageData.GetFirmwareInfo().GetUrl().isEmpty())
+ developerHomepageButton->setEnabled(true);
else
+ developerHomepageButton->setEnabled(false);
+
+ if (!loadedPackageData.GetFirmwareInfo().GetDonateUrl().isEmpty())
+ developerDonateButton->setEnabled(true);
+ else
+ developerDonateButton->setEnabled(false);
+
+ for (int i = 0; i < loadedPackageData.GetFirmwareInfo().GetDeviceInfos().length(); i++)
{
- startFlashButton->setEnabled(false);
+ const DeviceInfo& deviceInfo = loadedPackageData.GetFirmwareInfo().GetDeviceInfos()[i];
+ supportedDevicesListWidget->addItem(deviceInfo.GetManufacturer() + " " + deviceInfo.GetName() + " (" + deviceInfo.GetProduct() + ")");
}
- }
- else
- {
- bool atLeastOneFile = false;
- if (factoryfsCheckBox->isChecked())
+ for (int i = 0; i < loadedPackageData.GetFirmwareInfo().GetFileInfos().length(); i++)
{
- atLeastOneFile = true;
-
- if (IsArchive(factoryfsLineEdit->text()))
- {
- startFlashButton->setEnabled(false);
- return;
- }
+ const FileInfo& fileInfo = loadedPackageData.GetFirmwareInfo().GetFileInfos()[i];
+ includedFilesListWidget->addItem(fileInfo.GetFilename());
}
- if (kernelCheckBox->isChecked())
- {
- atLeastOneFile = true;
+ repartitionRadioButton->setChecked(loadedPackageData.GetFirmwareInfo().GetRepartition());
- if (IsArchive(kernelLineEdit->text()))
- {
- startFlashButton->setEnabled(false);
- return;
- }
- }
+ loadFirmwareButton->setEnabled(true);
+ }
+}
- if (paramCheckBox->isChecked())
- {
- atLeastOneFile = true;
+bool MainWindow::IsArchive(QString path)
+{
+ // Not a real check but hopefully it gets the message across, don't 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));
+}
- if (IsArchive(paramLineEdit->text()))
- {
- startFlashButton->setEnabled(false);
- return;
- }
- }
+QString MainWindow::PromptFileSelection(void)
+{
+ QString path = QFileDialog::getOpenFileName(this, "Select File", lastDirectory);
- if (primaryBootCheckBox->isChecked())
- {
- atLeastOneFile = true;
+ if (path != "")
+ lastDirectory = path.left(path.lastIndexOf('/') + 1);
- if (IsArchive(primaryBootLineEdit->text()))
- {
- startFlashButton->setEnabled(false);
- return;
- }
- }
+ return (path);
+}
- if (secondaryBootCheckBox->isChecked())
- {
- atLeastOneFile = true;
+QString MainWindow::PromptFileCreation(void)
+{
+ QString path = QFileDialog::getSaveFileName(this, "Save File", lastDirectory);
- if (IsArchive(secondaryBootLineEdit->text()))
- {
- startFlashButton->setEnabled(false);
- return;
- }
- }
+ if (path != "")
+ lastDirectory = path.left(path.lastIndexOf('/') + 1);
- if (cacheCheckBox->isChecked())
- {
- atLeastOneFile = true;
+ return (path);
+}
- if (IsArchive(cacheLineEdit->text()))
- {
- startFlashButton->setEnabled(false);
- return;
- }
- }
+void MainWindow::UpdatePartitionNamesInterface(void)
+{
+ populatingPartitionNames = true;
- if (databaseCheckBox->isChecked())
- {
- atLeastOneFile = true;
+ partitionNameComboBox->clear();
- if (IsArchive(databaseLineEdit->text()))
- {
- startFlashButton->setEnabled(false);
- return;
- }
- }
+ int partitionsListWidgetRow = partitionsListWidget->currentRow();
- if (modemCheckBox->isChecked())
- {
- atLeastOneFile = true;
+ if (partitionsListWidgetRow >= 0)
+ {
+ const FileInfo& partitionInfo = workingPackageData.GetFirmwareInfo().GetFileInfos()[partitionsListWidget->currentRow()];
- if (IsArchive(modemLineEdit->text()))
- {
- startFlashButton->setEnabled(false);
- return;
- }
- }
+ for (int i = 0; i < unusedPartitionIds.length(); i++)
+ partitionNameComboBox->addItem(currentPitData.FindEntry(unusedPartitionIds[i])->GetPartitionName());
- if (recoveryCheckBox->isChecked())
- {
- atLeastOneFile = true;
+ partitionNameComboBox->addItem(currentPitData.FindEntry(partitionInfo.GetPartitionId())->GetPartitionName());
+ partitionNameComboBox->setCurrentIndex(unusedPartitionIds.length());
- if (IsArchive(recoveryLineEdit->text()))
- {
- startFlashButton->setEnabled(false);
- return;
- }
+ partitionNameComboBox->setEnabled(true);
+ }
+ else
+ {
+ partitionNameComboBox->setEnabled(false);
+ }
+
+ populatingPartitionNames = false;
+}
+
+void MainWindow::UpdateStartButton(void)
+{
+ if (heimdallRunning)
+ {
+ startFlashButton->setEnabled(false);
+ return;
+ }
+
+ bool allPartitionsValid = true;
+
+ QList<FileInfo>& fileList = workingPackageData.GetFirmwareInfo().GetFileInfos();
+
+ for (int i = 0; i < fileList.length(); i++)
+ {
+ if (fileList[i].GetFilename().isEmpty())
+ {
+ allPartitionsValid = false;
+ break;
}
+ }
+
+ bool validSettings = allPartitionsValid && fileList.length() > 0;
- startFlashButton->setEnabled(atLeastOneFile);
+ startFlashButton->setEnabled(validSettings);
+ functionTabWidget->setTabEnabled(functionTabWidget->indexOf(createPackageTab), validSettings);
+}
+
+void MainWindow::UpdateBuildPackageButton(void)
+{
+ const FirmwareInfo& firmwareInfo = workingPackageData.GetFirmwareInfo();
+
+ if (firmwareInfo.GetName().isEmpty() || firmwareInfo.GetVersion().isEmpty() || firmwareInfo.GetPlatformInfo().GetName().isEmpty()
+ || firmwareInfo.GetPlatformInfo().GetVersion().isEmpty() || firmwareInfo.GetDevelopers().isEmpty() || firmwareInfo.GetDeviceInfos().isEmpty())
+ {
+ buildPackageButton->setEnabled(false);
+ }
+ else
+ {
+ buildPackageButton->setEnabled(true);
}
}
@@ -186,33 +246,52 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
lastDirectory = QDir::toNativeSeparators(QApplication::applicationDirPath());
+ populatingPartitionNames = false;
+
+ functionTabWidget->setTabEnabled(functionTabWidget->indexOf(createPackageTab), false);
+
QObject::connect(actionDonate, SIGNAL(triggered()), this, SLOT(OpenDonationWebpage()));
QObject::connect(actionAboutHeimdall, SIGNAL(triggered()), this, SLOT(ShowAbout()));
+ QObject::connect(browseFirmwarePackageButton, SIGNAL(clicked()), this, SLOT(SelectFirmwarePackage()));
+ QObject::connect(developerHomepageButton, SIGNAL(clicked()), this, SLOT(OpenDeveloperHomepage()));
+ QObject::connect(developerDonateButton, SIGNAL(clicked()), this, SLOT(OpenDeveloperDonationWebpage()));
+ QObject::connect(loadFirmwareButton, SIGNAL(clicked()), this, SLOT(LoadFirmwarePackage()));
+
+ QObject::connect(partitionsListWidget, SIGNAL(currentRowChanged(int)), this, SLOT(SelectPartition(int)));
+ QObject::connect(addPartitionButton, SIGNAL(clicked()), this, SLOT(AddPartition()));
+ QObject::connect(removePartitionButton, SIGNAL(clicked()), this, SLOT(RemovePartition()));
+
+ QObject::connect(partitionNameComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(SelectPartitionName(int)));
+ QObject::connect(partitionFileBrowseButton, SIGNAL(clicked()), this, SLOT(SelectPartitionFile()));
+
QObject::connect(pitBrowseButton, SIGNAL(clicked()), this, SLOT(SelectPit()));
- QObject::connect(factoryfsBrowseButton, SIGNAL(clicked()), this, SLOT(SelectFactoryfs()));
- QObject::connect(kernelBrowseButton, SIGNAL(clicked()), this, SLOT(SelectKernel()));
- QObject::connect(paramBrowseButton, SIGNAL(clicked()), this, SLOT(SelectParam()));
- QObject::connect(primaryBootBrowseButton, SIGNAL(clicked()), this, SLOT(SelectPrimaryBootloader()));
- QObject::connect(secondaryBootBrowseButton, SIGNAL(clicked()), this, SLOT(SelectSecondaryBootloader()));
- QObject::connect(cacheBrowseButton, SIGNAL(clicked()), this, SLOT(SelectCache()));
- QObject::connect(databaseBrowseButton, SIGNAL(clicked()), this, SLOT(SelectDatabase()));
- QObject::connect(modemBrowseButton, SIGNAL(clicked()), this, SLOT(SelectModem()));
- QObject::connect(recoveryBrowseButton, SIGNAL(clicked()), this, SLOT(SelectRecovery()));
-
- QObject::connect(repartitionCheckBox, SIGNAL(stateChanged(int)), this, SLOT(SetRepartionEnabled(int)));
- QObject::connect(factoryfsCheckBox, SIGNAL(stateChanged(int)), this, SLOT(SetFactoryfsEnabled(int)));
- QObject::connect(kernelCheckBox, SIGNAL(stateChanged(int)), this, SLOT(SetKernelEnabled(int)));
- QObject::connect(paramCheckBox, SIGNAL(stateChanged(int)), this, SLOT(SetParamEnabled(int)));
- QObject::connect(primaryBootCheckBox, SIGNAL(stateChanged(int)), this, SLOT(SetPrimaryBootloaderEnabled(int)));
- QObject::connect(secondaryBootCheckBox, SIGNAL(stateChanged(int)), this, SLOT(SetSecondaryBootloaderEnabled(int)));
- QObject::connect(cacheCheckBox, SIGNAL(stateChanged(int)), this, SLOT(SetCacheEnabled(int)));
- QObject::connect(databaseCheckBox, SIGNAL(stateChanged(int)), this, SLOT(SetDatabaseEnabled(int)));
- QObject::connect(modemCheckBox, SIGNAL(stateChanged(int)), this, SLOT(SetModemEnabled(int)));
- QObject::connect(recoveryCheckBox, SIGNAL(stateChanged(int)), this, SLOT(SetRecoveryEnabled(int)));
+ QObject::connect(repartitionCheckBox, SIGNAL(stateChanged(int)), this, SLOT(SetRepartition(int)));
QObject::connect(startFlashButton, SIGNAL(clicked()), this, SLOT(StartFlash()));
+ QObject::connect(createFirmwareNameLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(FirmwareNameChanged(const QString&)));
+ QObject::connect(createFirmwareVersionLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(FirmwareVersionChanged(const QString&)));
+ QObject::connect(createPlatformNameLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(PlatformNameChanged(const QString&)));
+ QObject::connect(createPlatformVersionLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(PlatformVersionChanged(const QString&)));
+
+ QObject::connect(createHomepageLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(HomepageUrlChanged(const QString&)));
+ QObject::connect(createDonateLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(DonateUrlChanged(const QString&)));
+
+ QObject::connect(createDevelopersListWidget, SIGNAL(currentRowChanged(int)), this, SLOT(SelectDeveloper(int)));
+ QObject::connect(createDeveloperNameLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(DeveloperNameChanged(const QString&)));
+ QObject::connect(addDeveloperButton, SIGNAL(clicked()), this, SLOT(AddDeveloper()));
+ QObject::connect(removeDeveloperButton, SIGNAL(clicked()), this, SLOT(RemoveDeveloper()));
+
+ QObject::connect(createDevicesListWidget, SIGNAL(currentRowChanged(int)), this, SLOT(SelectDevice(int)));
+ QObject::connect(deviceManufacturerLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(DeviceInfoChanged(const QString&)));
+ QObject::connect(deviceNameLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(DeviceInfoChanged(const QString&)));
+ QObject::connect(deviceProductCodeLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(DeviceInfoChanged(const QString&)));
+ QObject::connect(addDeviceButton, SIGNAL(clicked()), this, SLOT(AddDevice()));
+ QObject::connect(removeDeviceButton, SIGNAL(clicked()), this, SLOT(RemoveDevice()));
+
+ QObject::connect(buildPackageButton, SIGNAL(clicked()), this, SLOT(BuildPackage()));
+
QObject::connect(&process, SIGNAL(readyRead()), this, SLOT(HandleHeimdallStdout()));
QObject::connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(HandleHeimdallReturned(int, QProcess::ExitStatus)));
QObject::connect(&process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(HandleHeimdallError(QProcess::ProcessError)));
@@ -232,284 +311,314 @@ void MainWindow::ShowAbout(void)
aboutForm.show();
}
-void MainWindow::SelectPit(void)
+void MainWindow::SelectFirmwarePackage(void)
{
+ loadedPackageData.Clear();
+ UpdatePackageUserInterface();
+
QString path = PromptFileSelection();
- pitLineEdit->setText(path);
+ firmwarePackageLineEdit->setText(path);
- SetRepartionEnabled(path != "");
+ if (firmwarePackageLineEdit->text() != "")
+ {
+ if (Packaging::ExtractPackage(firmwarePackageLineEdit->text(), &loadedPackageData))
+ {
+ UpdatePackageUserInterface();
+ }
+ else
+ {
+ // TODO: Error?
+ loadedPackageData.Clear();
+ }
+ }
}
-void MainWindow::SelectFactoryfs(void)
+void MainWindow::OpenDeveloperHomepage(void)
{
- QString path = PromptFileSelection();
- factoryfsLineEdit->setText(path);
-
- SetFactoryfsEnabled(path != "");
+ QDesktopServices::openUrl(QUrl(loadedPackageData.GetFirmwareInfo().GetUrl(), QUrl::TolerantMode));
}
-void MainWindow::SelectKernel(void)
+void MainWindow::OpenDeveloperDonationWebpage(void)
{
- QString path = PromptFileSelection();
- kernelLineEdit->setText(path);
-
- SetKernelEnabled(path != "");
+ QDesktopServices::openUrl(QUrl(loadedPackageData.GetFirmwareInfo().GetDonateUrl(), QUrl::TolerantMode));
}
-void MainWindow::SelectParam(void)
+void MainWindow::LoadFirmwarePackage(void)
{
- QString path = PromptFileSelection();
- paramLineEdit->setText(path);
+ workingPackageData.Clear();
+ currentPitData.Clear();
+
+ // Make flashSettings responsible for the temporary files
+ workingPackageData.GetFiles().append(loadedPackageData.GetFiles());
+ loadedPackageData.RemoveAllFiles();
- SetParamEnabled(path != "");
-}
+ const QList<FileInfo> packageFileInfos = loadedPackageData.GetFirmwareInfo().GetFileInfos();
-void MainWindow::SelectPrimaryBootloader(void)
-{
- QString path = PromptFileSelection();
- primaryBootLineEdit->setText(path);
+ for (int i = 0; i < packageFileInfos.length(); i++)
+ {
+ for (int j = 0; j < workingPackageData.GetFiles().length(); j++)
+ {
+ if (workingPackageData.GetFiles()[j]->fileTemplate() == ("XXXXXX-" + packageFileInfos[i].GetFilename()))
+ {
+ FileInfo partitionInfo(packageFileInfos[i].GetPartitionId(), QDir::current().absoluteFilePath(workingPackageData.GetFiles()[j]->fileName()));
+ workingPackageData.GetFirmwareInfo().GetFileInfos().append(partitionInfo);
- SetPrimaryBootloaderEnabled(path != "");
-}
+ break;
+ }
+ }
+ }
-void MainWindow::SelectSecondaryBootloader(void)
-{
- QString path = PromptFileSelection();
- secondaryBootLineEdit->setText(path);
+ // Find the PIT file and read it
+ for (int i = 0; i < workingPackageData.GetFiles().length(); i++)
+ {
+ QTemporaryFile *file = workingPackageData.GetFiles()[i];
- SetSecondaryBootloaderEnabled(path != "");
-}
+ if (file->fileTemplate() == ("XXXXXX-" + loadedPackageData.GetFirmwareInfo().GetPitFilename()))
+ {
+ workingPackageData.GetFirmwareInfo().SetPitFilename(QDir::current().absoluteFilePath(file->fileName()));
-void MainWindow::SelectCache(void)
-{
- QString path = PromptFileSelection();
- cacheLineEdit->setText(path);
+ if (!ReadPit(file))
+ {
+ // TODO: Error
+ loadedPackageData.Clear();
+ UpdatePackageUserInterface();
- SetCacheEnabled(path != "");
-}
+ workingPackageData.Clear();
+ UpdateUnusedPartitionIds();
+ return;
+ }
-void MainWindow::SelectDatabase(void)
-{
- QString path = PromptFileSelection();
- databaseLineEdit->setText(path);
+ break;
+ }
+ }
- SetDatabaseEnabled(path != "");
-}
+ UpdateUnusedPartitionIds();
+ workingPackageData.GetFirmwareInfo().SetRepartition(loadedPackageData.GetFirmwareInfo().GetRepartition());
-void MainWindow::SelectModem(void)
-{
- QString path = PromptFileSelection();
- modemLineEdit->setText(path);
+ loadedPackageData.Clear();
+ UpdatePackageUserInterface();
+ firmwarePackageLineEdit->clear();
- SetModemEnabled(path != "");
-}
+ partitionsListWidget->clear();
-void MainWindow::SelectRecovery(void)
-{
- QString path = PromptFileSelection();
- recoveryLineEdit->setText(path);
+ // Populate partitionsListWidget with partition names (from the PIT file)
+ for (int i = 0; i < workingPackageData.GetFirmwareInfo().GetFileInfos().length(); i++)
+ {
+ const FileInfo& partitionInfo = workingPackageData.GetFirmwareInfo().GetFileInfos()[i];
- SetRecoveryEnabled(path != "");
-}
+ const PitEntry *pitEntry = currentPitData.FindEntry(partitionInfo.GetPartitionId());
-void MainWindow::SetRepartionEnabled(int enabled)
-{
- if (repartitionCheckBox->isChecked() != (enabled != 0))
- repartitionCheckBox->setChecked(enabled);
+ if (pitEntry)
+ {
+ partitionsListWidget->addItem(pitEntry->GetPartitionName());
+ }
+ else
+ {
+ // TODO: "Firmware package includes invalid partition IDs."
+ loadedPackageData.GetFirmwareInfo().Clear();
+ currentPitData.Clear();
+ UpdateUnusedPartitionIds();
- if (enabled)
- {
- repartitionCheckBox->setEnabled(true);
- pitLineEdit->setEnabled(true);
- repartitionCheckBox->setChecked(true);
- }
- else
- {
- repartitionCheckBox->setEnabled(pitLineEdit->text() != "");
- pitLineEdit->setEnabled(false);
+ partitionsListWidget->clear();
+ return;
+ }
}
-
- UpdateStartButton();
-}
-void MainWindow::SetFactoryfsEnabled(int enabled)
-{
- if (factoryfsCheckBox->isChecked() != (enabled != 0))
- factoryfsCheckBox->setChecked(enabled);
+ partitionNameComboBox->clear();
+ partitionIdLineEdit->clear();
+ partitionFileLineEdit->clear();
+ partitionFileBrowseButton->setEnabled(false);
- if (enabled)
- {
- factoryfsCheckBox->setEnabled(true);
- factoryfsLineEdit->setEnabled(true);
- factoryfsCheckBox->setChecked(true);
- }
- else
- {
- factoryfsCheckBox->setEnabled(factoryfsLineEdit->text() != "");
- factoryfsLineEdit->setEnabled(false);
- }
-
- UpdateStartButton();
-}
+ repartitionCheckBox->setEnabled(true);
+ repartitionCheckBox->setChecked(workingPackageData.GetFirmwareInfo().GetRepartition());
+ partitionsListWidget->setEnabled(true);
+ addPartitionButton->setEnabled(true);
+ removePartitionButton->setEnabled(true && partitionsListWidget->currentRow() >= 0);
-void MainWindow::SetKernelEnabled(int enabled)
-{
- if (kernelCheckBox->isChecked() != (enabled != 0))
- kernelCheckBox->setChecked(enabled);
+ pitLineEdit->setText(workingPackageData.GetFirmwareInfo().GetPitFilename());
+
+ functionTabWidget->setCurrentWidget(flashTab);
- if (enabled)
- {
- kernelCheckBox->setEnabled(true);
- kernelLineEdit->setEnabled(true);
- kernelCheckBox->setChecked(true);
- }
- else
- {
- kernelCheckBox->setEnabled(kernelLineEdit->text() != "");
- kernelLineEdit->setEnabled(false);
- }
-
UpdateStartButton();
}
-void MainWindow::SetParamEnabled(int enabled)
+void MainWindow::SelectPartitionName(int index)
{
- if (paramCheckBox->isChecked() != (enabled != 0))
- paramCheckBox->setChecked(enabled);
-
- if (enabled)
- {
- paramCheckBox->setEnabled(true);
- paramLineEdit->setEnabled(true);
- paramCheckBox->setChecked(true);
- }
- else
+ if (!populatingPartitionNames && index != -1 && index != unusedPartitionIds.length())
{
- paramCheckBox->setEnabled(paramLineEdit->text() != "");
- paramLineEdit->setEnabled(false);
+ unsigned int newPartitionIndex = unusedPartitionIds[index];
+ unusedPartitionIds.removeAt(index);
+
+ FileInfo& partitionInfo = workingPackageData.GetFirmwareInfo().GetFileInfos()[partitionsListWidget->currentRow()];
+ unusedPartitionIds.append(partitionInfo.GetPartitionId());
+ partitionInfo.SetPartitionId(newPartitionIndex);
+
+ partitionNameComboBox->clear();
+
+ // Update interface
+ UpdatePartitionNamesInterface();
+ partitionIdLineEdit->setText(QString::number(newPartitionIndex));
+ partitionsListWidget->currentItem()->setText(currentPitData.FindEntry(newPartitionIndex)->GetPartitionName());
}
-
- UpdateStartButton();
}
-void MainWindow::SetPrimaryBootloaderEnabled(int enabled)
+void MainWindow::SelectPartitionFile(void)
{
- if (primaryBootCheckBox->isChecked() != (enabled != 0))
- primaryBootCheckBox->setChecked(enabled);
+ QString path = PromptFileSelection();
- if (enabled)
- {
- primaryBootCheckBox->setEnabled(true);
- primaryBootLineEdit->setEnabled(true);
- primaryBootCheckBox->setChecked(true);
- }
- else
+ if (path != "")
{
- primaryBootCheckBox->setEnabled(primaryBootLineEdit->text() != "");
- primaryBootLineEdit->setEnabled(false);
+ workingPackageData.GetFirmwareInfo().GetFileInfos()[partitionsListWidget->currentRow()].SetFilename(path);
+ partitionFileLineEdit->setText(path);
+
+ pitBrowseButton->setEnabled(true);
+ partitionsListWidget->setEnabled(true);
+ UpdateStartButton();
+
+ if (unusedPartitionIds.length() > 0)
+ addPartitionButton->setEnabled(true);
}
-
- UpdateStartButton();
}
-void MainWindow::SetSecondaryBootloaderEnabled(int enabled)
+void MainWindow::SelectPartition(int row)
{
- if (secondaryBootCheckBox->isChecked() != (enabled != 0))
- secondaryBootCheckBox->setChecked(enabled);
-
- if (enabled)
+ if (row >= 0)
{
- secondaryBootCheckBox->setEnabled(true);
- secondaryBootLineEdit->setEnabled(true);
- secondaryBootCheckBox->setChecked(true);
+ const FileInfo& partitionInfo = workingPackageData.GetFirmwareInfo().GetFileInfos()[row];
+
+ UpdatePartitionNamesInterface();
+
+ partitionIdLineEdit->setText(QString::number(partitionInfo.GetPartitionId()));
+ partitionFileLineEdit->setText(partitionInfo.GetFilename());
+ partitionFileBrowseButton->setEnabled(true);
+
+ removePartitionButton->setEnabled(true);
}
else
{
- secondaryBootCheckBox->setEnabled(secondaryBootLineEdit->text() != "");
- secondaryBootLineEdit->setEnabled(false);
+ UpdatePartitionNamesInterface();
+
+ partitionIdLineEdit->clear();
+ partitionFileLineEdit->clear();
+ partitionFileBrowseButton->setEnabled(false);
+
+ removePartitionButton->setEnabled(false);
}
-
- UpdateStartButton();
}
-void MainWindow::SetCacheEnabled(int enabled)
+void MainWindow::AddPartition(void)
{
- if (cacheCheckBox->isChecked() != (enabled != 0))
- cacheCheckBox->setChecked(enabled);
+ FileInfo partitionInfo(unusedPartitionIds.first(), "");
+ workingPackageData.GetFirmwareInfo().GetFileInfos().append(partitionInfo);
+ UpdateUnusedPartitionIds();
- if (enabled)
- {
- cacheCheckBox->setEnabled(true);
- cacheLineEdit->setEnabled(true);
- cacheCheckBox->setChecked(true);
- }
- else
- {
- cacheCheckBox->setEnabled(cacheLineEdit->text() != "");
- cacheLineEdit->setEnabled(false);
- }
-
+ pitBrowseButton->setEnabled(false);
+ addPartitionButton->setEnabled(false);
+
+ partitionsListWidget->addItem(currentPitData.FindEntry(partitionInfo.GetPartitionId())->GetPartitionName());
+ partitionsListWidget->setCurrentRow(partitionsListWidget->count() - 1);
+ partitionsListWidget->setEnabled(false);
UpdateStartButton();
}
-void MainWindow::SetDatabaseEnabled(int enabled)
+void MainWindow::RemovePartition(void)
{
- if (databaseCheckBox->isChecked() != (enabled != 0))
- databaseCheckBox->setChecked(enabled);
+ workingPackageData.GetFirmwareInfo().GetFileInfos().removeAt(partitionsListWidget->currentRow());
+ UpdateUnusedPartitionIds();
- if (enabled)
- {
- databaseCheckBox->setEnabled(true);
- databaseLineEdit->setEnabled(true);
- databaseCheckBox->setChecked(true);
- }
- else
- {
- databaseCheckBox->setEnabled(databaseLineEdit->text() != "");
- databaseLineEdit->setEnabled(false);
- }
-
+ QListWidgetItem *item = partitionsListWidget->currentItem();
+ partitionsListWidget->setCurrentRow(-1);
+ delete item;
+
+ pitBrowseButton->setEnabled(true);
+ addPartitionButton->setEnabled(true);
+ partitionsListWidget->setEnabled(true);
UpdateStartButton();
}
-void MainWindow::SetModemEnabled(int enabled)
+void MainWindow::SelectPit(void)
{
- if (modemCheckBox->isChecked() != (enabled != 0))
- modemCheckBox->setChecked(enabled);
+ QString path = PromptFileSelection();
+ bool validPit = path != "";
+
+ // In order to map files in the old PIT to file in the new one, we first must use partition names instead of IDs.
+ QList<FileInfo> fileInfos = workingPackageData.GetFirmwareInfo().GetFileInfos();
+
+ int partitionNamesCount = fileInfos.length();
+ QString *partitionNames = new QString[fileInfos.length()];
+ for (int i = 0; i < fileInfos.length(); i++)
+ partitionNames[i] = currentPitData.FindEntry(fileInfos[i].GetPartitionId())->GetPartitionName();
- if (enabled)
+ currentPitData.Clear();
+
+ if (validPit)
{
- modemCheckBox->setEnabled(true);
- modemLineEdit->setEnabled(true);
- modemCheckBox->setChecked(true);
+ QFile pitFile(path);
+
+ if (ReadPit(&pitFile))
+ {
+ workingPackageData.GetFirmwareInfo().SetPitFilename(path);
+
+ partitionsListWidget->clear();
+ int partitionInfoIndex = 0;
+
+ for (int i = 0; i < partitionNamesCount; i++)
+ {
+ const PitEntry *pitEntry = currentPitData.FindEntry(partitionNames[i].toAscii().constData());
+
+ if (pitEntry)
+ {
+ fileInfos[partitionInfoIndex++].SetPartitionId(pitEntry->GetPartitionIdentifier());
+ partitionsListWidget->addItem(pitEntry->GetPartitionName());
+ }
+ else
+ {
+ fileInfos.removeAt(partitionInfoIndex);
+ }
+ }
+ }
+ else
+ {
+ validPit = false;
+ }
}
- else
+
+ // If the selected PIT was invalid, attempt to reload the old one.
+ if (!validPit)
{
- modemCheckBox->setEnabled(databaseLineEdit->text() != "");
- modemLineEdit->setEnabled(false);
+ // TODO: "The file selected was not a valid PIT file."
+ QFile originalPitFile(workingPackageData.GetFirmwareInfo().GetPitFilename());
+
+ if (ReadPit(&originalPitFile))
+ {
+ validPit = true;
+ }
+ else
+ {
+ // TODO: "Failed to reload working PIT data."
+ workingPackageData.Clear();
+ partitionsListWidget->clear();
+ }
}
-
+
+ UpdateUnusedPartitionIds();
+
+ delete [] partitionNames;
+
+ pitLineEdit->setText(workingPackageData.GetFirmwareInfo().GetPitFilename());
+
+ repartitionCheckBox->setEnabled(validPit);
+ partitionsListWidget->setEnabled(validPit);
+
+ addPartitionButton->setEnabled(validPit);
+ removePartitionButton->setEnabled(validPit && partitionsListWidget->currentRow() >= 0);
+
UpdateStartButton();
}
-void MainWindow::SetRecoveryEnabled(int enabled)
+void MainWindow::SetRepartition(int enabled)
{
- if (recoveryCheckBox->isChecked() != (enabled != 0))
- recoveryCheckBox->setChecked(enabled);
-
- if (enabled)
- {
- recoveryCheckBox->setEnabled(true);
- recoveryLineEdit->setEnabled(true);
- recoveryCheckBox->setChecked(true);
- }
- else
- {
- recoveryCheckBox->setEnabled(databaseLineEdit->text() != "");
- recoveryLineEdit->setEnabled(false);
- }
-
- UpdateStartButton();
+ workingPackageData.GetFirmwareInfo().SetRepartition(enabled);
}
void MainWindow::StartFlash(void)
@@ -522,68 +631,13 @@ void MainWindow::StartFlash(void)
if (repartitionCheckBox->isChecked())
{
+ arguments.append("--repartition");
+
arguments.append("--pit");
arguments.append(pitLineEdit->text());
}
- if (factoryfsCheckBox->isChecked())
- {
- arguments.append("--factoryfs");
- arguments.append(factoryfsLineEdit->text());
- }
-
- if (kernelCheckBox->isChecked())
- {
- arguments.append("--kernel");
- arguments.append(kernelLineEdit->text());
- }
-
- if (paramCheckBox->isChecked())
- {
- arguments.append("--param");
- arguments.append(paramLineEdit->text());
- }
-
- if (primaryBootCheckBox->isChecked())
- {
- arguments.append("--primary-boot");
- arguments.append(primaryBootLineEdit->text());
- }
-
- if (secondaryBootCheckBox->isChecked())
- {
- arguments.append("--secondary-boot");
- arguments.append(secondaryBootLineEdit->text());
- }
-
- if (cacheCheckBox->isChecked())
- {
- arguments.append("--cache");
- arguments.append(cacheLineEdit->text());
- }
-
- if (databaseCheckBox->isChecked())
- {
- arguments.append("--dbdata");
- arguments.append(databaseLineEdit->text());
- }
-
- if (modemCheckBox->isChecked())
- {
- arguments.append("--modem");
- arguments.append(modemLineEdit->text());
- }
-
- if (recoveryCheckBox->isChecked())
- {
- arguments.append("--recovery");
- arguments.append(recoveryLineEdit->text());
- }
-
- if (repartitionCheckBox->isChecked())
- {
- arguments.append("--repartition");
- }
+ // TODO: Loop through partitions and append them.
flashProgressBar->setEnabled(true);
UpdateStartButton();
@@ -600,13 +654,13 @@ void MainWindow::StartFlash(void)
QStringList environment = QProcess::systemEnvironment();
QStringList paths;
- // Ensure /usr/local/bin is in PATH
+ // Ensure /usr/bin is in PATH
for (int i = 0; i < environment.length(); i++)
{
if (environment[i].left(5) == "PATH=")
{
paths = environment[i].mid(5).split(':');
- paths.prepend("/usr/local/bin");
+ paths.prepend("/usr/bin");
break;
}
}
@@ -640,6 +694,140 @@ void MainWindow::StartFlash(void)
}
}
+void MainWindow::FirmwareNameChanged(const QString& text)
+{
+ workingPackageData.GetFirmwareInfo().SetName(text);
+ UpdateBuildPackageButton();
+}
+
+void MainWindow::FirmwareVersionChanged(const QString& text)
+{
+ workingPackageData.GetFirmwareInfo().SetVersion(text);
+ UpdateBuildPackageButton();
+}
+
+void MainWindow::PlatformNameChanged(const QString& text)
+{
+ workingPackageData.GetFirmwareInfo().GetPlatformInfo().SetName(text);
+ UpdateBuildPackageButton();
+}
+
+void MainWindow::PlatformVersionChanged(const QString& text)
+{
+ workingPackageData.GetFirmwareInfo().GetPlatformInfo().SetVersion(text);
+ UpdateBuildPackageButton();
+}
+
+void MainWindow::HomepageUrlChanged(const QString& text)
+{
+ workingPackageData.GetFirmwareInfo().SetUrl(text);
+}
+
+void MainWindow::DonateUrlChanged(const QString& text)
+{
+ workingPackageData.GetFirmwareInfo().SetDonateUrl(text);
+}
+
+void MainWindow::DeveloperNameChanged(const QString& text)
+{
+ if (text.isEmpty())
+ addDeveloperButton->setEnabled(false);
+ else
+ addDeveloperButton->setEnabled(true);
+}
+
+void MainWindow::SelectDeveloper(int row)
+{
+ if (row >= 0)
+ removeDeveloperButton->setEnabled(true);
+ else
+ removeDeveloperButton->setEnabled(false);
+}
+
+void MainWindow::AddDeveloper(void)
+{
+ workingPackageData.GetFirmwareInfo().GetDevelopers().append(createDeveloperNameLineEdit->text());
+
+ createDevelopersListWidget->addItem(createDeveloperNameLineEdit->text());
+ createDeveloperNameLineEdit->clear();
+
+ UpdateBuildPackageButton();
+}
+
+void MainWindow::RemoveDeveloper(void)
+{
+ workingPackageData.GetFirmwareInfo().GetDevelopers().removeAt(createDevelopersListWidget->currentRow());
+
+ QListWidgetItem *item = createDevelopersListWidget->currentItem();
+ createDevelopersListWidget->setCurrentRow(-1);
+ delete item;
+
+ removeDeveloperButton->setEnabled(false);
+
+ UpdateBuildPackageButton();
+}
+
+void MainWindow::DeviceInfoChanged(const QString& text)
+{
+ if (deviceManufacturerLineEdit->text().isEmpty() || deviceNameLineEdit->text().isEmpty() || deviceProductCodeLineEdit->text().isEmpty())
+ addDeviceButton->setEnabled(false);
+ else
+ addDeviceButton->setEnabled(true);
+}
+
+void MainWindow::SelectDevice(int row)
+{
+ if (row >= 0)
+ removeDeviceButton->setEnabled(true);
+ else
+ removeDeviceButton->setEnabled(false);
+}
+
+void MainWindow::AddDevice(void)
+{
+ workingPackageData.GetFirmwareInfo().GetDeviceInfos().append(DeviceInfo(deviceManufacturerLineEdit->text(), deviceNameLineEdit->text(),
+ deviceProductCodeLineEdit->text()));
+
+ createDevicesListWidget->addItem(deviceManufacturerLineEdit->text() + " " + deviceNameLineEdit->text() + " (" + deviceProductCodeLineEdit->text() + ")");
+ deviceManufacturerLineEdit->clear();
+ deviceNameLineEdit->clear();
+ deviceProductCodeLineEdit->clear();
+
+ UpdateBuildPackageButton();
+}
+
+void MainWindow::RemoveDevice(void)
+{
+ workingPackageData.GetFirmwareInfo().GetDeviceInfos().removeAt(createDevicesListWidget->currentRow());
+
+ QListWidgetItem *item = createDevicesListWidget->currentItem();
+ createDevicesListWidget->setCurrentRow(-1);
+ delete item;
+
+ removeDeviceButton->setEnabled(false);
+
+ UpdateBuildPackageButton();
+}
+
+void MainWindow::BuildPackage(void)
+{
+ QString packagePath = PromptFileCreation();
+
+ if (!packagePath.endsWith(".tar.gz", Qt::CaseInsensitive))
+ {
+ if (packagePath.endsWith(".tar", Qt::CaseInsensitive))
+ packagePath.append(".gz");
+ else if (packagePath.endsWith(".gz", Qt::CaseInsensitive))
+ packagePath.replace(packagePath.length() - 3, ".tar.gz");
+ else if (packagePath.endsWith(".tgz", Qt::CaseInsensitive))
+ packagePath.replace(packagePath.length() - 4, ".tar.gz");
+ else
+ packagePath.append(".tar.gz");
+ }
+
+ Packaging::BuildPackage(packagePath, workingPackageData);
+}
+
void MainWindow::HandleHeimdallStdout(void)
{
QString output = process.read(1024);
diff --git a/heimdall-frontend/Source/mainwindow.h b/heimdall-frontend/Source/mainwindow.h
index added4e..7525d67 100644
--- a/heimdall-frontend/Source/mainwindow.h
+++ b/heimdall-frontend/Source/mainwindow.h
@@ -21,13 +21,21 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
+// Qt
+#include <QList>
+#include <QMainWindow>
+#include <QProcess>
+#include <QTemporaryFile>
+
+// libpit
+#include "libpit.h"
+
// Heimdall Frontend
#include "aboutform.h"
#include "ui_mainwindow.h"
+#include "PackageData.h"
-// Qt
-#include <QMainWindow>
-#include <QProcess>
+using namespace libpit;
namespace HeimdallFrontend
{
@@ -45,11 +53,29 @@ namespace HeimdallFrontend
bool heimdallRunning;
QProcess process;
+ PackageData loadedPackageData;
+
+ PitData currentPitData;
+ PackageData workingPackageData;
+
+ bool populatingPartitionNames;
+ QList<unsigned int> unusedPartitionIds;
+
+ void UpdateUnusedPartitionIds(void);
+ bool ReadPit(QFile *file);
+
+ void UpdatePackageUserInterface(void);
+
bool IsArchive(QString path);
QString PromptFileSelection(void);
+ QString PromptFileCreation(void);
+
+ void UpdatePartitionNamesInterface(void);
void UpdateStartButton(void);
+ void UpdateBuildPackageButton(void);
+
public:
explicit MainWindow(QWidget *parent = 0);
@@ -60,30 +86,43 @@ namespace HeimdallFrontend
void OpenDonationWebpage(void);
void ShowAbout(void);
+ void SelectFirmwarePackage(void);
+ void OpenDeveloperHomepage(void);
+ void OpenDeveloperDonationWebpage(void);
+ void LoadFirmwarePackage(void);
+
+ void SelectPartitionName(int index);
+ void SelectPartitionFile(void);
+
+ void SelectPartition(int row);
+ void AddPartition(void);
+ void RemovePartition(void);
+
void SelectPit(void);
- void SelectFactoryfs(void);
- void SelectKernel(void);
- void SelectParam(void);
- void SelectPrimaryBootloader(void);
- void SelectSecondaryBootloader(void);
- void SelectCache(void);
- void SelectDatabase(void);
- void SelectModem(void);
- void SelectRecovery(void);
-
- void SetRepartionEnabled(int enabled);
- void SetFactoryfsEnabled(int enabled);
- void SetKernelEnabled(int enabled);
- void SetParamEnabled(int enabled);
- void SetPrimaryBootloaderEnabled(int enabled);
- void SetSecondaryBootloaderEnabled(int enabled);
- void SetCacheEnabled(int enabled);
- void SetDatabaseEnabled(int enabled);
- void SetModemEnabled(int enabled);
- void SetRecoveryEnabled(int enabled);
+ void SetRepartition(int enabled);
void StartFlash(void);
+ void FirmwareNameChanged(const QString& text);
+ void FirmwareVersionChanged(const QString& text);
+ void PlatformNameChanged(const QString& text);
+ void PlatformVersionChanged(const QString& text);
+
+ void HomepageUrlChanged(const QString& text);
+ void DonateUrlChanged(const QString& text);
+
+ void DeveloperNameChanged(const QString& text);
+ void SelectDeveloper(int row);
+ void AddDeveloper(void);
+ void RemoveDeveloper(void);
+
+ void DeviceInfoChanged(const QString& text);
+ void SelectDevice(int row);
+ void AddDevice(void);
+ void RemoveDevice(void);
+
+ void BuildPackage(void);
+
void HandleHeimdallStdout(void);
void HandleHeimdallReturned(int exitCode, QProcess::ExitStatus exitStatus);
void HandleHeimdallError(QProcess::ProcessError error);