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"