void tst_qtemporaryfile::readwrite() { QFETCH(qint64, amount); const int dataSize = 4096; QByteArray data; data.fill('a', dataSize); QBENCHMARK { for (qint64 i = 0; i < amount; ++i) { QTemporaryFile file; file.open(); file.write(data); file.seek(0); file.read(dataSize); file.close(); } } }
bool KNMusicTagApev2::writeTag(const KNMusicAnalysisItem &analysisItem) { //Write the data according to the detail info. const KNMusicDetailInfo &detailInfo=analysisItem.detailInfo; //Check the file is still exist or not. QFile musicFile(detailInfo.filePath); //If the file is not exist then return false. if(!musicFile.exists()) { return false; } //Find the original tag data. //ID3v1 header flag. bool hasId3v1=false; //Generate a header structure. APEHeader header; //Generate the raw tag data list. QList<APETagItem> itemList; //Initial the tag start position. qint64 tagDataStart=-1, tagDataLength=-1; //Open the file first. if(musicFile.open(QIODevice::ReadOnly)) { //Generate a data stream. QDataStream musicDataStream(&musicFile); if(musicFile.size() > 128) { //ID3v1 header cache. char id3v1Header[3]; //Check whether there's ID3v1 tag in the music file. musicDataStream.skipRawData(musicFile.size()-128); //Read ID3v1 header. musicDataStream.readRawData(id3v1Header, 3); //Check the header, and we can know that whether there's ID3v1 //header. hasId3v1=(id3v1Header[0]=='T' && id3v1Header[1]=='A' && id3v1Header[2]=='G'); } //Check the beginning of the file. if(checkHeader(0, musicDataStream, header)) { //Set the tag data start. tagDataStart=0; //Set the tag length to header length. tagDataLength=header.size; //Check whether is a footer in the tag. if(header.size > APEv2HeaderSize) { //Check the footer. APEHeader footer; //Tried to parse the footer. if(checkHeader(header.size, musicDataStream, footer)) { //Update the tag length. tagDataLength+=APEv2HeaderSize; } } //Reset the device to start. musicDataStream.device()->reset(); //Skip the file data. musicDataStream.skipRawData(APEv2HeaderSize); } //Check the end of the file. else if(checkHeader(musicFile.size()-APEv2HeaderSize, musicDataStream, header)) { //Save the tag start data. int tagContentStart=musicFile.size()-header.size; //Check the footer. APEHeader footer; //Check whether there's a header in the tag. if(checkHeader(tagContentStart-APEv2HeaderSize, musicDataStream, footer)) { //Save the tag data start position as the header start position. //This is APEv2 tag. tagDataStart=tagContentStart-APEv2HeaderSize; //The tag length will be a header size + tag size. tagDataLength=APEv2HeaderSize + header.size; } else { //This is APEv1 tag. tagDataStart=tagContentStart; //The tag length will be tag size. tagDataLength=header.size; } //Reset the device to start. musicDataStream.device()->reset(); //Skip the file data. musicDataStream.skipRawData(tagContentStart); } //Check the position before ID3v1. Some file may have both ID3v1 and //APEv1/APEv2. else if(musicFile.size()>=(ID3v1nAPEv2 + APEv2HeaderSize) && //File size first. checkHeader(musicFile.size()-ID3v1nAPEv2, musicDataStream, header)) { //Save the tag start position. int tagContentStart=musicFile.size()-ID3v1Size-header.size; //Check the footer. APEHeader footer; //Check whether there's a header in the tag. if(checkHeader(tagContentStart-APEv2HeaderSize, musicDataStream, footer)) { //Save the tag data start position as the header start position. //This is APEv2 tag. tagDataStart=tagContentStart-APEv2HeaderSize; //The tag length will be a header size + tag size. tagDataLength=APEv2HeaderSize + header.size; } else { //This is APEv1 tag. tagDataStart=tagContentStart; //The tag length will be tag size. tagDataLength=header.size; } //Reset the device to start. musicDataStream.device()->reset(); //Skip the file data. musicDataStream.skipRawData(tagContentStart); } //Parse the data if we find the header. if(tagDataStart!=-1) { //Read the tag from the file. char *rawTagData=new char[header.size]; musicDataStream.readRawData(rawTagData, header.size); //Parse the raw tag data list. parseRawData(rawTagData, header, itemList); //Recover the memory. delete[] rawTagData; } //Close the music file. musicFile.close(); } //Add all the text labels to detail frames if it's not empty. for(int i=0; i<MusicDataCount; i++) { //Check if the text is empty. if(detailInfo.textLists[i].toString().isEmpty()) { continue; } //Get the frame key from hash group. QString key=m_indexKey.value(i, QString()); //If the key is empty, means you cannot write this data to APEv2. if(key.isEmpty()) { continue; } //Generate a data frame. APETagItem item; //Save the key. item.key=key; //According to the frame, generate the item. switch(i) { case DiscNumber: //If disc count isn't empty, then add disc count to disc number //data. item.value=detailInfo.textLists[DiscCount].toString().isEmpty()? detailInfo.textLists[DiscNumber].toString().toUtf8(): (detailInfo.textLists[DiscNumber].toString()+"/"+ detailInfo.textLists[DiscCount].toString()).toUtf8(); default: //Add the whole data to the item. item.value=detailInfo.textLists[i].toString().toUtf8(); } //Remove the all the original item. //We have to check the key from the back to the first, and we won't get //mess to the index. for(int i=itemList.size()-1; i>-1; i--) { //If the key is the same as current key, if(itemList.at(i).key==key) { //Remove it. itemList.removeAt(i); } } //Add current key to item list. itemList.append(item); } //Now translate the frame structure data to the raw data. QByteArray contentData; //Prepare the cache size. char numberCache[4]; //Simply transfered the APETagItem to content data. for(auto i=itemList.constBegin(); i!=itemList.constEnd(); ++i) { //Get the item size. quint32 size=(*i).value.size(); //First transfer item size to raw data into cache. numberToData(size, numberCache); //Add item size to content data. contentData.append(numberCache, 4); //Transfer the flag to raw data into cache. numberToData((*i).flag, numberCache); //Add flag data to content data. contentData.append(numberCache, 4); //Add item key to content data. contentData.append((*i).key.toUtf8()); //Add 0x00 for item key terminator. contentData.append('\0'); //Add item value. contentData.append((*i).value); } //Update the header data. header.size=contentData.size()+32; header.itemCount=itemList.size(); //We will write the APEv2 data to the end part of the file. Just before the //ID3v1. //Check the header data. //Open the music file again. if(!musicFile.open(QIODevice::ReadOnly)) { return false; } //Generate a temporary file, write the new data to the temporary file. QTemporaryFile updatedTagFile; //Open the temporary file, if we cannot open the temporary file it will be //failed to write the tag. if(!updatedTagFile.open()) { //Close the opened music file. musicFile.close(); return false; } //Initial the file size. qint64 dataSurplusSize=musicFile.size(); /* * Algorithm: * We treat the file as these two kinds of format: * APEv2 | xxxx (Content) (| ID3v1) * or * xxxx (Content) | APEv2 (| ID3v1) * So we have to process the ID3v1 at first. Then the file should be like * the following: * APEv2 | xxxx (Content) * or * xxxx (Content) | APEv2 * And now, we only have to check if the APEv2 is at the beginning or the * end of the content. */ //If we have ID3v1, then remove the tag from the copying bytes. if(hasId3v1) { //Reduce the ID3v1 tag size. dataSurplusSize-=128; } //Check whether we have original APEv2 header. if(tagDataStart!=-1) { //Reduce the tag size from the file size. dataSurplusSize-=tagDataLength; //Check whether the header is at the start of the music file. if(tagDataStart==0) { //It's at the beginning of the file. //Skip the Original APEv2 tag at the beginning of the file. musicFile.seek(tagDataLength); } } //Generate the music data cache. char *turboCache=new char[DataCacheSize]; int bytesRead; //Now copy all the content from the original file to temporary file. while(dataSurplusSize>0) { //Read the original data. bytesRead=musicFile.read(turboCache, (DataCacheSize < dataSurplusSize ? DataCacheSize : dataSurplusSize)); //Write the cache to temporary file. updatedTagFile.write(turboCache, bytesRead); //Reduce the surplus size. dataSurplusSize-=bytesRead; } //According to the hydrogenaud.io, we have to put the APEv2 at the end of //the file. //Write new APEv2 tag to the file. /* * From http://wiki.hydrogenaud.io/index.php?title=Ape_Tags_Flags: * Bit 29: * 0: This is the footer, not the header * 1: This is the header, not the footer */ //First set the item flag to header in header data bytearray. updatedTagFile.write(generateHeaderData(header, true)); //Then, write the content data. updatedTagFile.write(contentData); //Last, write the footer data. updatedTagFile.write(generateHeaderData(header, false)); //If there's ID3v1 tag, then copy the ID3v1 data from the original file. if(hasId3v1) { //Seek to the ID3v1 tag start. musicFile.seek(musicFile.size()-128); //Read 128 bytes ID3v1 tag. musicFile.read(turboCache, 128); //Write the cache to temporary file. updatedTagFile.write(turboCache, 128); } //Close the music file. musicFile.close(); //Reset the temporary file. updatedTagFile.reset(); //Reopen the music file as write only mode, write all the udpated tag file //data to the music file. if(!musicFile.open(QIODevice::WriteOnly)) { return false; } //Copy data from temporary file to music file. bytesRead=updatedTagFile.read(turboCache, DataCacheSize); while(bytesRead>0) { //Write the cache to music file. musicFile.write(turboCache, bytesRead); //Read new data from the original file to cache. bytesRead=updatedTagFile.read(turboCache, DataCacheSize); } //Close the music file and temporary file. musicFile.close(); updatedTagFile.close(); //Clear up the turbo cache. delete[] turboCache; //The tag rewrite is finished. return true; }
bool KNMusicCueListParser::writeDetail(const KNMusicAnalysisItem &analysisItem) { //Get the track index. const KNMusicDetailInfo &detailInfo=analysisItem.detailInfo; //Prepare the orignial file data and the temporary file data. QTemporaryFile updatedListFile; QFile listFile(detailInfo.trackFilePath); //Open the list file. if(!listFile.open(QIODevice::ReadOnly)) { return false; } //Open the list file. if(!updatedListFile.open()) { //Close the opened music file. listFile.close(); return false; } //Read the list file. //Initial the list file and temporary file stream as a text file. QTextStream trackStream(&listFile), temporaryStream(&updatedListFile); //Read until the track index comes to the track index. QString rawLine=trackStream.readLine(); //Check the raw line. while(!rawLine.isNull()) { //Simplified the raw line. QString commandRawLine=rawLine.simplified(); //Generate command part and data part cache. QString rawCommand, rawData; //Parse the command. parseCommand(commandRawLine, rawCommand, rawData); //Now we are only taken care about the TRACK. if(rawCommand=="TRACK") { //Use the trackIndex variable find the space temporarily. int trackIndex=rawData.indexOf(' '); //Get the track index. trackIndex= (trackIndex==-1?rawData:rawData.left(trackIndex)).toInt(); //Check the track index. if(trackIndex==detailInfo.trackIndex) { //Hit the track. //First we have to output the raw line data. temporaryStream << rawLine << '\n'; //Then check the detail info data. //We have to write out these informations: // PERFORMER Artist // TITLE Name QString trackData; trackData.append(generateLine( " TITLE ", detailInfo.textLists[Name].toString())); trackData.append(generateLine( " PERFORMER ", detailInfo.textLists[Artist].toString())); //Write the track data to temporary file. temporaryStream << trackData; //Read the original file again until to INDEX, skip all the //other data. rawLine=trackStream.readLine(); //Simplified the raw line. commandRawLine=rawLine.simplified(); //Parse the raw line. parseCommand(commandRawLine, rawCommand, rawData); //Read until we get the INDEX. while(!rawLine.isNull() && rawCommand!="INDEX") { //Continue skip the file. rawLine=trackStream.readLine(); //Simplified the raw line. commandRawLine=rawLine.simplified(); //Parse the raw line. parseCommand(commandRawLine, rawCommand, rawData); } //So now the data should be the first INDEX. //Output the data and continue copy the data. temporaryStream << rawLine << '\n'; } else { //Simply output the data. temporaryStream << rawLine << '\n'; } } else { //Simply output the data. temporaryStream << rawLine << '\n'; } //Read the next line. rawLine=trackStream.readLine(); } //Flush the data. temporaryStream << flush; //Now the data should be all done. //Close the list file. listFile.close(); //Reset the temporary file. updatedListFile.reset(); //Reopen the list file, open as write only mode. if(!listFile.open(QIODevice::WriteOnly)) { return false; } //Write all the data to list file. //Generate the music data cache. char *turboCache=new char[DataCacheSize]; //Now copy all the content from the original file to temporary file. int bytesRead=updatedListFile.read(turboCache, DataCacheSize); while(bytesRead>0) { //Write the cache to the list file. listFile.write(turboCache, bytesRead); //Read new data from the original file to cache. bytesRead=updatedListFile.read(turboCache, DataCacheSize); } //Close the list file and temporary file. listFile.close(); updatedListFile.close(); //Clear up the turbo cache. delete[] turboCache; //Written finished. return true; }
bool KNMusicTagM4a::writeTag(const KNMusicAnalysisItem &analysisItem) { //Get the detail info. const KNMusicDetailInfo &detailInfo=analysisItem.detailInfo; //Prepare and get the music file. QFile musicFile(detailInfo.filePath); //Open the file as read only mode. if(!musicFile.open(QIODevice::ReadOnly)) { //Failed to open the source. return false; } //Generate a temporary file, write the new data to the temporary file. QTemporaryFile updatedTagFile; //Open the temporary file, if we cannot open the temporary file it will be //failed to write the tag. if(!updatedTagFile.open()) { //Close the opened music file. musicFile.close(); return false; } //Generate a data stream for music file. QDataStream musicDataStream(&musicFile); //Read and copy the fytp box. If the first box isn't fytp box, then ignore //the file. M4ABox ftypBox; if(!getBox(musicDataStream, ftypBox) || ftypBox.name!="ftyp") { //Close both file. musicFile.close(); updatedTagFile.close(); //Failed to find a m4a file, return false. return false; } //Write ftyp data to the temporary file. writeBox(ftypBox, updatedTagFile); //We have to keep reading until we find out the moov box. //Output all the other data to updated tag file. M4ABox moovBox; for(;;) { //If we can get a new box. if(getBox(musicDataStream, moovBox)) { //Check out the name. if(moovBox.name=="moov") { break; } else { //Copy the data. writeBox(moovBox, updatedTagFile); } } else { //If we cannot find a box, means there's no "moov" box, failed to //write data. //Close both file. musicFile.close(); updatedTagFile.close(); //Failed to find a m4a file, return false. return false; } } //When we comes to here, we should find the "moov" box, expand the box to //find out the "udta" box. QList<M4ABox> moovExpandList; extractBox(moovBox, moovExpandList); //Generate a empty box for "udta" box. M4ABox udtaBox; //Check the expand list. if(moovExpandList.isEmpty() || !findBox("udta", udtaBox, moovExpandList)) { //If the name of the udta box is still empty, means there's no udta box //in the moov box, then we are faild to parse. //Close both file. musicFile.close(); updatedTagFile.close(); //Failed to find a m4a file, return false. return false; } //Expand the "udta" box, and find "meta" box. QList<M4ABox> udtaExpandList; extractBox(udtaBox, udtaExpandList); //Generate a empty box for "meta" box. M4ABox metaBox; //Check the expand list and find box. if(udtaExpandList.isEmpty() || !findBox("meta", metaBox, udtaExpandList)) { //If the name of the meta box is still empty, means we cannot find meta //box in the meta box, then we are finished to parse. //Close both file. musicFile.close(); updatedTagFile.close(); //Failed to find a m4a file, return false. return false; } //Okay, now we can parse the meta box. //Generate a box for ilst. M4ABox ilstBox; QList<M4ABox> metaExpandList; //Extract the meta box. extractMetaBox(metaBox, metaExpandList); //Find all box of the expand list. if(metaExpandList.isEmpty() || !findBox("ilst", ilstBox, metaExpandList)) { //We cannot find ilst box in the meta box. //Close both file. musicFile.close(); updatedTagFile.close(); //Failed to find a m4a file, return false. return false; } //Prepare the ilst expand list. QList<M4ABox> ilstExpandList; //Expand the ilst box. extractBox(ilstBox, ilstExpandList); //Now we have to write data to ilst expand list. for(int i=0; i<MusicDataCount; ++i) { //Get the atom name of current data. QString atomName=m_indexAtomMap.value(i, QString()); //Check if the atom name is empty, then go to the next. if(atomName.isEmpty()) { continue; } //Remove the exist data inside the ilst expand list. for(int j=ilstExpandList.size()-1; j>-1; --j) { //Check the name of the item. if(ilstExpandList.at(j).name==atomName) { //Remove it. ilstExpandList.removeAt(j); } } //Generate the raw data. QByteArray rawData; //Write the data to raw data. switch(i) { case TrackNumber: //Append three 0x00 first. rawData.append((char)0x00); rawData.append((char)0x00); rawData.append((char)0x00); //Append the track index. rawData.append((char)detailInfo.textLists[TrackNumber].toString() .toInt()); //Append splitter 0x00. rawData.append((char)0x00); //Append the track count. rawData.append((char)detailInfo.textLists[TrackCount].toString() .toInt()); //Append two 0x00 after. rawData.append((char)0x00); rawData.append((char)0x00); break; case DiscNumber: //Append three 0x00 first. rawData.append((char)0x00); rawData.append((char)0x00); rawData.append((char)0x00); //Append the disc index. rawData.append((char)detailInfo.textLists[DiscNumber].toString() .toInt()); //Append splitter 0x00. rawData.append((char)0x00); //Append the disc count. rawData.append((char)detailInfo.textLists[DiscCount].toString() .toInt()); //Append two 0x00 after. rawData.append((char)0x00); rawData.append((char)0x00); break; case Rating: //Append the rating to bytes. rawData.append((char)detailInfo.textLists[Rating].toString() .toInt()); break; default: //Translate the text data to UTF-8, without BOM. rawData=detailInfo.textLists[i].toString().toUtf8(); } //Generate the box. ilstExpandList.append(generateItemBox(i, atomName, rawData)); } //Remove all the album art atom. for(int j=ilstExpandList.size()-1; j>-1; --j) { //Check the name of the item. if(ilstExpandList.at(j).name=="covr") { //Remove it. ilstExpandList.removeAt(j); } } //Check album art. if(!analysisItem.coverImage.isNull()) { //Generate the raw data for the image. //Add the png raw data to image data. QByteArray imageData; QBuffer imageBuffer(&imageData); //Open the image buffer. imageBuffer.open(QIODevice::WriteOnly); //Save the data to image data. analysisItem.coverImage.save(&imageBuffer, "PNG"); //Close the image buffer. imageBuffer.close(); //Check the image data, if the data is not empty, then insert data. if(imageData.isEmpty()) { //Generate the flag data. char covrFlag[5]; covrFlag[0]=0x00; covrFlag[1]=0x00; covrFlag[2]=0x00; covrFlag[3]=14; //Generate item box, insert to list. ilstExpandList.append(generateItemBox(covrFlag, "covr", imageData)); } } //Combine the ilst data together. M4ABox updatedIlstBox=zipBox("ilst", ilstExpandList); //Clear the list and original ilst box. ilstExpandList.clear(); clearBox(ilstBox); //Replace the original ilst box. for(int i=metaExpandList.size()-1; i>-1; --i) { //Check the name. if(metaExpandList.at(i).name=="ilst") { //Replace the item. metaExpandList.replace(i, updatedIlstBox); //Stop searching. break; } } //Combine the meta expand list data. QByteArray metaRawData=combineBoxList(metaExpandList); //Clear up the meta expand list. metaExpandList.clear(); //Append the first four bytes raw data to the meta box raw data. metaRawData.prepend(metaBox.data, 4); //Clear up the no use meta box. clearBox(metaBox); clearBox(updatedIlstBox); //Set the data to new meta box. metaBox.name="meta"; metaBox.independence=true; metaBox.size=metaRawData.size(); metaBox.data=new char[metaBox.size]; memcpy(metaBox.data, metaRawData.data(), metaBox.size); //Replace the original meta box. for(int i=udtaExpandList.size()-1; i>-1; --i) { //Check the name. if(udtaExpandList.at(i).name=="meta") { //Replace the item. udtaExpandList.replace(i, metaBox); //Stop Searching. break; } } //Combine the udta data together. M4ABox updatedUdtaBox=zipBox("udta", udtaExpandList); //Clear the list and original udta box. udtaExpandList.clear(); clearBox(udtaBox); //Replace the original udta box. for(int i=moovExpandList.size()-1; i>-1; --i) { //Check the name. if(moovExpandList.at(i).name=="udta") { //Replace the item moovExpandList.replace(i, updatedUdtaBox); //Stop searching. break; } } //Combine the moov data together. M4ABox updatedMoovBox=zipBox("moov", moovExpandList); //Clear the list and original moov box. moovExpandList.clear(); clearBox(moovBox); //Write the new moov box to the updated tag file. writeBox(updatedMoovBox, updatedTagFile); //Clear up the updated moov box. clearBox(updatedMoovBox); //Copy the left data to the updated tag file. //Generate the music data cache. char *turboCache=new char[DataCacheSize]; //Copy the music data from the original music file, copy the //MusicDataCacheSize bytes once, until there's no bytes to copy. int bytesRead=musicFile.read(turboCache, DataCacheSize); while(bytesRead>0) { //Write the cache to temporary file. updatedTagFile.write(turboCache, bytesRead); //Read new data from the original file to cache. bytesRead=musicFile.read(turboCache, DataCacheSize); } //Close the music file. musicFile.close(); //Reset the temporary file. updatedTagFile.reset(); //Reopen the music file as write only mode, write all the udpated tag file //data to the music file. if(!musicFile.open(QIODevice::WriteOnly)) { //Close the updated tag file. updatedTagFile.close(); //Failed to write data. return false; } //Copy data from temporary file to music file. bytesRead=updatedTagFile.read(turboCache, DataCacheSize); while(bytesRead>0) { //Write the cache to music file. musicFile.write(turboCache, bytesRead); //Read new data from cache to the original file. bytesRead=updatedTagFile.read(turboCache, DataCacheSize); } //Close the music file and temporary file. musicFile.close(); updatedTagFile.close(); //Clear up the turbo memory. delete[] turboCache; //The tag rewrite is finished. return true; }
bool Packaging::ExtractTar(QTemporaryFile& tarFile, PackageData *packageData) { TarHeader tarHeader; if (!tarFile.open()) { Alerts::DisplayError(QString("Error opening temporary TAR archive:\n%1").arg(tarFile.fileName())); return (false); } bool previousEmpty = false; QProgressDialog progressDialog("Extracting files...", "Cancel", 0, tarFile.size()); progressDialog.setWindowModality(Qt::ApplicationModal); progressDialog.setWindowTitle("Heimdall Frontend"); while (!tarFile.atEnd()) { qint64 dataRead = tarFile.read(tarHeader.buffer, TarHeader::kBlockLength); if (dataRead != TarHeader::kBlockLength) { progressDialog.close(); Alerts::DisplayError("Package's TAR archive is malformed."); tarFile.close(); return (false); } progressDialog.setValue(tarFile.pos()); if (progressDialog.wasCanceled()) { tarFile.close(); progressDialog.close(); return (false); } //bool ustarFormat = strcmp(tarHeader.fields.magic, ustarMagic) == 0; bool empty = true; for (int i = 0; i < TarHeader::kBlockLength; i++) { if (tarHeader.buffer[i] != 0) { empty = false; break; } } if (empty) { if (previousEmpty) { // Two empty blocks in a row means we've reached the end of the archive. break; } } else { int checksum = 0; for (char *bufferIndex = tarHeader.buffer; bufferIndex < tarHeader.fields.checksum; bufferIndex++) checksum += static_cast<unsigned char>(*bufferIndex); checksum += 8 * ' '; checksum += static_cast<unsigned char>(tarHeader.fields.typeFlag); // Both the TAR and USTAR formats have terrible documentation, it's not clear if the following code is required. /*if (ustarFormat) { for (char *bufferIndex = tarHeader.fields.linkName; bufferIndex < tarHeader.fields.prefix + 155; bufferIndex++) checksum += static_cast<unsigned char>(*bufferIndex); }*/ bool parsed = false; // The size field is not always null terminated, so we must create a copy and null terminate it for parsing. char fileSizeString[13]; memcpy(fileSizeString, tarHeader.fields.size, 12); fileSizeString[12] = '\0'; qulonglong fileSize = QString(fileSizeString).toULongLong(&parsed, 8); if (!parsed) { progressDialog.close(); Alerts::DisplayError("Tar header contained an invalid file size."); tarFile.close(); return (false); } if (fileSize > 0 && tarHeader.fields.typeFlag == '0') { // We're working with a file. QString filename = QString::fromUtf8(tarHeader.fields.name); QTemporaryFile *outputFile = new QTemporaryFile("XXXXXX-" + filename); packageData->GetFiles().append(outputFile); if (!outputFile->open()) { progressDialog.close(); Alerts::DisplayError(QString("Failed to open output file: \n%1").arg(outputFile->fileName())); tarFile.close(); return (false); } qulonglong dataRemaining = fileSize; char readBuffer[TarHeader::kBlockReadCount * TarHeader::kBlockLength]; // Copy the file contents from tarFile to outputFile while (dataRemaining > 0) { qint64 fileDataToRead = (dataRemaining < TarHeader::kBlockReadCount * TarHeader::kBlockLength) ? dataRemaining : TarHeader::kBlockReadCount * TarHeader::kBlockLength; qint64 dataRead = tarFile.read(readBuffer, fileDataToRead + (TarHeader::kBlockLength - fileDataToRead % TarHeader::kBlockLength) % TarHeader::kBlockLength); if (dataRead < fileDataToRead || dataRead % TarHeader::kBlockLength != 0) { progressDialog.close(); Alerts::DisplayError("Unexpected read error whilst extracting package files."); tarFile.close(); outputFile->close(); remove(outputFile->fileName().toStdString().c_str()); return (false); } outputFile->write(readBuffer, fileDataToRead); dataRemaining -= fileDataToRead; progressDialog.setValue(tarFile.pos()); if (progressDialog.wasCanceled()) { tarFile.close(); outputFile->close(); remove(outputFile->fileName().toStdString().c_str()); progressDialog.close(); return (false); } } outputFile->close(); } else { progressDialog.close(); Alerts::DisplayError("Heimdall packages shouldn't contain links or directories."); tarFile.close(); return (false); } } previousEmpty = empty; } progressDialog.close(); tarFile.close(); return (true); }