From 386600baf96591e662a068bf3a9558f09d6904ec Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 22 Mar 2019 13:13:39 +0400 Subject: [PATCH] Save and load sticker set thumbnails. --- .../SourceFiles/boxes/sticker_set_box.cpp | 78 ++--- .../SourceFiles/chat_helpers/stickers.cpp | 105 +++++-- Telegram/SourceFiles/chat_helpers/stickers.h | 8 +- Telegram/SourceFiles/mainwidget.cpp | 98 +------ Telegram/SourceFiles/mtproto/type_utils.h | 3 - Telegram/SourceFiles/storage/localstorage.cpp | 277 ++++++++++-------- 6 files changed, 287 insertions(+), 282 deletions(-) diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.cpp b/Telegram/SourceFiles/boxes/sticker_set_box.cpp index cf971281c..ff0040f62 100644 --- a/Telegram/SourceFiles/boxes/sticker_set_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_set_box.cpp @@ -84,6 +84,7 @@ private: int32 _setHash = 0; MTPDstickerSet::Flags _setFlags = 0; TimeId _setInstallDate = TimeId(0); + ImagePtr _setThumbnail; MTPInputStickerSet _input; @@ -195,9 +196,8 @@ void StickerSetBox::Inner::gotSet(const MTPmessages_StickerSet &set) { _packOvers.clear(); _selected = -1; setCursor(style::cur_default); - if (set.type() == mtpc_messages_stickerSet) { - auto &d = set.c_messages_stickerSet(); - auto &v = d.vdocuments.v; + set.match([&](const MTPDmessages_stickerSet &data) { + const auto &v = data.vdocuments.v; _pack.reserve(v.size()); _packOvers.reserve(v.size()); for (const auto &item : v) { @@ -205,51 +205,58 @@ void StickerSetBox::Inner::gotSet(const MTPmessages_StickerSet &set) { if (!document->sticker()) continue; _pack.push_back(document); - _packOvers.push_back(Animation()); + _packOvers.emplace_back(); } - auto &packs = d.vpacks.v; - for (auto i = 0, l = packs.size(); i != l; ++i) { - if (packs.at(i).type() != mtpc_stickerPack) continue; - auto &pack = packs.at(i).c_stickerPack(); - if (auto emoji = Ui::Emoji::Find(qs(pack.vemoticon))) { - emoji = emoji->original(); - auto &stickers = pack.vdocuments.v; + for (const auto &pack : data.vpacks.v) { + pack.match([&](const MTPDstickerPack &pack) { + if (const auto emoji = Ui::Emoji::Find(qs(pack.vemoticon))) { + const auto original = emoji->original(); + auto &stickers = pack.vdocuments.v; - Stickers::Pack p; - p.reserve(stickers.size()); - for (auto j = 0, c = stickers.size(); j != c; ++j) { - auto doc = Auth().data().document(stickers[j].v); - if (!doc || !doc->sticker()) continue; + auto p = Stickers::Pack(); + p.reserve(stickers.size()); + for (auto j = 0, c = stickers.size(); j != c; ++j) { + auto doc = Auth().data().document(stickers[j].v); + if (!doc || !doc->sticker()) continue; - p.push_back(doc); + p.push_back(doc); + } + _emoji.insert(original, p); } - _emoji.insert(emoji, p); - } + }); } - if (d.vset.type() == mtpc_stickerSet) { - auto &s = d.vset.c_stickerSet(); - _setTitle = Stickers::GetSetTitle(s); - _setShortName = qs(s.vshort_name); - _setId = s.vid.v; - _setAccess = s.vaccess_hash.v; - _setCount = s.vcount.v; - _setHash = s.vhash.v; - _setFlags = s.vflags.v; - _setInstallDate = s.has_installed_date() - ? s.vinstalled_date.v + data.vset.match([&](const MTPDstickerSet & set) { + _setTitle = Stickers::GetSetTitle(set); + _setShortName = qs(set.vshort_name); + _setId = set.vid.v; + _setAccess = set.vaccess_hash.v; + _setCount = set.vcount.v; + _setHash = set.vhash.v; + _setFlags = set.vflags.v; + _setInstallDate = set.has_installed_date() + ? set.vinstalled_date.v : TimeId(0); + _setThumbnail = set.has_thumb() + ? App::image(set.vthumb) + : ImagePtr(); auto &sets = Auth().data().stickerSetsRef(); - auto it = sets.find(_setId); + const auto it = sets.find(_setId); if (it != sets.cend()) { - auto clientFlags = it->flags & (MTPDstickerSet_ClientFlag::f_featured | MTPDstickerSet_ClientFlag::f_not_loaded | MTPDstickerSet_ClientFlag::f_unread | MTPDstickerSet_ClientFlag::f_special); + using ClientFlag = MTPDstickerSet_ClientFlag; + const auto clientFlags = it->flags + & (ClientFlag::f_featured + | ClientFlag::f_not_loaded + | ClientFlag::f_unread + | ClientFlag::f_special); _setFlags |= clientFlags; it->flags = _setFlags; it->installDate = _setInstallDate; it->stickers = _pack; it->emoji = _emoji; + it->thumbnail = _setThumbnail; } - } - } + }); + }); if (_pack.isEmpty()) { Ui::show(Box(lang(lng_stickers_not_found))); @@ -307,7 +314,8 @@ void StickerSetBox::Inner::installDone(const MTPmessages_StickerSetInstallResult _setCount, _setHash, _setFlags, - _setInstallDate)); + _setInstallDate, + _setThumbnail)); } else { it->flags = _setFlags; it->installDate = _setInstallDate; diff --git a/Telegram/SourceFiles/chat_helpers/stickers.cpp b/Telegram/SourceFiles/chat_helpers/stickers.cpp index b53d30c6d..1be20e17b 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers.cpp @@ -253,7 +253,8 @@ void SetIsFaved(not_null document, std::optionalstickers.indexOf(document); if (index == 0) { @@ -467,7 +468,8 @@ void SpecialSetReceived( 0, // count 0, // hash MTPDstickerSet_ClientFlag::f_special | 0, - TimeId(0))); + TimeId(0), + ImagePtr())); } else { it->title = setTitle; } @@ -545,19 +547,28 @@ void SpecialSetReceived( Auth().data().notifyStickersUpdated(); } -void FeaturedSetsReceived(const QVector &data, const QVector &unread, int32 hash) { - OrderedSet unreadMap; - for_const (auto &unreadSetId, unread) { - unreadMap.insert(unreadSetId.v); - } +void FeaturedSetsReceived( + const QVector &data, + const QVector &unread, + int32 hash) { + auto &&unreadIds = ranges::view::all( + unread + ) | ranges::view::transform([](const MTPlong &id) { + return id.v; + }); + const auto unreadMap = base::flat_set{ + unreadIds.begin(), + unreadIds.end() + }; auto &setsOrder = Auth().data().featuredStickerSetsOrderRef(); setsOrder.clear(); auto &sets = Auth().data().stickerSetsRef(); - QMap setsToRequest; + auto setsToRequest = base::flat_map(); for (auto &set : sets) { - set.flags &= ~MTPDstickerSet_ClientFlag::f_featured; // mark for removing + // Mark for removing. + set.flags &= ~MTPDstickerSet_ClientFlag::f_featured; } for (int i = 0, l = data.size(); i != l; ++i) { auto &setData = data[i]; @@ -583,6 +594,9 @@ void FeaturedSetsReceived(const QVector &data, const QVect const auto installDate = set->has_installed_date() ? set->vinstalled_date.v : TimeId(0); + const auto thumbnail = set->has_thumb() + ? App::image(set->vthumb) + : ImagePtr(); if (it == sets.cend()) { auto setClientFlags = MTPDstickerSet_ClientFlag::f_featured | MTPDstickerSet_ClientFlag::f_not_loaded; @@ -597,7 +611,8 @@ void FeaturedSetsReceived(const QVector &data, const QVect set->vcount.v, set->vhash.v, set->vflags.v | setClientFlags, - installDate)); + installDate, + thumbnail)); } else { it->access = set->vaccess_hash.v; it->title = title; @@ -606,6 +621,7 @@ void FeaturedSetsReceived(const QVector &data, const QVect it->flags = set->vflags.v | clientFlags; it->flags |= MTPDstickerSet_ClientFlag::f_featured; it->installDate = installDate; + it->thumbnail = thumbnail; if (unreadMap.contains(it->id)) { it->flags |= MTPDstickerSet_ClientFlag::f_unread; } else { @@ -619,7 +635,7 @@ void FeaturedSetsReceived(const QVector &data, const QVect } setsOrder.push_back(set->vid.v); if (it->stickers.isEmpty() || (it->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) { - setsToRequest.insert(set->vid.v, set->vaccess_hash.v); + setsToRequest.emplace(set->vid.v, set->vaccess_hash.v); } } } @@ -645,10 +661,10 @@ void FeaturedSetsReceived(const QVector &data, const QVect LOG(("API Error: received featured stickers hash %1 while counted hash is %2").arg(hash).arg(Local::countFeaturedStickersHash())); } - if (!setsToRequest.isEmpty()) { + if (!setsToRequest.empty()) { auto &api = Auth().api(); - for (auto i = setsToRequest.cbegin(), e = setsToRequest.cend(); i != e; ++i) { - api.scheduleStickerSetRequest(i.key(), i.value()); + for (const auto [setId, accessHash] : setsToRequest) { + api.scheduleStickerSetRequest(setId, accessHash); } api.requestStickerSets(); } @@ -843,7 +859,7 @@ std::optional>> GetEmojiListFromSet( auto result = std::vector>(); for (auto i = it->emoji.cbegin(), e = it->emoji.cend(); i != e; ++i) { if (i->contains(document)) { - result.push_back(i.key()); + result.emplace_back(i.key()); } } if (result.empty()) { @@ -868,7 +884,8 @@ Set *FeedSet(const MTPDstickerSet &set) { set.vcount.v, set.vhash.v, set.vflags.v | MTPDstickerSet_ClientFlag::f_not_loaded, - set.has_installed_date() ? set.vinstalled_date.v : TimeId(0))); + set.has_installed_date() ? set.vinstalled_date.v : TimeId(0), + set.has_thumb() ? App::image(set.vthumb) : ImagePtr())); } else { it->access = set.vaccess_hash.v; it->title = title; @@ -881,8 +898,11 @@ Set *FeedSet(const MTPDstickerSet &set) { | MTPDstickerSet_ClientFlag::f_special); it->flags = set.vflags.v | clientFlags; it->installDate = set.has_installed_date() - ? set.vinstalled_date.v + ? (set.vinstalled_date.v ? set.vinstalled_date.v : unixtime()) : TimeId(0); + it->thumbnail = set.has_thumb() + ? App::image(set.vthumb) + : ImagePtr(); if (it->count != set.vcount.v || it->hash != set.vhash.v || it->emoji.isEmpty()) { @@ -908,14 +928,23 @@ Set *FeedSet(const MTPDstickerSet &set) { Set *FeedSetFull(const MTPmessages_StickerSet &data) { Expects(data.type() == mtpc_messages_stickerSet); Expects(data.c_messages_stickerSet().vset.type() == mtpc_stickerSet); - auto &d = data.c_messages_stickerSet(); - auto set = FeedSet(d.vset.c_stickerSet()); + + const auto &d = data.c_messages_stickerSet(); + const auto &s = d.vset.c_stickerSet(); + + auto &sets = Auth().data().stickerSetsRef(); + auto it = sets.find(s.vid.v); + const auto wasArchived = (it->flags & MTPDstickerSet::Flag::f_archived); + + auto set = FeedSet(s); set->flags &= ~MTPDstickerSet_ClientFlag::f_not_loaded; - auto &sets = Auth().data().stickerSetsRef(); auto &d_docs = d.vdocuments.v; auto custom = sets.find(Stickers::CustomSetId); + auto inputSet = MTP_inputStickerSetID( + MTP_long(set->id), + MTP_long(set->access)); auto pack = Pack(); pack.reserve(d_docs.size()); @@ -924,6 +953,9 @@ Set *FeedSetFull(const MTPmessages_StickerSet &data) { if (!document->sticker()) continue; pack.push_back(document); + if (document->sticker()->set.type() != mtpc_inputStickerSetID) { + document->sticker()->set = inputSet; + } if (custom != sets.cend()) { const auto index = custom->stickers.indexOf(document); if (index >= 0) { @@ -982,14 +1014,18 @@ Set *FeedSetFull(const MTPmessages_StickerSet &data) { } if (set) { + const auto isArchived = (set->flags & MTPDstickerSet::Flag::f_archived); if (set->flags & MTPDstickerSet::Flag::f_installed_date) { - if (!(set->flags & MTPDstickerSet::Flag::f_archived)) { + if (!isArchived) { Local::writeInstalledStickers(); } } if (set->flags & MTPDstickerSet_ClientFlag::f_featured) { Local::writeFeaturedStickers(); } + if (wasArchived != isArchived) { + Local::writeArchivedStickers(); + } } Auth().data().notifyStickersUpdated(); @@ -997,6 +1033,33 @@ Set *FeedSetFull(const MTPmessages_StickerSet &data) { return set; } +void NewSetReceived(const MTPmessages_StickerSet &data) { + bool writeArchived = false; + const auto &set = data.c_messages_stickerSet(); + const auto &s = set.vset.c_stickerSet(); + if (!s.has_installed_date()) { + LOG(("API Error: " + "updateNewStickerSet without install_date flag.")); + return; + } else if (s.is_archived()) { + LOG(("API Error: " + "updateNewStickerSet with archived flag.")); + return; + } else if (s.is_masks()) { + return; + } + auto &order = Auth().data().stickerSetsOrderRef(); + int32 insertAtIndex = 0, currentIndex = order.indexOf(s.vid.v); + if (currentIndex != insertAtIndex) { + if (currentIndex > 0) { + order.removeAt(currentIndex); + } + order.insert(insertAtIndex, s.vid.v); + } + + FeedSetFull(data); +} + QString GetSetTitle(const MTPDstickerSet &s) { auto title = qs(s.vtitle); if ((s.vflags.v & MTPDstickerSet::Flag::f_official) && !title.compare(qstr("Great Minds"), Qt::CaseInsensitive)) { diff --git a/Telegram/SourceFiles/chat_helpers/stickers.h b/Telegram/SourceFiles/chat_helpers/stickers.h index e5820e3d8..88f67fce1 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers.h +++ b/Telegram/SourceFiles/chat_helpers/stickers.h @@ -34,7 +34,8 @@ struct Set { int count, int32 hash, MTPDstickerSet::Flags flags, - TimeId installDate) + TimeId installDate, + ImagePtr thumbnail) : id(id) , access(access) , title(title) @@ -42,7 +43,8 @@ struct Set { , count(count) , hash(hash) , flags(flags) - , installDate(installDate) { + , installDate(installDate) + , thumbnail(thumbnail) { } uint64 id = 0; uint64 access = 0; @@ -51,6 +53,7 @@ struct Set { int32 hash = 0; MTPDstickerSet::Flags flags; TimeId installDate = 0; + ImagePtr thumbnail; Pack stickers; std::vector dates; Pack covers; @@ -94,6 +97,7 @@ std::optional>> GetEmojiListFromSet( Set *FeedSet(const MTPDstickerSet &data); Set *FeedSetFull(const MTPmessages_StickerSet &data); +void NewSetReceived(const MTPmessages_StickerSet &data); QString GetSetTitle(const MTPDstickerSet &s); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 93a95b4c1..edc4600ca 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -3382,7 +3382,8 @@ void MainWidget::incrementSticker(DocumentData *sticker) { 0, // count 0, // hash MTPDstickerSet_ClientFlag::f_special | 0, - TimeId(0))); + TimeId(0), + ImagePtr())); } else { it->title = lang(lng_recent_stickers); } @@ -4435,99 +4436,8 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { ////// Cloud sticker sets case mtpc_updateNewStickerSet: { - auto &d = update.c_updateNewStickerSet(); - bool writeArchived = false; - if (d.vstickerset.type() == mtpc_messages_stickerSet) { - auto &set = d.vstickerset.c_messages_stickerSet(); - if (set.vset.type() == mtpc_stickerSet) { - auto &s = set.vset.c_stickerSet(); - if (!s.has_installed_date()) { - LOG(("API Error: " - "updateNewStickerSet without install_date flag.")); - } - if (!s.is_masks()) { - auto &sets = session().data().stickerSetsRef(); - auto it = sets.find(s.vid.v); - if (it == sets.cend()) { - it = sets.insert(s.vid.v, Stickers::Set( - s.vid.v, - s.vaccess_hash.v, - Stickers::GetSetTitle(s), - qs(s.vshort_name), - s.vcount.v, - s.vhash.v, - s.vflags.v | MTPDstickerSet::Flag::f_installed_date, - s.has_installed_date() ? s.vinstalled_date.v : unixtime())); - } else { - it->flags |= MTPDstickerSet::Flag::f_installed_date; - if (!it->installDate) { - it->installDate = unixtime(); - } - if (it->flags & MTPDstickerSet::Flag::f_archived) { - it->flags &= ~MTPDstickerSet::Flag::f_archived; - writeArchived = true; - } - } - auto inputSet = MTP_inputStickerSetID(MTP_long(it->id), MTP_long(it->access)); - auto &v = set.vdocuments.v; - it->stickers.clear(); - it->stickers.reserve(v.size()); - for (const auto &item : v) { - const auto document = session().data().processDocument( - item); - if (!document->sticker()) continue; - - it->stickers.push_back(document); - if (document->sticker()->set.type() != mtpc_inputStickerSetID) { - document->sticker()->set = inputSet; - } - } - it->emoji.clear(); - auto &packs = set.vpacks.v; - for (auto i = 0, l = packs.size(); i != l; ++i) { - if (packs[i].type() != mtpc_stickerPack) continue; - auto &pack = packs.at(i).c_stickerPack(); - if (auto emoji = Ui::Emoji::Find(qs(pack.vemoticon))) { - emoji = emoji->original(); - auto &stickers = pack.vdocuments.v; - - Stickers::Pack p; - p.reserve(stickers.size()); - for (auto j = 0, c = stickers.size(); j != c; ++j) { - auto doc = session().data().document(stickers[j].v); - if (!doc->sticker()) continue; - - p.push_back(doc); - } - it->emoji.insert(emoji, p); - } - } - - auto &order = session().data().stickerSetsOrderRef(); - int32 insertAtIndex = 0, currentIndex = order.indexOf(s.vid.v); - if (currentIndex != insertAtIndex) { - if (currentIndex > 0) { - order.removeAt(currentIndex); - } - order.insert(insertAtIndex, s.vid.v); - } - - auto custom = sets.find(Stickers::CustomSetId); - if (custom != sets.cend()) { - for (int32 i = 0, l = it->stickers.size(); i < l; ++i) { - int32 removeIndex = custom->stickers.indexOf(it->stickers.at(i)); - if (removeIndex >= 0) custom->stickers.removeAt(removeIndex); - } - if (custom->stickers.isEmpty()) { - sets.erase(custom); - } - } - Local::writeInstalledStickers(); - if (writeArchived) Local::writeArchivedStickers(); - session().data().notifyStickersUpdated(); - } - } - } + const auto &d = update.c_updateNewStickerSet(); + Stickers::NewSetReceived(d.vstickerset); } break; case mtpc_updateStickerSetsOrder: { diff --git a/Telegram/SourceFiles/mtproto/type_utils.h b/Telegram/SourceFiles/mtproto/type_utils.h index 10ee31ad6..74c9ad790 100644 --- a/Telegram/SourceFiles/mtproto/type_utils.h +++ b/Telegram/SourceFiles/mtproto/type_utils.h @@ -95,9 +95,6 @@ enum class MTPDreplyKeyboardMarkup_ClientFlag : uint32 { DEFINE_MTP_CLIENT_FLAGS(MTPDreplyKeyboardMarkup) enum class MTPDstickerSet_ClientFlag : uint32 { - // old value for sticker set is not yet loaded flag - f_not_loaded__old = (1U << 31), - // sticker set is not yet loaded f_not_loaded = (1U << 30), diff --git a/Telegram/SourceFiles/storage/localstorage.cpp b/Telegram/SourceFiles/storage/localstorage.cpp index 7f2657283..ad99a964c 100644 --- a/Telegram/SourceFiles/storage/localstorage.cpp +++ b/Telegram/SourceFiles/storage/localstorage.cpp @@ -60,6 +60,10 @@ constexpr auto kSinglePeerTypeChannel = qint32(3); constexpr auto kSinglePeerTypeSelf = qint32(4); constexpr auto kSinglePeerTypeEmpty = qint32(0); +constexpr auto kStickersVersionTag = quint32(-1); +constexpr auto kStickersSerializeVersion = 1; +constexpr auto kMaxSavedStickerSetsCount = 1000; + using Database = Storage::Cache::Database; using FileKey = quint64; @@ -3339,55 +3343,43 @@ void cancelTask(TaskId id) { } void _writeStickerSet(QDataStream &stream, const Stickers::Set &set) { - bool notLoaded = (set.flags & MTPDstickerSet_ClientFlag::f_not_loaded); - if (notLoaded) { + const auto writeInfo = [&](int count) { stream << quint64(set.id) << quint64(set.access) << set.title << set.shortName - << qint32(-set.count) + << qint32(count) << qint32(set.hash) - << qint32(set.flags); - if (AppVersion > 1002008) { - stream << qint32(set.installDate); - } + << qint32(set.flags) + << qint32(set.installDate); + Serialize::writeStorageImageLocation( + stream, + set.thumbnail ? set.thumbnail->location() : StorageImageLocation()); + }; + if (set.flags & MTPDstickerSet_ClientFlag::f_not_loaded) { + writeInfo(-set.count); + return; + } else if (set.stickers.isEmpty()) { return; - } else { - if (set.stickers.isEmpty()) return; } - stream - << quint64(set.id) - << quint64(set.access) - << set.title - << set.shortName - << qint32(set.stickers.size()) - << qint32(set.hash) - << qint32(set.flags); - if (AppVersion > 1002008) { - stream << qint32(set.installDate); + writeInfo(set.stickers.size()); + for (const auto &sticker : set.stickers) { + Serialize::Document::writeToStream(stream, sticker); } - for (auto j = set.stickers.cbegin(), e = set.stickers.cend(); j != e; ++j) { - Serialize::Document::writeToStream(stream, *j); - } - if (AppVersion > 1002008) { - stream << qint32(set.dates.size()); - if (!set.dates.empty()) { - Assert(set.dates.size() == set.stickers.size()); - for (const auto date : set.dates) { - stream << qint32(date); - } + stream << qint32(set.dates.size()); + if (!set.dates.empty()) { + Assert(set.dates.size() == set.stickers.size()); + for (const auto date : set.dates) { + stream << qint32(date); } } - - if (AppVersion > 9018) { - stream << qint32(set.emoji.size()); - for (auto j = set.emoji.cbegin(), e = set.emoji.cend(); j != e; ++j) { - stream << j.key()->id() << qint32(j->size()); - for (int32 k = 0, l = j->size(); k < l; ++k) { - stream << quint64(j->at(k)->id); - } + stream << qint32(set.emoji.size()); + for (auto j = set.emoji.cbegin(), e = set.emoji.cend(); j != e; ++j) { + stream << j.key()->id() << qint32(j->size()); + for (const auto sticker : *j) { + stream << quint64(sticker->id); } } } @@ -3415,9 +3407,11 @@ void _writeStickerSets(FileKey &stickersKey, CheckSet checkSet, const Stickers:: _writeMap(); return; } + + // versionTag + version + count + quint32 size = sizeof(quint32) + sizeof(qint32) + sizeof(qint32); + int32 setsCount = 0; - QByteArray hashToWrite; - quint32 size = sizeof(quint32) + Serialize::bytearraySize(hashToWrite); for (const auto &set : sets) { auto result = checkSet(set); if (result == StickerSetCheckResult::Abort) { @@ -3427,12 +3421,23 @@ void _writeStickerSets(FileKey &stickersKey, CheckSet checkSet, const Stickers:: } // id + access + title + shortName + stickersCount + hash + flags + installDate - size += sizeof(quint64) * 2 + Serialize::stringSize(set.title) + Serialize::stringSize(set.shortName) + sizeof(quint32) + sizeof(qint32) * 3; - for (const auto sticker : std::as_const(set.stickers)) { + size += sizeof(quint64) * 2 + + Serialize::stringSize(set.title) + + Serialize::stringSize(set.shortName) + + sizeof(qint32) * 4 + + Serialize::storageImageLocationSize(set.thumbnail + ? set.thumbnail->location() + : StorageImageLocation()); + if (set.flags & MTPDstickerSet_ClientFlag::f_not_loaded) { + continue; + } + + for (const auto sticker : set.stickers) { sticker->refreshStickerThumbFileReference(); size += Serialize::Document::sizeInStream(sticker); } - size += sizeof(qint32); // dates count + + size += sizeof(qint32); // datesCount if (!set.dates.empty()) { Assert(set.stickers.size() == set.dates.size()); size += set.dates.size() * sizeof(qint32); @@ -3462,8 +3467,11 @@ void _writeStickerSets(FileKey &stickersKey, CheckSet checkSet, const Stickers:: _writeMap(WriteMapWhen::Fast); } EncryptedDescriptor data(size); - data.stream << quint32(setsCount) << hashToWrite; - for_const (auto &set, sets) { + data.stream + << quint32(kStickersVersionTag) + << qint32(kStickersSerializeVersion) + << qint32(setsCount); + for (const auto &set : sets) { auto result = checkSet(set); if (result == StickerSetCheckResult::Abort) { return; @@ -3487,54 +3495,60 @@ void _readStickerSets(FileKey &stickersKey, Stickers::Order *outOrder = nullptr, return; } - bool readingInstalled = (readingFlags == MTPDstickerSet::Flag::f_installed_date); + const auto failed = [&] { + clearKey(stickersKey); + stickersKey = 0; + }; auto &sets = Auth().data().stickerSetsRef(); if (outOrder) outOrder->clear(); - quint32 cnt; - QByteArray hash; - stickers.stream >> cnt >> hash; // ignore hash, it is counted - if (readingInstalled && stickers.version < 8019) { // bad data in old caches - cnt += 2; // try to read at least something + quint32 versionTag = 0; + qint32 version = 0; + stickers.stream >> versionTag >> version; + if (versionTag != kStickersVersionTag + || version <= 0 + || version > kStickersSerializeVersion) { + // Old data, without sticker set thumbnails. + return failed(); } - for (uint32 i = 0; i < cnt; ++i) { + qint32 count = 0; + stickers.stream >> count; + if (!_checkStreamStatus(stickers.stream) + || (count < 0) + || (count > kMaxSavedStickerSetsCount)) { + return failed(); + } + for (auto i = 0; i != count; ++i) { quint64 setId = 0, setAccess = 0; QString setTitle, setShortName; qint32 scnt = 0; - auto setInstallDate = qint32(0); + qint32 setInstallDate = 0; + qint32 setHash = 0; + MTPDstickerSet::Flags setFlags = 0; + qint32 setFlagsValue = 0; + StorageImageLocation setThumbnail; stickers.stream >> setId >> setAccess >> setTitle >> setShortName - >> scnt; - - qint32 setHash = 0; - MTPDstickerSet::Flags setFlags = 0; - if (stickers.version > 8033) { - qint32 setFlagsValue = 0; - stickers.stream >> setHash >> setFlagsValue; - setFlags = MTPDstickerSet::Flags::from_raw(setFlagsValue); - if (setFlags & MTPDstickerSet_ClientFlag::f_not_loaded__old) { - setFlags &= ~MTPDstickerSet_ClientFlag::f_not_loaded__old; - setFlags |= MTPDstickerSet_ClientFlag::f_not_loaded; - } - } - if (stickers.version > 1002008) { - stickers.stream >> setInstallDate; - } - if (readingInstalled && stickers.version < 9061) { - setFlags |= MTPDstickerSet::Flag::f_installed_date; + >> scnt + >> setHash + >> setFlagsValue + >> setInstallDate; + setThumbnail = Serialize::readStorageImageLocation( + stickers.version, + stickers.stream); + if (!_checkStreamStatus(stickers.stream)) { + return failed(); } + setFlags = MTPDstickerSet::Flags::from_raw(setFlagsValue); if (setId == Stickers::DefaultSetId) { setTitle = lang(lng_stickers_default_set); setFlags |= MTPDstickerSet::Flag::f_official | MTPDstickerSet_ClientFlag::f_special; - if (readingInstalled && outOrder && stickers.version < 9061) { - outOrder->push_front(setId); - } } else if (setId == Stickers::CustomSetId) { setTitle = qsl("Custom stickers"); setFlags |= MTPDstickerSet_ClientFlag::f_special; @@ -3544,11 +3558,7 @@ void _readStickerSets(FileKey &stickersKey, Stickers::Order *outOrder = nullptr, } else if (setId == Stickers::FavedSetId) { setTitle = Lang::Hard::FavedSetTitle(); setFlags |= MTPDstickerSet_ClientFlag::f_special; - } else if (setId) { - if (readingInstalled && outOrder && stickers.version < 9061) { - outOrder->push_back(setId); - } - } else { + } else if (!setId) { continue; } @@ -3564,33 +3574,37 @@ void _readStickerSets(FileKey &stickersKey, Stickers::Order *outOrder = nullptr, 0, setHash, MTPDstickerSet::Flags(setFlags), - setInstallDate)); + setInstallDate, + setThumbnail.isNull() ? ImagePtr() : Images::Create(setThumbnail))); } auto &set = it.value(); auto inputSet = MTP_inputStickerSetID(MTP_long(set.id), MTP_long(set.access)); + const auto fillStickers = set.stickers.isEmpty(); if (scnt < 0) { // disabled not loaded set - if (!set.count || set.stickers.isEmpty()) { + if (!set.count || fillStickers) { set.count = -scnt; } continue; } - bool fillStickers = set.stickers.isEmpty(); if (fillStickers) { set.stickers.reserve(scnt); set.count = 0; } Serialize::Document::StickerSetInfo info(setId, setAccess, setShortName); - OrderedSet read; + base::flat_set read; for (int32 j = 0; j < scnt; ++j) { auto document = Serialize::Document::readStickerFromStream(stickers.version, stickers.stream, info); - if (!document || !document->sticker()) continue; - - if (read.contains(document->id)) continue; - read.insert(document->id); - + if (!_checkStreamStatus(stickers.stream)) { + return failed(); + } else if (!document + || !document->sticker() + || read.contains(document->id)) { + continue; + } + read.emplace(document->id); if (fillStickers) { set.stickers.push_back(document); if (!(set.flags & MTPDstickerSet_ClientFlag::f_special)) { @@ -3602,64 +3616,71 @@ void _readStickerSets(FileKey &stickersKey, Stickers::Order *outOrder = nullptr, } } - if (stickers.version > 1002008) { - auto datesCount = qint32(0); - stickers.stream >> datesCount; - if (datesCount > 0) { - if (datesCount != scnt) { - // Bad file. - return; - } + qint32 datesCount = 0; + stickers.stream >> datesCount; + if (datesCount > 0) { + if (datesCount != scnt) { + return failed(); + } + const auto fillDates = (set.id == Stickers::CloudRecentSetId) + && (set.stickers.size() == datesCount); + if (fillDates) { + set.dates.clear(); set.dates.reserve(datesCount); - for (auto i = 0; i != datesCount; ++i) { - auto date = qint32(); - stickers.stream >> date; - if (set.id == Stickers::CloudRecentSetId) { - set.dates.push_back(TimeId(date)); - } + } + for (auto i = 0; i != datesCount; ++i) { + qint32 date = 0; + stickers.stream >> date; + if (fillDates) { + set.dates.push_back(TimeId(date)); } } } - if (stickers.version > 9018) { - qint32 emojiCount; - stickers.stream >> emojiCount; - for (int32 j = 0; j < emojiCount; ++j) { - QString emojiString; - qint32 stickersCount; - stickers.stream >> emojiString >> stickersCount; - Stickers::Pack pack; - pack.reserve(stickersCount); - for (int32 k = 0; k < stickersCount; ++k) { - quint64 id; - stickers.stream >> id; - const auto doc = Auth().data().document(id); - if (!doc->sticker()) continue; + qint32 emojiCount = 0; + stickers.stream >> emojiCount; + if (!_checkStreamStatus(stickers.stream) || emojiCount < 0) { + return failed(); + } + for (int32 j = 0; j < emojiCount; ++j) { + QString emojiString; + qint32 stickersCount; + stickers.stream >> emojiString >> stickersCount; + Stickers::Pack pack; + pack.reserve(stickersCount); + for (int32 k = 0; k < stickersCount; ++k) { + quint64 id; + stickers.stream >> id; + const auto doc = Auth().data().document(id); + if (!doc->sticker()) continue; - pack.push_back(doc); - } - if (fillStickers) { - if (auto emoji = Ui::Emoji::Find(emojiString)) { - emoji = emoji->original(); - set.emoji.insert(emoji, pack); - } + pack.push_back(doc); + } + if (fillStickers) { + if (auto emoji = Ui::Emoji::Find(emojiString)) { + emoji = emoji->original(); + set.emoji.insert(emoji, pack); } } } } // Read orders of installed and featured stickers. - if (outOrder && stickers.version >= 9061) { + if (outOrder) { stickers.stream >> *outOrder; } + if (!_checkStreamStatus(stickers.stream)) { + return failed(); + } // Set flags that we dropped above from the order. if (readingFlags && outOrder) { - for_const (auto setId, *outOrder) { + for (const auto setId : std::as_const(*outOrder)) { auto it = sets.find(setId); if (it != sets.cend()) { it->flags |= readingFlags; - if (readingInstalled && !it->installDate) { + if ((readingFlags == MTPDstickerSet::Flag::f_installed_date) + && !it->installDate) { it->installDate = kDefaultStickerInstallDate; } } @@ -3770,7 +3791,8 @@ void importOldRecentStickers() { (MTPDstickerSet::Flag::f_official | MTPDstickerSet::Flag::f_installed_date | MTPDstickerSet_ClientFlag::f_special), - kDefaultStickerInstallDate)).value(); + kDefaultStickerInstallDate, + ImagePtr())).value(); auto &custom = sets.insert(Stickers::CustomSetId, Stickers::Set( Stickers::CustomSetId, uint64(0), @@ -3780,7 +3802,8 @@ void importOldRecentStickers() { 0, // hash (MTPDstickerSet::Flag::f_installed_date | MTPDstickerSet_ClientFlag::f_special), - kDefaultStickerInstallDate)).value(); + kDefaultStickerInstallDate, + ImagePtr())).value(); QMap read; while (!stickers.stream.atEnd()) {