1 // This file is part of Desktop App Toolkit,
2 // a set of libraries for developing nice desktop applications.
3 //
4 // For license and copyright information please follow this link:
5 // https://github.com/desktop-app/legal/blob/master/LEGAL
6 //
7 #include "codegen/emoji/data.h"
8 
9 #include "codegen/emoji/data_old.h"
10 #include "codegen/emoji/data_read.h"
11 
12 namespace codegen {
13 namespace emoji {
14 namespace {
15 
16 using std::vector;
17 using std::map;
18 using std::set;
19 using std::find;
20 using std::make_pair;
21 using std::move;
22 using std::begin;
23 using std::end;
24 
25 // copied from emoji_box.cpp
26 struct Replace {
27 	InputId code;
28 	const char *replace;
29 };
30 
31 Replace Replaces[] = {
32 	{ { 0xD83DDE0AU }, ":-)" },
33 	{ { 0xD83DDE0DU }, "8-)" },
34 	{ { 0x2764U }, "<3" },
35 //	{ { 0xD83DDC8BU }, ":kiss:" },
36 //	{ { 0xD83DDE01U }, ":grin:" },
37 //	{ { 0xD83DDE02U }, ":joy:" },
38 	{ { 0xD83DDE1AU }, ":-*" },
39 //	{ { 0xD83DDE06U }, "xD" }, // Conflicts with typing xDDD...
40 //	{ { 0xD83DDC4DU }, ":like:" },
41 //	{ { 0xD83DDC4EU }, ":dislike:" },
42 //	{ { 0x261DU }, ":up:" },
43 //	{ { 0x270CU }, ":v:" },
44 //	{ { 0xD83DDC4CU }, ":ok:" },
45 	{ { 0xD83DDE0EU }, "B-)" },
46 	{ { 0xD83DDE03U }, ":-D" },
47 	{ { 0xD83DDE09U }, ";-)" },
48 	{ { 0xD83DDE1CU }, ";-P" },
49 	{ { 0xD83DDE0BU }, ":-p" },
50 	{ { 0xD83DDE14U }, "3(" },
51 	{ { 0xD83DDE1EU }, ":-(" },
52 	{ { 0xD83DDE0FU }, ":]" },
53 	{ { 0xD83DDE22U }, ":'(" },
54 	{ { 0xD83DDE2DU }, ":_(" },
55 	{ { 0xD83DDE29U }, ":((" },
56 //	{ { 0xD83DDE28U }, ":o" }, // Conflicts with typing :ok...
57 	{ { 0xD83DDE10U }, ":|" },
58 	{ { 0xD83DDE0CU }, "3-)" },
59 	{ { 0xD83DDE20U }, ">(" },
60 	{ { 0xD83DDE21U }, ">((" },
61 	{ { 0xD83DDE07U }, "O:)" },
62 	{ { 0xD83DDE30U }, ";o" },
63 	{ { 0xD83DDE33U }, "8|" },
64 	{ { 0xD83DDE32U }, "8o" },
65 	{ { 0xD83DDE37U }, ":X" },
66 	{ { 0xD83DDE08U }, "}:)" },
67 };
68 
69 InputCategory PostfixRequired = {
70  { 0x2122U, 0xFE0FU, },
71  { 0xA9U, 0xFE0FU, },
72  { 0xAEU, 0xFE0FU, },
73 };
74 
75 using ColorId = uint32;
76 ColorId Colors[] = {
77 	0xD83CDFFBU,
78 	0xD83CDFFCU,
79 	0xD83CDFFDU,
80 	0xD83CDFFEU,
81 	0xD83CDFFFU,
82 };
83 
84 constexpr auto ColorMask = 0xD83CDFFBU;
85 
86 // Original data has those emoji only with gender symbols.
87 // But they should be displayed as emoji even without gender symbols.
88 // So we map which gender symbol to use for an emoji without one.
89 std::map<InputId, uint32> WithoutGenderAliases = {
90 // { { 0xD83EDD26U, }, 0x2642U },
91 // { { 0xD83EDD37U, }, 0x2640U },
92 // { { 0xD83EDD38U, }, 0x2642U },
93 // { { 0xD83EDD3CU, }, 0x2640U },
94 // { { 0xD83EDD3DU, }, 0x2642U },
95 // { { 0xD83EDD3EU, }, 0x2640U },
96 // { { 0xD83EDD39U, }, 0x2642U },
97 // { { 0xD83EDDB8U, }, 0x2640U },
98 // { { 0xD83EDDB9U, }, 0x2640U },
99 // { { 0xD83EDDD6U, }, 0x2642U },
100 // { { 0xD83EDDD7U, }, 0x2640U },
101 // { { 0xD83EDDD8U, }, 0x2640U },
102 // { { 0xD83EDDD9U, }, 0x2640U },
103 // { { 0xD83EDDDAU, }, 0x2640U },
104 // { { 0xD83EDDDBU, }, 0x2640U },
105 // { { 0xD83EDDDCU, }, 0x2642U },
106 // { { 0xD83EDDDDU, }, 0x2642U },
107 // { { 0xD83EDDDEU, }, 0x2642U },
108 // { { 0xD83EDDDFU, }, 0x2642U },
109 };
110 
111 // Some flags are sent as one string, but are rendered as a different too.
112 std::map<InputId, InputId> FlagAliases = {
113 // { { 0xD83CDDE8U, 0xD83CDDF5U, }, { 0xD83CDDEBU, 0xD83CDDF7U, } },
114 // { { 0xD83CDDE7U, 0xD83CDDFBU, }, { 0xD83CDDF3U, 0xD83CDDF4U, } },
115 // { { 0xD83CDDE6U, 0xD83CDDE8U, }, { 0xD83CDDF8U, 0xD83CDDEDU, } },
116 //
117 // // This is different flag, but macOS shows that glyph :(
118 // { { 0xD83CDDE9U, 0xD83CDDECU, }, { 0xD83CDDEEU, 0xD83CDDF4U, } },
119 //
120 // { { 0xD83CDDF9U, 0xD83CDDE6U, }, { 0xD83CDDF8U, 0xD83CDDEDU, } },
121 // { { 0xD83CDDF2U, 0xD83CDDEBU, }, { 0xD83CDDEBU, 0xD83CDDF7U, } },
122 // { { 0xD83CDDEAU, 0xD83CDDE6U, }, { 0xD83CDDEAU, 0xD83CDDF8U, } },
123 };
124 
125 std::map<Id, std::vector<Id>> Aliases; // original -> list of aliased
126 std::set<Id> AliasesAdded;
127 
AddAlias(const Id & original,const Id & aliased)128 void AddAlias(const Id &original, const Id &aliased) {
129 	auto &aliases = Aliases[original];
130 	if (std::find(aliases.begin(), aliases.end(), aliased) == aliases.end()) {
131 		aliases.push_back(aliased);
132 	}
133 }
134 
135 constexpr auto kErrorBadData = 401;
136 
append(Id & id,uint32 code)137 void append(Id &id, uint32 code) {
138 	if (auto first = static_cast<uint16>((code >> 16) & 0xFFFFU)) {
139 		id.append(QChar(first));
140 	}
141 	id.append(QChar(static_cast<uint16>(code & 0xFFFFU)));
142 }
143 
BareIdFromInput(const InputId & id)144 [[nodiscard]] Id BareIdFromInput(const InputId &id) {
145 	auto result = Id();
146 	for (const auto unicode : id) {
147 		if (unicode != kPostfix) {
148 			append(result, unicode);
149 		}
150 	}
151 	return result;
152 }
153 
FillVariatedIds(const InputData & data)154 [[nodiscard]] set<Id> FillVariatedIds(const InputData &data) {
155 	auto result = set<Id>();
156 	for (const auto &row : data.colored) {
157 		auto variatedId = Id();
158 		if (row.size() < 2) {
159 			logDataError() << "colored string should have at least two characters.";
160 			return {};
161 		}
162 		for (auto i = size_t(0), size = row.size(); i != size; ++i) {
163 			auto code = row[i];
164 			if (i == 1) {
165 				if (code != ColorMask) {
166 					logDataError() << "color code should appear at index 1.";
167 					return {};
168 				}
169 			} else if (code == ColorMask) {
170 				logDataError() << "color code should appear only at index 1.";
171 				return {};
172 			} else if (code != kPostfix) {
173 				append(variatedId, code);
174 			}
175 		}
176 		result.emplace(variatedId);
177 	}
178 	return result;
179 }
180 
FillDoubleVariatedIds(const InputData & data)181 [[nodiscard]] map<Id, InputId> FillDoubleVariatedIds(const InputData &data) {
182 	auto result = map<Id, InputId>();
183 	for (const auto &[original, same, different] : data.doubleColored) {
184 		auto variatedId = Id();
185 		if (original.size() < 1) {
186 			logDataError() << "original string should have at least one character.";
187 			return {};
188 		} else if (same.size() < 2) {
189 			logDataError() << "colored string should have at least two characters.";
190 			return {};
191 		}
192 		if (same.size() == 2) {
193 			// original: 1
194 			// same: original + color
195 			// different: some1 + color1 + sep + some2 + sep + some3 + color2
196 			if (same[1] != ColorMask) {
197 				logDataError() << "color code should appear at index 1.";
198 				return {};
199 			} else if (same[0] == ColorMask) {
200 				logDataError() << "color code should appear only at index 1.";
201 				return {};
202 			} else if (same[0] == kPostfix) {
203 				logDataError() << "postfix in double colored is not supported.";
204 				return {};
205 			} else {
206 				append(variatedId, same[0]);
207 			}
208 			// add an alias to 'same' in the form ..
209 			// .. of 'some1 + color + sep + some2 + sep + some3 + color'
210 			if (std::count(different.begin(), different.end(), kJoiner) != 2) {
211 				logDataError() << "complex double colored bad different.";
212 				return {};
213 			}
214 			for (const auto color : Colors) {
215 				auto copy = same;
216 				copy[1] = color;
217 				auto sameWithColor = BareIdFromInput(copy);
218 				copy = different;
219 				for (auto &entry : copy) {
220 					if (entry == Colors[0] || entry == Colors[1]) {
221 						entry = color;
222 					}
223 				}
224 				auto differentWithColor = BareIdFromInput(copy);
225 				AddAlias(sameWithColor, differentWithColor);
226 			}
227 		} else {
228 			// same: som1 + color + sep + some2 + sep + some3 + color
229 			// different: some1 + color1 + sep + some2 + sep + some3 + color2
230 			auto copy = different;
231 			if (copy.size() < 7 || copy[1] != Colors[0] || copy[copy.size() - 1] != Colors[1]) {
232 				logDataError() << "complex double colored bad different.";
233 				return {};
234 			}
235 			copy[copy.size() - 1] = Colors[0];
236 			if (copy != same) {
237 				logDataError() << "complex double colored should colorize all the same.";
238 				return {};
239 			}
240 			if (original.size() == 1) {
241 				// original: 1
242 				// add an alias to 'same' in the form of 'original + color'
243 				for (const auto color : Colors) {
244 					auto copy = original;
245 					copy.push_back(color);
246 					auto originalWithColor = BareIdFromInput(copy);
247 					copy = same;
248 					for (auto &entry : copy) {
249 						if (entry == ColorMask) {
250 							entry = color;
251 						}
252 					}
253 					auto sameWithColor = BareIdFromInput(copy);
254 					AddAlias(originalWithColor, sameWithColor);
255 				}
256 			}
257 			variatedId = BareIdFromInput(original);
258 		}
259 		result.emplace(variatedId, different);
260 	}
261 	return result;
262 }
263 
FillPostfixRequiredIds()264 [[nodiscard]] set<Id> FillPostfixRequiredIds() {
265 	auto result = set<Id>();
266 	for (const auto &row : PostfixRequired) {
267 		result.emplace(BareIdFromInput(row));
268 	}
269 	return result;
270 }
271 
appendCategory(Data & result,const InputCategory & category,const set<Id> & variatedIds,const map<Id,InputId> & doubleVariatedIds,const set<Id> & postfixRequiredIds)272 void appendCategory(
273 		Data &result,
274 		const InputCategory &category,
275 		const set<Id> &variatedIds,
276 		const map<Id, InputId> &doubleVariatedIds,
277 		const set<Id> &postfixRequiredIds) {
278 	result.categories.emplace_back();
279 	for (auto &id : category) {
280 		auto emoji = Emoji();
281 		auto bareId = BareIdFromInput(id);
282 		auto from = id.cbegin(), to = id.cend();
283 		if (to - from == 2 && *(to - 1) == kPostfix) {
284 			emoji.postfixed = true;
285 			--to;
286 		}
287 		for (auto i = from; i != to; ++i) {
288 			auto code = *i;
289 			if (find(begin(Colors), end(Colors), code) != end(Colors)) {
290 				logDataError() << "color code found in a category emoji.";
291 				result = Data();
292 				return;
293 			}
294 			append(emoji.id, code);
295 		}
296 		if (bareId.isEmpty()) {
297 			logDataError() << "empty emoji id found.";
298 			result = Data();
299 			return;
300 		}
301 
302 		const auto addOne = [&](const Id &bareId, Emoji &&emoji) {
303 			auto it = result.map.find(bareId);
304 			if (it == result.map.cend()) {
305 				const auto index = result.list.size();
306 				it = result.map.emplace(bareId, index).first;
307 				result.list.push_back(move(emoji));
308 				if (const auto a = Aliases.find(bareId); a != end(Aliases)) {
309 					AliasesAdded.emplace(bareId);
310 					for (const auto &alias : a->second) {
311 						const auto ok = result.map.emplace(alias, index).second;
312 						if (!ok) {
313 							logDataError() << "some emoji alias already in the map.";
314 							result = Data();
315 							return result.map.end();
316 						}
317 					}
318 				}
319 				if (postfixRequiredIds.find(bareId) != end(postfixRequiredIds)) {
320 					result.postfixRequired.emplace(index);
321 				}
322 			} else if (result.list[it->second].postfixed != emoji.postfixed) {
323 				logDataError() << "same emoji found with different postfixed property.";
324 				result = Data();
325 				return result.map.end();
326 			} else if (result.list[it->second].id != emoji.id) {
327 				logDataError() << "same emoji found with different id.";
328 				result = Data();
329 				return result.map.end();
330 			}
331 			return it;
332 		};
333 
334 		auto it = addOne(bareId, move(emoji));
335 		if (it == result.map.end()) {
336 			return;
337 		}
338 		if (variatedIds.find(bareId) != end(variatedIds)) {
339 			result.list[it->second].variated = true;
340 
341 			auto baseId = Id();
342 			if (*from == kPostfix) {
343 				logDataError() << "bad first symbol in emoji.";
344 				result = Data();
345 				return;
346 			}
347 			append(baseId, *from++);
348 			// A few uncolored emoji contain two kPostfix.
349 			// (:(wo)man_lifting_weights:, :(wo)man_golfing:
350 			//  :(wo)man_bouncing_ball:, :(wo)man_detective:)
351 			// But colored emoji should have only one kPostfix.
352 			if (std::count(from, to, kPostfix) == 2 && *from == kPostfix) {
353 				from++;
354 			}
355 			for (auto color : Colors) {
356 				auto colored = Emoji();
357 				colored.colored = true;
358 				colored.id = baseId;
359 				append(colored.id, color);
360 				auto bareColoredId = colored.id;
361 				for (auto i = from; i != to; ++i) {
362 					append(colored.id, *i);
363 					if (*i != kPostfix) {
364 						append(bareColoredId, *i);
365 					}
366 				}
367 				if (addOne(bareColoredId, std::move(colored)) == result.map.end()) {
368 					return;
369 				}
370 			}
371 		} else if (const auto d = doubleVariatedIds.find(bareId); d != end(doubleVariatedIds)) {
372 			//result.list[it->second].doubleVariated = true;
373 
374 			const auto baseId = bareId;
375 			const auto &different = d->second;
376 			if (different.size() < 4
377 				|| different[1] != Colors[0]
378 				|| different[different.size() - 1] != Colors[1]) {
379 				logDataError() << "bad data in double-colored emoji.";
380 				result = Data();
381 				return;
382 			}
383 			for (auto color1 : Colors) {
384 				for (auto color2 : Colors) {
385 					auto colored = Emoji();
386 					//colored.colored = true;
387 					if (color1 == color2 && baseId.size() == 2) {
388 						colored.id = baseId;
389 						append(colored.id, color1);
390 					} else {
391 						auto copy = different;
392 						copy[1] = color1;
393 						copy[copy.size() - 1] = color2;
394 						for (const auto code : copy) {
395 							append(colored.id, code);
396 						}
397 					}
398 					auto bareColoredId = colored.id.replace(QChar(kPostfix), QString());
399 					if (addOne(bareColoredId, move(colored)) == result.map.end()) {
400 						return;
401 					}
402 				}
403 			}
404 
405 		}
406 		result.categories.back().push_back(it->second);
407 	}
408 }
409 
fillReplaces(Data & result)410 void fillReplaces(Data &result) {
411 	for (auto &replace : Replaces) {
412 		auto id = Id();
413 		for (auto code : replace.code) {
414 			append(id, code);
415 		}
416 		auto it = result.map.find(id);
417 		if (it == result.map.cend()) {
418 			logDataError() << "emoji from replaces not found in the map.";
419 			result = Data();
420 			return;
421 		}
422 		result.replaces.insert(make_pair(QString::fromUtf8(replace.replace), it->second));
423 	}
424 }
425 
CheckOldInCurrent(const InputData & data,const InputData & old,const std::set<Id> & variatedIds)426 bool CheckOldInCurrent(
427 		const InputData &data,
428 		const InputData &old,
429 		const std::set<Id> &variatedIds) {
430 	const auto genders = { 0x2640U, 0x2642U };
431 	const auto addGender = [](const InputId &was, uint32 gender) {
432 		auto result = was;
433 		result.push_back(0x200DU);
434 		result.push_back(gender);
435 		result.push_back(kPostfix);
436 		return result;
437 	};
438 	const auto addGenderByIndex = [&](const InputId &was, int index) {
439 		return addGender(was, *(begin(genders) + index));
440 	};
441 	static const auto find = [](
442 			const InputCategory &list,
443 			const InputId &id) {
444 		return (std::find(begin(list), end(list), id) != end(list));
445 	};
446 	static const auto findInMany = [](
447 			const InputData &data,
448 			const InputId &id) {
449 		for (const auto &current : data.categories) {
450 			if (find(current, id)) {
451 				return true;
452 			}
453 		}
454 		return find(data.other, id);
455 	};
456 	const auto emplaceColoredAlias = [](const InputId &real, const InputId &alias, uint32_t color) {
457 		if (real.size() < 2 || alias.size() < 2 || real[1] != Colors[0] || alias[1] != Colors[0]) {
458 			return false;
459 		}
460 		auto key = real;
461 		key[1] = color;
462 		auto value = alias;
463 		value[1] = color;
464 		AddAlias(BareIdFromInput(key), BareIdFromInput(value));
465 		return true;
466 	};
467 	auto result = true;
468 	for (auto c = begin(old.categories); c != end(old.categories); ++c) {
469 		const auto &category = *c;
470 		for (auto i = begin(category); i != end(category); ++i) {
471 			if (findInMany(data, *i)) {
472 				continue;
473 			}
474 
475 			// Some emoji were ending with kPostfix and now are not.
476 			if (i->back() == kPostfix) {
477 				auto other = *i;
478 				other.pop_back();
479 				if (findInMany(data, other)) {
480 					continue;
481 				}
482 			}
483 
484 			// Some emoji were not ending with kPostfix and now are.
485 			if (i->back() != kPostfix) {
486 				auto other = *i;
487 				other.push_back(kPostfix);
488 				if (findInMany(data, other)) {
489 					continue;
490 				}
491 			}
492 
493 			common::logError(kErrorBadData, "input")
494 				<< "Bad data: old emoji '"
495 				<< InputIdToString(*i).toStdString()
496 				<< "' (category "
497 				<< (c - begin(old.categories))
498 				<< ", index "
499 				<< (i - begin(category))
500 				<< ") not found in current.";
501 			result = false;
502 			continue;
503 
504 			// Old way - auto add genders. New way - add gender neutral images,
505 			// but don't add them to any category in the picker.
506 //			// Some emoji were without gender symbol and now have gender symbol.
507 //			// Try adding 0x200DU, 0x2640U, 0xFE0FU or 0x200DU, 0x2642U, 0xFE0FU.
508 //			const auto otherGenderIndex = [&] {
509 //				for (auto g = begin(genders); g != end(genders); ++g) {
510 //					auto altered = *i;
511 //					altered.push_back(kJoiner);
512 //					altered.push_back(*g);
513 //					altered.push_back(kPostfix);
514 //					if (findInMany(old, altered)) {
515 //						return int(g - begin(genders));
516 //					}
517 //				}
518 //				return -1;
519 //			}();
520 //			if (otherGenderIndex < 0) {
521 //				common::logError(kErrorBadData, "input")
522 //					<< "Bad data: old emoji '"
523 //					<< InputIdToString(*i).toStdString()
524 //					<< "' (category "
525 //					<< (c - begin(old.categories))
526 //					<< ", index "
527 //					<< (i - begin(category))
528 //					<< ") not found in current.";
529 //				result = false;
530 //				continue;
531 //			}
532 //
533 //			const auto genderIndex = (1 - otherGenderIndex);
534 //			const auto real = addGenderByIndex(*i, genderIndex);
535 //			const auto bare = BareIdFromInput(real);
536 //			if (!findInMany(data, real)) {
537 //				common::logError(kErrorBadData, "input")
538 //					<< "Bad data: old emoji '"
539 //					<< InputIdToString(*i).toStdString()
540 //					<< "' (category "
541 //					<< (c - begin(old.categories))
542 //					<< ", index "
543 //					<< (i - begin(category))
544 //					<< ") not found in current with added gender: "
545 //					<< genderIndex
546 //					<< ".";
547 //				result = false;
548 //			} else {
549 //				AddAlias(bare, BareIdFromInput(*i));
550 //			}
551 		}
552 	}
553 	for (auto i = begin(old.doubleColored); i != end(old.doubleColored); ++i) {
554 		auto found = false;
555 		for (auto j = begin(data.doubleColored); j != end(data.doubleColored); ++j) {
556 			if (j->original == i->original) {
557 				found = true;
558 				if (j->same != i->same || j->different != i->different) {
559 					common::logError(kErrorBadData, "input")
560 						<< "Bad data: old double colored emoji (index "
561 						<< (i - begin(old.doubleColored))
562 						<< ") not equal to current.";
563 					result = false;
564 				}
565 				break;
566 			}
567 		}
568 		if (!found) {
569 			common::logError(kErrorBadData, "input")
570 				<< "Bad data: old double colored emoji (index "
571 				<< (i - begin(old.doubleColored))
572 				<< ") not found in current.";
573 			result = false;
574 		}
575 	}
576 	for (auto i = begin(old.colored); i != end(old.colored); ++i) {
577 		if (find(data.colored, *i)) {
578 			continue;
579 		}
580 
581 		const auto otherGenderIndex = [&] {
582 			for (auto g = begin(genders); g != end(genders); ++g) {
583 				auto altered = *i;
584 				altered.push_back(kJoiner);
585 				altered.push_back(*g);
586 				altered.push_back(kPostfix);
587 				if (find(old.colored, altered)) {
588 					return int(g - begin(genders));
589 				}
590 			}
591 			return -1;
592 		}();
593 		if (otherGenderIndex < 0) {
594 			common::logError(kErrorBadData, "input")
595 				<< "Bad data: old colored emoji (index "
596 				<< (i - begin(old.colored))
597 				<< ") not found in current.";
598 			result = false;
599 			continue;
600 		}
601 
602 		const auto genderIndex = (1 - otherGenderIndex);
603 		const auto real = addGenderByIndex(*i, genderIndex);
604 		if (!find(data.colored, real)) {
605 			common::logError(kErrorBadData, "input")
606 				<< "Bad data: old colored emoji (index "
607 				<< (i - begin(old.colored))
608 				<< ") not found in current with added gender: "
609 				<< genderIndex
610 				<< ".";
611 			result = false;
612 			continue;
613 		} else {
614 			for (const auto color : Colors) {
615 				if (!emplaceColoredAlias(real, *i, color)) {
616 					common::logError(kErrorBadData, "input")
617 						<< "Bad data: bad colored emoji.";
618 					result = false;
619 					break;
620 				}
621 			}
622 		}
623 	}
624 
625 	for (const auto &entry : WithoutGenderAliases) {
626 		const auto &inputId = entry.first;
627 		const auto &gender = entry.second;
628 		if (findInMany(data, inputId)) {
629 			continue;
630 		}
631 		const auto real = [&] {
632 			auto result = addGender(inputId, gender);
633 			if (findInMany(data, result)) {
634 				return result;
635 			}
636 			result.push_back(kPostfix);
637 			return result;
638 		}();
639 		const auto bare = BareIdFromInput(real);
640 		if (!findInMany(data, real)) {
641 			common::logError(kErrorBadData, "input")
642 				<< "Bad data: without gender alias not found with gender.";
643 			result = false;
644 		} else {
645 			AddAlias(bare, BareIdFromInput(inputId));
646 		}
647 		if (variatedIds.find(bare) != variatedIds.end()) {
648 			auto colorReal = real;
649 			colorReal.insert(colorReal.begin() + 1, Colors[0]);
650 			auto colorInput = inputId;
651 			colorInput.insert(colorInput.begin() + 1, Colors[0]);
652 			for (const auto color : Colors) {
653 				if (!emplaceColoredAlias(colorReal, colorInput, color)) {
654 					common::logError(kErrorBadData, "input")
655 						<< "Bad data: bad colored emoji.";
656 					result = false;
657 					break;
658 				}
659 			}
660 
661 		}
662 	}
663 
664 	for (const auto &[inputId, real] : FlagAliases) {
665 		AddAlias(BareIdFromInput(real), BareIdFromInput(inputId));
666 	}
667 
668 	return result;
669 }
670 
CheckOldInCurrent(const InputData & data,const std::set<Id> & variatedIds,const std::vector<QString> & oldDataPaths)671 bool CheckOldInCurrent(
672 		const InputData &data,
673 		const std::set<Id> &variatedIds,
674 		const std::vector<QString> &oldDataPaths) {
675 	if (!CheckOldInCurrent(data, GetDataOld1(), variatedIds)
676 		|| !CheckOldInCurrent(data, GetDataOld2(), variatedIds)) {
677 		return false;
678 	}
679 	for (const auto &path : oldDataPaths) {
680 		const auto old = ReadData(path);
681 		if (old.colored.empty() || old.doubleColored.empty()) {
682 			return false;
683 		} else if (!CheckOldInCurrent(data, old, variatedIds)) {
684 			return false;
685 		}
686 	}
687 	return true;
688 }
689 
690 } // namespace
691 
logDataError()692 common::LogStream logDataError() {
693 	return common::logError(kErrorBadData, "input") << "Bad data: ";
694 }
695 
PrepareData(const QString & dataPath,const std::vector<QString> & oldDataPaths)696 Data PrepareData(const QString &dataPath, const std::vector<QString> &oldDataPaths) {
697 	Data result;
698 
699 	auto input = ReadData(dataPath);
700 	const auto variatedIds = FillVariatedIds(input);
701 	const auto doubleVariatedIds = FillDoubleVariatedIds(input);
702 	const auto postfixRequiredIds = FillPostfixRequiredIds();
703 	if (variatedIds.empty() || doubleVariatedIds.empty() || postfixRequiredIds.empty()) {
704 		return Data();
705 	}
706 
707 	if (!CheckOldInCurrent(input, variatedIds, oldDataPaths)) {
708 		return Data();
709 	}
710 
711 	for (const auto &category : input.categories) {
712 		appendCategory(result, category, variatedIds, doubleVariatedIds, postfixRequiredIds);
713 		if (result.list.empty()) {
714 			return Data();
715 		}
716 	}
717 	appendCategory(result, input.other, variatedIds, doubleVariatedIds, postfixRequiredIds);
718 	if (result.list.empty()) {
719 		return Data();
720 	}
721 	if (AliasesAdded.size() != Aliases.size()) {
722 		for (const auto &[key, list] : Aliases) {
723 			if (AliasesAdded.find(key) == AliasesAdded.end()) {
724 				QStringList expanded;
725 				for (const auto &ch : key) {
726 					expanded.push_back(QString::number(ch.unicode()));
727 				}
728 				common::logError(kErrorBadData, "input")
729 					<< "Bad data: Not added aliases list for: "
730 					<< expanded.join(QChar(',')).toStdString();
731 			}
732 		}
733 		return Data();
734 	}
735 
736 	fillReplaces(result);
737 	if (result.list.empty()) {
738 		return Data();
739 	}
740 
741 	return result;
742 }
743 
744 } // namespace emoji
745 } // namespace codegen
746