fixing merge conflicts

This commit is contained in:
dante ruiz 2019-09-13 15:23:40 -07:00
commit 54a827c67f
14 changed files with 9644 additions and 76 deletions

View file

@ -127,7 +127,12 @@ set(src_files
src/LauncherWindow.h
src/LauncherWindow.cpp
src/PathUtils.cpp
src/PathUtils.h)
src/PathUtils.h
src/Unzipper.h
src/Unzipper.cpp
deps/miniz/miniz.h
deps/miniz/miniz.cpp
)
set(TARGET_NAME ${PROJECT_NAME})
@ -176,6 +181,7 @@ elseif (APPLE)
endif()
target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/deps/
${Qt5Core_INCLUDE_DIRS}
${Qt5Quick_INCLUDE_DIRS}
${Qt5Gui_INCLUDE_DIRS}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

View file

@ -62,6 +62,8 @@ Item {
width: 394
height: 8
value: LauncherState.downloadProgress;
anchors {
top: secondText.bottom
topMargin: 30
@ -88,14 +90,14 @@ Item {
}
PropertyAnimation {
target: progressBar;
loops: Animation.Infinite
property: "value"
from: 0;
to: 1;
duration: 5000
running: true
}
//PropertyAnimation {
//target: progressBar;
//loops: Animation.Infinite
//property: "value"
//from: 0;
//to: 1;
//duration: 5000
//running: true
//}
}
}

View file

