Warning, file /multimedia/k3b/libk3b/projects/k3bcdrdaowriter.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     SPDX-FileCopyrightText: 2003-2009 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-FileCopyrightText: 2003 Klaus-Dieter Krannich <kd@k3b.org>
0004     SPDX-FileCopyrightText: 1998-2009 Sebastian Trueg <trueg@k3b.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 
0010 #include "k3bcdrdaowriter.h"
0011 
0012 #include "k3bcore.h"
0013 #include "k3bexternalbinmanager.h"
0014 #include "k3bdevicemanager.h"
0015 #include "k3bprocess.h"
0016 #include "k3bdevice.h"
0017 #include "k3bdevicehandler.h"
0018 #include "k3bthroughputestimator.h"
0019 #include "k3bglobals.h"
0020 #include "k3bglobalsettings.h"
0021 #include "k3b_i18n.h"
0022 
0023 #include <KIO/CopyJob>
0024 #include <KIO/DeleteJob>
0025 
0026 #include <QDebug>
0027 #include <QDir>
0028 #include <QFile>
0029 #include <QFileInfo>
0030 #include <QRegularExpression>
0031 #include <QString>
0032 #include <QStringList>
0033 #include <QTemporaryFile>
0034 #include <QTcpSocket>
0035 
0036 #include <sys/types.h>
0037 #include <sys/socket.h>
0038 #include <unistd.h>
0039 
0040 
0041 
0042 #define PGSMSG_MIN PGSMSG_RCD_ANALYZING
0043 #define PGSMSG_RCD_ANALYZING   1
0044 #define PGSMSG_RCD_EXTRACTING  2
0045 #define PGSMSG_WCD_LEADIN      3
0046 #define PGSMSG_WCD_DATA        4
0047 #define PGSMSG_WCD_LEADOUT     5
0048 #define PGSMSG_BLK             6
0049 #define PGSMSG_MAX PGSMSG_BLK
0050 
0051 struct ProgressMsg {
0052     int status;         // see PGSMSG_* constants
0053     int totalTracks;    // total number of tracks
0054     int track;          // actually written track
0055     int trackProgress;  // progress for current track 0..1000
0056     int totalProgress;  // total writing progress 0..1000
0057     int bufferFillRate; // buffer fill rate 0..100
0058 };
0059 
0060 #define PSGMSG_MINSIZE 24
0061 
0062 struct ProgressMsg2 {
0063     int status;         // see PGSMSG_* constants
0064     int totalTracks;    // total number of tracks
0065     int track;          // actually written track
0066     int trackProgress;  // progress for current track 0..1000
0067     int totalProgress;  // total writing progress 0..1000
0068     int bufferFillRate; // buffer fill rate 0..100
0069     int writerFillRate; // device write buffer fill rate 0..100
0070 };
0071 
0072 
0073 inline bool operator<( const ProgressMsg2& m1, const ProgressMsg2& m2 )
0074 {
0075     return m1.track < m2.track
0076         || ( m1.track == m2.track
0077              && m1.trackProgress < m2.trackProgress )
0078         || m1.totalProgress < m2.totalProgress;
0079 }
0080 
0081 
0082 inline bool operator==( const ProgressMsg2& m1, const ProgressMsg2& m2 )
0083 {
0084     return m1.status == m2.status
0085         && m1.track == m2.track
0086         && m1.totalTracks == m2.totalTracks
0087         && m1.trackProgress == m2.trackProgress
0088         && m1.totalProgress == m2.totalProgress
0089         && m1.bufferFillRate == m2.bufferFillRate;
0090 }
0091 
0092 inline bool operator!=( const ProgressMsg2& m1, const ProgressMsg2& m2 )
0093 {
0094     return !( m1 == m2 );
0095 }
0096 
0097 
0098 
0099 class K3b::CdrdaoWriter::Private
0100 {
0101 public:
0102     Private() {
0103     }
0104 
0105     K3b::ThroughputEstimator* speedEst;
0106 
0107     int usedSpeed;
0108 
0109     ProgressMsg2 oldMsg;
0110     ProgressMsg2 newMsg;
0111 
0112     unsigned int progressMsgSize;
0113 };
0114 
0115 
0116 K3b::CdrdaoWriter::CdrdaoWriter( K3b::Device::Device* dev, K3b::JobHandler* hdl,
0117                                  QObject* parent )
0118     : K3b::AbstractWriter( dev, hdl, parent ),
0119       m_command(WRITE),
0120       m_blankMode(FormattingQuick),
0121       m_sourceDevice(0),
0122       m_readRaw(false),
0123       m_multi(false),
0124       m_force(false),
0125       m_onTheFly(false),
0126       m_fastToc(false),
0127       m_readSubchan(None),
0128       m_taoSource(false),
0129       m_taoSourceAdjust(-1),
0130       m_paranoiaMode(-1),
0131       m_session(-1),
0132       m_eject( false ),
0133       m_process(0),
0134       m_comSock(0),
0135       m_currentTrack(0)
0136 {
0137     d = new Private();
0138     d->speedEst = new K3b::ThroughputEstimator( this );
0139     connect( d->speedEst, SIGNAL(throughput(int)),
0140              this, SLOT(slotThroughput(int)) );
0141 
0142     ::memset( &d->oldMsg, 0, sizeof(ProgressMsg2) );
0143     ::memset( &d->newMsg, 0, sizeof(ProgressMsg2) );
0144 #ifndef Q_OS_WIN32
0145     if( socketpair(AF_UNIX,SOCK_STREAM,0,m_cdrdaoComm) )
0146     {
0147 #endif
0148         qDebug() << "(K3b::CdrdaoWriter) could not open socketpair for cdrdao remote messages";
0149 #ifndef Q_OS_WIN32
0150     }
0151     else
0152     {
0153         delete m_comSock;
0154         m_comSock = new QTcpSocket();
0155         m_comSock->setSocketDescriptor( m_cdrdaoComm[1] );
0156         m_comSock->setReadBufferSize(49152);
0157         // magic number from Qt documentation
0158         connect( m_comSock, SIGNAL(readyRead()),
0159                  this, SLOT(parseCdrdaoMessage()));
0160     }
0161 #endif
0162 }
0163 
0164 K3b::CdrdaoWriter::~CdrdaoWriter()
0165 {
0166     delete d->speedEst;
0167     delete d;
0168 
0169 #ifndef Q_OS_WIN32
0170     // close the socket
0171     if( m_comSock ) {
0172         m_comSock->close();
0173         ::close( m_cdrdaoComm[0] );
0174     }
0175 #endif
0176     delete m_process;
0177     delete m_comSock;
0178 }
0179 
0180 
0181 bool K3b::CdrdaoWriter::active() const
0182 {
0183     return (m_process ? m_process->isRunning() : false);
0184 }
0185 
0186 
0187 void K3b::CdrdaoWriter::prepareArgumentList()
0188 {
0189 
0190     // binary
0191     *m_process << m_cdrdaoBinObject;
0192 
0193     // command
0194     switch ( m_command )
0195     {
0196     case COPY:
0197         *m_process << "copy";
0198         setWriteArguments();
0199         setReadArguments();
0200         setCopyArguments();
0201         break;
0202     case WRITE:
0203         *m_process << "write";
0204         setWriteArguments();
0205         break;
0206     case READ:
0207         *m_process << "read-cd";
0208         // source device and source driver
0209         if ( m_sourceDevice )
0210             *m_process << "--device"
0211                        << K3b::externalBinDeviceParameter(m_sourceDevice, m_cdrdaoBinObject);
0212         if( defaultToGenericMMC( m_sourceDevice, false ) ) {
0213             qDebug() << "(K3b::CdrdaoWriter) defaulting to generic-mmc driver for " << m_sourceDevice->blockDeviceName();
0214             *m_process << "--driver" << "generic-mmc";
0215         }
0216         setReadArguments();
0217         break;
0218     case BLANK:
0219         *m_process << "blank";
0220         setBlankArguments();
0221         break;
0222     }
0223 
0224     setCommonArguments();
0225 }
0226 
0227 void K3b::CdrdaoWriter::setWriteArguments()
0228 {
0229     // device and driver
0230     *m_process << "--device"
0231                << K3b::externalBinDeviceParameter(burnDevice(), m_cdrdaoBinObject);
0232 
0233     if( defaultToGenericMMC( burnDevice(), true ) ) {
0234         qDebug() << "(K3b::CdrdaoWriter) defaulting to generic-mmc driver for " << burnDevice()->blockDeviceName();
0235         *m_process << "--driver" << "generic-mmc:0x00000010";
0236     }
0237 
0238     // burn speed
0239     if( d->usedSpeed != 0 )
0240         *m_process << "--speed" << QString("%1").arg(d->usedSpeed);
0241 
0242     //simulate
0243     if( simulate() )
0244         *m_process << "--simulate";
0245 
0246     // multi
0247     if( m_multi )
0248         *m_process << "--multi";
0249 
0250     // force
0251     if( m_force )
0252         *m_process << "--force";
0253 
0254     // burnproof
0255     if ( !k3bcore->globalSettings()->burnfree() ) {
0256         if( m_cdrdaoBinObject->hasFeature( "disable-burnproof" ) )
0257             *m_process << "--buffer-under-run-protection" << "0";
0258         else
0259             emit infoMessage( i18n("Cdrdao %1 does not support disabling burnfree.",m_cdrdaoBinObject->version()), MessageWarning );
0260     }
0261 
0262     if( k3bcore->globalSettings()->force() ) {
0263         *m_process << "--force";
0264         emit infoMessage( i18n("'Force unsafe operations' enabled."), MessageWarning );
0265     }
0266 
0267     bool manualBufferSize =
0268         k3bcore->globalSettings()->useManualBufferSize();
0269     if( manualBufferSize ) {
0270         //
0271         // one buffer in cdrdao holds 1 second of audio data = 75 frames = 75 * 2352 bytes
0272         //
0273         int bufSizeInMb = k3bcore->globalSettings()->bufferSize();
0274         *m_process << "--buffers" << QString::number( bufSizeInMb*1024*1024/(75*2352) );
0275     }
0276 
0277     bool overburn =
0278         k3bcore->globalSettings()->overburn();
0279     if( overburn ) {
0280         if( m_cdrdaoBinObject->hasFeature("overburn") )
0281             *m_process << "--overburn";
0282         else
0283             emit infoMessage( i18n("Cdrdao %1 does not support overburning.",m_cdrdaoBinObject->version()), MessageWarning );
0284     }
0285 
0286 }
0287 
0288 void K3b::CdrdaoWriter::setReadArguments()
0289 {
0290     // readRaw
0291     if ( m_readRaw )
0292         *m_process << "--read-raw";
0293 
0294     // subchan
0295     if ( m_readSubchan != None )
0296     {
0297         *m_process << "--read-subchan";
0298         switch ( m_readSubchan )
0299         {
0300         case RW:
0301             *m_process << "rw";
0302             break;
0303         case RW_RAW:
0304             *m_process << "rw_raw";
0305             break;
0306         case None:
0307             break;
0308         }
0309     }
0310 
0311     // TAO Source
0312     if ( m_taoSource )
0313         *m_process << "--tao-source";
0314 
0315     // TAO Source Adjust
0316     if ( m_taoSourceAdjust != -1 )
0317         *m_process << "--tao-source-adjust"
0318                    << QString("%1").arg(m_taoSourceAdjust);
0319 
0320     // paranoia Mode
0321     if ( m_paranoiaMode != -1 )
0322         *m_process << "--paranoia-mode"
0323                    << QString("%1").arg(m_paranoiaMode);
0324 
0325     // session
0326     if ( m_session != -1 )
0327         *m_process << "--session"
0328                    << QString("%1").arg(m_session);
0329 
0330     // fast TOC
0331     if ( m_fastToc )
0332         *m_process << "--fast-toc";
0333 
0334 }
0335 
0336 void K3b::CdrdaoWriter::setCopyArguments()
0337 {
0338     // source device and source driver
0339     *m_process << "--source-device" << K3b::externalBinDeviceParameter(m_sourceDevice, m_cdrdaoBinObject);
0340     if( defaultToGenericMMC( m_sourceDevice, false ) ) {
0341         qDebug() << "(K3b::CdrdaoWriter) defaulting to generic-mmc driver for " << m_sourceDevice->blockDeviceName();
0342         *m_process << "--source-driver" << "generic-mmc";
0343     }
0344 
0345     // on-the-fly
0346     if ( m_onTheFly )
0347         *m_process << "--on-the-fly";
0348 }
0349 
0350 void K3b::CdrdaoWriter::setBlankArguments()
0351 {
0352     // device and driver
0353     *m_process << "--device"
0354                << K3b::externalBinDeviceParameter(burnDevice(), m_cdrdaoBinObject);
0355 
0356     if( defaultToGenericMMC( burnDevice(), true ) ) {
0357         qDebug() << "(K3b::CdrdaoWriter) defaulting to generic-mmc driver for " << burnDevice()->blockDeviceName();
0358         *m_process << "--driver" << "generic-mmc";
0359     }
0360 
0361     // burn speed
0362     if( d->usedSpeed != 0 )
0363         *m_process << "--speed" << QString("%1").arg(d->usedSpeed);
0364 
0365     // blank-mode
0366     switch (m_blankMode)
0367     {
0368     case FormattingComplete:
0369         *m_process << "--blank-mode" << "full";
0370         break;
0371     case FormattingQuick:
0372         *m_process << "--blank-mode" << "minimal";
0373         break;
0374     }
0375 }
0376 
0377 void K3b::CdrdaoWriter::setCommonArguments()
0378 {
0379 
0380     // additional user parameters from config
0381     const QStringList& params = m_cdrdaoBinObject->userParameters();
0382     for( QStringList::const_iterator it = params.begin(); it != params.end(); ++it )
0383         *m_process << *it;
0384 
0385 
0386     // display debug info
0387     *m_process << "-n" << "-v" << "2";
0388 
0389     // we have the power to do what ever we want. ;)
0390     *m_process << "--force";
0391 
0392     // eject
0393     if( m_eject )
0394         *m_process << "--eject";
0395 
0396     // remote
0397     *m_process << "--remote" <<  QString("%1").arg(m_cdrdaoComm[0]);
0398 
0399     // data File
0400     if ( ! m_dataFile.isEmpty() )
0401         *m_process << "--datafile" << m_dataFile;
0402 
0403     // BIN/CUE
0404     if ( ! m_cueFileLnk.isEmpty() )
0405         *m_process << m_cueFileLnk;
0406     // TOC File
0407     else if ( ! m_tocFile.isEmpty() )
0408         *m_process << m_tocFile;
0409 }
0410 
0411 K3b::CdrdaoWriter* K3b::CdrdaoWriter::addArgument( const QString& arg )
0412 {
0413     *m_process << arg;
0414     return this;
0415 }
0416 
0417 
0418 void K3b::CdrdaoWriter::start()
0419 {
0420     jobStarted();
0421 
0422     d->speedEst->reset();
0423 
0424     delete m_process;  // kdelibs want this!
0425     m_process = new K3b::Process();
0426     m_process->setSplitStdout(false);
0427     m_process->setOutputChannelMode( KProcess::MergedChannels );
0428     m_process->setFlags( K3bQProcess::RawStdin );
0429     connect( m_process, SIGNAL(stdoutLine(QString)),
0430              this, SLOT(slotStdLine(QString)) );
0431     connect( m_process, SIGNAL(finished(int,QProcess::ExitStatus)),
0432              this, SLOT(slotProcessExited(int,QProcess::ExitStatus)) );
0433 
0434     m_canceled = false;
0435     m_knownError = false;
0436 
0437     m_cdrdaoBinObject = k3bcore->externalBinManager()->binObject("cdrdao");
0438 
0439     if( !m_cdrdaoBinObject ) {
0440         emit infoMessage( i18n("Could not find %1 executable.",QString("cdrdao")), MessageError );
0441         jobFinished(false);
0442         return;
0443     }
0444 
0445     emit debuggingOutput( QLatin1String("Used versions"), QString::fromLatin1( "cdrdao: %1" ).arg( m_cdrdaoBinObject->version()) );
0446 
0447     if( !m_cdrdaoBinObject->copyright().isEmpty() )
0448         emit infoMessage( i18n("Using %1 %2 – Copyright © %3",m_cdrdaoBinObject->name(),m_cdrdaoBinObject->version(),m_cdrdaoBinObject->copyright()), MessageInfo );
0449 
0450 
0451     // the message size changed in cdrdao 1.1.8)
0452     if( m_cdrdaoBinObject->version() >= K3b::Version( 1, 1, 8 ) )
0453         d->progressMsgSize = sizeof(ProgressMsg2);
0454     else
0455         d->progressMsgSize = sizeof(ProgressMsg);
0456 
0457     // since the --speed parameter is used several times in this code we
0458     // determine the speed in auto once at the beginning
0459     d->usedSpeed = burnSpeed();
0460     if( d->usedSpeed == 0 ) {
0461         // try to determine the writeSpeed
0462         // if it fails determineMaximalWriteSpeed() will return 0 and
0463         // the choice is left to cdrdao
0464         d->usedSpeed = burnDevice()->determineMaximalWriteSpeed();
0465     }
0466     d->usedSpeed /= 175;
0467 
0468     switch ( m_command )
0469     {
0470     case WRITE:
0471     case COPY:
0472         if (!m_tocFile.isEmpty())
0473         {
0474 
0475             // if tocfile is a cuesheet than create symlinks to *.cue and the binary listed inside the cuesheet.
0476             // now works without the .bin extension too.
0477             if ( !cueSheet() ) {
0478                 m_backupTocFile = m_tocFile + ".k3bbak";
0479 
0480                 // workaround, cdrdao deletes the tocfile when --remote parameter is set
0481                 KIO::CopyJob* copyJob = KIO::copyAs(QUrl::fromLocalFile(m_tocFile),QUrl::fromLocalFile(m_backupTocFile), KIO::HideProgressInfo);
0482                 bool copyJobSucceed = true;
0483                 connect(copyJob, &KJob::result, [&](KJob*) {
0484                     if( copyJob->error() != KJob::NoError ) {
0485                         qDebug() << "(K3b::CdrdaoWriter) could not backup " << m_tocFile << " to " << m_backupTocFile;
0486                         emit infoMessage( i18n("Could not backup tocfile."), MessageError );
0487                         jobFinished(false);
0488                         copyJobSucceed = false;
0489                     }
0490                 } );
0491                 copyJob->exec();
0492                 if( !copyJobSucceed )
0493                     return;
0494             }
0495         }
0496         break;
0497     case BLANK:
0498     case READ:
0499         break;
0500     }
0501     prepareArgumentList();
0502     // set working dir to dir part of toc file (to allow rel names in toc-file)
0503     m_process->setWorkingDirectory( QFileInfo( m_tocFile ).absolutePath() );
0504 
0505     qDebug() << "***** cdrdao parameters:\n";
0506     QString s = m_process->joinedArgs();
0507     qDebug() << s << Qt::flush;
0508     emit debuggingOutput("cdrdao command:", s);
0509 
0510     m_currentTrack = 0;
0511     reinitParser();
0512 
0513     switch ( m_command )
0514     {
0515     case READ:
0516         emit newSubTask( i18n("Preparing read process...") );
0517         break;
0518     case WRITE:
0519         emit newSubTask( i18n("Preparing write process...") );
0520         break;
0521     case COPY:
0522         emit newSubTask( i18n("Preparing copy process...") );
0523         break;
0524     case BLANK:
0525         emit newSubTask( i18n("Preparing blanking process...") );
0526         break;
0527     }
0528 
0529     // FIXME: check the return value
0530     if( K3b::isMounted( burnDevice() ) ) {
0531         emit infoMessage( i18n("Unmounting medium"), MessageInfo );
0532         K3b::unmount( burnDevice() );
0533     }
0534 
0535     // block the device (including certain checks)
0536     k3bcore->blockDevice( burnDevice() );
0537 
0538     // lock the device for good in this process since it will
0539     // be opened in the growisofs process
0540     burnDevice()->close();
0541     burnDevice()->usageLock();
0542 
0543     if( !m_process->start( KProcess::MergedChannels ) )
0544     {
0545         // something went wrong when starting the program
0546         // it "should" be the executable
0547         qDebug() << "(K3b::CdrdaoWriter) could not start cdrdao";
0548         emit infoMessage( i18n("Could not start %1.",QString("cdrdao")), K3b::Job::MessageError );
0549         jobFinished(false);
0550     }
0551     else
0552     {
0553         switch ( m_command )
0554         {
0555         case WRITE:
0556             if( simulate() )
0557             {
0558                 // xgettext: no-c-format
0559                 emit infoMessage(i18n("Starting DAO simulation at %1x speed...",d->usedSpeed),
0560                                  K3b::Job::MessageInfo );
0561                 emit newTask( i18n("Simulating") );
0562             }
0563             else
0564             {
0565                 // xgettext: no-c-format
0566                 emit infoMessage( i18n("Starting DAO writing at %1x speed...",d->usedSpeed), K3b::Job::MessageInfo );
0567                 emit newTask( i18n("Writing") );
0568             }
0569             break;
0570         case READ:
0571             emit infoMessage(i18n("Starting reading..."), K3b::Job::MessageInfo );
0572             emit newTask( i18n("Reading") );
0573             break;
0574         case COPY:
0575             if( simulate() )
0576             {
0577                 // xgettext: no-c-format
0578                 emit infoMessage(i18n("Starting simulation copy at %1x speed...",d->usedSpeed), K3b::Job::MessageInfo );
0579                 emit newTask( i18n("Simulating") );
0580             }
0581             else
0582             {
0583                 // xgettext: no-c-format
0584                 emit infoMessage( i18n("Starting copy at %1x speed...",d->usedSpeed), K3b::Job::MessageInfo );
0585                 emit newTask( i18n("Copying") );
0586             }
0587             break;
0588         case BLANK:
0589             emit infoMessage(i18n("Starting blanking..."), K3b::Job::MessageInfo );
0590             emit newTask( i18n("Blanking") );
0591         }
0592     }
0593 }
0594 
0595 
0596 void K3b::CdrdaoWriter::cancel()
0597 {
0598     m_canceled = true;
0599 
0600     if( m_process ) {
0601         if( m_process->isRunning() ) {
0602             m_process->disconnect();
0603             m_process->terminate();
0604 
0605             // we need to unlock the device because cdrdao locked it while writing
0606             //
0607             // FIXME: try to determine wheater we are writing or reading and choose
0608             // the device to unblock based on that result.
0609             //
0610             if( m_command == READ ) {
0611                 // FIXME: this is a hack
0612                 setBurnDevice( m_sourceDevice );
0613             }
0614 
0615             // this will unblock and eject the drive and emit the finished/canceled signals
0616             K3b::AbstractWriter::cancel();
0617         }
0618     }
0619 }
0620 
0621 
0622 bool K3b::CdrdaoWriter::cueSheet()
0623 {
0624 
0625     // TODO: do this in the K3b::CueFileParser
0626 
0627     if ( m_tocFile.toLower().endsWith( ".cue" ) ) {
0628         QFile f( m_tocFile );
0629         if ( f.open( QIODevice::ReadOnly ) ) {
0630             QTextStream ts( &f );
0631             QString line = ts.readLine();
0632             f.close();
0633             int pos = line.indexOf( "FILE \"" );
0634             if( pos < 0 )
0635                 return false;
0636 
0637             pos += 6;
0638             int endPos = line.indexOf( "\" BINARY", pos+1 );
0639             if( endPos < 0 )
0640                 return false;
0641 
0642             line = line.mid( pos, endPos-pos );
0643             QFileInfo fi( QFileInfo( m_tocFile ).path() + '/' + QFileInfo( line ).fileName() );
0644             QString binpath = fi.filePath();
0645             qDebug() << QString("K3b::CdrdaoWriter::cueSheet() BinFilePath from CueFile: %1").arg( line );
0646             qDebug() << QString("K3b::CdrdaoWriter::cueSheet() absolute BinFilePath: %1").arg( binpath );
0647 
0648             if ( !fi.exists() )
0649                 return false;
0650 
0651             QTemporaryFile tempF;
0652             tempF.open();
0653             QString tempFile = tempF.fileName();
0654             tempF.remove();
0655 
0656             if ( symlink(QFile::encodeName( binpath ), QFile::encodeName( tempFile + ".bin") ) == -1 )
0657                 return false;
0658             if ( symlink(QFile::encodeName( m_tocFile ), QFile::encodeName( tempFile + ".cue") ) == -1 )
0659                 return false;
0660 
0661             qDebug() << QString("K3b::CdrdaoWriter::cueSheet() symlink BinFileName: %1.bin").arg( tempFile );
0662             qDebug() << QString("K3b::CdrdaoWriter::cueSheet() symlink CueFileName: %1.cue").arg( tempFile );
0663             m_binFileLnk = tempFile + ".bin";
0664             m_cueFileLnk = tempFile + ".cue";
0665             return true;
0666         }
0667     }
0668 
0669     return false;
0670 }
0671 
0672 void K3b::CdrdaoWriter::slotStdLine( const QString& line )
0673 {
0674     parseCdrdaoLine(line);
0675 }
0676 
0677 
0678 void K3b::CdrdaoWriter::slotProcessExited( int exitCode, QProcess::ExitStatus exitStatus )
0679 {
0680     // release the device within this process
0681     burnDevice()->usageUnlock();
0682 
0683     // unblock the device
0684     k3bcore->unblockDevice( burnDevice() );
0685 
0686     switch ( m_command )
0687     {
0688     case WRITE:
0689     case COPY:
0690         if ( !m_binFileLnk.isEmpty() ) {
0691             KIO::del(QUrl::fromLocalFile(m_cueFileLnk), KIO::HideProgressInfo);
0692             KIO::del(QUrl::fromLocalFile(m_binFileLnk), KIO::HideProgressInfo);
0693         }
0694         else if( (!QFile::exists( m_tocFile ) || K3b::filesize( QUrl::fromLocalFile(m_tocFile) ) == 0 ) && !m_onTheFly )
0695         {
0696             // cdrdao removed the tocfile :(
0697             // we need to recover it
0698             KIO::CopyJob* copyJob = KIO::copyAs(QUrl::fromLocalFile(m_backupTocFile), QUrl::fromLocalFile(m_tocFile), KIO::HideProgressInfo);
0699             connect( copyJob, &KJob::result, [&](KJob*) {
0700                 if( copyJob->error() != KJob::NoError ) {
0701                     qDebug() << "(K3b::CdrdaoWriter) restoring tocfile " << m_tocFile << " failed.";
0702                     emit infoMessage( i18n("Due to a bug in cdrdao the toc/cue file %1 has been deleted. "
0703                                            "K3b was unable to restore it from the backup %2.",m_tocFile,m_backupTocFile), MessageError );
0704                 } else {
0705                     KIO::DeleteJob* deleteJob = KIO::del(QUrl::fromLocalFile(m_backupTocFile), KIO::HideProgressInfo);
0706                     connect( deleteJob, &KJob::result, [&](KJob*){
0707                         if( deleteJob->error() != KJob::NoError ) {
0708                             qDebug() << "(K3b::CdrdaoWriter) delete tocfile backkup " << m_backupTocFile << " failed.";
0709                         }
0710                     } );
0711                     deleteJob->exec();
0712                 }
0713             } );
0714             copyJob->exec();
0715         }
0716         break;
0717     case BLANK:
0718     case READ:
0719         break;
0720     }
0721 
0722     if( m_canceled )
0723         return;
0724 
0725     if( exitStatus == QProcess::NormalExit )
0726     {
0727         switch( exitCode )
0728         {
0729         case 0:
0730             if( simulate() )
0731                 emit infoMessage( i18n("Simulation successfully completed"), K3b::Job::MessageSuccess );
0732             else
0733                 switch ( m_command )
0734                 {
0735                 case READ:
0736                     emit infoMessage( i18n("Reading successfully completed"), K3b::Job::MessageSuccess );
0737                     break;
0738                 case WRITE:
0739                     emit infoMessage( i18n("Writing successfully completed"), K3b::Job::MessageSuccess );
0740                     break;
0741                 case COPY:
0742                     emit infoMessage( i18n("Copying successfully completed"), K3b::Job::MessageSuccess );
0743                     break;
0744                 case BLANK:
0745                     emit infoMessage( i18n("Blanking successfully completed"), K3b::Job::MessageSuccess );
0746                     break;
0747                 }
0748 
0749             if( m_command == WRITE || m_command == COPY ) {
0750                 int s = d->speedEst->average();
0751                 emit infoMessage( ki18n("Average overall write speed: %1 KB/s (%2x)").subs(s).subs((double)s/150.0, 0, 'g', 2).toString(), MessageInfo );
0752             }
0753 
0754             jobFinished( true );
0755             break;
0756 
0757         default:
0758             if( !m_knownError && !wasSourceUnreadable() ) {
0759                 emit infoMessage( i18n("%1 returned an unknown error (code %2).",m_cdrdaoBinObject->name(), exitCode),
0760                                   K3b::Job::MessageError );
0761                 emit infoMessage( i18n("Please include the debugging output in your problem report."), K3b::Job::MessageError );
0762             }
0763 
0764             jobFinished( false );
0765             break;
0766         }
0767     }
0768     else
0769     {
0770         emit infoMessage( i18n("%1 crashed.", QString("cdrdao")), K3b::Job::MessageError );
0771         jobFinished( false );
0772     }
0773 }
0774 
0775 
0776 void K3b::CdrdaoWriter::unknownCdrdaoLine( const QString& line )
0777 {
0778     if( line.contains( "at speed" ) )
0779     {
0780         // parse the speed and inform the user if cdrdao switched it down
0781         int pos = line.indexOf( "at speed" );
0782         static const QRegularExpression rx("\\D");
0783         int po2 = line.indexOf( rx, pos + 9 );
0784         int speed = line.mid( pos+9, po2-pos-9 ).toInt();
0785         if( speed < d->usedSpeed )
0786         {
0787             // xgettext: no-c-format
0788             emit infoMessage( i18n("Medium or burner does not support writing at %1x speed",d->usedSpeed), K3b::Job::MessageWarning );
0789             // xgettext: no-c-format
0790             emit infoMessage( i18n("Switching down burn speed to %1x",speed), K3b::Job::MessageWarning );
0791         }
0792     }
0793 }
0794 
0795 
0796 void K3b::CdrdaoWriter::reinitParser()
0797 {
0798     ::memset( &d->oldMsg, 0, sizeof(ProgressMsg2) );
0799     ::memset( &d->newMsg, 0, sizeof(ProgressMsg2) );
0800 
0801     m_currentTrack=0;
0802 }
0803 
0804 void K3b::CdrdaoWriter::parseCdrdaoLine( const QString& str )
0805 {
0806     emit debuggingOutput( "cdrdao", str );
0807     //  qDebug() << "(cdrdaoparse)" << str;
0808     // find some messages from cdrdao
0809     // -----------------------------------------------------------------------------------------
0810     if( (str).startsWith( "Warning" ) || (str).startsWith( "MessageWarning" ) || (str).startsWith( "MessageError" ) )
0811     {
0812         parseCdrdaoError( str );
0813     }
0814     else if( (str).startsWith( "Wrote" ) && !str.contains("blocks") )
0815     {
0816         parseCdrdaoWrote( str );
0817     }
0818     else if( (str).startsWith( "Executing power" ) )
0819     {
0820         emit newSubTask( i18n("Executing Power calibration") );
0821     }
0822     else if( (str).startsWith( "Power calibration successful" ) )
0823     {
0824         emit infoMessage( i18n("Power calibration successful"), K3b::Job::MessageInfo );
0825         emit newSubTask( i18n("Preparing burn process...") );
0826     }
0827     else if( (str).startsWith( "Flushing cache" ) )
0828     {
0829         emit newSubTask( i18n("Flushing cache") );
0830     }
0831     else if( (str).startsWith( "Writing CD-TEXT lead" ) )
0832     {
0833         emit newSubTask( i18n("Writing CD-Text lead-in...") );
0834     }
0835     else if( (str).startsWith( "Turning BURN-Proof on" ) )
0836     {
0837         emit infoMessage( i18n("Turning BURN-Proof on"), K3b::Job::MessageInfo );
0838     }
0839     else if( str.startsWith( "Copying" ) )
0840     {
0841         emit infoMessage( str, K3b::Job::MessageInfo );
0842     }
0843     else if( str.startsWith( "Found ISRC" ) )
0844     {
0845         emit infoMessage( i18n("Found ISRC code"), K3b::Job::MessageInfo );
0846     }
0847     else if( str.startsWith( "Found pre-gap" ) )
0848     {
0849         emit infoMessage( i18n("Found pregap: %1", str.mid(str.indexOf(":")+1) ), K3b::Job::MessageInfo );
0850     }
0851     else
0852         unknownCdrdaoLine(str);
0853 }
0854 
0855 void K3b::CdrdaoWriter::parseCdrdaoError( const QString& line )
0856 {
0857     int pos = -1;
0858 
0859     if( line.contains( "No driver found" ) ||
0860         line.contains( "use option --driver" ) )
0861     {
0862         emit infoMessage( i18n("No cdrdao driver found."), K3b::Job::MessageError );
0863         emit infoMessage( i18n("Please select one manually in the device settings."), K3b::Job::MessageError );
0864         emit infoMessage( i18n("For most current drives this would be 'generic-mmc'."), K3b::Job::MessageError );
0865         m_knownError = true;
0866     }
0867     else if( line.contains( "Cannot setup device" ) )
0868     {
0869         // no nothing...
0870     }
0871     else if( line.contains( "not ready") )
0872     {
0873         emit infoMessage( i18n("Device not ready, waiting."),K3b::Job::MessageWarning );
0874     }
0875     else if( line.contains("Drive does not accept any cue sheet") )
0876     {
0877         emit infoMessage( i18n("Cue sheet not accepted."), K3b::Job::MessageError );
0878         m_knownError = true;
0879     }
0880     else if( (pos = line.indexOf( "Illegal option" )) > 0 ) {
0881         // MessageError: Illegal option: -wurst
0882         emit infoMessage( i18n("No valid %1 option: %2",m_cdrdaoBinObject->name(),line.mid(pos+16)),
0883                           MessageError );
0884         m_knownError = true;
0885     }
0886     else if( line.contains( "exceeds capacity" ) ) {
0887         emit infoMessage( i18n("Data does not fit on disk."), MessageError );
0888         if( m_cdrdaoBinObject->hasFeature("overburn") )
0889             emit infoMessage( i18n("Enable overburning in the advanced K3b settings to burn anyway."), MessageInfo );
0890         m_knownError = true;
0891     }
0892     //  else if( !line.contains( "remote progress message" ) )
0893 //     emit infoMessage( line, K3b::Job::MessageError );
0894 }
0895 
0896 void K3b::CdrdaoWriter::parseCdrdaoWrote( const QString& line )
0897 {
0898     int pos, po2;
0899     pos = line.indexOf( "Wrote" );
0900     po2 = line.indexOf( " ", pos + 6 );
0901     int processed = line.mid( pos+6, po2-pos-6 ).toInt();
0902 
0903     pos = line.indexOf( "of" );
0904     po2 = line.indexOf( " ", pos + 3 );
0905     m_size = line.mid( pos+3, po2-pos-3 ).toInt();
0906 
0907     d->speedEst->dataWritten( processed*1024 );
0908 
0909     emit processedSize( processed, m_size );
0910 }
0911 
0912 
0913 void K3b::CdrdaoWriter::parseCdrdaoMessage()
0914 {
0915     static const unsigned char msgSync[] = { 0xff, 0x00, 0xff, 0x00 };
0916     unsigned int avail = m_comSock->bytesAvailable();
0917     unsigned int msgs = avail / ( sizeof(msgSync)+d->progressMsgSize );
0918     unsigned int count = 0;
0919 
0920     if ( msgs < 1 )
0921         return;
0922     else if ( msgs > 1) {
0923         // move the read-index forward to the beginning of the most recent message
0924         count = ( msgs-1 ) * ( sizeof(msgSync)+d->progressMsgSize );
0925         m_comSock->seek( count );
0926         qDebug() << "(K3b::CdrdaoParser) " << msgs-1 << " message(s) skipped";
0927     }
0928 
0929     while( count < avail ) {
0930 
0931         // search for msg sync
0932         int state = 0;
0933         char buf;
0934         while( state < 4 ) {
0935             m_comSock->getChar( &buf );
0936             ++count;
0937             if( count == avail ) {
0938                 //        qDebug() << "(K3b::CdrdaoParser) remote message sync not found (" << count << ")";
0939                 return;
0940             }
0941 
0942             if( buf == msgSync[state] )
0943                 ++state;
0944             else
0945                 state = 0;
0946         }
0947 
0948         if( (avail - count) < d->progressMsgSize ) {
0949             qDebug() << "(K3b::CdrdaoParser) could not read complete remote message.";
0950             return;
0951         }
0952 
0953         // read one message (the message size changed in cdrdao 1.1.8)
0954         ::memset( &d->newMsg, 0, d->progressMsgSize );
0955         int size = m_comSock->read( (char*)&d->newMsg, d->progressMsgSize);
0956         if( size == -1 ) {
0957             qDebug() << "(K3b::CdrdaoParser) read error";
0958             return;
0959         }
0960         count += size;
0961 
0962         // sometimes the progress takes one step back (on my system when using paranoia-level 3)
0963         // so we just use messages that are greater than the previous or first messages
0964         if( d->oldMsg < d->newMsg
0965             || ( d->newMsg.track == 1 &&
0966                  d->newMsg.trackProgress <= 10 )) {
0967 
0968             if( d->newMsg.track != m_currentTrack ) {
0969                 switch( d->newMsg.status ) {
0970                 case PGSMSG_RCD_EXTRACTING:
0971                     emit nextTrack( d->newMsg.track, d->newMsg.totalTracks );
0972                     break;
0973                 case PGSMSG_WCD_LEADIN:
0974                     emit newSubTask( i18n("Writing leadin") );
0975                     break;
0976                 case PGSMSG_WCD_DATA:
0977                     emit nextTrack( d->newMsg.track, d->newMsg.totalTracks );
0978                     break;
0979                 case PGSMSG_WCD_LEADOUT:
0980                     emit newSubTask( i18n("Writing leadout") );
0981                     break;
0982                 }
0983 
0984                 m_currentTrack = d->newMsg.track;
0985             }
0986 
0987             if( d->newMsg.status == PGSMSG_WCD_LEADIN || d->newMsg.status == PGSMSG_WCD_LEADOUT ) {
0988                 // cdrdao >= 1.1.8 emits progress data when writing the lead-in and lead-out :)
0989                 emit subPercent( d->newMsg.totalProgress/10 );
0990             }
0991             else {
0992                 emit subPercent( d->newMsg.trackProgress/10 );
0993                 emit percent( d->newMsg.totalProgress/10 );
0994             }
0995 
0996             emit buffer(d->newMsg.bufferFillRate);
0997 
0998             if( d->progressMsgSize == (unsigned int)sizeof(ProgressMsg2) )
0999                 emit deviceBuffer( d->newMsg.writerFillRate );
1000 
1001             ::memcpy( &d->oldMsg, &d->newMsg, d->progressMsgSize );
1002         }
1003     }
1004 }
1005 
1006 
1007 void K3b::CdrdaoWriter::slotThroughput( int t )
1008 {
1009     // FIXME: determine sector size
1010     emit writeSpeed( t, K3b::Device::SPEED_FACTOR_CD_MODE1 );
1011 }
1012 
1013 
1014 QString K3b::CdrdaoWriter::findDriverFile( const K3b::ExternalBin* bin )
1015 {
1016     if( !bin )
1017         return QString();
1018 
1019     // cdrdao normally in (prefix)/bin and driver table in (prefix)/share/cdrdao
1020     QString path = bin->path();
1021     path.truncate( path.lastIndexOf("/") );
1022     path.truncate( path.lastIndexOf("/") );
1023     path += "/share/cdrdao/drivers";
1024     if( QFile::exists(path) )
1025         return path;
1026     else {
1027         qDebug() << "(K3b::CdrdaoWriter) could not find cdrdao driver table.";
1028         return QString();
1029     }
1030 }
1031 
1032 
1033 // returns true if the driver file could be opened and no driver could be found
1034 // TODO: cache the drivers
1035 bool K3b::CdrdaoWriter::defaultToGenericMMC( K3b::Device::Device* dev, bool writer )
1036 {
1037     QString driverTable = findDriverFile( m_cdrdaoBinObject );
1038     if( !driverTable.isEmpty() ) {
1039         QFile f( driverTable );
1040         if( f.open( QIODevice::ReadOnly ) ) {
1041             // read all drivers
1042             QStringList drivers;
1043             QTextStream fStr( &f );
1044             while( !fStr.atEnd() ) {
1045                 QString line = fStr.readLine();
1046                 if( line.isEmpty() )
1047                     continue;
1048                 if( line[0] == '#' )
1049                     continue;
1050                 if( line[0] == 'R' && writer )
1051                     continue;
1052                 if( line[0] == 'W' && !writer )
1053                     continue;
1054                 drivers.append(line);
1055             }
1056 
1057             // search for the driver
1058             for( QStringList::const_iterator it = drivers.constBegin(); it != drivers.constEnd(); ++it ) {
1059                 if( (*it).section( '|', 1, 1 ) == dev->vendor() &&
1060                     (*it).section( '|', 2, 2 ) == dev->description() )
1061                     return false;
1062             }
1063 
1064             // no driver found
1065             return true;
1066         }
1067         else {
1068             qDebug() << "(K3b::CdrdaoWriter) could not open driver table " << driverTable;
1069             return false;
1070         }
1071     }
1072     else
1073         return false;
1074 }
1075 
1076 #include "moc_k3bcdrdaowriter.cpp"