-
Notifications
You must be signed in to change notification settings - Fork 13
/
apkextractiontask.cpp
153 lines (143 loc) · 6.58 KB
/
apkextractiontask.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include "apkextractiontask.h"
#include <QUrl>
#include <QDebug>
#include <mcpelauncher/zip_extractor.h>
#include <mcpelauncher/minecraft_extract_utils.h>
#include <mcpelauncher/apkinfo.h>
#include <sstream>
#include "versionmanager.h"
#include "supportedandroidabis.h"
ApkExtractionTask::ApkExtractionTask(QObject *parent) : QThread(parent) {
connect(this, &QThread::started, this, &ApkExtractionTask::emitActiveChanged);
connect(this, &QThread::finished, this, &ApkExtractionTask::emitActiveChanged);
connect(this, &ApkExtractionTask::versionInformationObtained, this, &ApkExtractionTask::onVersionInformationObtained);
}
bool ApkExtractionTask::setSourceUrls(QList<QUrl> const& urls) {
QStringList list;
for (auto&& url : urls) {
if (!url.isLocalFile()) {
return false;
}
list.append(url.toLocalFile());
}
setSources(list);
return true;
}
static bool mergeDirsRecusive(QString from, QString to) {
if (!QDir(to).exists()) {
qDebug() << "Moving " << from << " to " << to;
if (!QDir().rename(from, to))
throw std::runtime_error(QObject::tr("renaming versionsfolder failed").toStdString());
return true;
} else {
qDebug() << "Merging " << from << " to " << to;
for (auto&& item : QDir(from).entryList()) {
auto f = from + "/" + item;
auto t = to + "/" + item;
qDebug() << "Checking " << f << " to " << t << " Isdir=" << QDir(f).exists() << " IsFile=" << QFile(f).exists();
if (item == "." || item == "..") {
continue;
}
if (QDir(f).exists()) {
mergeDirsRecusive(f, t);
} else if (QFile(f).exists()) {
QFile().rename(f, t);
}
}
return false;
}
}
void ApkExtractionTask::run() {
QTemporaryDir dir (versionManager()->getTempTemplate());
try {
std::string path = dir.path().toStdString();
ApkInfo apkInfo;
apkInfo.versionCode = 0;
for (auto && source : sources()) {
ZipExtractor extractor (source.toStdString());
{
auto manifest = extractor.readFile("AndroidManifest.xml");
axml::AXMLFile manifestFile (manifest.data(), manifest.size());
axml::AXMLParser manifestParser (manifestFile);
ApkInfo capkInfo = ApkInfo::fromXml(manifestParser);
if (!apkInfo.versionCode) {
apkInfo = capkInfo;
if(!m_allowedPackages.empty() && !m_allowedPackages.contains(QString::fromStdString(apkInfo.package))) {
throw std::runtime_error(QObject::tr("Trying to import a forbidden apk").toStdString());
}
} else if(apkInfo.versionCode != capkInfo.versionCode) {
throw std::runtime_error(QObject::tr("Trying to extract multiple apks with different versionsCodes is forbidden").toStdString());
} else if(apkInfo.package != capkInfo.package) {
throw std::runtime_error(QObject::tr("Trying to extract multiple apks with different package is forbidden").toStdString());
} else if(apkInfo.versionName.empty()) {
apkInfo.versionName = capkInfo.versionName;
}
}
qDebug() << "Apk info: versionCode=" << apkInfo.versionCode
<< " versionName=" << QString::fromStdString(apkInfo.versionName);
extractor.extractTo(MinecraftExtractUtils::filterMinecraftFiles(path),
[this](size_t current, size_t max, ZipExtractor::FileHandle const&, size_t, size_t) {
emit progress((float) current / max);
});
}
bool supported = false;
bool invalidapk = true;
std::stringstream errormsg;
for (auto &&abi : SupportedAndroidAbis::getAbis()) {
if (QFile(dir.path() + "/lib/" + QString::fromStdString(abi.first) + "/libminecraftpe.so").exists()) {
invalidapk = false;
if (!abi.second.compatible) {
errormsg << QObject::tr("This Launcher cannot load Minecraft (%1) on this PC:<br/>%2<br/>").arg(QString::fromStdString(abi.second.details)).arg(QString::fromStdString(abi.second.details)).toStdString();
} else {
supported = true;
}
}
}
if (!m_allowIncompatible && !supported) {
if (invalidapk) {
if (sources().size() == 1) {
errormsg << QObject::tr("The specified file is not a valid Minecraft apk, it doesn't contain libminecraftpe.so").toStdString();
} else {
errormsg << QObject::tr("The specified files are not a valid collection of Minecraft apks, they don't contain libminecraftpe.so").toStdString();
}
}
int i = 0;
for (auto &&abi : SupportedAndroidAbis::getAbis()) {
if (abi.second.compatible) {
if (i++) {
errormsg << ", ";
} else {
errormsg << "<br/>" << QObject::tr("Valid Minecraft apk CPU architectures for this pc / launcher are ").toStdString();
}
errormsg << abi.first;
}
}
if (!i) {
errormsg << "<br/>" << QObject::tr("No Minecraft apk's are valid for this pc / launcher").toStdString();
}
errormsg << "<br/>";
throw std::runtime_error(errormsg.str());
}
if (!m_versionName.isEmpty()) {
apkInfo.versionName = m_versionName.toStdString();
m_versionName.clear();
}
if (apkInfo.versionName.empty()) {
throw std::runtime_error(QObject::tr("unsupported, versionsname of the apk is empty").toStdString());
}
QString targetDir = versionManager()->getDirectoryFor(apkInfo.versionName);
if (mergeDirsRecusive(dir.path(), targetDir)) {
dir.setAutoRemove(false);
}
emit versionInformationObtained(QDir(targetDir).dirName(), QString::fromStdString(apkInfo.versionName), apkInfo.versionCode);
} catch (std::exception& e) {
m_versionName.clear();
emit error(e.what());
return;
}
m_versionName.clear();
emit finished();
}
void ApkExtractionTask::onVersionInformationObtained(const QString &directory, const QString &versionName, int versionCode) {
versionManager()->addVersion(directory, versionName, versionCode);
}