From f2e65dc53de28970b43dc5073d3203fec909721c Mon Sep 17 00:00:00 2001 From: so5iso4ka Date: Tue, 13 Oct 2026 21:24:13 +0300 Subject: [PATCH] fix(screenshots): wait for complete PNG before copying to clipboard Signed-off-by: so5iso4ka --- launcher/CMakeLists.txt | 2 + launcher/PNGUtils.cpp | 58 +++++++++++++++++ launcher/PNGUtils.h | 25 ++++++++ launcher/minecraft/ScreenshotsWatcher.cpp | 75 ++++++++++++++++------ launcher/minecraft/ScreenshotsWatcher.h | 16 ++++- tests/CMakeLists.txt | 3 + tests/PNGUtils_test.cpp | 69 ++++++++++++++++++++ tests/testdata/PNGUtils/kitteh.png | Bin 0 -> 28740 bytes 8 files changed, 226 insertions(+), 22 deletions(-) create mode 100644 launcher/PNGUtils.cpp create mode 100644 launcher/PNGUtils.h create mode 100644 tests/PNGUtils_test.cpp create mode 100644 tests/testdata/PNGUtils/kitteh.png diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index e380f431e..8f0be97de 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -39,6 +39,8 @@ set(CORE_SOURCES QVariantUtils.h RuntimeContext.h PSaveFile.h + PNGUtils.h + PNGUtils.cpp # Basic instance manipulation tasks (derived from InstanceTask) InstanceCreationTask.h diff --git a/launcher/PNGUtils.cpp b/launcher/PNGUtils.cpp new file mode 100644 index 000000000..2a444b446 --- /dev/null +++ b/launcher/PNGUtils.cpp @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Freesm Launcher - Minecraft Launcher + * Copyright (C) 2026 so5iso4ka + * + * This program 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, version 3. + * + * This program 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 this program. If not, see . + */ + +#include +#include + +#include "PNGUtils.h" + +bool PNGUtils::isCompletePNG(QByteArrayView bytes) +{ + using chunkLengthType = quint32; + constexpr qsizetype chunkLengthSize = 4, chunkTypeSize = 4, chunkCRCSize = 4, pngHeaderSize = 8; + constexpr qsizetype overheadSize = chunkLengthSize + chunkTypeSize + chunkCRCSize; + constexpr std::array pngHeader{ '\x89', 'P', 'N', 'G', '\r', '\n', '\x1a', '\n' }; + constexpr std::array iendCRC{ '\xAE', '\x42', '\x60', '\x82' }; + static_assert(sizeof(chunkLengthType) == chunkLengthSize); + + const qsizetype size = bytes.size(); + if (!bytes.startsWith(pngHeader)) { + return false; + } + + qsizetype i = pngHeaderSize; + while (i + chunkLengthSize + chunkTypeSize + chunkCRCSize <= size) { + chunkLengthType len = qFromBigEndian(bytes.sliced(i, chunkLengthSize).constData()); + if (len > size - i - overheadSize) { + return false; + } + + auto typeBytes = bytes.sliced(i + chunkLengthSize, chunkTypeSize); + if ((typeBytes == "IHDR" && i != pngHeaderSize) || (i == pngHeaderSize && typeBytes != "IHDR")) { + return false; + } + if (typeBytes == "IEND" && len == 0) { + auto crcBytes = bytes.sliced(i + chunkLengthSize + chunkTypeSize + len, chunkCRCSize); + return crcBytes == QByteArrayView(iendCRC) && i + overheadSize == size; + } + + i += overheadSize + len; + } + + return false; +} diff --git a/launcher/PNGUtils.h b/launcher/PNGUtils.h new file mode 100644 index 000000000..b5ccc3dc8 --- /dev/null +++ b/launcher/PNGUtils.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Freesm Launcher - Minecraft Launcher + * Copyright (C) 2026 so5iso4ka + * + * This program 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, version 3. + * + * This program 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 this program. If not, see . + */ + +#pragma once + +#include + +namespace PNGUtils { +bool isCompletePNG(QByteArrayView bytes); +} diff --git a/launcher/minecraft/ScreenshotsWatcher.cpp b/launcher/minecraft/ScreenshotsWatcher.cpp index 8bdc55521..2f27646e6 100644 --- a/launcher/minecraft/ScreenshotsWatcher.cpp +++ b/launcher/minecraft/ScreenshotsWatcher.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only /* * Freesm Launcher - Minecraft Launcher - * Copyright (C) 2025 so5iso4ka + * Copyright (C) 2026 so5iso4ka * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,19 +18,22 @@ #include #include +#include +#include #include "Application.h" #include "FileSystem.h" #include "MinecraftInstance.h" +#include "PNGUtils.h" #include "ScreenshotsWatcher.h" ScreenshotsWatcher::ScreenshotsWatcher(const QString& path) : m_watcher(QStringList{ path }) { m_retryTimer.setSingleShot(true); - m_retryTimer.setInterval(200); connect(&m_retryTimer, &QTimer::timeout, this, &ScreenshotsWatcher::tryCopy); connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &ScreenshotsWatcher::dirUpdated); + connect(&m_futureWatcher, &QFutureWatcher::finished, this, &ScreenshotsWatcher::onTryFinished); } void ScreenshotsWatcher::dirUpdated(const QString& path) @@ -47,34 +50,44 @@ void ScreenshotsWatcher::dirUpdated(const QString& path) m_pendingPath = file; m_attempt = 0; - m_lastSize = QFileInfo(m_pendingPath).size(); scheduleRetry(); } void ScreenshotsWatcher::tryCopy() { - const QFileInfo fileInfo(m_pendingPath); - if (!fileInfo.exists()) - return; + m_futureWatcher.setFuture(QtConcurrent::run(QThreadPool::globalInstance(), tryLoadPNG, m_pendingPath)); +} - if (m_lastSize == 0 || m_lastSize != fileInfo.size()) { - m_lastSize = fileInfo.size(); - scheduleRetry(); +void ScreenshotsWatcher::onTryFinished() +{ + auto future = m_futureWatcher.future(); + if (!future.isValid()) { return; } - m_lastSize = fileInfo.size(); - QImage img; - bool loaded = img.load(m_pendingPath); + auto [status, img, path] = future.result(); - if (!loaded) { - scheduleRetry(); + if (path != m_pendingPath) { return; } - const auto clipboard = QApplication::clipboard(); - if (clipboard == nullptr) { + switch (status) { + case LoadResult::Status::Ok: { + break; + } + case LoadResult::Status::Retry: { + scheduleRetry(); + return; + } + case LoadResult::Status::Failed: { + qDebug() << "Unable to load the image:" << path; + return; + } + } + + auto clipboard = QApplication::clipboard(); + if (!clipboard) { qDebug() << "Clipboard is null"; return; } @@ -85,10 +98,34 @@ void ScreenshotsWatcher::tryCopy() void ScreenshotsWatcher::scheduleRetry() { ++m_attempt; - if (m_attempt > 5) { - qDebug() << "Image wasn't loaded after 5 retries"; + if (m_attempt > 12) { // 100 + 200 + 400 + 800 + 1000 * 8 = 9500ms + qDebug() << "Image wasn't loaded in time: " << m_pendingPath; return; } - m_retryTimer.start(); + m_retryTimer.start(std::min(100 << (m_attempt - 1), 1000)); +} + +ScreenshotsWatcher::LoadResult ScreenshotsWatcher::tryLoadPNG(QString path) +{ + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) { + return { LoadResult::Status::Retry, {}, path }; + } + + if (file.size() > qint64{ 512 * 1024 * 1024 }) { + return { LoadResult::Status::Failed, {}, path }; + } + + auto data = file.readAll(); + if (!PNGUtils::isCompletePNG(data)) { + return { LoadResult::Status::Retry, {}, path }; + } + + QImage img; + if (!img.loadFromData(data, "PNG")) { + return { LoadResult::Status::Failed, {}, path }; + } + + return { LoadResult::Status::Ok, img, path }; } diff --git a/launcher/minecraft/ScreenshotsWatcher.h b/launcher/minecraft/ScreenshotsWatcher.h index 523629ea3..c6049a9d0 100644 --- a/launcher/minecraft/ScreenshotsWatcher.h +++ b/launcher/minecraft/ScreenshotsWatcher.h @@ -19,10 +19,11 @@ #pragma once #include +#include +#include #include #include #include -#include class ScreenshotsWatcher : public QObject { Q_OBJECT @@ -32,13 +33,22 @@ class ScreenshotsWatcher : public QObject { private slots: void dirUpdated(const QString& path); void tryCopy(); + void onTryFinished(); private: void scheduleRetry(); + struct LoadResult { + enum class Status { Ok, Retry, Failed }; + Status status{ Status::Failed }; + QImage image; + QString path; + }; + static LoadResult tryLoadPNG(QString path); + QFileSystemWatcher m_watcher; + QFutureWatcher m_futureWatcher; QString m_pendingPath; - qsizetype m_attempt{}; - qsizetype m_lastSize{}; + int m_attempt{}; QTimer m_retryTimer; }; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index bdfc11442..300fea2aa 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -66,3 +66,6 @@ ecm_add_test(XmlLogs_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR ecm_add_test(ConfigMigration_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test TEST_NAME ConfigMigration) + +ecm_add_test(PNGUtils_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test + TEST_NAME PNGUtils) \ No newline at end of file diff --git a/tests/PNGUtils_test.cpp b/tests/PNGUtils_test.cpp new file mode 100644 index 000000000..bdae08b8e --- /dev/null +++ b/tests/PNGUtils_test.cpp @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Freesm Launcher - Minecraft Launcher + * Copyright (C) 2026 so5iso4ka + * + * This program 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, version 3. + * + * This program 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 this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#include "PNGUtils.h" + +class PNGUtilsTest : public QObject { + Q_OBJECT + + private slots: + void test_Trivial() + { + QVERIFY(!PNGUtils::isCompletePNG(QByteArrayView{})); + QVERIFY(!PNGUtils::isCompletePNG(QByteArrayView{ "123" })); + QVERIFY(!PNGUtils::isCompletePNG(QByteArrayView{ "1234567890abcdef" })); + } + + void test_File() + { + QFile png(QFINDTESTDATA("testdata/PNGUtils/kitteh.png")); + QVERIFY(png.open(QIODevice::ReadOnly)); + + const QByteArray data = png.readAll(); + const qsizetype size = data.size(); + + QVERIFY(PNGUtils::isCompletePNG(data)); + QVERIFY(!PNGUtils::isCompletePNG(data.first(size - 1))); + QVERIFY(!PNGUtils::isCompletePNG(data.last(size - 1))); + QVERIFY(!PNGUtils::isCompletePNG(data + '\0')); + QVERIFY(!PNGUtils::isCompletePNG('\0' + data)); + QVERIFY(!PNGUtils::isCompletePNG(data.repeated(2))); + + auto badCRC = data; + auto badIHDR = data; + badCRC.back() ^= 1; + badIHDR.replace(12, 4, "IDAT"); + + QVERIFY(!PNGUtils::isCompletePNG(badCRC)); + QVERIFY(!PNGUtils::isCompletePNG(badIHDR)); + + for (qsizetype i = 0; i < size - 1; ++i) { + QVERIFY(!PNGUtils::isCompletePNG(QByteArrayView{ data }.first(i))); + } + } +}; + +QTEST_GUILESS_MAIN(PNGUtilsTest) + +#include "PNGUtils_test.moc" diff --git a/tests/testdata/PNGUtils/kitteh.png b/tests/testdata/PNGUtils/kitteh.png new file mode 100644 index 0000000000000000000000000000000000000000..e6ede241eef6fc02be9beb351c759ee244bd86a9 GIT binary patch literal 28740 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 epUn{ct27>vHoMTekENdg0000