diff options
Diffstat (limited to 'api/logic')
28 files changed, 198 insertions, 798 deletions
diff --git a/api/logic/CMakeLists.txt b/api/logic/CMakeLists.txt index db1a7dec..50c2cb6e 100644 --- a/api/logic/CMakeLists.txt +++ b/api/logic/CMakeLists.txt @@ -305,10 +305,6 @@ set(MINECRAFT_SOURCES minecraft/AssetsUtils.h minecraft/AssetsUtils.cpp - # Forge and all things forge related - minecraft/forge/ForgeXzDownload.h - minecraft/forge/ForgeXzDownload.cpp - # Skin upload utilities minecraft/SkinUpload.cpp minecraft/SkinUpload.h @@ -439,15 +435,14 @@ set(META_SOURCES ) set(FTB_SOURCES - modplatform/ftb/FtbPackFetchTask.h - modplatform/ftb/FtbPackFetchTask.cpp - modplatform/ftb/FtbPackInstallTask.h - modplatform/ftb/FtbPackInstallTask.cpp - - modplatform/ftb/FtbPrivatePackManager.h - modplatform/ftb/FtbPrivatePackManager.cpp - - modplatform/ftb/PackHelpers.h + modplatform/legacy_ftb/PackFetchTask.h + modplatform/legacy_ftb/PackFetchTask.cpp + modplatform/legacy_ftb/PackInstallTask.h + modplatform/legacy_ftb/PackInstallTask.cpp + modplatform/legacy_ftb/PrivatePackManager.h + modplatform/legacy_ftb/PrivatePackManager.cpp + + modplatform/legacy_ftb/PackHelpers.h ) set(FLAME_SOURCES @@ -456,8 +451,6 @@ set(FLAME_SOURCES modplatform/flame/PackManifest.cpp modplatform/flame/FileResolvingTask.h modplatform/flame/FileResolvingTask.cpp - modplatform/flame/UrlResolvingTask.h - modplatform/flame/UrlResolvingTask.cpp ) add_unit_test(Index @@ -498,7 +491,7 @@ set_target_properties(MultiMC_logic PROPERTIES CXX_VISIBILITY_PRESET hidden VISI generate_export_header(MultiMC_logic) # Link -target_link_libraries(MultiMC_logic xz-embedded MultiMC_unpack200 systeminfo MultiMC_quazip MultiMC_classparser ${NBT_NAME} ${ZLIB_LIBRARIES}) +target_link_libraries(MultiMC_logic systeminfo MultiMC_quazip MultiMC_classparser ${NBT_NAME} ${ZLIB_LIBRARIES}) target_link_libraries(MultiMC_logic Qt5::Core Qt5::Xml Qt5::Network Qt5::Concurrent) # Mark and export headers diff --git a/api/logic/Commandline.h b/api/logic/Commandline.h index 5f6e5510..1adfb79d 100644 --- a/api/logic/Commandline.h +++ b/api/logic/Commandline.h @@ -69,8 +69,8 @@ namespace ArgumentStyle { enum Enum { - Space, /**< --option=value */ - Equals, /**< --option value */ + Space, /**< --option value */ + Equals, /**< --option=value */ SpaceAndEquals, /**< --option[= ]value */ #ifdef Q_OS_WIN32 Default = Equals diff --git a/api/logic/Env.cpp b/api/logic/Env.cpp index 77546bbc..0d496d4e 100644 --- a/api/logic/Env.cpp +++ b/api/logic/Env.cpp @@ -97,6 +97,7 @@ void Env::initHttpMetaCache() m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath()); m_metacache->addBase("general", QDir("cache").absolutePath()); m_metacache->addBase("FTBPacks", QDir("cache/FTBPacks").absolutePath()); + m_metacache->addBase("TwitchPacks", QDir("cache/TwitchPacks").absolutePath()); m_metacache->addBase("skins", QDir("accounts/skins").absolutePath()); m_metacache->addBase("root", QDir::currentPath()); m_metacache->addBase("translations", QDir("translations").absolutePath()); diff --git a/api/logic/InstanceCopyTask.cpp b/api/logic/InstanceCopyTask.cpp index a576a0fd..35adeaf9 100644 --- a/api/logic/InstanceCopyTask.cpp +++ b/api/logic/InstanceCopyTask.cpp @@ -5,9 +5,10 @@ #include "pathmatcher/RegexpMatcher.h" #include <QtConcurrentRun> -InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, bool copySaves) +InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, bool copySaves, bool keepPlaytime) { m_origInstance = origInstance; + m_keepPlaytime = keepPlaytime; if(!copySaves) { @@ -46,6 +47,9 @@ void InstanceCopyTask::copyFinished() InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath)); inst->setName(m_instName); inst->setIconKey(m_instIcon); + if(!m_keepPlaytime) { + inst->resetTimePlayed(); + } emitSucceeded(); } diff --git a/api/logic/InstanceCopyTask.h b/api/logic/InstanceCopyTask.h index 8dd55b40..6465e92d 100644 --- a/api/logic/InstanceCopyTask.h +++ b/api/logic/InstanceCopyTask.h @@ -15,7 +15,7 @@ class MULTIMC_LOGIC_EXPORT InstanceCopyTask : public InstanceTask { Q_OBJECT public: - explicit InstanceCopyTask(InstancePtr origInstance, bool copySaves); + explicit InstanceCopyTask(InstancePtr origInstance, bool copySaves, bool keepPlaytime); protected: //! Entry point for tasks. @@ -28,4 +28,5 @@ private: /* data */ QFuture<bool> m_copyFuture; QFutureWatcher<bool> m_copyFutureWatcher; std::unique_ptr<IPathMatcher> m_matcher; + bool m_keepPlaytime; }; diff --git a/api/logic/Version.cpp b/api/logic/Version.cpp index 42eac669..6392a50f 100644 --- a/api/logic/Version.cpp +++ b/api/logic/Version.cpp @@ -78,7 +78,7 @@ void Version::parse() // FIXME: this is bad. versions can contain a lot more separators... QStringList parts = m_string.split('.'); - for (const auto part : parts) + for (const auto &part : parts) { m_sections.append(Section(part)); } diff --git a/api/logic/minecraft/ComponentUpdateTask.cpp b/api/logic/minecraft/ComponentUpdateTask.cpp index 15003160..84c0474c 100644 --- a/api/logic/minecraft/ComponentUpdateTask.cpp +++ b/api/logic/minecraft/ComponentUpdateTask.cpp @@ -451,13 +451,17 @@ static bool getTrivialComponentChanges(const ComponentIndex & index, const Requi auto & comp = (*compIter); if(comp->getVersion() != req.equalsVersion) { - if(comp->m_dependencyOnly) - { - decision = Decision::VersionNotSame; - } - else - { + if(comp->isCustom()) { decision = Decision::LockedVersionNotSame; + } else { + if(comp->m_dependencyOnly) + { + decision = Decision::VersionNotSame; + } + else + { + decision = Decision::LockedVersionNotSame; + } } break; } diff --git a/api/logic/minecraft/LaunchProfile.cpp b/api/logic/minecraft/LaunchProfile.cpp index c39bdf04..41705187 100644 --- a/api/logic/minecraft/LaunchProfile.cpp +++ b/api/logic/minecraft/LaunchProfile.cpp @@ -11,6 +11,7 @@ void LaunchProfile::clear() m_mainClass.clear(); m_appletClass.clear(); m_libraries.clear(); + m_mavenFiles.clear(); m_traits.clear(); m_jarMods.clear(); m_mainJar.reset(); @@ -157,6 +158,22 @@ void LaunchProfile::applyLibrary(LibraryPtr library) } } +void LaunchProfile::applyMavenFile(LibraryPtr mavenFile) +{ + if(!mavenFile->isActive()) + { + return; + } + + if(mavenFile->isNative()) + { + return; + } + + // unlike libraries, we do not keep only one version or try to dedupe them + m_mavenFiles.append(Library::limitedCopy(mavenFile)); +} + const LibraryPtr LaunchProfile::getMainJar() const { return m_mainJar; @@ -253,6 +270,11 @@ const QList<LibraryPtr> & LaunchProfile::getNativeLibraries() const return m_nativeLibraries; } +const QList<LibraryPtr> & LaunchProfile::getMavenFiles() const +{ + return m_mavenFiles; +} + void LaunchProfile::getLibraryFiles( const QString& architecture, QStringList& jars, diff --git a/api/logic/minecraft/LaunchProfile.h b/api/logic/minecraft/LaunchProfile.h index 77174079..c1752531 100644 --- a/api/logic/minecraft/LaunchProfile.h +++ b/api/logic/minecraft/LaunchProfile.h @@ -20,6 +20,7 @@ public: /* application of profile variables from patches */ void applyJarMods(const QList<LibraryPtr> &jarMods); void applyMods(const QList<LibraryPtr> &jarMods); void applyLibrary(LibraryPtr library); + void applyMavenFile(LibraryPtr library); void applyMainJar(LibraryPtr jar); void applyProblemSeverity(ProblemSeverity severity); /// clear the profile @@ -37,6 +38,7 @@ public: /* getters for profile variables */ const QList<LibraryPtr> & getJarMods() const; const QList<LibraryPtr> & getLibraries() const; const QList<LibraryPtr> & getNativeLibraries() const; + const QList<LibraryPtr> & getMavenFiles() const; const LibraryPtr getMainJar() const; void getLibraryFiles( const QString & architecture, @@ -79,10 +81,13 @@ private: /// the list of libraries QList<LibraryPtr> m_libraries; + /// the list of maven files to be placed in the libraries folder, but not acted upon + QList<LibraryPtr> m_mavenFiles; + /// the main jar LibraryPtr m_mainJar; - /// the list of libraries + /// the list of native libraries QList<LibraryPtr> m_nativeLibraries; /// traits, collected from all the version files (version files can only add) diff --git a/api/logic/minecraft/Library.cpp b/api/logic/minecraft/Library.cpp index a56e8110..9ff8dcdc 100644 --- a/api/logic/minecraft/Library.cpp +++ b/api/logic/minecraft/Library.cpp @@ -3,7 +3,6 @@ #include <net/Download.h> #include <net/ChecksumValidator.h> -#include <minecraft/forge/ForgeXzDownload.h> #include <Env.h> #include <FileSystem.h> @@ -88,26 +87,19 @@ QList< std::shared_ptr< NetAction > > Library::getDownloads( { options |= Net::Download::Option::AcceptLocalFiles; } - if (isForge()) + + if(sha1.size()) { - qDebug() << "XzDownload for:" << rawName() << "storage:" << storage << "url:" << url; - out.append(ForgeXzDownload::make(url, storage, entry)); + auto rawSha1 = QByteArray::fromHex(sha1.toLatin1()); + auto dl = Net::Download::makeCached(url, entry, options); + dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1)); + qDebug() << "Checksummed Download for:" << rawName() << "storage:" << storage << "url:" << url; + out.append(dl); } else { - if(sha1.size()) - { - auto rawSha1 = QByteArray::fromHex(sha1.toLatin1()); - auto dl = Net::Download::makeCached(url, entry, options); - dl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha1, rawSha1)); - qDebug() << "Checksummed Download for:" << rawName() << "storage:" << storage << "url:" << url; - out.append(dl); - } - else - { - out.append(Net::Download::makeCached(url, entry, options)); - qDebug() << "Download for:" << rawName() << "storage:" << storage << "url:" << url; - } + out.append(Net::Download::makeCached(url, entry, options)); + qDebug() << "Download for:" << rawName() << "storage:" << storage << "url:" << url; } return true; }; @@ -243,11 +235,6 @@ bool Library::isAlwaysStale() const return m_hint == "always-stale"; } -bool Library::isForge() const -{ - return m_hint == "forge-pack-xz"; -} - void Library::setStoragePrefix(QString prefix) { m_storagePrefix = prefix; diff --git a/api/logic/minecraft/MinecraftUpdate.cpp b/api/logic/minecraft/MinecraftUpdate.cpp index 00558e37..f9ff114d 100644 --- a/api/logic/minecraft/MinecraftUpdate.cpp +++ b/api/logic/minecraft/MinecraftUpdate.cpp @@ -14,7 +14,6 @@ */ #include "Env.h" -#include <minecraft/forge/ForgeXzDownload.h> #include "MinecraftUpdate.h" #include "MinecraftInstance.h" diff --git a/api/logic/minecraft/OneSixVersionFormat.cpp b/api/logic/minecraft/OneSixVersionFormat.cpp index 6f3b926b..a0b6fd0e 100644 --- a/api/logic/minecraft/OneSixVersionFormat.cpp +++ b/api/logic/minecraft/OneSixVersionFormat.cpp @@ -144,14 +144,14 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc } } - auto readLibs = [&](const char * which) + auto readLibs = [&](const char * which, QList<LibraryPtr> & out) { for (auto libVal : requireArray(root.value(which))) { QJsonObject libObj = requireObject(libVal); // parse the library auto lib = libraryFromJson(libObj, filename); - out->libraries.append(lib); + out.append(lib); } }; bool hasPlusLibs = root.contains("+libraries"); @@ -160,16 +160,20 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc { out->addProblem(ProblemSeverity::Warning, QObject::tr("Version file has both '+libraries' and 'libraries'. This is no longer supported.")); - readLibs("libraries"); - readLibs("+libraries"); + readLibs("libraries", out->libraries); + readLibs("+libraries", out->libraries); } else if (hasLibs) { - readLibs("libraries"); + readLibs("libraries", out->libraries); } else if(hasPlusLibs) { - readLibs("+libraries"); + readLibs("+libraries", out->libraries); + } + + if(root.contains("mavenFiles")) { + readLibs("mavenFiles", out->mavenFiles); } // if we have mainJar, just use it @@ -276,6 +280,15 @@ QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch } root.insert("libraries", array); } + if (!patch->mavenFiles.isEmpty()) + { + QJsonArray array; + for (auto value: patch->mavenFiles) + { + array.append(OneSixVersionFormat::libraryToJson(value.get())); + } + root.insert("mavenFiles", array); + } if (!patch->jarMods.isEmpty()) { QJsonArray array; diff --git a/api/logic/minecraft/VersionFile.cpp b/api/logic/minecraft/VersionFile.cpp index cfb9e504..5bad57e9 100644 --- a/api/logic/minecraft/VersionFile.cpp +++ b/api/logic/minecraft/VersionFile.cpp @@ -41,6 +41,10 @@ void VersionFile::applyTo(LaunchProfile *profile) { profile->applyLibrary(library); } + for (auto mavenFile : mavenFiles) + { + profile->applyMavenFile(mavenFile); + } profile->applyProblemSeverity(getProblemSeverity()); } @@ -53,4 +57,4 @@ void VersionFile::applyTo(LaunchProfile *profile) throw MinecraftVersionMismatch(uid, dependsOnMinecraftVersion, theirVersion); } } -*/
\ No newline at end of file +*/ diff --git a/api/logic/minecraft/VersionFile.h b/api/logic/minecraft/VersionFile.h index 3dc9db96..29ddd0bc 100644 --- a/api/logic/minecraft/VersionFile.h +++ b/api/logic/minecraft/VersionFile.h @@ -75,6 +75,9 @@ public: /* data */ /// Mojang: list of libraries to add to the version QList<LibraryPtr> libraries; + /// MultiMC: list of maven files to put in the libraries folder, but not in classpath + QList<LibraryPtr> mavenFiles; + /// The main jar (Minecraft version library, normally) LibraryPtr mainJar; diff --git a/api/logic/minecraft/forge/ForgeXzDownload.cpp b/api/logic/minecraft/forge/ForgeXzDownload.cpp deleted file mode 100644 index c6465469..00000000 --- a/api/logic/minecraft/forge/ForgeXzDownload.cpp +++ /dev/null @@ -1,393 +0,0 @@ -/* Copyright 2013-2019 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Env.h" -#include "ForgeXzDownload.h" -#include <FileSystem.h> - -#include <QCryptographicHash> -#include <QFileInfo> -#include <QDateTime> -#include <QDir> -#include <QDebug> - -ForgeXzDownload::ForgeXzDownload(QString url, QString relative_path, MetaEntryPtr entry) : NetAction() -{ - m_entry = entry; - m_target_path = entry->getFullPath(); - m_pack200_xz_file.setFileTemplate("./dl_temp.XXXXXX"); - m_status = Job_NotStarted; - m_url_path = relative_path; - m_url = url + ".pack.xz"; -} - -void ForgeXzDownload::start() -{ - if(m_status == Job_Aborted) - { - qWarning() << "Attempt to start an aborted Download:" << m_url.toString(); - emit aborted(m_index_within_job); - return; - } - m_status = Job_InProgress; - if (!m_entry->isStale()) - { - m_status = Job_Finished; - emit succeeded(m_index_within_job); - return; - } - // can we actually create the real, final file? - if (!FS::ensureFilePathExists(m_target_path)) - { - m_status = Job_Failed; - emit failed(m_index_within_job); - return; - } - - qDebug() << "Downloading " << m_url.toString(); - QNetworkRequest request(m_url); - request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->getETag().toLatin1()); - request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Cached)"); - - QNetworkReply *rep = ENV.qnam().get(request); - - m_reply.reset(rep); - connect(rep, SIGNAL(downloadProgress(qint64, qint64)), SLOT(downloadProgress(qint64, qint64))); - connect(rep, SIGNAL(finished()), SLOT(downloadFinished())); - connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError))); - connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead())); -} - -void ForgeXzDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) -{ - m_total_progress = bytesTotal; - m_progress = bytesReceived; - emit netActionProgress(m_index_within_job, bytesReceived, bytesTotal); -} - -void ForgeXzDownload::downloadError(QNetworkReply::NetworkError error) -{ - if(error == QNetworkReply::OperationCanceledError) - { - qCritical() << "Aborted " << m_url.toString(); - m_status = Job_Aborted; - } - else - { - // error happened during download. - qCritical() << "Failed " << m_url.toString() << " with reason " << error; - m_status = Job_Failed; - } -} - -void ForgeXzDownload::failAndTryNextMirror() -{ - m_status = Job_Failed; - emit failed(m_index_within_job); -} - -void ForgeXzDownload::downloadFinished() -{ - // if the download succeeded - if (m_status != Job_Failed && m_status != Job_Aborted) - { - // nothing went wrong... - m_status = Job_Finished; - if (m_pack200_xz_file.isOpen()) - { - // we actually downloaded something! process and isntall it - decompressAndInstall(); - return; - } - else - { - // something bad happened -- on the local machine! - m_status = Job_Failed; - m_pack200_xz_file.remove(); - m_reply.reset(); - emit failed(m_index_within_job); - return; - } - } - else if(m_status == Job_Aborted) - { - m_pack200_xz_file.remove(); - m_reply.reset(); - emit failed(m_index_within_job); - emit aborted(m_index_within_job); - return; - } - // else the download failed - else - { - m_status = Job_Failed; - m_pack200_xz_file.close(); - m_pack200_xz_file.remove(); - m_reply.reset(); - failAndTryNextMirror(); - return; - } -} - -void ForgeXzDownload::downloadReadyRead() -{ - - if (!m_pack200_xz_file.isOpen()) - { - if (!m_pack200_xz_file.open()) - { - /* - * Can't open the file... the job failed - */ - m_reply->abort(); - emit failed(m_index_within_job); - return; - } - } - m_pack200_xz_file.write(m_reply->readAll()); -} - -#include "xz.h" -#include "unpack200.h" -#include <stdexcept> -#include <unistd.h> - -const size_t buffer_size = 8196; - -// NOTE: once this gets here, it can't be aborted anymore. we don't care. -void ForgeXzDownload::decompressAndInstall() -{ - // rewind the downloaded temp file - m_pack200_xz_file.seek(0); - // de-xz'd file - QTemporaryFile pack200_file("./dl_temp.XXXXXX"); - pack200_file.open(); - - bool xz_success = false; - // first, de-xz - { - uint8_t in[buffer_size]; - uint8_t out[buffer_size]; - struct xz_buf b; - struct xz_dec *s; - enum xz_ret ret; - xz_crc32_init(); - xz_crc64_init(); - s = xz_dec_init(XZ_DYNALLOC, 1 << 26); - if (s == nullptr) - { - xz_dec_end(s); - failAndTryNextMirror(); - return; - } - b.in = in; - b.in_pos = 0; - b.in_size = 0; - b.out = out; - b.out_pos = 0; - b.out_size = buffer_size; - while (!xz_success) - { - if (b.in_pos == b.in_size) - { - b.in_size = m_pack200_xz_file.read((char *)in, sizeof(in)); - b.in_pos = 0; - } - - ret = xz_dec_run(s, &b); - - if (b.out_pos == sizeof(out)) - { - auto wresult = pack200_file.write((char *)out, b.out_pos); - if (wresult < 0 || size_t(wresult) != b.out_pos) - { - // msg = "Write error\n"; - xz_dec_end(s); - failAndTryNextMirror(); - return; - } - - b.out_pos = 0; - } - - if (ret == XZ_OK) - continue; - - if (ret == XZ_UNSUPPORTED_CHECK) - { - // unsupported check. this is OK, but we should log this - continue; - } - - auto wresult = pack200_file.write((char *)out, b.out_pos); - if (wresult < 0 || size_t(wresult) != b.out_pos) - { - // write error - pack200_file.close(); - xz_dec_end(s); - return; - } - - switch (ret) - { - case XZ_STREAM_END: - xz_dec_end(s); - xz_success = true; - break; - - case XZ_MEM_ERROR: - qCritical() << "Memory allocation failed\n"; - xz_dec_end(s); - failAndTryNextMirror(); - return; - - case XZ_MEMLIMIT_ERROR: - qCritical() << "Memory usage limit reached\n"; - xz_dec_end(s); - failAndTryNextMirror(); - return; - - case XZ_FORMAT_ERROR: - qCritical() << "Not a .xz file\n"; - xz_dec_end(s); - failAndTryNextMirror(); - return; - - case XZ_OPTIONS_ERROR: - qCritical() << "Unsupported options in the .xz headers\n"; - xz_dec_end(s); - failAndTryNextMirror(); - return; - - case XZ_DATA_ERROR: - case XZ_BUF_ERROR: - qCritical() << "File is corrupt\n"; - xz_dec_end(s); - failAndTryNextMirror(); - return; - - default: - qCritical() << "Bug!\n"; - xz_dec_end(s); - failAndTryNextMirror(); - return; - } - } - } - m_pack200_xz_file.remove(); - - // revert pack200 - pack200_file.seek(0); - int handle_in = pack200_file.handle(); - // FIXME: dispose of file handles, pointers and the like. Ideally wrap in objects. - if(handle_in == -1) - { - qCritical() << "Error reopening " << pack200_file.fileName(); - failAndTryNextMirror(); - return; - } - int handle_in_dup = dup (handle_in); - if(handle_in_dup == -1) - { - qCritical() << "Error reopening " << pack200_file.fileName(); - failAndTryNextMirror(); - return; - } - FILE *file_in = fdopen (handle_in_dup, "rb"); - if(!file_in) - { - qCritical() << "Error reopening " << pack200_file.fileName(); - failAndTryNextMirror(); - return; - } - QFile qfile_out(m_target_path); - if(!qfile_out.open(QIODevice::WriteOnly)) - { - qCritical() << "Error opening " << qfile_out.fileName(); - failAndTryNextMirror(); - return; - } - int handle_out = qfile_out.handle(); - if(handle_out == -1) - { - qCritical() << "Error opening " << qfile_out.fileName(); - failAndTryNextMirror(); - return; - } - int handle_out_dup = dup (handle_out); - if(handle_out_dup == -1) - { - qCritical() << "Error reopening " << qfile_out.fileName(); - failAndTryNextMirror(); - return; - } - FILE *file_out = fdopen (handle_out_dup, "wb"); - if(!file_out) - { - qCritical() << "Error opening " << qfile_out.fileName(); - failAndTryNextMirror(); - return; |
