8 #include "../backuphelper.h"
9 #include "../exceptions.h"
10 #include "../mediafileinfo.h"
12 #include "resources/config.h"
14 #include <c++utilities/conversion/stringbuilder.h>
15 #include <c++utilities/conversion/stringconversion.h>
21 #include <initializer_list>
25 #include <unordered_set>
28 using namespace std::placeholders;
38 std::uint64_t MatroskaContainer::m_maxFullParseSize = 0x3200000;
43 MatroskaContainer::MatroskaContainer(
MediaFileInfo &fileInfo, std::uint64_t startOffset)
70 m_tracksElements.clear();
71 m_segmentInfoElements.clear();
72 m_tagsElements.clear();
73 m_chaptersElements.clear();
74 m_attachmentsElements.clear();
76 m_editionEntries.clear();
77 m_attachments.clear();
87 static const string context(
"validating Matroska file index (cues)");
88 bool cuesElementsFound =
false;
90 unordered_set<EbmlElement::IdentifierType> ids;
91 bool cueTimeFound =
false, cueTrackPositionsFound =
false;
92 unique_ptr<EbmlElement> clusterElement;
93 std::uint64_t pos, prevClusterSize = 0, currentOffset = 0;
97 segmentElement->parse(diag);
100 segmentChildElement = segmentChildElement->nextSibling()) {
101 segmentChildElement->parse(diag);
102 switch (segmentChildElement->id()) {
107 cuesElementsFound =
true;
110 cuePointElement = cuePointElement->nextSibling()) {
111 cuePointElement->parse(diag);
112 cueTimeFound = cueTrackPositionsFound =
false;
113 switch (cuePointElement->id()) {
120 cuePointChildElement = cuePointChildElement->nextSibling()) {
121 cuePointChildElement->parse(diag);
122 switch (cuePointChildElement->id()) {
127 DiagLevel::Warning,
"\"CuePoint\"-element contains multiple \"CueTime\" elements.", context);
133 cueTrackPositionsFound =
true;
135 clusterElement.reset();
137 subElement = subElement->nextSibling()) {
138 subElement->parse(diag);
139 switch (subElement->id()) {
147 if (ids.count(subElement->id())) {
149 "\"CueTrackPositions\"-element contains multiple \"" % subElement->idToString() +
"\" elements.",
152 ids.insert(subElement->id());
161 "\"CueTrackPositions\"-element contains unknown element \"" % subElement->idToString() +
"\".",
164 switch (subElement->id()) {
171 clusterElement = make_unique<EbmlElement>(
172 *
this, segmentElement->dataOffset() + subElement->readUInteger() - currentOffset);
174 clusterElement->parse(diag);
177 "\"CueClusterPosition\" element at " % numberToString(subElement->startOffset())
178 +
" does not point to \"Cluster\"-element (points to "
179 + numberToString(clusterElement->startOffset()) +
").",
187 pos = subElement->readUInteger();
203 "\"CueTrackPositions\"-element does not contain mandatory element \"CueTrack\".", context);
205 if (!clusterElement) {
207 "\"CueTrackPositions\"-element does not contain mandatory element \"CueClusterPosition\".", context);
210 EbmlElement referenceElement(*
this, clusterElement->dataOffset() + pos);
212 referenceElement.
parse(diag);
213 switch (referenceElement.
id()) {
220 "\"CueRelativePosition\" element does not point to \"Block\"-, \"BlockGroup\", or "
221 "\"SimpleBlock\"-element (points to "
235 "\"CuePoint\"-element contains unknown element \"" % cuePointElement->idToString() +
"\".", context);
241 DiagLevel::Warning,
"\"CuePoint\"-element does not contain mandatory element \"CueTime\".", context);
243 if (!cueTrackPositionsFound) {
245 DiagLevel::Warning,
"\"CuePoint\"-element does not contain mandatory element \"CueClusterPosition\".", context);
255 clusterElementChild = clusterElementChild->nextSibling()) {
256 clusterElementChild->parse(diag);
257 switch (clusterElementChild->id()) {
263 if ((pos = clusterElementChild->readUInteger()) > 0
264 && (segmentChildElement->startOffset() - segmentElement->dataOffset() + currentOffset) != pos) {
266 argsToString(
"\"Position\"-element at ", clusterElementChild->startOffset(),
" points to ", pos,
267 " which is not the offset of the containing \"Cluster\"-element."),
273 if ((pos = clusterElementChild->readUInteger()) != prevClusterSize) {
275 argsToString(
"\"PrevSize\"-element at ", clusterElementChild->startOffset(),
" should be ", prevClusterSize,
276 " but is ", pos,
"."),
283 prevClusterSize = segmentChildElement->totalSize();
288 currentOffset += segmentElement->totalSize();
292 if (!cuesElementsFound) {
309 inline bool excludesOffset(
const vector<EbmlElement *> &elements, std::uint64_t offset)
311 return find_if(elements.cbegin(), elements.cend(), std::bind(
sameOffset, offset, _1)) == elements.cend();
316 for (
const auto &entry : m_editionEntries) {
317 const auto &chapters = entry->chapters();
318 if (index < chapters.size()) {
319 return chapters[index].get();
321 index -= chapters.size();
330 for (
const auto &entry : m_editionEntries) {
331 count += entry->chapters().size();
339 static const auto randomEngine(
340 default_random_engine(
static_cast<default_random_engine::result_type
>(chrono::system_clock::now().time_since_epoch().count())));
341 std::uint64_t attachmentId;
342 auto dice(bind(uniform_int_distribution<decltype(attachmentId)>(), randomEngine));
343 std::uint8_t tries = 0;
345 attachmentId = dice();
347 for (
const auto &
attachment : m_attachments) {
350 goto generateRandomId;
355 m_attachments.emplace_back(make_unique<MatroskaAttachment>());
371 if (!segmentElement) {
374 for (
const EbmlElement *childElement = segmentElement->
firstChild(); childElement; childElement = childElement->nextSibling()) {
375 if (childElement->id() == elementId) {
378 for (
const auto &seekInfo : m_seekInfos) {
379 for (
const auto &info : seekInfo->info()) {
380 if (info.first == elementId) {
403 static const string context(
"parsing header of Matroska container");
407 m_tracksElements.clear();
408 m_segmentInfoElements.clear();
409 m_tagsElements.clear();
412 std::uint64_t currentOffset = 0;
413 vector<MatroskaSeekInfo>::difference_type seekInfosIndex = 0;
416 for (
EbmlElement *topLevelElement =
m_firstElement.get(); topLevelElement; topLevelElement = topLevelElement->nextSibling()) {
418 topLevelElement->parse(diag);
419 switch (topLevelElement->id()) {
421 for (
EbmlElement *subElement = topLevelElement->
firstChild(); subElement; subElement = subElement->nextSibling()) {
423 subElement->parse(diag);
424 switch (subElement->id()) {
441 m_maxIdLength = subElement->readUInteger();
445 " bytes is not supported."),
451 m_maxSizeLength = subElement->readUInteger();
455 " bytes is not supported."),
462 diag.emplace_back(
DiagLevel::Critical,
"Unable to parse all children of EBML header.", context);
469 for (
EbmlElement *subElement = topLevelElement->
firstChild(); subElement; subElement = subElement->nextSibling()) {
471 subElement->parse(diag);
472 switch (subElement->id()) {
474 m_seekInfos.emplace_back(make_unique<MatroskaSeekInfo>());
475 m_seekInfos.back()->parse(subElement, diag);
478 if (
excludesOffset(m_tracksElements, subElement->startOffset())) {
479 m_tracksElements.push_back(subElement);
483 if (
excludesOffset(m_segmentInfoElements, subElement->startOffset())) {
484 m_segmentInfoElements.push_back(subElement);
489 m_tagsElements.push_back(subElement);
493 if (
excludesOffset(m_chaptersElements, subElement->startOffset())) {
494 m_chaptersElements.push_back(subElement);
498 if (
excludesOffset(m_attachmentsElements, subElement->startOffset())) {
499 m_attachmentsElements.push_back(subElement);
505 for (
auto i = m_seekInfos.cbegin() + seekInfosIndex, end = m_seekInfos.cend(); i != end; ++i, ++seekInfosIndex) {
506 for (
const auto &infoPair : (*i)->info()) {
507 std::uint64_t offset = currentOffset + topLevelElement->dataOffset() + infoPair.second;
510 argsToString(
"Offset (", offset,
") denoted by \"SeekHead\" element is invalid."), context);
512 auto element = make_unique<EbmlElement>(*
this, offset);
514 element->parse(diag);
515 if (element->id() != infoPair.first) {
517 argsToString(
"ID of element ", element->idToString(),
" at ", offset,
518 " does not match the ID denoted in the \"SeekHead\" element (0x",
519 numberToString(infoPair.first, 16),
")."),
522 switch (element->id()) {
557 argsToString(
"Can not parse element at ", offset,
" (denoted using \"SeekHead\" element)."), context);
563 if (((!m_tracksElements.empty() && !m_tagsElements.empty()) ||
fileInfo().size() > m_maxFullParseSize)
564 && !m_segmentInfoElements.empty()) {
570 diag.emplace_back(
DiagLevel::Critical,
"Unable to parse all children of \"Segment\"-element.", context);
574 currentOffset += topLevelElement->totalSize();
580 DiagLevel::Critical, argsToString(
"Unable to parse top-level element at ", topLevelElement->startOffset(),
'.'), context);
588 parseSegmentInfo(diag);
590 diag.emplace_back(
DiagLevel::Critical,
"Unable to parse EBML (segment) \"Info\"-element.", context);
603 void MatroskaContainer::parseSegmentInfo(
Diagnostics &diag)
605 if (m_segmentInfoElements.empty()) {
609 for (EbmlElement *element : m_segmentInfoElements) {
610 element->parse(diag);
611 EbmlElement *subElement = element->firstChild();
612 double rawDuration = 0.0;
614 bool hasTitle =
false;
616 subElement->parse(diag);
617 switch (subElement->id()) {
619 m_titles.emplace_back(subElement->readString());
623 rawDuration = subElement->readFloat();
629 subElement = subElement->nextSibling();
636 if (rawDuration > 0.0) {
647 void MatroskaContainer::readTrackStatisticsFromTags(Diagnostics &diag)
659 static const string context(
"parsing tags of Matroska container");
662 element->parse(diag);
663 for (
EbmlElement *subElement = element->
firstChild(); subElement; subElement = subElement->nextSibling()) {
664 subElement->parse(diag);
665 switch (subElement->id()) {
667 m_tags.emplace_back(make_unique<MatroskaTag>());
669 m_tags.back()->parse(*subElement, diag);
680 diag.emplace_back(
DiagLevel::Warning,
"\"Tags\"-element contains unknown child. It will be ignored.", context);
685 readTrackStatisticsFromTags(diag);
689 readTrackStatisticsFromTags(diag);
694 static const string context(
"parsing tracks of Matroska container");
697 element->parse(diag);
698 for (
EbmlElement *subElement = element->
firstChild(); subElement; subElement = subElement->nextSibling()) {
699 subElement->parse(diag);
700 switch (subElement->id()) {
702 m_tracks.emplace_back(make_unique<MatroskaTrack>(*subElement));
716 "\"Tracks\"-element contains unknown child element \"" % subElement->idToString() +
"\". It will be ignored.", context);
721 readTrackStatisticsFromTags(diag);
725 readTrackStatisticsFromTags(diag);
730 static const string context(
"parsing editions/chapters of Matroska container");
733 element->parse(diag);
734 for (
EbmlElement *subElement = element->
firstChild(); subElement; subElement = subElement->nextSibling()) {
735 subElement->parse(diag);
736 switch (subElement->id()) {
738 m_editionEntries.emplace_back(make_unique<MatroskaEditionEntry>(subElement));
740 m_editionEntries.back()->parseNested(diag);
742 m_editionEntries.pop_back();
744 diag.emplace_back(
DiagLevel::Critical, argsToString(
"Unable to parse edition entry ", m_editionEntries.size(),
'.'), context);
752 "\"Chapters\"-element contains unknown child element \"" % subElement->idToString() +
"\". It will be ignored.", context);
764 static const string context(
"parsing attachments of Matroska container");
765 for (
EbmlElement *element : m_attachmentsElements) {
767 element->parse(diag);
768 for (
EbmlElement *subElement = element->
firstChild(); subElement; subElement = subElement->nextSibling()) {
769 subElement->parse(diag);
770 switch (subElement->id()) {
772 m_attachments.emplace_back(make_unique<MatroskaAttachment>());
774 m_attachments.back()->parse(subElement, diag);
776 m_attachments.pop_back();
778 diag.emplace_back(
DiagLevel::Critical, argsToString(
"Unable to parse attached file ", m_attachments.size(),
'.'), context);
786 "\"Attachments\"-element contains unknown child element \"" % subElement->idToString() +
"\". It will be ignored.", context);
846 static const string context(
"making Matroska container");
847 progress.
updateStep(
"Calculating element sizes ...");
857 if (!level0Element) {
864 vector<MatroskaTagMaker> tagMaker;
865 tagMaker.reserve(
tags().size());
866 std::uint64_t tagElementsSize = 0;
867 std::uint64_t tagsSize;
868 vector<MatroskaAttachmentMaker> attachmentMaker;
869 attachmentMaker.reserve(m_attachments.size());
870 std::uint64_t attachedFileElementsSize = 0;
871 std::uint64_t attachmentsSize;
872 vector<MatroskaTrackHeaderMaker> trackHeaderMaker;
873 trackHeaderMaker.reserve(
tracks().size());
874 std::uint64_t trackHeaderElementsSize = 0;
875 std::uint64_t trackHeaderSize;
879 unsigned int segmentIndex = 0;
881 vector<SegmentData> segmentData;
883 std::uint64_t offset;
885 std::uint64_t totalOffset;
887 std::uint64_t currentPosition = 0;
889 vector<tuple<std::uint64_t, std::uint64_t>> crc32Offsets;
891 std::uint8_t sizeLength;
893 std::uint64_t clusterSize, clusterReadSize, clusterReadOffset;
905 unsigned int lastSegmentIndex = numeric_limits<unsigned int>::max();
907 std::uint64_t newPadding;
913 std::uint64_t ebmlHeaderDataSize = 2 * 7;
915 for (
auto headerValue :
926 constexpr
const char muxingAppName[] = APP_NAME
" v" APP_VERSION;
927 constexpr std::uint64_t muxingAppElementDataSize =
sizeof(muxingAppName) - 1;
928 constexpr std::uint64_t muxingAppElementTotalSize = 2 + 1 + muxingAppElementDataSize;
931 const std::uint64_t writingAppElementDataSize
933 const std::uint64_t writingAppElementTotalSize = 2 + 1 + writingAppElementDataSize;
940 if (tagMaker.back().requiredSize() > 3) {
942 tagElementsSize += tagMaker.back().requiredSize();
954 if (attachmentMaker.back().requiredSize() > 3) {
956 attachedFileElementsSize += attachmentMaker.back().requiredSize();
969 if (trackHeaderMaker.back().requiredSize() > 3) {
971 trackHeaderElementsSize += trackHeaderMaker.back().requiredSize();
983 for (
bool firstClusterFound =
false, firstTagFound =
false; level0Element; level0Element = level0Element->
nextSibling()) {
984 level0Element->
parse(diag);
985 switch (level0Element->
id()) {
988 for (level1Element = level0Element->
firstChild(); level1Element && !firstClusterFound && !firstTagFound;
990 level1Element->
parse(diag);
991 switch (level1Element->
id()) {
994 firstTagFound =
true;
997 firstClusterFound =
true;
1000 if (firstTagFound) {
1002 }
else if (firstClusterFound) {
1009 segmentData.resize(lastSegmentIndex + 1);
1020 "Unable to parse content in top-level element at " % numberToString(level0Element->
startOffset()) +
" of original file.", context);
1024 progress.
nextStepOrStop(
"Calculating offsets of elements before cluster ...");
1025 calculateSegmentData:
1028 std::uint64_t currentOffset = ebmlHeaderSize;
1030 std::uint64_t readOffset = 0;
1035 if (rewriteRequired) {
1046 for (level0Element =
firstElement(), currentPosition = newPadding = segmentIndex = 0; level0Element;
1048 switch (level0Element->
id()) {
1077 newCuesPos = currentCuesPos;
1090 calculateSegmentSize:
1103 goto calculateSegmentSize;
1107 segment.
infoDataSize = muxingAppElementTotalSize + writingAppElementTotalSize;
1109 if (segmentIndex <
m_titles.size()) {
1111 if (!
title.empty()) {
1116 for (level2Element = level1Element->
firstChild(); level2Element; level2Element = level2Element->
nextSibling()) {
1117 level2Element->
parse(diag);
1118 switch (level2Element->
id()) {
1136 if (trackHeaderSize) {
1139 goto calculateSegmentSize;
1151 goto calculateSegmentSize;
1167 goto calculateSegmentSize;
1174 if (attachmentsSize) {
1177 goto calculateSegmentSize;
1191 goto calculateSegmentSize;
1194 progress.
updateStep(
"Calculating cluster offsets and index size ...");
1199 progress.
updateStep(
"Calculating cluster offsets ...");
1203 if (!rewriteRequired) {
1210 if (totalOffset <= segment.firstClusterElement->
startOffset()) {
1219 diag.emplace_back(
DiagLevel::Critical,
"Header size of \"Segment\"-element from original file is invalid.", context);
1224 nonRewriteCalculations:
1229 goto calculateSegmentSize;
1232 bool cuesInvalidated =
false;
1240 cuesInvalidated =
true;
1245 if (index % 50 == 0) {
1249 if (cuesInvalidated) {
1251 goto addCuesElementSize;
1256 progress.
updateStep(
"Calculating offsets of elements after cluster ...");
1260 goto calculateSegmentSize;
1272 goto calculateSegmentSize;
1279 if (attachmentsSize) {
1282 goto calculateSegmentSize;
1296 goto nonRewriteCalculations;
1299 totalOffset = currentOffset + 4 + sizeLength + offset;
1304 if (totalOffset <= segment.firstClusterElement->
startOffset()) {
1310 rewriteRequired =
true;
1313 rewriteRequired =
true;
1316 rewriteRequired =
true;
1319 diag.emplace_back(
DiagLevel::Warning, argsToString(
"There are no clusters in segment ", segmentIndex,
"."), context);
1322 if (rewriteRequired) {
1328 rewriteRequired =
false;
1334 rewriteRequired =
false;
1337 goto calculateSegmentData;
1350 bool cuesInvalidated =
false;
1356 cuesInvalidated =
true;
1359 goto calculateSegmentSize;
1362 clusterSize = clusterReadSize = 0;
1363 for (level2Element = level1Element->
firstChild(); level2Element; level2Element = level2Element->
nextSibling()) {
1364 level2Element->
parse(diag);
1368 cuesInvalidated =
true;
1370 switch (level2Element->
id()) {
1378 clusterSize += level2Element->
totalSize();
1380 clusterReadSize += level2Element->
totalSize();
1389 if ((index % 50 == 0) &&
fileInfo().size()) {
1395 if (cuesInvalidated) {
1398 goto addCuesElementSize;
1402 progress.
updateStep(
"Calculating offsets of elements after cluster ...");
1406 goto calculateSegmentSize;
1421 goto calculateSegmentSize;
1428 if (attachmentsSize) {
1431 goto calculateSegmentSize;
1449 readOffset += level0Element->
totalSize();
1456 "The top-level element \"" % level0Element->
idToString() +
"\" of the original file is unknown and will just be copied.",
1458 currentOffset += level0Element->
totalSize();
1459 readOffset += level0Element->
totalSize();
1463 if (!rewriteRequired) {
1465 if ((rewriteRequired = (newPadding >
fileInfo().maxPadding() || newPadding <
fileInfo().minPadding()))) {
1467 goto calculateSegmentData;
1477 }
catch (
const std::ios_base::failure &failure) {
1478 diag.emplace_back(
DiagLevel::Critical, argsToString(
"An IO error occurred when parsing the original file: ", failure.what()), context);
1489 NativeFileStream backupStream;
1490 BinaryWriter outputWriter(&outputStream);
1493 if (rewriteRequired) {
1494 if (
fileInfo().saveFilePath().empty()) {
1500 }
catch (
const std::ios_base::failure &failure) {
1502 DiagLevel::Critical, argsToString(
"Creation of temporary file (to rewrite the original file) failed: ", failure.what()), context);
1508 backupStream.exceptions(ios_base::badbit | ios_base::failbit);
1512 }
catch (
const std::ios_base::failure &failure) {
1513 diag.emplace_back(
DiagLevel::Critical, argsToString(
"Opening streams to write output file failed: ", failure.what()), context);
1525 for (
auto &maker : attachmentMaker) {
1526 maker.bufferCurrentAttachments(diag);
1532 outputStream.open(
fileInfo().path(), ios_base::in | ios_base::out | ios_base::binary);
1533 }
catch (
const std::ios_base::failure &failure) {
1534 diag.emplace_back(
DiagLevel::Critical, argsToString(
"Opening the file with write permissions failed: ", failure.what()), context);
1545 outputStream.write(buff, sizeLength);
1555 for (level0Element =
firstElement(), segmentIndex = 0, currentPosition = 0; level0Element; level0Element = level0Element->
nextSibling()) {
1558 switch (level0Element->
id()) {
1573 progress.
updateStep(
"Writing segment header ...");
1576 outputStream.write(buff, sizeLength);
1577 segment.
newDataOffset = offset =
static_cast<std::uint64_t
>(outputStream.tellp());
1583 *(buff + 1) =
static_cast<char>(0x84);
1585 crc32Offsets.emplace_back(outputStream.tellp(), segment.
totalDataSize);
1586 outputStream.write(buff, 6);
1598 outputStream.write(buff, sizeLength);
1600 for (level2Element = level1Element->
firstChild(); level2Element; level2Element = level2Element->
nextSibling()) {
1601 switch (level2Element->
id()) {
1614 if (segmentIndex <
m_titles.size()) {
1616 if (!
title.empty()) {
1623 fileInfo().writingApplication().empty() ? muxingAppName :
fileInfo().writingApplication().data(), writingAppElementDataSize);
1627 if (trackHeaderElementsSize) {
1630 outputStream.write(buff, sizeLength);
1631 for (
auto &maker : trackHeaderMaker) {
1632 maker.make(outputStream);
1648 outputStream.write(buff, sizeLength);
1649 for (
auto &maker : tagMaker) {
1650 maker.make(outputStream);
1654 if (attachmentsSize) {
1657 outputStream.write(buff, sizeLength);
1658 for (
auto &maker : attachmentMaker) {
1659 maker.make(outputStream, diag);
1672 std::uint64_t voidLength;
1675 *buff =
static_cast<char>(voidLength = segment.
newPadding - 2) | 0x80;
1678 BE::getBytes(
static_cast<std::uint64_t
>((voidLength = segment.
newPadding - 9) | 0x100000000000000), buff);
1682 outputStream.write(buff, sizeLength);
1684 for (; voidLength; --voidLength) {
1685 outputStream.put(0);
1691 if (rewriteRequired) {
1694 static_cast<std::uint8_t
>((
static_cast<std::uint64_t
>(outputStream.tellp()) - offset) * 100 / segment.
totalDataSize));
1696 auto clusterSizesIterator = segment.
clusterSizes.cbegin();
1697 unsigned int index = 0;
1700 clusterSize = currentPosition + (
static_cast<std::uint64_t
>(outputStream.tellp()) - offset);
1704 outputStream.write(buff, sizeLength);
1706 for (level2Element = level1Element->
firstChild(); level2Element; level2Element = level2Element->
nextSibling()) {
1707 switch (level2Element->
id()) {
1715 level2Element->
copyEntirely(outputStream, diag,
nullptr);
1720 if (index % 50 == 0) {
1722 static_cast<std::uint8_t
>((
static_cast<std::uint64_t
>(outputStream.tellp()) - offset) * 100 / segment.
totalDataSize));
1728 static_cast<std::uint8_t
>((
static_cast<std::uint64_t
>(outputStream.tellp()) - offset) * 100 / segment.
totalDataSize));
1729 for (; level1Element; level1Element = level1Element->
nextSibling()) {
1730 for (level2Element = level1Element->
firstChild(); level2Element; level2Element = level2Element->
nextSibling()) {
1731 switch (level2Element->
id()) {
1735 level2Element->
dataSize() > 8 ? 8 :
static_cast<std::uint8_t
>(level2Element->
dataSize()));
1737 if (level2Element->
dataSize() < sizeLength) {
1739 outputStream.seekp(
static_cast<streamoff
>(level2Element->
startOffset()));
1743 outputStream.seekp(
static_cast<streamoff
>(level2Element->
dataOffset()));
1744 outputStream.write(buff, sizeLength);
1755 progress.
updateStep(
"Writing segment tail ...");
1767 outputStream.write(buff, sizeLength);
1768 for (
auto &maker : tagMaker) {
1769 maker.make(outputStream);
1773 if (attachmentsSize) {
1776 outputStream.write(buff, sizeLength);
1777 for (
auto &maker : attachmentMaker) {
1778 maker.make(outputStream, diag);
1793 level0Element->
copyEntirely(outputStream, diag,
nullptr);
1794 currentPosition += level0Element->
totalSize();
1799 progress.
updateStep(
"Reparsing output file ...");
1800 if (rewriteRequired) {
1805 if (!
fileInfo().saveFilePath().empty()) {
1811 outputStream.close();
1812 outputStream.open(
fileInfo().path(), ios_base::in | ios_base::out | ios_base::binary);
1815 const auto newSize =
static_cast<std::uint64_t
>(outputStream.tellp());
1819 outputStream.close();
1821 if (truncate(
fileInfo().path().c_str(),
static_cast<iostream::off_type
>(newSize)) == 0) {
1827 outputStream.open(
fileInfo().path(), ios_base::in | ios_base::out | ios_base::binary);
1837 diag.emplace_back(
DiagLevel::Critical,
"Unable to reparse the header of the new file.", context);
1842 if (!crc32Offsets.empty()) {
1843 progress.
updateStep(
"Updating CRC-32 checksums ...");
1844 for (
const auto &crc32Offset : crc32Offsets) {
1845 outputStream.seekg(
static_cast<streamoff
>(get<0>(crc32Offset) + 6));
1846 outputStream.seekp(
static_cast<streamoff
>(get<0>(crc32Offset) + 2));
1847 writer().writeUInt32LE(
reader().readCrc32(get<1>(crc32Offset) - 6));
1852 outputStream.flush();