fixed misc bugs
This commit is contained in:
parent
6790b6c350
commit
47e7f4eea4
|
@ -67,9 +67,7 @@ void BasicFileInfo::open(bool readonly)
|
|||
void BasicFileInfo::reopen(bool readonly)
|
||||
{
|
||||
close();
|
||||
m_file.open(m_path.c_str(), readonly
|
||||
? ios_base::in | ios_base::binary
|
||||
: ios_base::in | ios_base::out | ios_base::binary);
|
||||
m_file.open(m_path, readonly ? ios_base::in | ios_base::binary : ios_base::in | ios_base::out | ios_base::binary);
|
||||
m_file.seekg(0, ios_base::end);
|
||||
m_size = m_file.tellg();
|
||||
m_file.seekg(0, ios_base::beg);
|
||||
|
|
|
@ -257,6 +257,43 @@ byte EbmlElement::makeId(GenericFileElement::identifierType id, char *buff)
|
|||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Makes the size denotation for the specified \a size and stores it to \a buff.
|
||||
* \param size Specifies the size to be denoted.
|
||||
* \param buff Specifies the buffer to store the denotation. Must be at least 8 bytes long.
|
||||
* \returns Returns the number of bytes written to \a buff.
|
||||
* \throws Throws InvalidDataException() if \a size can not be represented.
|
||||
*/
|
||||
byte EbmlElement::makeSizeDenotation(uint64 size, char *buff)
|
||||
{
|
||||
if(size < 126) {
|
||||
*buff = static_cast<byte>(size | 0x80);
|
||||
return 1;
|
||||
} else if(size <= 16382ul) {
|
||||
BE::getBytes(static_cast<uint16>(size | 0x4000), buff);
|
||||
return 2;
|
||||
} else if(size <= 2097150ul) {
|
||||
BE::getBytes(static_cast<uint32>((size | 0x200000) << 0x08), buff);
|
||||
return 3;
|
||||
} else if(size <= 268435454ul) {
|
||||
BE::getBytes(static_cast<uint32>(size | 0x10000000), buff);
|
||||
return 4;
|
||||
} else if(size <= 34359738366ul) {
|
||||
BE::getBytes(static_cast<uint64>((size | 0x800000000) << 0x18), buff);
|
||||
return 5;
|
||||
} else if(size <= 4398046511102ul) {
|
||||
BE::getBytes(static_cast<uint64>((size | 0x40000000000) << 0x10), buff);
|
||||
return 6;
|
||||
} else if(size <= 562949953421310ul) {
|
||||
BE::getBytes(static_cast<uint64>((size | 0x2000000000000) << 0x08), buff);
|
||||
return 7;
|
||||
} else if(size <= 72057594037927934ul) {
|
||||
BE::getBytes(static_cast<uint64>(size | 0x100000000000000), buff);
|
||||
return 8;
|
||||
}
|
||||
throw InvalidDataException();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Makes the size denotation for the specified \a size and stores it to \a buff.
|
||||
* \param size Specifies the size to be denoted.
|
||||
|
@ -353,6 +390,40 @@ byte EbmlElement::makeUInteger(uint64 value, char *buff)
|
|||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Writes \a value to \a buff.
|
||||
* \returns Returns the number of bytes written to \a buff.
|
||||
* \param minBytes Specifies the minimum number of bytes to use.
|
||||
*/
|
||||
byte EbmlElement::makeUInteger(uint64 value, char *buff, byte minBytes)
|
||||
{
|
||||
if(minBytes <= 1 && value <= 0xFFul) {
|
||||
*buff = static_cast<char>(value);
|
||||
return 1;
|
||||
} else if(minBytes <= 2 && value <= 0xFFFFul) {
|
||||
BE::getBytes(static_cast<uint16>(value), buff);
|
||||
return 2;
|
||||
} else if(minBytes <= 3 && value <= 0xFFFFFFul) {
|
||||
BE::getBytes(static_cast<uint32>(value << 0x08), buff);
|
||||
return 3;
|
||||
} else if(minBytes <= 4 && value <= 0xFFFFFFFFul) {
|
||||
BE::getBytes(static_cast<uint32>(value), buff);
|
||||
return 4;
|
||||
} else if(minBytes <= 5 && value <= 0xFFFFFFFFFFul) {
|
||||
BE::getBytes(static_cast<uint64>(value << 0x18), buff);
|
||||
return 5;
|
||||
} else if(minBytes <= 6 && value <= 0xFFFFFFFFFFFFul) {
|
||||
BE::getBytes(static_cast<uint64>(value << 0x10), buff);
|
||||
return 6;
|
||||
} else if(minBytes <= 7 && value <= 0xFFFFFFFFFFFFFFul) {
|
||||
BE::getBytes(static_cast<uint64>(value << 0x08), buff);
|
||||
return 7;
|
||||
} else {
|
||||
BE::getBytes(static_cast<uint64>(value), buff);
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Makes a simple EBML element.
|
||||
* \param stream Specifies the stream to write the data to.
|
||||
|
|
|
@ -65,9 +65,11 @@ public:
|
|||
static byte calculateIdLength(identifierType id);
|
||||
static byte calculateSizeDenotationLength(uint64 size);
|
||||
static byte makeId(identifierType id, char *buff);
|
||||
static byte makeSizeDenotation(uint64 size, char *buff, byte minBytes = 1);
|
||||
static byte makeSizeDenotation(uint64 size, char *buff);
|
||||
static byte makeSizeDenotation(uint64 size, char *buff, byte minBytes);
|
||||
static byte calculateUIntegerLength(uint64 integer);
|
||||
static byte makeUInteger(uint64 value, char *buff);
|
||||
static byte makeUInteger(uint64 value, char *buff, byte minBytes);
|
||||
static void makeSimpleElement(std::ostream &stream, identifierType id, uint64 content);
|
||||
static void makeSimpleElement(std::ostream &stream, identifierType id, const std::string &content);
|
||||
static void makeSimpleElement(std::ostream &stream, GenericFileElement::identifierType id, const char *data, std::size_t dataSize);
|
||||
|
|
|
@ -705,7 +705,6 @@ void MatroskaContainer::internalParseAttachments()
|
|||
struct SegmentData
|
||||
{
|
||||
SegmentData() :
|
||||
element(nullptr),
|
||||
hasCrc32(false),
|
||||
cuesElement(nullptr),
|
||||
infoDataSize(0),
|
||||
|
@ -715,11 +714,10 @@ struct SegmentData
|
|||
newPadding(0),
|
||||
sizeDenotationLength(0),
|
||||
totalDataSize(0),
|
||||
totalSize(0)
|
||||
totalSize(0),
|
||||
newDataOffset(0)
|
||||
{}
|
||||
|
||||
// "Segment"-element object (original file)
|
||||
EbmlElement *element;
|
||||
// whether CRC-32 checksum is present
|
||||
bool hasCrc32;
|
||||
// used to make "SeekHead"-element
|
||||
|
@ -746,6 +744,8 @@ struct SegmentData
|
|||
uint64 totalDataSize;
|
||||
// total size of the segment data (in the new file, including header)
|
||||
uint64 totalSize;
|
||||
// data offset of the segment in the new file
|
||||
uint64 newDataOffset;
|
||||
};
|
||||
|
||||
void MatroskaContainer::internalMakeFile()
|
||||
|
@ -861,8 +861,7 @@ void MatroskaContainer::internalMakeFile()
|
|||
|
||||
// inspect layout of original file
|
||||
// - number of segments
|
||||
// - media data / first cluster offset
|
||||
// - last level 0 element and last "Segment"-element
|
||||
// - position of tags relative to the media data
|
||||
try {
|
||||
for(bool firstClusterFound = false, firstTagFound = false; level0Element; level0Element = level0Element->nextSibling()) {
|
||||
level0Element->parse();
|
||||
|
@ -1098,7 +1097,7 @@ addCuesElementSize:
|
|||
segment.sizeDenotationLength = level0Element->headerSize() - 4;
|
||||
|
||||
nonRewriteCalculations:
|
||||
// pretend writing "Cluster"-elements assuming there is not rewrite required
|
||||
// pretend writing "Cluster"-elements assuming there is no rewrite required
|
||||
// -> update offset in "SeakHead"-element
|
||||
if(segment.seekInfo.push(0, MatroskaIds::Cluster, level1Element->startOffset() - 4 - segment.sizeDenotationLength - ebmlHeaderSize)) {
|
||||
goto calculateSegmentSize;
|
||||
|
@ -1153,6 +1152,7 @@ nonRewriteCalculations:
|
|||
if(segment.sizeDenotationLength != (sizeLength = EbmlElement::calculateSizeDenotationLength(segment.totalDataSize))) {
|
||||
// assumption was wrong -> recalculate with new length
|
||||
segment.sizeDenotationLength = sizeLength;
|
||||
level1Element = segment.firstClusterElement;
|
||||
goto nonRewriteCalculations;
|
||||
}
|
||||
|
||||
|
@ -1405,7 +1405,7 @@ nonRewriteCalculations:
|
|||
outputWriter.writeUInt32BE(MatroskaIds::Segment);
|
||||
sizeLength = EbmlElement::makeSizeDenotation(segment.totalDataSize, buff);
|
||||
outputStream.write(buff, sizeLength);
|
||||
offset = outputStream.tellp(); // store segment data offset here
|
||||
segment.newDataOffset = offset = outputStream.tellp(); // store segment data offset here
|
||||
|
||||
// write CRC-32 element ...
|
||||
if(segment.hasCrc32) {
|
||||
|
@ -1519,6 +1519,8 @@ nonRewriteCalculations:
|
|||
}
|
||||
}
|
||||
|
||||
// write media data / "Cluster"-elements
|
||||
level1Element = level0Element->childById(MatroskaIds::Cluster);
|
||||
if(rewriteRequired) {
|
||||
|
||||
// update status, check whether the operation has been aborted
|
||||
|
@ -1527,7 +1529,6 @@ nonRewriteCalculations:
|
|||
}
|
||||
updateStatus("Writing clusters ...", static_cast<double>(static_cast<uint64>(outputStream.tellp()) - offset) / segment.totalDataSize);
|
||||
// write "Cluster"-element
|
||||
level1Element = level0Element->childById(MatroskaIds::Cluster);
|
||||
for(auto clusterSizesIterator = segment.clusterSizes.cbegin();
|
||||
level1Element; level1Element = level1Element->siblingById(MatroskaIds::Cluster), ++clusterSizesIterator) {
|
||||
// calculate position of cluster in segment
|
||||
|
@ -1557,6 +1558,29 @@ nonRewriteCalculations:
|
|||
}
|
||||
}
|
||||
} else {
|
||||
// can't just skip existing "Cluster"-elements: "Position"-elements must be updated
|
||||
for(; level1Element; level1Element = level1Element->nextSibling()) {
|
||||
for(level2Element = level1Element->firstChild(); level2Element; level2Element = level2Element->nextSibling()) {
|
||||
switch(level2Element->id()) {
|
||||
case MatroskaIds::Position:
|
||||
// calculate new position
|
||||
sizeLength = EbmlElement::makeUInteger(level1Element->startOffset() - segmentData.front().newDataOffset, buff, level2Element->dataSize());
|
||||
// new position can only applied if it doesn't need more bytes than the previous position
|
||||
if(level2Element->dataSize() < sizeLength) {
|
||||
// can't update position -> void position elements ("Position"-elements seem a bit useless anyways)
|
||||
outputStream.seekp(level2Element->startOffset());
|
||||
outputStream.put(EbmlIds::Void);
|
||||
} else {
|
||||
// update position
|
||||
outputStream.seekp(level2Element->dataOffset());
|
||||
outputStream.write(buff, sizeLength);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
// skip existing "Cluster"-elements
|
||||
outputStream.seekp(segment.clusterEndOffset);
|
||||
}
|
||||
|
@ -1612,12 +1636,33 @@ nonRewriteCalculations:
|
|||
|
||||
// reparse what is written so far
|
||||
updateStatus("Reparsing output file ...");
|
||||
// -> report new size
|
||||
fileInfo().reportSizeChanged(outputStream.tellp());
|
||||
if(rewriteRequired) {
|
||||
outputStream.close(); // the outputStream needs to be reopened to be able to read again
|
||||
// report new size
|
||||
fileInfo().reportSizeChanged(outputStream.tellp());
|
||||
// the outputStream needs to be reopened to be able to read again
|
||||
outputStream.close();
|
||||
outputStream.open(fileInfo().path(), ios_base::in | ios_base::out | ios_base::binary);
|
||||
setStream(outputStream);
|
||||
} else {
|
||||
// TODO: truncate file
|
||||
const auto newSize = static_cast<uint64>(outputStream.tellp());
|
||||
if(newSize < fileInfo().size()) {
|
||||
// file is smaller after the modification -> truncate
|
||||
// -> close stream before truncating
|
||||
outputStream.close();
|
||||
// -> truncate file
|
||||
if(truncate(fileInfo().path().c_str(), newSize) == 0) {
|
||||
fileInfo().reportSizeChanged(newSize);
|
||||
} else {
|
||||
addNotification(NotificationType::Critical, "Unable to truncate the file.", context);
|
||||
}
|
||||
// -> reopen the stream again
|
||||
outputStream.open(fileInfo().path(), ios_base::in | ios_base::out | ios_base::binary);
|
||||
} else {
|
||||
// file is longer after the modification -> just report new size
|
||||
fileInfo().reportSizeChanged(newSize);
|
||||
}
|
||||
}
|
||||
reset();
|
||||
try {
|
||||
|
|
|
@ -196,7 +196,7 @@ bool MatroskaCuePositionUpdater::updateSize(EbmlElement *element, int shift)
|
|||
// apply new size
|
||||
size = newSize;
|
||||
return updated;
|
||||
} catch(out_of_range &) {
|
||||
} catch(const out_of_range &) {
|
||||
// the element is out of the scope of the cue position updater (likely the Segment element)
|
||||
return shift;
|
||||
}
|
||||
|
|
|
@ -680,23 +680,29 @@ calculatePadding:
|
|||
// reparse what is written so far
|
||||
updateStatus("Reparsing output file ...");
|
||||
if(rewriteRequired) {
|
||||
outputStream.close(); // the outputStream needs to be reopened to be able to read again
|
||||
// report new size
|
||||
fileInfo().reportSizeChanged(outputStream.tellp());
|
||||
// the outputStream needs to be reopened to be able to read again
|
||||
outputStream.close();
|
||||
outputStream.open(fileInfo().path(), ios_base::in | ios_base::out | ios_base::binary);
|
||||
setStream(outputStream);
|
||||
} else {
|
||||
// ensure invalid bytes at the end are truncated (if the modified file is smaller then the original file)
|
||||
const auto newSize = static_cast<uint64>(outputStream.tellp());
|
||||
if(newSize < fileInfo().size()) {
|
||||
// close stream before truncating
|
||||
// file is smaller after the modification -> truncate
|
||||
// -> close stream before truncating
|
||||
outputStream.close();
|
||||
// truncate file
|
||||
// -> truncate file
|
||||
if(truncate(fileInfo().path().c_str(), newSize) == 0) {
|
||||
fileInfo().reportSizeChanged(newSize);
|
||||
} else {
|
||||
addNotification(NotificationType::Critical, "Unable to truncate the file.", context);
|
||||
}
|
||||
// reopen the stream again
|
||||
// -> reopen the stream again
|
||||
outputStream.open(fileInfo().path(), ios_base::in | ios_base::out | ios_base::binary);
|
||||
} else {
|
||||
// file is longer after the modification -> just report new size
|
||||
fileInfo().reportSizeChanged(newSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue