fixed misc bugs

This commit is contained in:
Martchus 2015-12-22 17:00:14 +01:00
parent 6790b6c350
commit 47e7f4eea4
6 changed files with 143 additions and 21 deletions

View File

@ -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);

View File

@ -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.

View File

@ -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);

View File

@ -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 {

View File

@ -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;
}

View File

@ -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);
}
}