diff --git a/loader/include/Geode/utils/file.hpp b/loader/include/Geode/utils/file.hpp index cb77f689..2976c730 100644 --- a/loader/include/Geode/utils/file.hpp +++ b/loader/include/Geode/utils/file.hpp @@ -154,6 +154,16 @@ namespace geode::utils::file { */ static Result create(ByteVector const& data); + /** + * Set a callback to be called with the progress of the unzip operation, first + * argument is the current entry, second argument is the total entries + * @note This is not thread-safe + * @param callback Callback to call with the progress of the unzip operation + */ + void setProgressCallback( + utils::MiniFunction callback + ); + /** * Path to the opened zip * @returns The path to the zip that is being read, or an empty path @@ -200,6 +210,13 @@ namespace geode::utils::file { Path const& to, bool deleteZipAfter = false ); + + static Result<> intoDir( + utils::MiniFunction progressCallback, + Path const& from, + Path const& to, + bool deleteZipAfter = false + ); }; /** diff --git a/loader/src/loader/Index.cpp b/loader/src/loader/Index.cpp index 8998ed6e..ee9b6058 100644 --- a/loader/src/loader/Index.cpp +++ b/loader/src/loader/Index.cpp @@ -337,9 +337,20 @@ void Index::Impl::downloadIndex(std::string commitHash) { // unzip new index log::debug("Unzipping index"); - IndexUpdateEvent(UpdateProgress(100, "Unzipping index")).post(); - auto unzip = file::Unzip::intoDir(targetFile, targetDir, true) - .expect("Unable to unzip new index"); + uint32_t nextPercent = 1; + auto unzip = file::Unzip::intoDir( + [&](uint32_t current, uint32_t total) { + if (total == 0) return; + + if (static_cast(current) / total * 100 >= nextPercent) { + Loader::get()->queueInMainThread([nextPercent]() { + IndexUpdateEvent(UpdateProgress(nextPercent, "Extracting")).post(); + }); + nextPercent++; + } + }, + targetFile, targetDir, true + ).expect("Unable to unzip new index"); if (!unzip) { auto const err = unzip.unwrapErr(); log::error("Failed to unzip latest index: {}", err); diff --git a/loader/src/ui/internal/list/ModListLayer.cpp b/loader/src/ui/internal/list/ModListLayer.cpp index e4df8f26..28c2ba80 100644 --- a/loader/src/ui/internal/list/ModListLayer.cpp +++ b/loader/src/ui/internal/list/ModListLayer.cpp @@ -628,7 +628,7 @@ void ModListLayer::onIndexUpdate(IndexUpdateEvent* event) { [&](UpdateProgress const& prog) { auto msg = prog.second; // amazing - if (prog.second == "Downloading") { + if (prog.second == "Downloading" || prog.second == "Extracting") { msg += fmt::format(" {}%", prog.first); } m_listLabel->setString((msg + "...").c_str()); diff --git a/loader/src/utils/file.cpp b/loader/src/utils/file.cpp index 1d93a60c..67c1ebd5 100644 --- a/loader/src/utils/file.cpp +++ b/loader/src/utils/file.cpp @@ -170,6 +170,7 @@ private: int32_t m_mode; std::variant m_srcDest; std::unordered_map m_entries; + utils::MiniFunction m_progressCallback; Result<> init() { // open stream from file @@ -281,6 +282,10 @@ public: return Ok(std::move(ret)); } + void setProgressCallback(utils::MiniFunction callback) { + m_progressCallback = callback; + } + Result<> extractAt(Path const& dir, Path const& name) { auto entry = m_entries.at(name); @@ -318,12 +323,21 @@ public: .expect("Unable to navigate to first entry (code {error})") ); + uint64_t numEntries; + + GEODE_UNWRAP( + mzTry(mz_zip_get_number_entry(m_handle, &numEntries)) + .expect("Unable to get number of entries (code {error})") + ); + + uint32_t currentEntry = 0; // while not at MZ_END_OF_LIST do { mz_zip_file* info = nullptr; if (mz_zip_entry_get_info(m_handle, &info) != MZ_OK) { return Err("Unable to get entry info"); } + currentEntry++; Path filePath; filePath.assign(info->filename, info->filename + info->filename_size); @@ -341,6 +355,7 @@ public: else { GEODE_UNWRAP(this->extractAt(dir, filePath)); } + m_progressCallback(currentEntry, numEntries); } else { log::error( @@ -505,6 +520,12 @@ Unzip::Path Unzip::getPath() const { return m_impl->getPath(); } +void Unzip::setProgressCallback( + utils::MiniFunction callback +) { + return m_impl->setProgressCallback(callback); +} + std::vector Unzip::getEntries() const { return map::keys(m_impl->getEntries()); } @@ -550,6 +571,22 @@ Result<> Unzip::intoDir( return Ok(); } +Result<> Unzip::intoDir( + utils::MiniFunction progressCallback, + Path const& from, + Path const& to, + bool deleteZipAfter +) { + GEODE_UNWRAP_INTO(auto unzip, Unzip::create(from)); + unzip.setProgressCallback(progressCallback); + GEODE_UNWRAP(unzip.extractAllTo(to)); + if (deleteZipAfter) { + std::error_code ec; + ghc::filesystem::remove(from, ec); + } + return Ok(); +} + // Zip Zip::Zip() : m_impl(nullptr) {}