Example #1
0
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();
        }
    }
}
Example #2
0
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;
}
Example #3
0
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;
}
Example #4
0
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;
}
Example #5
0
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);
}