mirror of
https://github.com/ioquake/launch.git
synced 2024-11-28 14:02:02 +00:00
Use a primitive form of file transactions when copying pk3 files from a CD or Steam.
This commit is contained in:
parent
fdb1b8144f
commit
5897c21ddc
6 changed files with 105 additions and 20 deletions
48
filecopy.cpp
48
filecopy.cpp
|
@ -20,16 +20,35 @@ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include <time.h>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include "filecopy.h"
|
||||
|
||||
FileCopyWorker::FileCopyWorker(const QList<FileCopyOperation> &files) : files(files), isCancelled(false)
|
||||
QString FileUtils::uniqueFilename(const QString &filename)
|
||||
{
|
||||
QString unique;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
QFileInfo fi(filename);
|
||||
unique = fi.path() + QString("/") + QString::number(qrand(), 16) + QString("_") + fi.fileName();
|
||||
|
||||
if (!QFile::exists(unique))
|
||||
break;
|
||||
}
|
||||
|
||||
return unique;
|
||||
}
|
||||
|
||||
FileCopyWorker::FileCopyWorker(const QList<FileOperation> &files) : files(files), isCancelled(false)
|
||||
{
|
||||
}
|
||||
|
||||
void FileCopyWorker::copy()
|
||||
{
|
||||
qsrand(time(NULL));
|
||||
|
||||
for (int i = 0; i < files.size(); i++)
|
||||
{
|
||||
QFile sourceFile(files.at(i).source);
|
||||
|
@ -37,17 +56,23 @@ void FileCopyWorker::copy()
|
|||
if (!sourceFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
emit errorMessage(QString("'%1': %2").arg(files.at(i).source).arg(sourceFile.errorString()));
|
||||
return;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
QFile destinationFile(files.at(i).dest);
|
||||
const QString tempDestFilename = FileUtils::uniqueFilename(files.at(i).dest);
|
||||
QFile destinationFile(tempDestFilename);
|
||||
|
||||
if (!destinationFile.open(QIODevice::WriteOnly))
|
||||
{
|
||||
emit errorMessage(QString("'%1': %2").arg(files.at(i).dest).arg(destinationFile.errorString()));
|
||||
return;
|
||||
emit errorMessage(QString("'%1': %2").arg(tempDestFilename).arg(destinationFile.errorString()));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
FileOperation fo;
|
||||
fo.source = tempDestFilename;
|
||||
fo.dest = files.at(i).dest;
|
||||
renameOperations.append(fo);
|
||||
|
||||
emit fileChanged(QFileInfo(sourceFile.fileName()).fileName());
|
||||
const qint64 totalBytes = sourceFile.size();
|
||||
qint64 totalBytesWritten = 0;
|
||||
|
@ -66,11 +91,20 @@ void FileCopyWorker::copy()
|
|||
QMutexLocker locker(&cancelMutex);
|
||||
|
||||
if (isCancelled)
|
||||
break;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
emit copyFinished();
|
||||
emit copyFinished(renameOperations);
|
||||
return;
|
||||
|
||||
// Delete all the destination files.
|
||||
cleanup:
|
||||
for (int i = 0; i < renameOperations.size(); i++)
|
||||
{
|
||||
QFile::remove(renameOperations.at(i).source);
|
||||
}
|
||||
}
|
||||
|
||||
void FileCopyWorker::cancel()
|
||||
|
|
33
filecopy.h
33
filecopy.h
|
@ -27,18 +27,42 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
#include <QObject>
|
||||
#include <QMutex>
|
||||
|
||||
struct FileCopyOperation
|
||||
struct FileOperation
|
||||
{
|
||||
QString source;
|
||||
QString dest;
|
||||
};
|
||||
|
||||
class FileUtils
|
||||
{
|
||||
public:
|
||||
static QString uniqueFilename(const QString &filename);
|
||||
};
|
||||
|
||||
/*
|
||||
Input: list of files to copy (ctor).
|
||||
Output: list of files to rename in order to complete the transaction (copyFinished signal).
|
||||
|
||||
Example (using a single file):
|
||||
|
||||
Input source: F:\QUAKE3\baseq3\pak0.pk3
|
||||
Input dest: C:\Program Files (x86)\Quake III Arena\baseq3\pak0.pk3
|
||||
|
||||
Source is copied to dest, but the destination file is renamed by prepending a random prefix, e.g. 34d8f_pak0.pk3.
|
||||
|
||||
Output source: C:\Program Files (x86)\Quake III Arena\baseq3\34d8f_pak0.pk3
|
||||
Output dest: C:\Program Files (x86)\Quake III Arena\baseq3\pak0.pk3
|
||||
|
||||
The transaction is completed by deleting output dest and renaming output source to output dest.
|
||||
|
||||
If the worker is cancelled, all created files are deleted.
|
||||
*/
|
||||
class FileCopyWorker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FileCopyWorker(const QList<FileCopyOperation> &files);
|
||||
FileCopyWorker(const QList<FileOperation> &files);
|
||||
void cancel();
|
||||
|
||||
public slots:
|
||||
|
@ -48,14 +72,15 @@ signals:
|
|||
void fileChanged(const QString &filename);
|
||||
void progressChanged(qint64 bytesWritten, qint64 bytesTotal);
|
||||
void errorMessage(const QString &message);
|
||||
void copyFinished();
|
||||
void copyFinished(QList<FileOperation> renameOperations);
|
||||
|
||||
private:
|
||||
static const int bufferSize = 32 * 1024;
|
||||
const QList<FileCopyOperation> files;
|
||||
const QList<FileOperation> files;
|
||||
char buffer[bufferSize];
|
||||
bool isCancelled;
|
||||
QMutex cancelMutex;
|
||||
QList<FileOperation> renameOperations;
|
||||
};
|
||||
|
||||
#endif // FILECOPY_H
|
||||
|
|
|
@ -44,13 +44,13 @@ void InstallWizard::clearFileCopyOperations()
|
|||
|
||||
void InstallWizard::addFileCopyOperation(const QString &source, const QString &dest)
|
||||
{
|
||||
FileCopyOperation fco;
|
||||
fco.source = source;
|
||||
fco.dest = dest;
|
||||
fileCopyOperations.append(fco);
|
||||
FileOperation fo;
|
||||
fo.source = source;
|
||||
fo.dest = dest;
|
||||
fileCopyOperations.append(fo);
|
||||
}
|
||||
|
||||
const QList<FileCopyOperation> &InstallWizard::getFileCopyOperations() const
|
||||
const QList<FileOperation> &InstallWizard::getFileCopyOperations() const
|
||||
{
|
||||
return fileCopyOperations;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ public:
|
|||
|
||||
void clearFileCopyOperations();
|
||||
void addFileCopyOperation(const QString &source, const QString &dest);
|
||||
const QList<FileCopyOperation> &getFileCopyOperations() const;
|
||||
const QList<FileOperation> &getFileCopyOperations() const;
|
||||
|
||||
bool getIsQuake3PatchRequired() const;
|
||||
void setIsQuake3PatchRequired(bool value);
|
||||
|
@ -46,7 +46,7 @@ private:
|
|||
Ui::InstallWizard *ui;
|
||||
QPushButton *cancelButton;
|
||||
Settings *settings;
|
||||
QList<FileCopyOperation> fileCopyOperations;
|
||||
QList<FileOperation> fileCopyOperations;
|
||||
bool isQuake3PatchRequired;
|
||||
QString quakePath;
|
||||
};
|
||||
|
|
|
@ -37,6 +37,7 @@ void InstallWizard_Copy::initializePage()
|
|||
}
|
||||
|
||||
// Start copy thread.
|
||||
qRegisterMetaType<QList<FileOperation> >("QList<FileOperation>");
|
||||
copyWorker = new FileCopyWorker(((InstallWizard *)wizard())->getFileCopyOperations());
|
||||
copyWorker->moveToThread(©Thread);
|
||||
connect(©Thread, &QThread::finished, copyWorker, &QObject::deleteLater);
|
||||
|
@ -90,10 +91,35 @@ void InstallWizard_Copy::setCopyErrorMessage(const QString &message)
|
|||
ui->lblStatus->setText(message);
|
||||
}
|
||||
|
||||
void InstallWizard_Copy::finishCopy()
|
||||
void InstallWizard_Copy::finishCopy(QList<FileOperation> renameOperations)
|
||||
{
|
||||
copyThread.quit();
|
||||
copyThread.wait();
|
||||
|
||||
// Complete the transaction.
|
||||
for (int i = 0; i < renameOperations.size(); i++)
|
||||
{
|
||||
const FileOperation &fo = renameOperations.at(i);
|
||||
const QString randomDest = FileUtils::uniqueFilename(fo.dest);
|
||||
|
||||
// Rename dest to random.
|
||||
if (!QFile::rename(fo.dest, randomDest))
|
||||
{
|
||||
ui->lblStatus->setText(QString("Transaction error renaming '%1' to '%2'").arg(fo.dest).arg(randomDest));
|
||||
return;
|
||||
}
|
||||
|
||||
// Rename source to dest.
|
||||
if (!QFile::rename(fo.source, fo.dest))
|
||||
{
|
||||
ui->lblStatus->setText(QString("Transaction error renaming '%1' to '%2'").arg(fo.source).arg(fo.dest));
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete random.
|
||||
QFile::remove(randomDest);
|
||||
}
|
||||
|
||||
isCopyFinished = true;
|
||||
emit completeChanged();
|
||||
wizard()->next();
|
||||
|
|
|
@ -26,7 +26,7 @@ private slots:
|
|||
void setCopyFilename(const QString &filename);
|
||||
void setCopyProgress(qint64 bytesWritten, qint64 bytesTotal);
|
||||
void setCopyErrorMessage(const QString &message);
|
||||
void finishCopy();
|
||||
void finishCopy(QList<FileOperation> renameOperations);
|
||||
|
||||
signals:
|
||||
void copy();
|
||||
|
|
Loading…
Reference in a new issue