@ -10,6 +10,7 @@ TextField {
horizontalAlignment: TextInput.AlignLeft
placeholderText: "PlaceHolder"
property string seperatorColor: "#FFFFFF"
selectByMouse: true
background: Item {
anchors.fill: parent
Rectangle {

View file

@ -4,6 +4,7 @@ import QtQuick 2.3
import QtQuick.Controls 2.1
import HQLauncher 1.0
import "HFControls"
Item {
id: root
width: 515
@ -21,4 +22,14 @@ Item {
loader.source = url;
});
}
Text {
font.pixelSize: 12
anchors.right: root.right
anchors.bottom: root.bottom
color: "#FFFFFF"
text: LauncherState.uiState.toString() + " - " + LauncherState.applicationState
}
}

View file

@ -12,7 +12,7 @@ Launcher::Launcher(int& argc, char**argv) : QGuiApplication(argc, argv) {
QString resourceBinaryLocation = QGuiApplication::applicationDirPath() + "/resources.rcc";
QResource::registerResource(resourceBinaryLocation);
_launcherState = std::make_shared<LauncherState>();
_launcherState->setUIState(LauncherState::SPLASH_SCREEN);
//_launcherState->setUIState(LauncherState::SPLASH_SCREEN);
_launcherWindow = std::make_unique<LauncherWindow>();
_launcherWindow->rootContext()->setContextProperty("LauncherState", _launcherState.get());
_launcherWindow->rootContext()->setContextProperty("PathUtils", new PathUtils());

View file

@ -1,6 +1,11 @@
#include "LauncherState.h"
#include "PathUtils.h"
#include "Unzipper.h"
#ifdef Q_OS_WIN
#include <Windows.h>
#endif
#include <array>
#include <QNetworkRequest>
@ -14,56 +19,58 @@
#include <QDebug>
#include <QQmlEngine>
#include <QThreadPool>
#include <QStandardPaths>
bool LatestBuilds::getBuild(QString tag, Build* outBuild) {
if (tag.isNull()) {
tag = defaultTag;
}
for (auto& build : builds) {
if (build.tag == tag) {
*outBuild = build;
return true;
}
}
return false;
}
static const std::array<QString, LauncherState::UIState::UI_STATE_NUM> QML_FILE_FOR_UI_STATE =
{ { "SplashScreen.qml", "HFBase/CreateAccountBase.qml", "DisplayName.qml",
"Download.qml", "DownloadFinished.qml", "HFBase/Error.qml" } };
{ { "SplashScreen.qml", "qml/HFBase/CreateAccountBase.qml", "DisplayName.qml",
"Download.qml", "DownloadFinished.qml", "qml/HFBase/Error.qml" } };
void LauncherState::ASSERT_STATE(LauncherState::ApplicationState state) {
if (_applicationState != state) {
#ifdef Q_OS_WIN
__debugbreak();
#endif
setApplicationState(ApplicationState::UnexpectedError);
}
}
void LauncherState::ASSERT_STATE(std::vector<LauncherState::ApplicationState> states) {
for (auto state : states) {
if (_applicationState == state) {
return;
}
}
#ifdef Q_OS_WIN
__debugbreak();
#endif
setApplicationState(ApplicationState::UnexpectedError);
}
LauncherState::LauncherState() {
// TODO Show splash screen until this request is complete
auto request = new QNetworkRequest(QUrl("https://thunder.highfidelity.com/builds/api/tags/latest/?format=json"));
auto reply = _networkAccessManager.get(*request);
QObject::connect(reply, &QNetworkReply::finished, [reply, this]() {
if (reply->error()) {
qDebug() << "Error getting builds from thunder: " << reply->errorString();
} else {
qDebug() << "Builds reply has been received";
auto data = reply->readAll();
QJsonParseError parseError;
auto doc = QJsonDocument::fromJson(data, &parseError);
if (parseError.error) {
qDebug() << "Error parsing response from thunder: " << data;
} else {
auto root = doc.object();
if (!root.contains("default_tag")) {
return;
}
_latestBuilds.defaultTag = root["default_tag"].toString();
auto results = root["results"];
if (!results.isArray()) {
return;
}
for (auto result : results.toArray()) {
auto entry = result.toObject();
Build build;
build.tag = entry["name"].toString();
build.latestVersion = entry["latest_version"].toInt();
build.buildNumber = entry["build_number"].toInt();
#ifdef Q_OS_WIN
build.installerZipURL = entry["installers"].toObject()["windows"].toObject()["zip_url"].toString();
#elif defined(Q_OS_MACOS)
build.installerZipURL = entry["installers"].toObject()["mac"].toObject()["zip_url"].toString();
#else
#error "Launcher is only supported on Windows and Mac OS"
#endif
_latestBuilds.builds.push_back(build);
}
}
}
//setUIState(LauncherState::LOGIN_SCREEN);
});
_launcherDirectory = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
// TODO Fix launcher directory
qDebug() << "Launcher directory: " << _launcherDirectory.absolutePath();
_launcherDirectory.mkpath(_launcherDirectory.absolutePath());
requestBuilds();
}
QString LauncherState::getCurrentUISource() const {
@ -74,13 +81,33 @@ void LauncherState::declareQML() {
qmlRegisterType<LauncherState>("HQLauncher", 1, 0, "LauncherStateEnums");
}
void LauncherState::setUIState(UIState state) {
_uiState = state;
emit updateSourceUrl(PathUtils::resourcePath(getCurrentUISource()));
}
LauncherState::UIState LauncherState::getUIState() const {
return _uiState;
switch (_applicationState) {
case ApplicationState::Init:
case ApplicationState::RequestingBuilds:
return SPLASH_SCREEN;
case ApplicationState::WaitingForLogin:
case ApplicationState::RequestingLogin:
return LOGIN_SCREEN;
case ApplicationState::DownloadingClient:
case ApplicationState::InstallingClient:
case ApplicationState::DownloadingContentCache:
case ApplicationState::InstallingContentCache:
return DOWNLOAD_SCREEN;
case ApplicationState::LaunchingHighFidelity:
return DOWNLOAD_FINSISHED;
case ApplicationState::UnexpectedError:
#ifdef Q_OS_WIN
__debugbreak();
#endif
return ERROR_SCREEN;
default:
qDebug() << "FATAL: No UI for" << _applicationState;
#ifdef Q_OS_WIN
__debugbreak();
#endif
return ERROR_SCREEN;
}
}
void LauncherState::setLastLoginError(LastLoginError lastLoginError) {
@ -91,7 +118,71 @@ LauncherState::LastLoginError LauncherState::getLastLoginError() const {
return _lastLoginError;
}
void LauncherState::requestBuilds() {
ASSERT_STATE(ApplicationState::Init);
setApplicationState(ApplicationState::RequestingBuilds);
// TODO Show splash screen until this request is complete
auto request = new QNetworkRequest(QUrl("https://thunder.highfidelity.com/builds/api/tags/latest/?format=json"));
auto reply = _networkAccessManager.get(*request);
QObject::connect(reply, &QNetworkReply::finished, this, &LauncherState::receivedBuildsReply);
}
void LauncherState::receivedBuildsReply() {
auto reply = static_cast<QNetworkReply*>(sender());
ASSERT_STATE(ApplicationState::RequestingBuilds);
if (reply->error()) {
qDebug() << "Error getting builds from thunder: " << reply->errorString();
} else {
qDebug() << "Builds reply has been received";
auto data = reply->readAll();
QJsonParseError parseError;
auto doc = QJsonDocument::fromJson(data, &parseError);
if (parseError.error) {
qDebug() << "Error parsing response from thunder: " << data;
} else {
auto root = doc.object();
if (!root.contains("default_tag")) {
setApplicationState(ApplicationState::UnexpectedError);
return;
}
_latestBuilds.defaultTag = root["default_tag"].toString();
auto results = root["results"];
if (!results.isArray()) {
setApplicationState(ApplicationState::UnexpectedError);
return;
}
for (auto result : results.toArray()) {
auto entry = result.toObject();
Build build;
build.tag = entry["name"].toString();
build.latestVersion = entry["latest_version"].toInt();
build.buildNumber = entry["build_number"].toInt();
#ifdef Q_OS_WIN
build.installerZipURL = entry["installers"].toObject()["windows"].toObject()["zip_url"].toString();
#elif defined(Q_OS_MACOS)
build.installerZipURL = entry["installers"].toObject()["mac"].toObject()["zip_url"].toString();
#else
#error "Launcher is only supported on Windows and Mac OS"
#endif
_latestBuilds.builds.push_back(build);
}
}
}
setApplicationState(ApplicationState::WaitingForLogin);
}
void LauncherState::login(QString username, QString password) {
ASSERT_STATE(ApplicationState::WaitingForLogin);
setApplicationState(ApplicationState::RequestingLogin);
qDebug() << "Got login: " << username << password;
auto request = new QNetworkRequest(QUrl("https://metaverse.highfidelity.com/oauth/token"));
@ -104,8 +195,294 @@ void LauncherState::login(QString username, QString password) {
query.addQueryItem("scope", "owner");
auto reply = _networkAccessManager.post(*request, query.toString().toUtf8());
QObject::connect(reply, &QNetworkReply::finished, [reply, this]() {
qDebug() << "Got response for login: " << reply->readAll();
});
QObject::connect(reply, &QNetworkReply::finished, this, &LauncherState::receivedLoginReply);
}
Q_INVOKABLE void LauncherState::receivedLoginReply() {
ASSERT_STATE(ApplicationState::RequestingLogin);
// TODO Check for errors
auto reply = static_cast<QNetworkReply*>(sender());
if (reply->error()) {
setApplicationState(ApplicationState::UnexpectedError);
return;
}
auto data = reply->readAll();
QJsonParseError parseError;
auto doc = QJsonDocument::fromJson(data, &parseError);
auto root = doc.object();
if (!root.contains("access_token")
|| !root.contains("token_type")
|| !root.contains("expires_in")
|| !root.contains("refresh_token")
|| !root.contains("scope")
|| !root.contains("created_at")) {
setApplicationState(ApplicationState::UnexpectedError);
return;
}
_loginResponse.accessToken = root["access_token"].toString();
_loginResponse.refreshToken = root["refresh_token"].toString();
_loginResponse.tokenType = root["token_type"].toString();
qDebug() << "Got response for login: " << data;
_loginTokenResponse = data;
downloadClient();
}
QString LauncherState::getContentCachePath() const {
return _launcherDirectory.filePath("cache");
}
bool LauncherState::shouldDownloadContentCache() const {
return !_contentCacheURL.isNull() && !QFile::exists(getContentCachePath());
}
void LauncherState::downloadClient() {
ASSERT_STATE(ApplicationState::RequestingLogin);
Build build;
if (!_latestBuilds.getBuild(_buildTag, &build)) {
qDebug() << "Cannot determine latest build";
setApplicationState(ApplicationState::UnexpectedError);
return;
}
_downloadProgress = 0;
setApplicationState(ApplicationState::DownloadingClient);
// Start client download
{
qDebug() << "Latest build: " << build.tag << build.buildNumber << build.latestVersion << build.installerZipURL;
auto request = new QNetworkRequest(QUrl(build.installerZipURL));
auto reply = _networkAccessManager.get(*request);
_clientZipFile.setFileName(_launcherDirectory.absoluteFilePath("client.zip"));
qDebug() << "Opening " << _clientZipFile.fileName();
if (!_clientZipFile.open(QIODevice::WriteOnly)) {
setApplicationState(ApplicationState::UnexpectedError);
return;
}
connect(reply, &QNetworkReply::finished, this, &LauncherState::clientDownloadComplete);
connect(reply, &QNetworkReply::readyRead, this, [this, reply]() {
char buf[4096];
while (reply->bytesAvailable() > 0) {
qint64 size;
size = reply->read(buf, (qint64)sizeof(buf));
if (size == 0) {
break;
}
_clientZipFile.write(buf, size);
}
});
connect(reply, &QNetworkReply::downloadProgress, this, [this](qint64 received, qint64 total) {
_downloadProgress = (float)received / (float)total;
emit downloadProgressChanged();
});
}
}
void LauncherState::clientDownloadComplete() {
ASSERT_STATE(ApplicationState::DownloadingClient);
_clientZipFile.close();
installClient();
}
void LauncherState::installClient() {
ASSERT_STATE(ApplicationState::DownloadingClient);
setApplicationState(ApplicationState::InstallingClient);
auto installDir = _launcherDirectory.absoluteFilePath("interface_install");
_launcherDirectory.mkpath("interface_install");
_downloadProgress = 0;
qDebug() << "Unzipping " << _clientZipFile.fileName() << " to " << installDir;
auto unzipper = new Unzipper(_clientZipFile.fileName(), QDir(installDir));
unzipper->setAutoDelete(true);
connect(unzipper, &Unzipper::progress, this, [this](float progress) {
qDebug() << "Unzipper progress: " << progress;
_downloadProgress = progress;
emit downloadProgressChanged();
});
connect(unzipper, &Unzipper::finished, this, [this](bool error, QString errorMessage) {
if (error) {
qDebug() << "Unzipper finished with error: " << errorMessage;
setApplicationState(ApplicationState::UnexpectedError);
} else {
qDebug() << "Unzipper finished without error";
downloadContentCache();
}
});
QThreadPool::globalInstance()->start(unzipper);
//launchClient();
}
void LauncherState::downloadContentCache() {
ASSERT_STATE(ApplicationState::InstallingClient);
// Start content set cache download
if (shouldDownloadContentCache()) {
setApplicationState(ApplicationState::DownloadingContentCache);
_downloadProgress = 0;
auto request = new QNetworkRequest(QUrl(_contentCacheURL));
auto reply = _networkAccessManager.get(*request);
_contentZipFile.setFileName(_launcherDirectory.absoluteFilePath("content_cache.zip"));
qDebug() << "Opening " << _contentZipFile.fileName();
if (!_contentZipFile.open(QIODevice::WriteOnly)) {
setApplicationState(ApplicationState::UnexpectedError);
return;
}
connect(reply, &QNetworkReply::finished, this, &LauncherState::contentCacheDownloadComplete);
connect(reply, &QNetworkReply::readyRead, this, [this, reply]() {
char buf[4096];
while (reply->bytesAvailable() > 0) {
qint64 size;
size = reply->read(buf, (qint64)sizeof(buf));
if (size == 0) {
break;
}
_contentZipFile.write(buf, size);
}
});
connect(reply, &QNetworkReply::downloadProgress, this, [this](qint64 received, qint64 total) {
_downloadProgress = (float)received / (float)total;
emit downloadProgressChanged();
});
} else {
launchClient();
}
}
void LauncherState::contentCacheDownloadComplete() {
ASSERT_STATE(ApplicationState::DownloadingContentCache);
_contentZipFile.close();
installContentCache();
}
void LauncherState::installContentCache() {
ASSERT_STATE(ApplicationState::DownloadingContentCache);
setApplicationState(ApplicationState::InstallingContentCache);
auto installDir = getContentCachePath();
qDebug() << "Unzipping " << _contentZipFile.fileName() << " to " << installDir;
_downloadProgress = 0;
auto unzipper = new Unzipper(_contentZipFile.fileName(), QDir(installDir));
unzipper->setAutoDelete(true);
connect(unzipper, &Unzipper::progress, this, [this](float progress) {
qDebug() << "Unzipper progress (content cache): " << progress;
_downloadProgress = progress;
emit downloadProgressChanged();
});
connect(unzipper, &Unzipper::finished, this, [this](bool error, QString errorMessage) {
if (error) {
qDebug() << "Unzipper finished with error: " << errorMessage;
setApplicationState(ApplicationState::UnexpectedError);
} else {
qDebug() << "Unzipper finished without error";
launchClient();
}
});
QThreadPool::globalInstance()->start(unzipper);
}
void LauncherState::launchClient() {
ASSERT_STATE({ ApplicationState::InstallingClient, ApplicationState::InstallingContentCache });
setApplicationState(ApplicationState::LaunchingHighFidelity);
QDir installDirectory = _launcherDirectory.filePath("interface_install");
auto clientPath = installDirectory.absoluteFilePath("interface.exe");
QString homePath = "hifi://hq";
QString defaultScriptsPath = installDirectory.filePath("scripts/simplifiedUIBootstrapper");
QString displayName = "fixMe";
QString contentCachePath = _launcherDirectory.filePath("cache");
// TODO Fix parameters
QString params = "--url " + homePath
+ " --setBookmark hqhome=\"" + homePath + "\""
+ " --defaultScriptsOverride " + QDir::toNativeSeparators(defaultScriptsPath)
+ " --displayName " + displayName
+ " --cache " + contentCachePath;
if (!_loginTokenResponse.isEmpty()) {
params += " --tokens \"" + _loginTokenResponse.replace("\"", "\\\"") + "\"";
}
#if defined(Q_OS_WIN)
STARTUPINFO si;
PROCESS_INFORMATION pi;
// set the size of the structures
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// start the program up
BOOL success = CreateProcess(
clientPath.toUtf8().data(),
params.toUtf8().data(),
nullptr, // Process handle not inheritable
nullptr, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
CREATE_NEW_CONSOLE, // Opens file in a separate console
nullptr, // Use parent's environment block
nullptr, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi // Pointer to PROCESS_INFORMATION structure
);
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
exit(0);
#elif defined(Q_OS_MACOS)
// TODO Implement launching of client
#else
#error UNSUPPORTED PLATFORM
#endif
}
void LauncherState::setApplicationState(ApplicationState state) {
qDebug() << "Changing application state: " << _applicationState << " -> " << state;
if (state == ApplicationState::UnexpectedError) {
#ifdef Q_OS_WIN
__debugbreak();
#endif
}
_applicationState = state;
emit uiStateChanged();
emit updateSourceUrl(PathUtils::resourcePath(getCurrentUISource()));
emit applicationStateChanged();
}
LauncherState::ApplicationState LauncherState::getApplicationState() const {
return _applicationState;
}

View file

@ -1,7 +1,11 @@
#pragma once
#include <QDir>
#include <QObject>
#include <QString>
#include <QUrl>
#include <QNetworkAccessManager>
#include <QFile>
struct Build {
QString tag;
@ -11,13 +15,25 @@ struct Build {
};
struct LatestBuilds {
bool getBuild(QString tag, Build* outBuild);
QString defaultTag;
std::vector<Build> builds;
};
struct LoginResponse {
QString accessToken;
QString tokenType;
QString refreshToken;
};
class LauncherState : public QObject {
Q_OBJECT
Q_PROPERTY(UIState uiState READ getUIState NOTIFY uiStateChanged);
Q_PROPERTY(ApplicationState applicationState READ getApplicationState NOTIFY applicationStateChanged);
Q_PROPERTY(float downloadProgress READ getDownloadProgress NOTIFY downloadProgressChanged);
public:
LauncherState();
~LauncherState() = default;
@ -31,7 +47,25 @@ public:
ERROR_SCREEN,
UI_STATE_NUM
};
Q_ENUMS(UIState)
enum class ApplicationState {
Init,
UnexpectedError,
RequestingBuilds,
WaitingForLogin,
RequestingLogin,
DownloadingClient,
DownloadingContentCache,
InstallingClient,
InstallingContentCache,
LaunchingHighFidelity
};
enum LastLoginError {
NONE = 0,
@ -39,29 +73,74 @@ public:
CREDENTIALS,
LAST_ERROR_NUM
};
Q_ENUMS(LastLoginError)
Q_ENUM(UIState);
Q_ENUM(ApplicationState)
Q_ENUM(LastLoginError)
Q_INVOKABLE QString getCurrentUISource() const;
void ASSERT_STATE(LauncherState::ApplicationState state);
void ASSERT_STATE(std::vector<LauncherState::ApplicationState> states);
static void declareQML();
void setUIState(UIState state);
UIState getUIState() const;
void setLastLoginError(LastLoginError lastLoginError);
LastLoginError getLastLoginError() const;
void setApplicationState(ApplicationState state);
ApplicationState getApplicationState() const;
// Request builds
void requestBuilds();
Q_INVOKABLE void receivedBuildsReply();
// LOGIN
// Login
Q_INVOKABLE void login(QString username, QString password);
Q_INVOKABLE void receivedLoginReply();
// Client
void downloadClient();
void installClient();
// Content Cache
void downloadContentCache();
void installContentCache();
// Launching
void launchClient();
Q_INVOKABLE float getDownloadProgress() const { return _downloadProgress; }
signals:
void updateSourceUrl(QString sourceUrl);
void uiStateChanged();
void applicationStateChanged();
void downloadProgressChanged();
private slots:
void clientDownloadComplete();
void contentCacheDownloadComplete();
private:
bool shouldDownloadContentCache() const;
QString getContentCachePath() const;
QNetworkAccessManager _networkAccessManager;
LatestBuilds _latestBuilds;
QDir _launcherDirectory;
UIState _uiState { SPLASH_SCREEN };
// Application State
ApplicationState _applicationState { ApplicationState::Init };
LoginResponse _loginResponse;
LastLoginError _lastLoginError { NONE };
QString _buildTag { QString::null };
QString _contentCacheURL{ "https://orgs.highfidelity.com/content-cache/content_cache_small-only_data8.zip" }; // QString::null }; // If null, there is no content cache to download
QString _loginTokenResponse;
QFile _clientZipFile;
QFile _contentZipFile;
float _downloadProgress { 0 };
};

View file

@ -7,6 +7,9 @@
void LauncherWindow::keyPressEvent(QKeyEvent* event) {
QQuickView::keyPressEvent(event);
if (!event->isAccepted()) {
if (event->key() == Qt::Key_Escape) {
exit(0);
}
}
}

View file

@ -0,0 +1,74 @@
#include "Unzipper.h"
#include <QDir>
#include <QDebug>
#include <miniz/miniz.h>
Unzipper::Unzipper(const QString& zipFilePath, const QDir& outputDirectory) :
_zipFilePath(zipFilePath), _outputDirectory(outputDirectory) {
}
void Unzipper::run() {
qDebug() << "Reading zip file" << _zipFilePath << ", extracting to" << _outputDirectory.absolutePath();
mz_zip_archive zip_archive;
memset(&zip_archive, 0, sizeof(zip_archive));
auto status = mz_zip_reader_init_file(&zip_archive, _zipFilePath.toUtf8().data(), 0);
if (!status) {
auto zip_error = mz_zip_get_last_error(&zip_archive);
auto zip_error_msg = mz_zip_get_error_string(zip_error);
emit finished(true, "Failed to initialize miniz: " + QString::number(zip_error) + " " + zip_error_msg);
return;
}
int fileCount = (int)mz_zip_reader_get_num_files(&zip_archive);
qDebug() << "Zip archive has a file count of " << fileCount;
if (fileCount == 0) {
mz_zip_reader_end(&zip_archive);
emit finished(false, "");
return;
}
mz_zip_archive_file_stat file_stat;
if (!mz_zip_reader_file_stat(&zip_archive, 0, &file_stat)) {
mz_zip_reader_end(&zip_archive);
emit finished(true, "Zip archive cannot be stat'd");
return;
}
uint64_t totalSize = 0;
uint64_t totalCompressedSize = 0;
bool _shouldFail = false;
for (int i = 0; i < fileCount; i++) {
if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat)) continue;
QString filename = file_stat.m_filename;
QString fullFilename = _outputDirectory.filePath(filename);
if (mz_zip_reader_is_file_a_directory(&zip_archive, i)) {
if (!_outputDirectory.mkpath(fullFilename)) {
mz_zip_reader_end(&zip_archive);
emit finished(true, "Unzipping error while creating folder: " + fullFilename);
return;
}
continue;
}
if (mz_zip_reader_extract_to_file(&zip_archive, i, fullFilename.toUtf8().data(), 0)) {
totalCompressedSize += (uint64_t)file_stat.m_comp_size;
totalSize += (uint64_t)file_stat.m_uncomp_size;
emit progress((float)totalCompressedSize / (float)zip_archive.m_archive_size);
} else {
emit finished(true, "Unzipping error unzipping file: " + fullFilename);
return;
}
}
qDebug() << "Done unzipping archive, total size:" << totalSize;
mz_zip_reader_end(&zip_archive);
emit finished(false, "");
}

View file

@ -0,0 +1,21 @@
#pragma once
#include <QString>
#include <QDir>
#include <QObject>
#include <QRunnable>
class Unzipper : public QObject, public QRunnable {
Q_OBJECT
public:
Unzipper(const QString& zipFilePath, const QDir& outputDirectory);
void run() override;
signals:
void progress(float progress);
void finished(bool error, QString errorMessage);
private:
const QString _zipFilePath;
const QDir _outputDirectory;
};

View file

@ -14,9 +14,8 @@ Q_IMPORT_PLUGIN(QtQuick2Plugin);
Q_IMPORT_PLUGIN(QtQuickControls2Plugin);
Q_IMPORT_PLUGIN(QtQuickTemplates2Plugin);
int main(int argc, char *argv[])
{
QString name { "HQLauncher" };
int main(int argc, char *argv[]) {
QString name { "High Fidelity" };
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QCoreApplication::setOrganizationName(name);