|
@@ -0,0 +1,213 @@
|
|
|
|
+#include "patchdownloader.h"
|
|
|
|
+#include "models/filesystem.h"
|
|
|
|
+#include <QApplication>
|
|
|
|
+#include <QDir>
|
|
|
|
+#include <QStringList>
|
|
|
|
+#include <QUrlQuery>
|
|
|
|
+#include <QVariant>
|
|
|
|
+#include <QDebug>
|
|
|
|
+
|
|
|
|
+PatchDownloader::PatchDownloader(QObject *parent) : QObject(parent)
|
|
|
|
+{
|
|
|
|
+ patch_download_dir = QDir(QApplication::applicationDirPath() + "/data");
|
|
|
|
+
|
|
|
|
+ connect(&update_check_timer, &QTimer::timeout, this, &PatchDownloader::checkForUpdates);
|
|
|
|
+ update_check_timer.setInterval(1000 * 60 * 5); // 5 minutes
|
|
|
|
+ update_check_timer.start();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+PatchDownloader::~PatchDownloader()
|
|
|
|
+{
|
|
|
|
+ clearDownloadQueue();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void PatchDownloader::checkForUpdates()
|
|
|
|
+{
|
|
|
|
+ if (!download_queue.empty()) {
|
|
|
|
+ qDebug() << "PatchDownloader: downloads are not ready yet, passing checkForUpdates";
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!updatePatchList())
|
|
|
|
+ return;
|
|
|
|
+ removeOldPatchesFromDirecrory();
|
|
|
|
+ addMissingPatchesToDownloadList();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void PatchDownloader::onDownloaderCompleted(Downloader *downloader_ptr)
|
|
|
|
+{
|
|
|
|
+ downloader_ptr->targetFile->close();
|
|
|
|
+ downloader_ptr->targetFile->deleteLater();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void PatchDownloader::onDownloaderProgressChanged(Downloader*)
|
|
|
|
+{
|
|
|
|
+ quint64 totalSize = 0;
|
|
|
|
+ quint64 downloadedSize = 0;
|
|
|
|
+ quint64 summary_speed = 0;
|
|
|
|
+ quint64 time_elapsed = 0;
|
|
|
|
+
|
|
|
|
+ foreach (Downloader* downloader, download_queue) {
|
|
|
|
+ totalSize += downloader->getBytesTotal();
|
|
|
|
+ downloadedSize += downloader->getBytesDownloaded();
|
|
|
|
+
|
|
|
|
+ if (downloader->getBytesTotal() != downloader->getBytesDownloaded()) {
|
|
|
|
+ summary_speed += downloader->getSpeed();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ time_elapsed = (totalSize - downloadedSize) / qMax(quint64(1), summary_speed);
|
|
|
|
+
|
|
|
|
+ if (totalSize == downloadedSize)
|
|
|
|
+ emit downloadCompleted();
|
|
|
|
+ else
|
|
|
|
+ emit progressChanged(downloadedSize, totalSize,
|
|
|
|
+ Downloader::getSpeedFormatted(summary_speed),
|
|
|
|
+ Downloader::getElapsedTimeFormatted(time_elapsed));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int PatchDownloader::versionFromPatchFilename(QString filename)
|
|
|
|
+{
|
|
|
|
+ int version = 0;
|
|
|
|
+ for (int i = filename.indexOf("_v") + 2; i < filename.indexOf("_v") + 7; i += 2) {
|
|
|
|
+ version = version * 10 + (filename.at(i).toLatin1() - '0');
|
|
|
|
+ }
|
|
|
|
+ return version;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+bool PatchDownloader::updatePatchList()
|
|
|
|
+{
|
|
|
|
+ QUrlQuery query; // query for building GET-request aka patch-version
|
|
|
|
+
|
|
|
|
+ QStringList names;
|
|
|
|
+ names << "sound" << "text" << "image" << "loadscreen" << "texture" << "font";
|
|
|
|
+
|
|
|
|
+ foreach(QString patch_name, names) {
|
|
|
|
+ query.addQueryItem(patch_name, "100");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ QUrl target_url;
|
|
|
|
+ target_url.setUrl("http://translate.lotros.ru/groupware/check_updates");
|
|
|
|
+ target_url.setQuery(query);
|
|
|
|
+
|
|
|
|
+ QByteArray target_array;
|
|
|
|
+ Downloader downloader;
|
|
|
|
+ downloader.setUrl(target_url);
|
|
|
|
+ downloader.targetBytearray = &target_array;
|
|
|
|
+ downloader.start();
|
|
|
|
+ downloader.waitForDownloaded();
|
|
|
|
+
|
|
|
|
+ if (target_array.isEmpty()) {
|
|
|
|
+ qDebug() << __FUNCTION__ << "Cannot download, target_array is empty!";
|
|
|
|
+ emit getPatchListError();
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ QStringList entry_list = QString(target_array).split('|');
|
|
|
|
+
|
|
|
|
+ if (entry_list.size() != names.size()) {
|
|
|
|
+ qDebug() << __FUNCTION__ << "Entry list size is not equal to patch names size!" << QString(target_array);
|
|
|
|
+ emit getPatchListError();
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ patches.clear();
|
|
|
|
+ for (int i = 0; i < entry_list.size(); ++i) {
|
|
|
|
+ QStringList patch_data = entry_list[i].split(":::");
|
|
|
|
+ if (patch_data.size() != 3) {
|
|
|
|
+ qDebug() << __FUNCTION__ << "Incorrect patch entry size! Entry: " << entry_list[i];
|
|
|
|
+ emit getPatchListError();
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ patches.append({patch_data[0], patch_data[1], patch_data[2]});
|
|
|
|
+ }
|
|
|
|
+ return true;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+bool PatchDownloader::removeOldPatchesFromDirecrory()
|
|
|
|
+{
|
|
|
|
+ QStringList actual_hash_list;
|
|
|
|
+ foreach (Patch patch, patches) {
|
|
|
|
+ actual_hash_list.append(patch.md5_hash);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ qDebug() << "Removing old patches. Current hash list " << actual_hash_list;
|
|
|
|
+
|
|
|
|
+ QStringList paths = patch_download_dir.entryList(QDir::Files);
|
|
|
|
+
|
|
|
|
+ foreach (QString filename, paths) {
|
|
|
|
+ QString hash = FileSystem::fileHash(patch_download_dir.absolutePath() + "/" + filename, QCryptographicHash::Md5);
|
|
|
|
+ if (!actual_hash_list.contains(hash)) {
|
|
|
|
+ qDebug() << "File " << filename << " with hash " << hash << "seems outdated, deleting!";
|
|
|
|
+ if (!QFile::remove(patch_download_dir.absolutePath() + "/" +filename)) {
|
|
|
|
+ qDebug() << __FUNCTION__ << "Unable to remove file " << filename;
|
|
|
|
+ emit removeFileError(patch_download_dir.absolutePath() + "/" + filename);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return true;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+bool PatchDownloader::addMissingPatchesToDownloadList()
|
|
|
|
+{
|
|
|
|
+ QStringList file_hashes;
|
|
|
|
+ QStringList paths = patch_download_dir.entryList(QDir::Files);
|
|
|
|
+
|
|
|
|
+ QDir dir(patch_download_dir);
|
|
|
|
+ if (!dir.exists())
|
|
|
|
+ QDir().mkdir(patch_download_dir.absolutePath());
|
|
|
|
+
|
|
|
|
+ foreach (QString filename, paths) {
|
|
|
|
+ file_hashes << FileSystem::fileHash(patch_download_dir.absolutePath() + "/" + filename, QCryptographicHash::Md5);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ bool download_started = false;
|
|
|
|
+
|
|
|
|
+ foreach (Patch patch, patches) {
|
|
|
|
+ if (file_hashes.contains(patch.md5_hash))
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ QString patch_filepath = patch_download_dir.absolutePath() + "/" + patch.url.fileName();
|
|
|
|
+
|
|
|
|
+ if (FileSystem::fileExists(patch_filepath) && !QFile::remove(patch_filepath)) {
|
|
|
|
+ qDebug() << __FUNCTION__ << "Unable to remove file " << patch_filepath;
|
|
|
|
+ emit removeFileError(patch_filepath);
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!download_started) {
|
|
|
|
+ download_started = true;
|
|
|
|
+ emit downloadStarted();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ qDebug() << "Starting download of file " << patch_filepath << " from url " << patch.url;
|
|
|
|
+
|
|
|
|
+ Downloader* downloader = new Downloader();
|
|
|
|
+ connect(downloader, &Downloader::progressChanged, this, &PatchDownloader::onDownloaderProgressChanged);
|
|
|
|
+ connect(downloader, &Downloader::downloadFinished, this, &PatchDownloader::onDownloaderCompleted);
|
|
|
|
+ downloader->setUrl(patch.url);
|
|
|
|
+ downloader->targetFile = new QFile(patch_filepath, downloader);
|
|
|
|
+ downloader->targetFile->open(QIODevice::ReadWrite);
|
|
|
|
+ downloader->start();
|
|
|
|
+ download_queue.append(downloader);
|
|
|
|
+ }
|
|
|
|
+ return true;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void PatchDownloader::clearDownloadQueue()
|
|
|
|
+{
|
|
|
|
+ for (int i = 0; i < download_queue.size(); ++i) {
|
|
|
|
+ Downloader* downloader = download_queue[i];
|
|
|
|
+
|
|
|
|
+ if (download_queue.indexOf(downloader) != i)
|
|
|
|
+ continue; // this downloader was already stopped and deleted in previous iterations.
|
|
|
|
+ // this shouldn't normally happen because it means that file was being downloaded many times
|
|
|
|
+
|
|
|
|
+ downloader->stop();
|
|
|
|
+ downloader->targetFile->close();
|
|
|
|
+ downloader->targetFile->deleteLater();
|
|
|
|
+ downloader->deleteLater();
|
|
|
|
+ }
|
|
|
|
+ download_queue.clear();
|
|
|
|
+}
|