Browse Source

Added legacy launcher and fixed bugs

Ivan Arkhipov 4 years ago
parent
commit
7db2f5ff3b

+ 1 - 6
src/Legacy/legacyapplication.cpp

@@ -28,7 +28,7 @@ LegacyApplication::LegacyApplication() {
 LegacyApplication::~LegacyApplication() {
     // gui will be deleted automatically on close due to Qt::WA_DeleteOnClose attribute
     patch_managers_thread_->terminate();
-    patch_managers_thread_->wait();
+    patch_managers_thread_->wait(10 * 1000); // Waiting 10 seconds
     delete patch_managers_thread_;
 }
 
@@ -44,11 +44,6 @@ bool LegacyApplication::init() {
     QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, QApplication::applicationDirPath());
     QSettings::setPath(QSettings::IniFormat, QSettings::SystemScope, QApplication::applicationDirPath());
 
-    qDebug() << "Checking if there's another instance of Legacy";
-    if (!checkSingleAppInstance()) {
-        return false;
-    }
-
     qDebug() << "Initialising fonts and resources";
     QDir::setCurrent(QApplication::applicationDirPath());
 

+ 13 - 0
src/Legacy/main.cpp

@@ -1,4 +1,7 @@
 #include <QApplication>
+#include <QLockFile>
+#include <QDir>
+#include <QMessageBox>
 #include "legacyapplication.h"
 
 // Global. Should be updated only by MainWindow!!!
@@ -9,6 +12,16 @@ int main(int argc, char *argv[])
 {
     QApplication app(argc, argv);
 
+    QLockFile lockFile(QDir::temp().absoluteFilePath("rulotro.lock"));
+    if(!lockFile.tryLock(100)){
+        qDebug() << "Lock file already exists! Some other application is already running...";
+        QMessageBox msgBox;
+        msgBox.setIcon(QMessageBox::Warning);
+        msgBox.setText("Приложение уже запущено.\nРазрешено запускать только один экземпляр приложения.\nЕсли вы уверены, что Наследие закрыто, попробуйте перезагрузить компьютер.");
+        msgBox.exec();
+        return false;
+    }
+
     if (!LegacyApplication::instance().init()) {
         return 1;
     }

+ 1 - 1
src/Legacy/models/downloader.cpp

@@ -40,7 +40,7 @@ bool Downloader::isStarted() const
 
 double Downloader::getPercent() const
 {
-    return double(bytes_downloaded_) * 100.0 / double(bytes_total_);
+    return bytes_total_ > 0.01 ? double(bytes_downloaded_) * 100.0 / double(bytes_total_) : 0;
 }
 
 quint64 Downloader::getBytesTotal() const

+ 0 - 5
src/Legacy/models/patchinstaller.cpp

@@ -401,11 +401,6 @@ void PatchInstaller::startGame(bool remove_dat_files) {
         return;
     }
 
-    if (!FileSystem::fileExists(QApplication::applicationDirPath() + "/Launcher.exe")) {
-        qCritical() << __FUNCTION__ << "Starting game FAILED - no game launcher in legacy directory found!";
-        return;
-    }
-
     QStringList args;
     args << "-nosplashscreen";
     if (_current_applied_patches_info.loadscreens_patch_hashsum != "") {

+ 8 - 15
src/Legacy/utils.cpp

@@ -66,8 +66,14 @@ void logMessageHandler(QtMsgType type, const QMessageLogContext &context, const
                 .arg(line_number)
                 .arg(function);
 
-#ifdef QT_DEBUG
-    QFile log_file("legacy_v2.log");
+#ifndef QT_DEBUG
+    QFile log_file("legacy_log.txt");
+    QFileInfo log_file_info(log_file);
+    if (log_file_info.size() >= 10 * 1024 * 1024) {
+        QFile::remove("legacy_log.old.txt");
+        QFile::copy("legacy_log.txt", "legacy_log.old.txt");
+        QFile::remove("legacy_log.txt");
+    }
     log_file.open(QIODevice::ReadWrite | QIODevice::Append);
     QTextStream stream(&log_file);
     stream << s;
@@ -78,19 +84,6 @@ void logMessageHandler(QtMsgType type, const QMessageLogContext &context, const
     fflush(stderr);
 }
 
-bool checkSingleAppInstance() {
-    QLockFile lockFile(QDir::temp().absoluteFilePath("rulotro.lock"));
-    if(!lockFile.tryLock(1)){
-        qDebug() << "Lock file already exists! Some other application is already running...";
-        QMessageBox msgBox;
-        msgBox.setIcon(QMessageBox::Warning);
-        msgBox.setText("Приложение уже запущено.\nРазрешено запускать только один экземпляр приложения.");
-        msgBox.exec();
-        return false;
-    }
-    return true;
-}
-
 AppErrorStatus CheckAppPrerequesities() {
     QString game_folder_path = Settings::getValue("Lotro/game_path").toString();
     QString locale_prefix = Settings::getValue("Lotro/original_locale").toString();

+ 0 - 2
src/Legacy/utils.h

@@ -13,8 +13,6 @@ bool checkInternetConnection(QUrl url);
 
 void logMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
 
-bool checkSingleAppInstance();
-
 enum AppErrorStatus : int {
     E_PATCHED_BY_OLD_LEGACY = 32,
     E_WRONG_GAME_FOLDER = 16,

+ 16 - 4
src/LegacyLauncher/LegacyLauncher.pro

@@ -1,8 +1,8 @@
 include( ../../common.pri )
 include( ../../app.pri )
 
-QT = core widgets
-CONFIG += c++11
+QT += core gui widgets network multimedia multimediawidgets
+QMAKE_LFLAGS += -static -static-libgcc -static-libstdc++
 
 # The following define makes your compiler emit warnings if you use
 # any feature of Qt which as been marked deprecated (the exact warnings
@@ -16,9 +16,18 @@ DEFINES += QT_DEPRECATED_WARNINGS
 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
 
 SOURCES += \
-        main.cpp
+        main.cpp \
+        launcher.cpp \
+        downloader.cpp \
+        filesystem.cpp
 
-RESOURCES +=
+HEADERS += \
+        launcher.h \
+        downloader.h \
+        filesystem.h
+
+FORMS += \
+        launcher.ui
 
 # Default rules for deployment.
 qnx: target.path = /tmp/$${TARGET}/bin
@@ -26,3 +35,6 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin
 !isEmpty(target.path): INSTALLS += target
 
 win32:RC_ICONS = $${PROJECT_ROOT_PATH}/resources/icon.ico
+
+RESOURCES += \
+    ../../resources/prelauncher_resources.qrc

+ 254 - 0
src/LegacyLauncher/downloader.cpp

@@ -0,0 +1,254 @@
+#include "downloader.h"
+#include <QEventLoop>
+#include <QApplication>
+#include <QDebug>
+#include <QTime>
+
+Downloader::Downloader(QObject *parent) : QObject(parent), busy_(false), m_WebCtrl(this)
+{
+    qRegisterMetaType<Downloader::Status>();
+    connect(&m_WebCtrl, SIGNAL(finished(QNetworkReply*)), this, SLOT(onDownloadFinished(QNetworkReply*)));
+}
+
+Downloader::~Downloader() {
+}
+
+QUrl Downloader::getUrl() const
+{
+    return url_;
+}
+
+void Downloader::setUrl(const QUrl &_url)
+{
+    url_ = _url;
+}
+
+void Downloader::waitForDownloaded() const
+{
+    if (!busy_)
+        return;
+
+    QEventLoop loop;
+    connect(this, &Downloader::downloadFinished, &loop, &QEventLoop::quit);
+    loop.exec();
+}
+
+bool Downloader::isStarted() const
+{
+    return busy_;
+}
+
+double Downloader::getPercent() const
+{
+    return bytes_total_ > 0.01 ? double(bytes_downloaded_) * 100.0 / double(bytes_total_) : 0;
+}
+
+quint64 Downloader::getBytesTotal() const
+{
+    return bytes_total_;
+}
+
+quint64 Downloader::getBytesDownloaded() const
+{
+    return bytes_downloaded_;
+}
+
+quint64 Downloader::getElapsedTime() const
+{
+    quint64 delta_size = getBytesTotal() - getBytesDownloaded();
+    quint64 avg_speed = qMax(quint64(1), getAverageSpeed());
+
+    while (delta_size > 1024 && avg_speed > 1024) { // Making precision smaller to get smoother dynamics
+        delta_size /= 1024;
+        avg_speed /= 1024;
+    }
+
+    return delta_size / avg_speed;
+}
+
+quint64 Downloader::getCurrentSpeed() const
+{
+    return download_speed_;
+}
+
+quint64 Downloader::getAverageSpeed() const
+{
+    return average_speed_;
+}
+
+Downloader::Status Downloader::getDownloadStatus() const
+{
+    return {isStarted(), getPercent(), getBytesTotal(), getBytesDownloaded(), getCurrentSpeed(), getAverageSpeed(), getElapsedTime()};
+}
+
+QString Downloader::getSpeedFormatted(quint64 speed_bytes_per_sec)
+{
+    float speed = speed_bytes_per_sec;
+
+    QString unit;
+    if (speed < 1024) {
+        unit = "bytes/sec";
+    } else if (speed < 1024*1024) {
+        speed /= 1024;
+        unit = "kB/s";
+    } else {
+        speed /= 1024*1024;
+        unit = "MB/s";
+    }
+    return QString::number(speed, 'f', 1) + " " + unit;
+}
+
+QString Downloader::getSizeFormatted(quint64 bytes)
+{
+    float size = bytes;
+    QString unit;
+    if (size < 1024) {
+        unit = "байт";
+    } else if (size < 1024 * 1024) {
+        size /= 1024;
+        unit = "кб";
+    } else if (size < 1024 * 1024 * 1024){
+        size /= 1024*1024;
+        unit = "мб";
+    } else {
+        size /= 1024 * 1024 * 1024;
+        unit = "гб";
+    }
+    return QString::number(size, 'f', 1) + " " + unit;
+}
+
+QString Downloader::getElapsedTimeFormatted(quint64 elapsed_time_secs)
+{
+    qint64 secs = elapsed_time_secs;
+    qint64 mins = 0;
+    qint64 hours = 0;
+    qint64 days = 0;
+
+    if (secs > 60) {
+        mins = secs / 60;
+        secs %= 60;
+    }
+
+    if (mins > 60) {
+        hours = mins / 60;
+        mins %= 60;
+    }
+
+    if (hours > 24) {
+        days = hours / 24;
+        hours %= 24;
+    }
+
+    if (days > 0)
+        return " очень много (низкая скорость)";
+
+    QString result = "";
+
+    if (hours > 0) {
+        result += QString::number(hours);
+        if (hours % 10 == 1 && hours / 10 != 1)
+            result += " час ";
+        else if (hours % 10 > 1 && hours % 10 < 5 && hours / 10 != 1)
+            result += " часа ";
+        else
+            result += " часов ";
+    }
+
+    if (mins > 0) {
+        result += QString::number(mins);
+        if (mins % 10 == 1 && mins / 10 != 1)
+            result += " минута ";
+        else if (mins % 10 > 1 && mins % 10 < 5 && mins / 10 != 1)
+            result += " минуты ";
+        else
+            result += " минут ";
+    }
+
+    if (secs > 0 && hours == 0) {
+        result += QString::number(secs);
+        if (secs % 10 == 1 && secs / 10 != 1)
+            result += " секунда ";
+        else if (secs % 10 > 1 && secs % 10 < 5 && secs / 10 != 1)
+            result += " секунды ";
+        else
+            result += " секунд ";
+    }
+
+    if (result == "")
+        result = "совсем чуть-чуть";
+
+    return result;
+}
+
+void Downloader::start()
+{
+    if (busy_) {
+        qDebug() << "Cannot download " << url_ << ", downloader is busy!";
+        return;
+    }
+    qDebug() << "Starting download " << url_;
+
+    last_tick_time_.restart();
+    bytes_downloaded_ = 0;
+    bytes_total_ = 0;
+    download_speed_ = 0;
+    bytes_downloaded_before_tick_ = 0;
+
+    busy_ = true;
+    QNetworkRequest request(url_);
+    m_CurrentReply = m_WebCtrl.get(request);
+    m_CurrentReply->setReadBufferSize(download_speed_limit);
+    connect(m_CurrentReply, &QNetworkReply::readyRead, this, &Downloader::onReadyRead);
+    connect(m_CurrentReply, &QNetworkReply::downloadProgress, this, &Downloader::onDownloadProgressChanged);
+}
+
+void Downloader::updateDownloadSpeedLimit(int bytes_per_sec)
+{
+    download_speed_limit = bytes_per_sec;
+    if (m_CurrentReply)
+        m_CurrentReply->setReadBufferSize(bytes_per_sec);
+}
+
+void Downloader::onDownloadProgressChanged(qint64 bytesReceived, qint64 bytesTotal)
+{
+    bytes_downloaded_ = bytesReceived;
+    bytes_total_ = bytesTotal;
+
+    if (last_tick_time_.elapsed() >= 100) {
+        download_speed_ = (bytes_downloaded_ - bytes_downloaded_before_tick_) * 1000.0 / (last_tick_time_.elapsed());
+
+        average_speed_ = (average_speed_ * update_ticks_counter_ + download_speed_) / (update_ticks_counter_ + 1);
+        ++update_ticks_counter_;
+
+        last_tick_time_.restart();
+        bytes_downloaded_before_tick_ = bytes_downloaded_;
+        emit progressChanged(this, getDownloadStatus());
+    }
+}
+
+void Downloader::stop()
+{
+    if (m_CurrentReply) {
+        m_CurrentReply->abort();
+    }
+    busy_ = false;
+}
+
+void Downloader::onDownloadFinished(QNetworkReply*) {
+    if (m_CurrentReply)
+        m_CurrentReply->deleteLater();
+
+    download_speed_ = 0;
+    busy_ = false;
+
+    emit downloadFinished(this);
+}
+
+void Downloader::onReadyRead()
+{
+    QByteArray readdata = m_CurrentReply->readAll();
+    if (targetFile && targetFile->isWritable())
+        targetFile->write(readdata);
+    if (targetBytearray)
+        targetBytearray->append(readdata);
+}

+ 117 - 0
src/LegacyLauncher/downloader.h

@@ -0,0 +1,117 @@
+#ifndef INCLUDEILEDOWNLOADER_H
+#define INCLUDEILEDOWNLOADER_H
+
+#include <QObject>
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+#include <QFile>
+#include <QTime>
+#include <QByteArray>
+
+class Downloader : public QObject
+{
+    Q_OBJECT
+
+public:
+    struct Status {
+        bool running = false;
+        double percent = 0.0;
+
+        quint64 total_bytes = 0;
+        quint64 downloaded_bytes = 0;
+        quint64 current_speed = 0;
+        quint64 average_speed = 0;
+        quint64 elapsed_time = 0;
+    };
+
+    friend Status operator+(const Status& a, const Status &b) {
+        Status result;
+        result.running = a.running || b.running;
+        result.total_bytes = a.total_bytes + b.total_bytes;
+        result.downloaded_bytes = a.downloaded_bytes + b.downloaded_bytes;
+        result.percent = double(result.downloaded_bytes) * 100.0 / double(result.total_bytes);
+        result.average_speed = a.average_speed + b.average_speed;
+        result.current_speed = a.current_speed + b.current_speed;
+
+
+        // Counting elapsed time
+        quint64 delta_size = result.total_bytes - result.downloaded_bytes;
+        quint64 avg_speed = qMax(quint64(1), result.average_speed);
+
+        while (delta_size > 1024 && avg_speed > 1024) { // Making precision smaller to get smoother dynamics
+            delta_size /= 1024;
+            avg_speed /= 1024;
+        }
+        result.elapsed_time = delta_size / avg_speed;
+
+
+        return result;
+    }
+
+    explicit Downloader(QObject *parent = 0);
+private:
+    Q_DISABLE_COPY(Downloader)
+
+public:
+    virtual ~Downloader();
+
+    QUrl getUrl() const;
+    void setUrl(const QUrl& _url);
+    void waitForDownloaded() const;
+
+    bool isStarted() const;
+    double getPercent() const;
+    quint64 getBytesTotal() const;
+    quint64 getBytesDownloaded() const;
+    quint64 getElapsedTime() const;
+    quint64 getCurrentSpeed() const;
+    quint64 getAverageSpeed() const;
+
+    Status getDownloadStatus() const;
+
+    static QString getSizeFormatted(quint64 bytes);
+    static QString getSpeedFormatted(quint64 speed_bytes_per_sec);
+    static QString getElapsedTimeFormatted(quint64 elapsed_time_secs);
+
+signals:
+    void downloadFinished(Downloader* this_downloader_ptr);
+    void progressChanged(Downloader* this_downloader_ptr, Status status);
+
+public slots:
+    void start();
+    void updateDownloadSpeedLimit(int bytes_per_sec);
+    void stop();
+
+private slots:
+    void onDownloadProgressChanged(qint64 bytesReceived, qint64 bytesTotal);
+    void onDownloadFinished(QNetworkReply* pReply);
+    void onReadyRead();
+
+public:
+    QFile* targetFile {nullptr};
+    QByteArray* targetBytearray {nullptr};
+
+private:
+    QTime last_tick_time_;
+    quint64 download_speed_ = 0; // bytes per second
+    quint64 average_speed_ = 0;
+    quint64 update_ticks_counter_ = 0;
+
+    quint64 bytes_total_ = 0;
+    quint64 bytes_downloaded_ = 0;
+
+    quint64 bytes_downloaded_before_tick_ = 0; // Нужны для подсчёта текущей скорости скачивания, а не
+
+    bool busy_;
+    QUrl url_;
+    QNetworkReply* m_CurrentReply {nullptr};
+    QNetworkAccessManager m_WebCtrl;
+
+    unsigned download_speed_limit {0};
+};
+
+
+Q_DECLARE_METATYPE(Downloader::Status)
+
+#endif // INCLUDEILEDOWNLOADER_H

+ 98 - 0
src/LegacyLauncher/filesystem.cpp

@@ -0,0 +1,98 @@
+#ifndef FILESYSTEM_H
+#define FILESYSTEM_H
+
+#include <QObject>
+#include <QFile>
+#include <QDir>
+#include <QFileInfo>
+#include <QCryptographicHash>
+#include <QDebug>
+#include <QSettings>
+
+#include "filesystem.h"
+
+namespace FileSystem {
+    bool fileExists(QString path) {
+        QFileInfo check_file(path);
+        return check_file.exists() && check_file.isFile();
+    }
+
+    bool folderExists(QString path) {
+        return QDir(path).exists();
+    }
+
+    bool createFilePath(QString file_path) { // Creates all necessary directories for file
+        QDir dir;
+        return dir.mkpath(QFileInfo(file_path).absoluteDir().absolutePath());
+    }
+
+    QString fileHash(const QString &fileName, QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Md5) {
+        QFile file(fileName);
+        if (file.open(QIODevice::ReadOnly)) {
+            QCryptographicHash hash(hashAlgorithm);
+            hash.addData(&file);
+            QByteArray hashData = hash.result();
+            return hashData.toHex();
+        }
+        return QByteArray();
+    }
+
+
+    void clearFolder(QDir &dir) {
+        //Получаем список файлов
+        QStringList lstFiles = dir.entryList(QDir::Files);
+
+        //Удаляем файлы
+        foreach (QString entry, lstFiles){
+            QString entryAbsPath = dir.absolutePath() + "/" + entry;
+            //QFile::setPermissions(entryAbsPath, QFile::ReadOwner | QFile::WriteOwner);
+            qDebug() << dir.absolutePath();
+            QFile::remove(entryAbsPath);
+        }
+    }
+
+
+    QStringList recognizeRegistryLotroPath() {
+        QStringList paths;
+
+        #ifdef _WIN32
+            // Windows 8, 10
+            QSettings n("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\12bbe590-c890-11d9-9669-0800200c9a66_is1", QSettings::NativeFormat);
+            foreach (QString key, n.allKeys()) {
+                qDebug() << key;
+                if(key.contains("InstallLocation") || key.contains("installlocation")){
+                    QString folder = n.value(key).toString()
+                            .replace("\\", "/")
+                            .replace("/TurbineLauncher.exe", "")
+                            .replace("/LotroLauncher.exe", "")
+                            .replace("\"", "");
+
+                    if(fileExists(folder + "/LotroLauncher.exe"))
+                        paths.append(folder);
+                }
+            }
+
+
+            // Windows 7
+            QSettings m("HKEY_CLASSES_ROOT\\Local Settings\\Software\\Microsoft\\Windows\\Shell\\MuiCache", QSettings::NativeFormat);
+            foreach (QString key, m.allKeys()) {
+              if((key.contains("TurbineLauncher.exe") || key.contains("LotroLauncher.exe")) && fileExists(key)){
+                  QString folder = n.value(key).toString()
+                          .replace("\\", "/")
+                          .replace("/TurbineLauncher.exe", "")
+                          .replace("/LotroLauncher.exe", "")
+                          .replace("\"", "");
+
+                  if(fileExists(folder + "/LotroLauncher.exe"))
+                      paths.append(folder);
+              }
+            }
+        #else
+            // Реализация под Linux
+        #endif
+
+        return paths;
+    }
+}
+
+#endif // FILESYSTEM_H

+ 26 - 0
src/LegacyLauncher/filesystem.h

@@ -0,0 +1,26 @@
+#ifndef FILESYSTEM_H
+#define FILESYSTEM_H
+
+#include <QObject>
+#include <QFile>
+#include <QDir>
+#include <QFileInfo>
+#include <QCryptographicHash>
+#include <QDebug>
+#include <QSettings>
+
+namespace FileSystem {
+    extern bool fileExists(QString path);
+
+    extern bool folderExists(QString path);
+
+    extern bool createFilePath(QString file_path);
+
+    extern QString fileHash(const QString &fileName, QCryptographicHash::Algorithm hashAlgorithm = QCryptographicHash::Md5);
+
+    extern void clearFolder(QDir &dir);
+
+    extern QStringList recognizeRegistryLotroPath();
+}
+
+#endif // FILESYSTEM_H

+ 122 - 0
src/LegacyLauncher/launcher.cpp

@@ -0,0 +1,122 @@
+#include "launcher.h"
+#include "ui_launcher.h"
+#include "filesystem.h"
+
+#include <QProcess>
+
+GIFAnimationDemoWidget::GIFAnimationDemoWidget( QWidget* parent ) :
+    QWidget( parent, Qt::Window | Qt::FramelessWindowHint ),
+    ui( new Ui::GIFAnimationDemoWidget )
+{
+
+    ui->setupUi(this);
+    setWindowTitle("ВКО: Наследие v2 - обновление");
+    ui->lbHint->setText("Получение списка файлов...");
+    movie = new QMovie(":/second.gif");
+    movie->setScaledSize(ui->lbMovie->size());
+    movie->start();
+    ui->lbMovie->setMovie(movie);
+
+    data_downloader = new Downloader();
+}
+
+GIFAnimationDemoWidget::~GIFAnimationDemoWidget() {
+    data_downloader->deleteLater();
+    delete ui;
+}
+
+void GIFAnimationDemoWidget::startFilesDownload()
+{
+    ui->lbHint->setText("Начинаем проверку обновлений...");
+    FileSystem::createFilePath(target_folder);
+    data_downloader->setUrl(QUrl(base_url + "/filelist.txt"));
+    data_downloader->targetBytearray = &downloaded_data;
+    data_downloader->disconnect();
+    connect(data_downloader, &Downloader::downloadFinished, this, &GIFAnimationDemoWidget::onFileListReady);
+    data_downloader->start();
+}
+
+void GIFAnimationDemoWidget::resizeEvent(QResizeEvent *)
+{
+    qDebug() << "LOL" << ui->lbMovie->size();
+    movie->setScaledSize(ui->lbMovie->size());
+    ui->lbMovie->setMovie(movie);
+}
+
+void GIFAnimationDemoWidget::onFileListReady()
+{
+    const QString file_list(downloaded_data);
+    qDebug() << "GOT FILE LIST: " << file_list;
+    const QStringList files_info = file_list.split("\r\n");
+    for (QString file_info: files_info) {
+        const QStringList file_info_splitted = file_info.split("|");
+        if (file_info_splitted.length() != 2) {
+            continue;
+        }
+        const QString file_path = file_info_splitted[0];
+        const QString file_hash = file_info_splitted[1];
+        const QString target_file_path = target_folder + QString(file_path).replace("release/", "");
+
+        if (!FileSystem::fileExists(target_file_path) || FileSystem::fileHash(target_file_path) != file_hash) {
+            qDebug() << "Adding to queue file " << file_path;
+            files_to_download.push_back(file_path);
+        }
+    }
+    files_total = files_to_download.size();
+    files_downloaded = 0;
+    downloadNextEnqueuedFile();
+}
+
+void GIFAnimationDemoWidget::onDownloadNextEnqueuedFileFinished(Downloader *downloader)
+{
+    qDebug() << "Finished downloading:" << downloader->getUrl();
+    files_downloaded += 1;
+    downloader->targetFile->close();
+    downloader->targetFile->deleteLater();
+    downloadNextEnqueuedFile();
+}
+
+void GIFAnimationDemoWidget::onDownloadProgressChanged(Downloader *downloader, Downloader::Status status)
+{
+    QString download_status = "Загрузка файла " + QString::number(files_downloaded + 1) + " из " + QString::number(files_total) +
+                              ": " + QString::number(status.percent, 'f', 1) + "% (" + Downloader::getSpeedFormatted(status.current_speed) + ")";
+
+    ui->lbHint->setText(download_status);
+}
+
+void GIFAnimationDemoWidget::downloadNextEnqueuedFile()
+{
+    if (files_to_download.empty()) {
+        ui->lbHint->setText("Загрузка завершена, запускаем Наследие...");
+        startApplication();
+        return;
+    }
+    const QString file_path = files_to_download.front();
+    files_to_download.pop_front();
+    const QString target_file_path = target_folder + QString(file_path).replace("release/", "");
+    data_downloader->setUrl(QUrl(base_url + file_path));
+    data_downloader->targetFile = new QFile(target_file_path);
+    data_downloader->targetFile->open(QIODevice::WriteOnly);
+    data_downloader->disconnect();
+    connect(data_downloader, &Downloader::progressChanged, this, &GIFAnimationDemoWidget::onDownloadProgressChanged);
+    connect(data_downloader, &Downloader::downloadFinished, this, &GIFAnimationDemoWidget::onDownloadNextEnqueuedFileFinished);
+    data_downloader->start();
+}
+
+void GIFAnimationDemoWidget::startApplication()
+{
+    QProcess process;
+    process.setProgram(target_folder + "Legacy.exe");
+    process.setArguments({"launcherpath=" + QString(QApplication::applicationFilePath().toUtf8().toBase64())});
+    process.startDetached();
+    process.waitForFinished(-1);
+    QMetaObject::invokeMethod(this, &GIFAnimationDemoWidget::close, Qt::QueuedConnection);
+}
+
+void GIFAnimationDemoWidget::startNewAnimation() {
+    delete movie;
+    movie = new QMovie(":/second.gif");
+    ui->lbMovie->setMovie(movie);
+    movie->setScaledSize(QSize(ui->lbMovie->width(), ui->lbMovie->height()));
+    movie->start();
+}

+ 59 - 0
src/LegacyLauncher/launcher.h

@@ -0,0 +1,59 @@
+#ifndef GIFANIMATIONDEMOWIDGET_H
+#define GIFANIMATIONDEMOWIDGET_H
+
+#include <QWidget>
+#include <QMovie>
+#include <QMediaPlayer>
+#include <QMediaPlaylist>
+#include <QVideoWidget>
+#include <QList>
+#include <QStandardPaths>
+
+#include "downloader.h"
+
+namespace Ui {
+class GIFAnimationDemoWidget;
+}
+
+class GIFAnimationDemoWidget : public QWidget {
+    Q_OBJECT
+
+public:
+    explicit GIFAnimationDemoWidget( QWidget* parent = 0 );
+    ~GIFAnimationDemoWidget();
+
+    void startFilesDownload();
+
+protected:
+    void resizeEvent(QResizeEvent *event) override;
+
+
+private slots:
+    void onFileListReady();
+    void onDownloadNextEnqueuedFileFinished(Downloader* downloader);
+    void onDownloadProgressChanged(Downloader* downloader, Downloader::Status status);
+
+    void downloadNextEnqueuedFile();
+    void startNewAnimation();
+    void startApplication();
+
+private:
+    const QString base_url = "http://translate.lotros.ru/upload/launcher_v2/";
+    const QString target_folder = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/";
+
+    Downloader* data_downloader;
+    QByteArray downloaded_data;
+    QList<QString> files_to_download;
+
+    int files_total = 0;
+    int files_downloaded = 0;
+
+    Ui::GIFAnimationDemoWidget* ui;
+
+    QMovie * movie;
+    QMediaPlayer *player;
+    QMediaPlaylist *playlist;
+    QVideoWidget *videoWidget;
+};
+
+#endif // GIFANIMATIONDEMOWIDGET_H

+ 93 - 0
src/LegacyLauncher/launcher.ui

@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>GIFAnimationDemoWidget</class>
+ <widget class="QWidget" name="GIFAnimationDemoWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>450</width>
+    <height>350</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>GIFAnimationDemoWidget</string>
+  </property>
+  <property name="windowOpacity">
+   <double>1.000000000000000</double>
+  </property>
+  <property name="autoFillBackground">
+   <bool>false</bool>
+  </property>
+  <property name="styleSheet">
+   <string notr="true">background-color: rgb(255, 255, 255);</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <property name="sizeConstraint">
+    <enum>QLayout::SetNoConstraint</enum>
+   </property>
+   <property name="leftMargin">
+    <number>0</number>
+   </property>
+   <property name="topMargin">
+    <number>0</number>
+   </property>
+   <property name="rightMargin">
+    <number>0</number>
+   </property>
+   <property name="bottomMargin">
+    <number>0</number>
+   </property>
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="styleSheet">
+      <string notr="true">background-color: #000;
+border: none;</string>
+     </property>
+     <property name="title">
+      <string/>
+     </property>
+     <widget class="QLabel" name="lbHint">
+      <property name="geometry">
+       <rect>
+        <x>10</x>
+        <y>330</y>
+        <width>440</width>
+        <height>15</height>
+       </rect>
+      </property>
+      <property name="sizePolicy">
+       <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+        <horstretch>0</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+      <property name="styleSheet">
+       <string notr="true">background-color: none;
+color: rgb(255, 255, 255);</string>
+      </property>
+      <property name="text">
+       <string>TextLabel</string>
+      </property>
+     </widget>
+     <widget class="QLabel" name="lbMovie">
+      <property name="geometry">
+       <rect>
+        <x>0</x>
+        <y>0</y>
+        <width>450</width>
+        <height>330</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string/>
+      </property>
+     </widget>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections/>
+</ui>

+ 18 - 13
src/LegacyLauncher/legacylauncher_plugin_import.cpp

@@ -1,13 +1,18 @@
-// This file is autogenerated by qmake. It imports static plugin classes for
-// static plugins specified using QTPLUGIN and QT_PLUGIN_CLASS.<plugin> variables.
-#include <QtPlugin>
-Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin)
-Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)
-Q_IMPORT_PLUGIN(QGifPlugin)
-Q_IMPORT_PLUGIN(QICNSPlugin)
-Q_IMPORT_PLUGIN(QICOPlugin)
-Q_IMPORT_PLUGIN(QJpegPlugin)
-Q_IMPORT_PLUGIN(QTgaPlugin)
-Q_IMPORT_PLUGIN(QTiffPlugin)
-Q_IMPORT_PLUGIN(QWbmpPlugin)
-Q_IMPORT_PLUGIN(QWebpPlugin)
+// This file is autogenerated by qmake. It imports static plugin classes for
+// static plugins specified using QTPLUGIN and QT_PLUGIN_CLASS.<plugin> variables.
+#include <QtPlugin>
+Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin)
+Q_IMPORT_PLUGIN(DSServicePlugin)
+Q_IMPORT_PLUGIN(AudioCaptureServicePlugin)
+Q_IMPORT_PLUGIN(QWindowsAudioPlugin)
+Q_IMPORT_PLUGIN(QM3uPlaylistPlugin)
+Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)
+Q_IMPORT_PLUGIN(QGifPlugin)
+Q_IMPORT_PLUGIN(QICNSPlugin)
+Q_IMPORT_PLUGIN(QICOPlugin)
+Q_IMPORT_PLUGIN(QJpegPlugin)
+Q_IMPORT_PLUGIN(QTgaPlugin)
+Q_IMPORT_PLUGIN(QTiffPlugin)
+Q_IMPORT_PLUGIN(QWbmpPlugin)
+Q_IMPORT_PLUGIN(QWebpPlugin)
+Q_IMPORT_PLUGIN(QGenericEnginePlugin)

+ 25 - 14
src/LegacyLauncher/main.cpp

@@ -1,18 +1,29 @@
-#include <QFile>
-#include <QProcess>
-#include <QCoreApplication>
-#include <QDebug>
-#include <QMessageBox>
+#include "launcher.h"
+
 #include <QApplication>
+#include <QLockFile>
+#include <QMessageBox>
+#include <QDir>
+#include <QWidget>
 
-int main(int argc, char *argv[]) {
-    QApplication app(argc, argv);
-    QMessageBox::information(nullptr, "Привет, мир!", "Я прелаунчер! Сейчас запущу Наследие. Вообще, я сначала должен обновить его, но делать пока этого я не умею...");
-    QProcess process;
-    process.setProgram(QApplication::applicationDirPath() + "/Legacy.exe");
-    process.setArguments({"prelaunched"});
-    if (!process.startDetached()) {
-        QMessageBox::critical(nullptr, "Дичь произошла...", "Не могу запустить процесс Наследия! Проверьте существование и доступность файла " + QApplication::applicationDirPath() + "/Legacy_v2.exe");
+int main( int argc, char* argv[] ) {
+    QApplication a( argc, argv );
+    QCoreApplication::setOrganizationName("LotroLegacy");
+    QCoreApplication::setOrganizationDomain("translate.lotros.ru");
+    QCoreApplication::setApplicationName("Legacy_v2");
+
+    GIFAnimationDemoWidget w;
+
+    QLockFile lockFile(QDir::temp().absoluteFilePath("rulotro_launcher.lock"));
+    if(!lockFile.tryLock(100)){
+        QMessageBox msgBox;
+        msgBox.setIcon(QMessageBox::Warning);
+        msgBox.setText("Приложение уже запущено.\nРазрешено запускать только один экземпляр приложения.");
+        msgBox.exec();
+        return 1;
     }
-    return 0;
+
+    w.show();
+    w.startFilesDownload();
+    return a.exec();
 }