refactor: add a HashUtils place for hashing stuff
Signed-off-by: flow <flowlnlnln@gmail.com>
This commit is contained in:
		| @@ -494,6 +494,8 @@ set(API_SOURCES | |||||||
|     modplatform/modrinth/ModrinthAPI.cpp |     modplatform/modrinth/ModrinthAPI.cpp | ||||||
|     modplatform/helpers/NetworkModAPI.h |     modplatform/helpers/NetworkModAPI.h | ||||||
|     modplatform/helpers/NetworkModAPI.cpp |     modplatform/helpers/NetworkModAPI.cpp | ||||||
|  |     modplatform/helpers/HashUtils.h | ||||||
|  |     modplatform/helpers/HashUtils.cpp | ||||||
| ) | ) | ||||||
|  |  | ||||||
| set(FTB_SOURCES | set(FTB_SOURCES | ||||||
|   | |||||||
							
								
								
									
										81
									
								
								launcher/modplatform/helpers/HashUtils.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								launcher/modplatform/helpers/HashUtils.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | |||||||
|  | #include "HashUtils.h" | ||||||
|  |  | ||||||
|  | #include <QDebug> | ||||||
|  | #include <QFile> | ||||||
|  |  | ||||||
|  | #include "FileSystem.h" | ||||||
|  |  | ||||||
|  | #include <MurmurHash2.h> | ||||||
|  |  | ||||||
|  | namespace Hashing { | ||||||
|  |  | ||||||
|  | static ModPlatform::ProviderCapabilities ProviderCaps; | ||||||
|  |  | ||||||
|  | Hasher::Ptr createHasher(QString file_path, ModPlatform::Provider provider) | ||||||
|  | { | ||||||
|  |     switch (provider) { | ||||||
|  |         case ModPlatform::Provider::MODRINTH: | ||||||
|  |             return createModrinthHasher(file_path); | ||||||
|  |         case ModPlatform::Provider::FLAME: | ||||||
|  |             return createFlameHasher(file_path); | ||||||
|  |         default: | ||||||
|  |             qCritical() << "[Hashing]" | ||||||
|  |                         << "Unrecognized mod platform!"; | ||||||
|  |             return nullptr; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Hasher::Ptr createModrinthHasher(QString file_path) | ||||||
|  | { | ||||||
|  |     return new ModrinthHasher(file_path); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Hasher::Ptr createFlameHasher(QString file_path) | ||||||
|  | { | ||||||
|  |     return new FlameHasher(file_path); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ModrinthHasher::executeTask() | ||||||
|  | { | ||||||
|  |     QFile file(m_path); | ||||||
|  |  | ||||||
|  |     try { | ||||||
|  |         file.open(QFile::ReadOnly); | ||||||
|  |     } catch (FS::FileSystemException& e) { | ||||||
|  |         qCritical() << QString("Failed to open JAR file in %1").arg(m_path); | ||||||
|  |         qCritical() << QString("Reason: ") << e.cause(); | ||||||
|  |  | ||||||
|  |         emitFailed("Failed to open file for hashing."); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     auto hash_type = ProviderCaps.hashType(ModPlatform::Provider::MODRINTH).first(); | ||||||
|  |     m_hash = ProviderCaps.hash(ModPlatform::Provider::MODRINTH, &file, hash_type); | ||||||
|  |  | ||||||
|  |     file.close(); | ||||||
|  |  | ||||||
|  |     if (m_hash.isEmpty()) { | ||||||
|  |         emitFailed("Empty hash!"); | ||||||
|  |     } else { | ||||||
|  |         emitSucceeded(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void FlameHasher::executeTask() | ||||||
|  | { | ||||||
|  |     // CF-specific | ||||||
|  |     auto should_filter_out = [](char c) { return (c == 9 || c == 10 || c == 13 || c == 32); }; | ||||||
|  |  | ||||||
|  |     std::ifstream file_stream(m_path.toStdString(), std::ifstream::binary); | ||||||
|  |     // TODO: This is very heavy work, but apparently QtConcurrent can't use move semantics, so we can't boop this to another thread. | ||||||
|  |     // How do we make this non-blocking then? | ||||||
|  |     m_hash = QString::number(MurmurHash2(std::move(file_stream), 4 * MiB, should_filter_out)); | ||||||
|  |  | ||||||
|  |     if (m_hash.isEmpty()) { | ||||||
|  |         emitFailed("Empty hash!"); | ||||||
|  |     } else { | ||||||
|  |         emitSucceeded(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace Hashing | ||||||
							
								
								
									
										47
									
								
								launcher/modplatform/helpers/HashUtils.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								launcher/modplatform/helpers/HashUtils.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <QString> | ||||||
|  |  | ||||||
|  | #include "modplatform/ModIndex.h" | ||||||
|  | #include "tasks/Task.h" | ||||||
|  |  | ||||||
|  | namespace Hashing { | ||||||
|  |  | ||||||
|  | class Hasher : public Task { | ||||||
|  |    public: | ||||||
|  |     using Ptr = shared_qobject_ptr<Hasher>; | ||||||
|  |  | ||||||
|  |     Hasher(QString file_path) : m_path(std::move(file_path)) {} | ||||||
|  |  | ||||||
|  |     /* We can't really abort this task, but we can say we aborted and finish our thing quickly :) */ | ||||||
|  |     bool abort() override { return true; } | ||||||
|  |  | ||||||
|  |     void executeTask() override = 0; | ||||||
|  |  | ||||||
|  |     QString getResult() const { return m_hash; }; | ||||||
|  |     QString getPath() const { return m_path; }; | ||||||
|  |  | ||||||
|  |    protected: | ||||||
|  |     QString m_hash; | ||||||
|  |     QString m_path; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class FlameHasher : public Hasher { | ||||||
|  |    public: | ||||||
|  |     FlameHasher(QString file_path) : Hasher(file_path) { setObjectName(QString("FlameHasher: %1").arg(file_path)); } | ||||||
|  |  | ||||||
|  |     void executeTask() override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class ModrinthHasher : public Hasher { | ||||||
|  |    public: | ||||||
|  |     ModrinthHasher(QString file_path) : Hasher(file_path) { setObjectName(QString("ModrinthHasher: %1").arg(file_path)); } | ||||||
|  |  | ||||||
|  |     void executeTask() override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | Hasher::Ptr createHasher(QString file_path, ModPlatform::Provider provider); | ||||||
|  | Hasher::Ptr createFlameHasher(QString file_path); | ||||||
|  | Hasher::Ptr createModrinthHasher(QString file_path); | ||||||
|  |  | ||||||
|  | }  // namespace Hashing | ||||||
| @@ -2,11 +2,14 @@ | |||||||
| #include "ModrinthAPI.h" | #include "ModrinthAPI.h" | ||||||
| #include "ModrinthPackIndex.h" | #include "ModrinthPackIndex.h" | ||||||
|  |  | ||||||
| #include "FileSystem.h" |  | ||||||
| #include "Json.h" | #include "Json.h" | ||||||
|  |  | ||||||
| #include "ModDownloadTask.h" | #include "ModDownloadTask.h" | ||||||
|  |  | ||||||
|  | #include "modplatform/helpers/HashUtils.h" | ||||||
|  |  | ||||||
|  | #include "tasks/ConcurrentTask.h" | ||||||
|  |  | ||||||
| static ModrinthAPI api; | static ModrinthAPI api; | ||||||
| static ModPlatform::ProviderCapabilities ProviderCaps; | static ModPlatform::ProviderCapabilities ProviderCaps; | ||||||
|  |  | ||||||
| @@ -32,6 +35,8 @@ void ModrinthCheckUpdate::executeTask() | |||||||
|     // Create all hashes |     // Create all hashes | ||||||
|     QStringList hashes; |     QStringList hashes; | ||||||
|     auto best_hash_type = ProviderCaps.hashType(ModPlatform::Provider::MODRINTH).first(); |     auto best_hash_type = ProviderCaps.hashType(ModPlatform::Provider::MODRINTH).first(); | ||||||
|  |  | ||||||
|  |     ConcurrentTask hashing_task(this, "MakeModrinthHashesTask", 10); | ||||||
|     for (auto* mod : m_mods) { |     for (auto* mod : m_mods) { | ||||||
|         if (!mod->enabled()) { |         if (!mod->enabled()) { | ||||||
|             emit checkFailed(mod, tr("Disabled mods won't be updated, to prevent mod duplication issues!")); |             emit checkFailed(mod, tr("Disabled mods won't be updated, to prevent mod duplication issues!")); | ||||||
| @@ -44,27 +49,25 @@ void ModrinthCheckUpdate::executeTask() | |||||||
|         // need to generate a new hash if the current one is innadequate |         // need to generate a new hash if the current one is innadequate | ||||||
|         // (though it will rarely happen, if at all) |         // (though it will rarely happen, if at all) | ||||||
|         if (mod->metadata()->hash_format != best_hash_type) { |         if (mod->metadata()->hash_format != best_hash_type) { | ||||||
|             QByteArray jar_data; |             auto hash_task = Hashing::createModrinthHasher(mod->fileinfo().absoluteFilePath()); | ||||||
|  |             connect(hash_task.get(), &Task::succeeded, [&] { | ||||||
|             QFile file(mod->fileinfo().absoluteFilePath()); |                 QString hash (hash_task->getResult()); | ||||||
|             try { |                 hashes.append(hash); | ||||||
|                 file.open(QFile::ReadOnly); |                 mappings.insert(hash, mod); | ||||||
|             } catch (FS::FileSystemException& e) { |             }); | ||||||
|                 qCritical() << QString("Failed to open JAR file of %1").arg(mod->name()); |             connect(hash_task.get(), &Task::failed, [this, hash_task] { failed("Failed to generate hash"); }); | ||||||
|                 qCritical() << QString("Reason: ") << e.cause(); |             hashing_task.addTask(hash_task); | ||||||
|  |         } else { | ||||||
|                 failed(e.what()); |             hashes.append(hash); | ||||||
|                 return; |             mappings.insert(hash, mod); | ||||||
|             } |  | ||||||
|  |  | ||||||
|             hash = ProviderCaps.hash(ModPlatform::Provider::MODRINTH, &file, best_hash_type); |  | ||||||
|             file.close(); |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         hashes.append(hash); |  | ||||||
|         mappings.insert(hash, mod); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     QEventLoop loop; | ||||||
|  |     connect(&hashing_task, &Task::finished, [&loop]{ loop.quit(); }); | ||||||
|  |     hashing_task.start(); | ||||||
|  |     loop.exec(); | ||||||
|  |  | ||||||
|     auto* response = new QByteArray(); |     auto* response = new QByteArray(); | ||||||
|     auto job = api.latestVersions(hashes, best_hash_type, m_game_versions, m_loaders, response); |     auto job = api.latestVersions(hashes, best_hash_type, m_game_versions, m_loaders, response); | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 flow
					flow