serialize_document.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "storage/serialize_document.h"
  8. #include "storage/serialize_common.h"
  9. #include "storage/serialize_peer.h"
  10. #include "data/data_session.h"
  11. #include "data/stickers/data_stickers.h"
  12. #include "ui/image/image.h"
  13. #include "main/main_session.h"
  14. namespace Serialize {
  15. namespace {
  16. constexpr auto kVersionTag = int32(0x7FFFFFFF);
  17. constexpr auto kVersion = 6;
  18. enum StickerSetType {
  19. StickerSetTypeEmpty = 0,
  20. StickerSetTypeID = 1,
  21. StickerSetTypeShortName = 2,
  22. StickerSetTypeEmoji = 3,
  23. StickerSetTypeMasks = 4,
  24. };
  25. } // namespace
  26. void Document::writeToStream(QDataStream &stream, DocumentData *document) {
  27. stream
  28. << quint64(document->id)
  29. << quint64(document->_access)
  30. << qint32(document->date)
  31. << document->_fileReference
  32. << qint32(kVersionTag)
  33. << qint32(kVersion)
  34. << document->filename()
  35. << document->mimeString()
  36. << qint32(document->_dc)
  37. // FileSize: Right now any file size fits 32 bit.
  38. << qint32(uint32(document->size))
  39. << qint32(document->dimensions.width())
  40. << qint32(document->dimensions.height())
  41. << qint32(document->type);
  42. if (const auto sticker = document->sticker()) {
  43. stream << sticker->alt;
  44. if (sticker->setType == Data::StickersType::Emoji) {
  45. stream << qint32(StickerSetTypeEmoji);
  46. } else if (sticker->setType == Data::StickersType::Masks) {
  47. stream << qint32(StickerSetTypeMasks);
  48. } else if (sticker->set.id) {
  49. stream << qint32(StickerSetTypeID);
  50. } else {
  51. stream << qint32(StickerSetTypeEmpty);
  52. }
  53. }
  54. stream << qint64(document->hasDuration() ? document->duration() : -1);
  55. if (document->type == StickerDocument) {
  56. const auto premium = document->isPremiumSticker()
  57. || document->isPremiumEmoji();
  58. stream << qint32(premium ? 1 : 0);
  59. stream << qint32(document->emojiUsesTextColor() ? 1 : 0);
  60. }
  61. writeImageLocation(stream, document->thumbnailLocation());
  62. stream << qint32(document->thumbnailByteSize());
  63. writeImageLocation(stream, document->videoThumbnailLocation());
  64. stream
  65. << qint32(document->videoThumbnailByteSize())
  66. << qint32(document->inlineThumbnailIsPath() ? 1 : 0)
  67. << document->inlineThumbnailBytes();
  68. }
  69. DocumentData *Document::readFromStreamHelper(
  70. not_null<Main::Session*> session,
  71. int streamAppVersion,
  72. QDataStream &stream,
  73. const StickerSetInfo *info) {
  74. quint64 id, access;
  75. QString name, mime;
  76. qint32 date, dc, size, width, height, type, versionTag, version = 0;
  77. QByteArray fileReference;
  78. stream >> id >> access >> date;
  79. if (streamAppVersion >= 9061) {
  80. if (streamAppVersion >= 1003013) {
  81. stream >> fileReference;
  82. }
  83. stream >> versionTag;
  84. if (versionTag == kVersionTag) {
  85. stream >> version;
  86. }
  87. } else {
  88. versionTag = 0;
  89. version = 0;
  90. }
  91. stream
  92. >> name
  93. >> mime
  94. >> dc
  95. >> size // FileSize: Right now any file size fits 32 bit.
  96. >> width
  97. >> height
  98. >> type;
  99. QVector<MTPDocumentAttribute> attributes;
  100. if (!name.isEmpty()) {
  101. attributes.push_back(MTP_documentAttributeFilename(MTP_string(name)));
  102. }
  103. qint64 duration = -1;
  104. qint32 isPremiumSticker = 0;
  105. qint32 useTextColor = 0;
  106. if (type == StickerDocument) {
  107. QString alt;
  108. qint32 typeOfSet;
  109. stream >> alt >> typeOfSet;
  110. if (version >= 6) {
  111. stream >> duration >> isPremiumSticker >> useTextColor;
  112. } else if (version >= 3) {
  113. qint32 oldDuration = -1;
  114. stream >> oldDuration;
  115. duration = (oldDuration < 0) ? oldDuration : oldDuration * 1000;
  116. if (version >= 4) {
  117. stream >> isPremiumSticker;
  118. if (version >= 5) {
  119. stream >> useTextColor;
  120. }
  121. }
  122. }
  123. if (typeOfSet == StickerSetTypeEmpty) {
  124. attributes.push_back(MTP_documentAttributeSticker(MTP_flags(0), MTP_string(alt), MTP_inputStickerSetEmpty(), MTPMaskCoords()));
  125. } else if (info) {
  126. if (info->setId == Data::Stickers::DefaultSetId
  127. || info->setId == Data::Stickers::CloudRecentSetId
  128. || info->setId == Data::Stickers::CloudRecentAttachedSetId
  129. || info->setId == Data::Stickers::FavedSetId
  130. || info->setId == Data::Stickers::CustomSetId
  131. || info->setId == Data::Stickers::CollectibleSetId) {
  132. typeOfSet = StickerSetTypeEmpty;
  133. }
  134. switch (typeOfSet) {
  135. case StickerSetTypeID: {
  136. attributes.push_back(MTP_documentAttributeSticker(MTP_flags(0), MTP_string(alt), MTP_inputStickerSetID(MTP_long(info->setId), MTP_long(info->accessHash)), MTPMaskCoords()));
  137. } break;
  138. case StickerSetTypeMasks: {
  139. attributes.push_back(MTP_documentAttributeSticker(MTP_flags(MTPDdocumentAttributeSticker::Flag::f_mask), MTP_string(alt), MTP_inputStickerSetID(MTP_long(info->setId), MTP_long(info->accessHash)), MTPMaskCoords()));
  140. } break;
  141. case StickerSetTypeEmoji: {
  142. if (version < 5) {
  143. // We didn't store useTextColor yet, can't use.
  144. stream.setStatus(QDataStream::ReadCorruptData);
  145. return nullptr;
  146. }
  147. using Flag = MTPDdocumentAttributeCustomEmoji::Flag;
  148. attributes.push_back(MTP_documentAttributeCustomEmoji(
  149. MTP_flags((isPremiumSticker ? Flag(0) : Flag::f_free)
  150. | (useTextColor ? Flag::f_text_color : Flag(0))),
  151. MTP_string(alt),
  152. MTP_inputStickerSetID(
  153. MTP_long(info->setId),
  154. MTP_long(info->accessHash))));
  155. } break;
  156. case StickerSetTypeEmpty:
  157. default: {
  158. attributes.push_back(MTP_documentAttributeSticker(MTP_flags(0), MTP_string(alt), MTP_inputStickerSetEmpty(), MTPMaskCoords()));
  159. } break;
  160. }
  161. }
  162. } else {
  163. if (version >= 6) {
  164. stream >> duration;
  165. } else {
  166. qint32 oldDuration = -1;
  167. stream >> oldDuration;
  168. duration = (oldDuration < 0) ? oldDuration : oldDuration * 1000;
  169. }
  170. if (type == AnimatedDocument) {
  171. attributes.push_back(MTP_documentAttributeAnimated());
  172. }
  173. }
  174. std::optional<ImageLocation> videoThumb;
  175. qint32 thumbnailByteSize = 0, videoThumbnailByteSize = 0;
  176. qint32 inlineThumbnailIsPath = 0;
  177. QByteArray inlineThumbnailBytes;
  178. const auto thumb = readImageLocation(streamAppVersion, stream);
  179. if (version >= 1) {
  180. stream >> thumbnailByteSize;
  181. videoThumb = readImageLocation(streamAppVersion, stream);
  182. stream >> videoThumbnailByteSize;
  183. if (version >= 2) {
  184. stream >> inlineThumbnailIsPath >> inlineThumbnailBytes;
  185. }
  186. } else {
  187. videoThumb = ImageLocation();
  188. }
  189. if (width > 0 && height > 0) {
  190. if (duration >= 0) {
  191. auto flags = MTPDdocumentAttributeVideo::Flags(0);
  192. if (type == RoundVideoDocument) {
  193. flags |= MTPDdocumentAttributeVideo::Flag::f_round_message;
  194. }
  195. attributes.push_back(MTP_documentAttributeVideo(
  196. MTP_flags(flags),
  197. MTP_double(duration / 1000.),
  198. MTP_int(width),
  199. MTP_int(height),
  200. MTPint(), // preload_prefix_size
  201. MTPdouble(), // video_start_ts
  202. MTPstring())); // video_codec
  203. } else {
  204. attributes.push_back(MTP_documentAttributeImageSize(
  205. MTP_int(width),
  206. MTP_int(height)));
  207. }
  208. }
  209. if ((stream.status() != QDataStream::Ok)
  210. || (!dc && !access)
  211. || !thumb
  212. || !videoThumb) {
  213. stream.setStatus(QDataStream::ReadCorruptData);
  214. return nullptr;
  215. }
  216. const auto storage = std::get_if<StorageFileLocation>(
  217. &thumb->file().data);
  218. if (thumb->valid()
  219. && (!storage || !storage->isDocumentThumbnail())) {
  220. stream.setStatus(QDataStream::ReadCorruptData);
  221. // We can't convert legacy thumbnail location to modern, because
  222. // size letter ('s' or 'm') is lost, it was not saved in legacy.
  223. return nullptr;
  224. }
  225. return session->data().document(
  226. id,
  227. access,
  228. fileReference,
  229. date,
  230. attributes,
  231. mime,
  232. InlineImageLocation{
  233. inlineThumbnailBytes,
  234. (inlineThumbnailIsPath == 1),
  235. },
  236. ImageWithLocation{
  237. .location = *thumb,
  238. .bytesCount = thumbnailByteSize
  239. },
  240. ImageWithLocation{
  241. .location = *videoThumb,
  242. .bytesCount = videoThumbnailByteSize
  243. },
  244. (isPremiumSticker == 1),
  245. dc,
  246. int64(uint32(size)));
  247. }
  248. DocumentData *Document::readStickerFromStream(
  249. not_null<Main::Session*> session,
  250. int streamAppVersion,
  251. QDataStream &stream,
  252. const StickerSetInfo &info) {
  253. return readFromStreamHelper(session, streamAppVersion, stream, &info);
  254. }
  255. DocumentData *Document::readFromStream(
  256. not_null<Main::Session*> session,
  257. int streamAppVersion,
  258. QDataStream &stream) {
  259. return readFromStreamHelper(session, streamAppVersion, stream, nullptr);
  260. }
  261. int Document::sizeInStream(DocumentData *document) {
  262. int result = 0;
  263. // id + access + date
  264. result += sizeof(quint64) + sizeof(quint64) + sizeof(qint32);
  265. // file_reference + version tag + version
  266. result += bytearraySize(document->_fileReference) + sizeof(qint32) * 2;
  267. // + namelen + name + mimelen + mime + dc + size
  268. result += stringSize(document->filename()) + stringSize(document->mimeString()) + sizeof(qint32) + sizeof(qint32);
  269. // + width + height
  270. result += sizeof(qint32) + sizeof(qint32);
  271. // + type
  272. result += sizeof(qint32);
  273. if (auto sticker = document->sticker()) { // type == StickerDocument
  274. // + altlen + alt + type-of-set
  275. result += stringSize(sticker->alt) + sizeof(qint32);
  276. } else {
  277. // + duration
  278. result += sizeof(qint32);
  279. }
  280. // + thumb loc
  281. result += Serialize::imageLocationSize(document->thumbnailLocation());
  282. result += sizeof(qint32); // thumbnail_byte_size
  283. result += Serialize::imageLocationSize(document->videoThumbnailLocation());
  284. result += sizeof(qint32); // video_thumbnail_byte_size
  285. result += sizeof(qint32) + Serialize::bytearraySize(document->inlineThumbnailBytes());
  286. return result;
  287. }
  288. } // namespace Serialize