forked from madsciencecoder/shticker-book-rewritten
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathupdateworker.cpp
306 lines (258 loc) · 12 KB
/
updateworker.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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
/*
* Copyright (c) 2015-2016 Joshua Snyder
* Distributed under the GNU GPL v3. For full terms see the file LICENSE
*
* This file is part of Shticker Book Rewritten.
*
* Shticker Book Rewritten is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Shticker Book Rewritten is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Shticker Book Rewritten. If not, see <http://www.gnu.org/licenses/>.
*/
#include "updateworker.h"
#include "globaldefines.h"
#include "extractionworker.h"
#include "downloadworker.h"
#include "patchworker.h"
#include "utilities.h"
#include <QJsonObject>
#include <QJsonArray>
#include <QString>
#include <QByteArray>
#include <QFile>
#include <QUrl>
#include <QDir>
#include <QSettings>
UpdateWorker::UpdateWorker(QObject *parent) : QObject(parent)
{
QSettings settings("Shticker-Book-Rewritten", "Shticker-Book-Rewritten");
settings.beginGroup("FilesPath");
filePath = settings.value("path").toString();
settings.endGroup();
filePath = filePath + QString("/");
cachePath = QString(filePath + ".cache/");
}
void UpdateWorker::startUpdating()
{
emit sendMessage(QString("Checking for outdated game files."));
jsonWorker = new JsonWorker(this);
//download the patch manifest
connect(jsonWorker, SIGNAL(documentReady(QJsonDocument)), this, SLOT(patchManifestReady(QJsonDocument)));
jsonWorker->startRequest(QUrl(PATCH_MANIFEST_URL));
}
void UpdateWorker::patchManifestReady(QJsonDocument patchManifest)
{
qDebug() << "Received patch manifest";
//begin parsing the patch manifest
QJsonObject manifestObject = patchManifest.object();
QStringList fileNamesList = manifestObject.keys(); //get a list of all the filenames in the JSON file
QString fileName; //used to store the filename we are working on in the following foreach loop
QByteArray fileHash;
//stores the file number we are working on in the foreach loop
int currentFileNum = 1;
//run through each filename and determine if we need to update that file
foreach(fileName, fileNamesList)
{
QJsonObject fileObject = manifestObject[fileName].toObject(); //create an object for each file in the JSON document
QJsonArray platforms = fileObject["only"].toArray(); //look for the platform the file is for
emit sendMessage(QString("Updating file ") + QString::number(currentFileNum) + QString (" of ") + QString::number(fileNamesList.size()) + QString(" (") + fileName + QString(")"));
qDebug() << "Checking" << fileName << "for updates";
if(platforms.contains(PLATFORM))
{
QString localBz2FileName = cachePath + fileObject["dl"].toString();
QString localFileName = filePath + fileName;
QFile file(localFileName);
bool fileIsOld = true;
int i = 0;
//loop to run until the file is fully updated or maximum of 5 times to prevent infinite loops
while(fileIsOld && i<5)
{
i++;
//check to see if the file already exists or not
if(file.exists())
{
//generate the SHA1 hash of the file to compare with the patch manifest
fileHash = getHash(localFileName);
qDebug() << "Local file's hash is:" << fileHash;
//Check if the hash matches the patch manifest
if(fileHash == fileObject["hash"].toString())
{
qDebug() << "Hash matches patch manifest, skipping update\n";
fileIsOld = false;
}
else
{
qDebug() << "Hash does not match patch manifest, checking for a patch";
QJsonObject patchListObject = fileObject["patches"].toObject();
QStringList patchList = patchListObject.keys();
QString manifestHash;
bool hasBeenPatched = false;
foreach(manifestHash, patchList)
{
//check if the patch applies to our hash
if(fileHash == manifestHash)
{
QJsonObject patchObject = patchListObject[manifestHash].toObject();
PatchWorker *patchWorker = new PatchWorker(this);
QString patchFileName = cachePath + patchObject["filename"].toString();
QString extractedFileName = patchFileName + ".decomp";
//get the patch file
getNewFile(patchObject["filename"].toString(), patchFileName, extractedFileName, patchObject["compPatchHash"].toString());
//verify the downloaded patch is good
if(getHash(extractedFileName) == patchObject["patchHash"].toString())
{
qDebug() << "Downloaded patch matches manifest";
}
else
{
qDebug() << "Downloaded patch does not match manifet";
}
//patch the file
qDebug() << "extracted name:" << extractedFileName << "old name:" << localFileName;
patchWorker->patchFile(extractedFileName,localFileName);
hasBeenPatched = true;
//check if the patch updated the file fully
if(getHash(localFileName) == fileObject["hash"].toString())
{
qDebug() << "File has been patched fully\n";
fileIsOld = false;
}
else
{
qDebug() << "Downloaded file fails integrity check, looping again to check for patches. This is try" << i << "of 5";
}
//delete the patch files
QFile::remove(patchFileName);
QFile::remove(extractedFileName);
}
}
//check if it was patched and if not no patch is available but still out of date so try to get a new copy
if(hasBeenPatched == false)
{
qDebug() << "No patch found, downloading a fresh copy";
getNewFile(fileObject["dl"].toString(), localBz2FileName, localFileName, fileObject["compHash"].toString());
//check to make sure it is up to date
fileHash = getHash(localFileName);
if(fileHash == fileObject["hash"].toString())
{
qDebug() << "Downloaded file's integrity has been verified\n";
fileIsOld = false;
}
else
{
qDebug() << "Downloaded file fails integrity check, looping again to check for patches. This is try" << i << "of 5";
}
}
//delete the zipped file
QFile::remove(localBz2FileName);
}
}
else
{
//file doesn't exist, download new one
getNewFile(fileObject["dl"].toString(), localBz2FileName, localFileName, fileObject["compHash"].toString());
//check to make sure it is up to date
fileHash = getHash(localFileName);
if(fileHash == fileObject["hash"].toString())
{
qDebug() << "Downloaded file's integrity has been verified\n";
fileIsOld = false;
}
else
{
qDebug() << "Downloaded file fails integrity check, looping again to check for patches. This is try" << i << "of 5";
}
//delete the zipped file
QFile::remove(localBz2FileName);
}
}
}
else
{
qDebug() << "Skipping, it is not for our platform\n";
}
currentFileNum++;
}
emit sendMessage(QString("All files are up to date!"));
emit hideProgressBar();
emit updateComplete();
//on Linux platforms we need to check if TTREngine is executable since it isn't by default
#ifdef Q_OS_LINUX
#include <QFileInfo>
QFileInfo engine(QString(filePath + "TTREngine"));
if(engine.isExecutable())
{
qDebug() << "TTREngine is already executable\n";
}
else
{
qDebug() << "TTREngine is not executable, setting it\n";
QFile engineFile(QString(filePath + "TTREngine"));
engineFile.setPermissions(QFile::ExeOwner | QFile::ReadUser | QFile::WriteOwner);
engineFile.flush();
engineFile.close();
engineFile.deleteLater();
}
#endif
}
void UpdateWorker::startDownload(QString fileToDownload)
{
DownloadWorker *downloadWorker = new DownloadWorker(this);
QString downloadFileName = cachePath + fileToDownload;
qDebug() << "Downloading" << fileToDownload;
connect(downloadWorker, SIGNAL(hideProgressBar()), this, SLOT(relayHideProgressBar()));
connect(downloadWorker, SIGNAL(showProgressBar()), this, SLOT(relayShowProgressBar()));
connect(downloadWorker, SIGNAL(sendDownloadProgress(qint64,qint64)), this, SLOT(relayDownloadProgress(qint64,qint64)));
//retreive the file
bool sucessfulDownload = downloadWorker->getFile(CDN_URL + fileToDownload, downloadFileName);
if (sucessfulDownload)
{
qDebug() << "Successfully downloaded file" << downloadFileName;
}
else
{
qDebug() << "Error downloding file" << downloadFileName << endl;
}
}
void UpdateWorker::getNewFile(QString dlFile, QString localBz2FileName, QString localFileName, QString compHash)
{
//get the file
startDownload(dlFile);
//make sure download succeeded, logging for debugging purposes but still extracting because TTR has been known to not update hashes
QByteArray fileHash = getHash(localBz2FileName);
if(fileHash == compHash)
{
qDebug() << "Integrity of" << dlFile << "is valid.";
}
else
{
qDebug() << "Failure verifying the integrity of" << dlFile;
}
//extract the file
emit sendMessage(QString("Extracting " + dlFile));
ExtractionWorker *extractionWorker = new ExtractionWorker(this);
extractionWorker->extractBz2(localBz2FileName, localFileName);
//delete the worker now that we are done with it
extractionWorker->deleteLater();
}
void UpdateWorker::relayDownloadProgress(qint64 bytesReceived, qint64 totalBytes)
{
//convert the progress into a percentage since QProgressBar only uses an int
emit sendProgressBarReceived((bytesReceived * 100) / totalBytes);
}
void UpdateWorker::relayHideProgressBar()
{
emit hideProgressBar();
}
void UpdateWorker::relayShowProgressBar()
{
emit showProgressBar();
}