diff options
Diffstat (limited to 'libk3b/projects/k3bcdrdaowriter.cpp')
-rw-r--r-- | libk3b/projects/k3bcdrdaowriter.cpp | 1101 |
1 files changed, 1101 insertions, 0 deletions
diff --git a/libk3b/projects/k3bcdrdaowriter.cpp b/libk3b/projects/k3bcdrdaowriter.cpp new file mode 100644 index 0000000..c49cb4b --- /dev/null +++ b/libk3b/projects/k3bcdrdaowriter.cpp @@ -0,0 +1,1101 @@ +/* + * + * $Id: k3bcdrdaowriter.cpp 654649 2007-04-16 17:55:50Z trueg $ + * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> + * Klaus-Dieter Krannich <kd@k3b.org> + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#include "k3bcdrdaowriter.h" + +#include <k3bcore.h> +#include <k3bexternalbinmanager.h> +#include <k3bdevicemanager.h> +#include <k3bprocess.h> +#include <k3bdevice.h> +#include <k3bdevicehandler.h> +#include <k3bthroughputestimator.h> +#include <k3bglobals.h> +#include <k3bglobalsettings.h> + +#include <qstring.h> +#include <qstringlist.h> +#include <qvaluelist.h> +#include <qregexp.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qdir.h> +#include <qurl.h> +#include <qsocket.h> +#include <qsocketdevice.h> + +#include <klocale.h> +#include <kdebug.h> +#include <kio/netaccess.h> +#include <kstandarddirs.h> +#include <ktempfile.h> + +#include <sys/types.h> +#include <sys/socket.h> + + + +#define PGSMSG_MIN PGSMSG_RCD_ANALYZING +#define PGSMSG_RCD_ANALYZING 1 +#define PGSMSG_RCD_EXTRACTING 2 +#define PGSMSG_WCD_LEADIN 3 +#define PGSMSG_WCD_DATA 4 +#define PGSMSG_WCD_LEADOUT 5 +#define PGSMSG_BLK 6 +#define PGSMSG_MAX PGSMSG_BLK + +struct ProgressMsg { + int status; // see PGSMSG_* constants + int totalTracks; // total number of tracks + int track; // actually written track + int trackProgress; // progress for current track 0..1000 + int totalProgress; // total writing progress 0..1000 + int bufferFillRate; // buffer fill rate 0..100 +}; + +#define PSGMSG_MINSIZE 24 + +struct ProgressMsg2 { + int status; // see PGSMSG_* constants + int totalTracks; // total number of tracks + int track; // actually written track + int trackProgress; // progress for current track 0..1000 + int totalProgress; // total writing progress 0..1000 + int bufferFillRate; // buffer fill rate 0..100 + int writerFillRate; // device write buffer fill rate 0..100 +}; + + +inline bool operator<( const ProgressMsg2& m1, const ProgressMsg2& m2 ) +{ + return m1.track < m2.track + || ( m1.track == m2.track + && m1.trackProgress < m2.trackProgress ) + || m1.totalProgress < m2.totalProgress; +} + + +inline bool operator==( const ProgressMsg2& m1, const ProgressMsg2& m2 ) +{ + return m1.status == m2.status + && m1.track == m2.track + && m1.totalTracks == m2.totalTracks + && m1.trackProgress == m2.trackProgress + && m1.totalProgress == m2.totalProgress + && m1.bufferFillRate == m2.bufferFillRate; +} + +inline bool operator!=( const ProgressMsg2& m1, const ProgressMsg2& m2 ) +{ + return !( m1 == m2 ); +} + + + +class K3bCdrdaoWriter::Private +{ +public: + Private() { + } + + K3bThroughputEstimator* speedEst; + + int usedSpeed; + + ProgressMsg2 oldMsg; + ProgressMsg2 newMsg; + + unsigned int progressMsgSize; +}; + + +K3bCdrdaoWriter::K3bCdrdaoWriter( K3bDevice::Device* dev, K3bJobHandler* hdl, + QObject* parent, const char* name ) + : K3bAbstractWriter( dev, hdl, parent, name ), + m_command(WRITE), + m_blankMode(MINIMAL), + m_sourceDevice(0), + m_readRaw(false), + m_multi(false), + m_force(false), + m_onTheFly(false), + m_fastToc(false), + m_readSubchan(None), + m_taoSource(false), + m_taoSourceAdjust(-1), + m_paranoiaMode(-1), + m_session(-1), + m_process(0), + m_comSock(0), + m_currentTrack(0), + m_forceNoEject(false) +{ + d = new Private(); + d->speedEst = new K3bThroughputEstimator( this ); + connect( d->speedEst, SIGNAL(throughput(int)), + this, SLOT(slotThroughput(int)) ); + + m_eject = k3bcore->globalSettings()->ejectMedia(); + + ::memset( &d->oldMsg, 0, sizeof(ProgressMsg2) ); + ::memset( &d->newMsg, 0, sizeof(ProgressMsg2) ); + + if( socketpair(AF_UNIX,SOCK_STREAM,0,m_cdrdaoComm) ) + { + kdDebug() << "(K3bCdrdaoWriter) could not open socketpair for cdrdao remote messages" << endl; + } + else + { + delete m_comSock; + m_comSock = new QSocket(); + m_comSock->setSocket(m_cdrdaoComm[1]); + m_comSock->socketDevice()->setReceiveBufferSize(49152); + // magic number from Qt documentation + m_comSock->socketDevice()->setBlocking(false); + connect( m_comSock, SIGNAL(readyRead()), + this, SLOT(parseCdrdaoMessage())); + } +} + +K3bCdrdaoWriter::~K3bCdrdaoWriter() +{ + delete d->speedEst; + delete d; + + // close the socket + if( m_comSock ) { + m_comSock->close(); + ::close( m_cdrdaoComm[0] ); + } + delete m_process; + delete m_comSock; +} + + +int K3bCdrdaoWriter::fd() const +{ + if( m_process ) + return m_process->stdinFd(); + else + return -1; +} + + +bool K3bCdrdaoWriter::active() const +{ + return (m_process ? m_process->isRunning() : false); +} + + +void K3bCdrdaoWriter::prepareArgumentList() +{ + + // binary + *m_process << m_cdrdaoBinObject; + + // command + switch ( m_command ) + { + case COPY: + *m_process << "copy"; + setWriteArguments(); + setReadArguments(); + setCopyArguments(); + break; + case WRITE: + *m_process << "write"; + setWriteArguments(); + break; + case READ: + *m_process << "read-cd"; + // source device and source driver + if ( m_sourceDevice ) + *m_process << "--device" + << K3b::externalBinDeviceParameter(m_sourceDevice, m_cdrdaoBinObject); + if ( m_sourceDevice->cdrdaoDriver() != "auto" ) + *m_process << "--driver" << m_sourceDevice->cdrdaoDriver(); + else if( defaultToGenericMMC( m_sourceDevice, false ) ) { + kdDebug() << "(K3bCdrdaoWriter) defaulting to generic-mmc driver for " << m_sourceDevice->blockDeviceName() << endl; + *m_process << "--driver" << "generic-mmc"; + } + setReadArguments(); + break; + case BLANK: + *m_process << "blank"; + setBlankArguments(); + break; + } + + setCommonArguments(); +} + +void K3bCdrdaoWriter::setWriteArguments() +{ + // device and driver + *m_process << "--device" + << K3b::externalBinDeviceParameter(burnDevice(), m_cdrdaoBinObject); + + if( burnDevice()->cdrdaoDriver() != "auto" ) + { + *m_process << "--driver"; + if( burnDevice()->cdTextCapable() == 1 ) + *m_process << QString("%1:0x00000010").arg( burnDevice()->cdrdaoDriver() ); + else + *m_process << burnDevice()->cdrdaoDriver(); + } + else if( defaultToGenericMMC( burnDevice(), true ) ) { + kdDebug() << "(K3bCdrdaoWriter) defaulting to generic-mmc driver for " << burnDevice()->blockDeviceName() << endl; + *m_process << "--driver" << "generic-mmc:0x00000010"; + } + + // burn speed + if( d->usedSpeed != 0 ) + *m_process << "--speed" << QString("%1").arg(d->usedSpeed); + + //simulate + if( simulate() ) + *m_process << "--simulate"; + + // multi + if( m_multi ) + *m_process << "--multi"; + + // force + if( m_force ) + *m_process << "--force"; + + // burnproof + if ( !k3bcore->globalSettings()->burnfree() ) { + if( m_cdrdaoBinObject->hasFeature( "disable-burnproof" ) ) + *m_process << "--buffer-under-run-protection" << "0"; + else + emit infoMessage( i18n("Cdrdao %1 does not support disabling burnfree.").arg(m_cdrdaoBinObject->version), WARNING ); + } + + if( k3bcore->globalSettings()->force() ) { + *m_process << "--force"; + emit infoMessage( i18n("'Force unsafe operations' enabled."), WARNING ); + } + + bool manualBufferSize = + k3bcore->globalSettings()->useManualBufferSize(); + if( manualBufferSize ) { + // + // one buffer in cdrdao holds 1 second of audio data = 75 frames = 75 * 2352 bytes + // + int bufSizeInMb = k3bcore->globalSettings()->bufferSize(); + *m_process << "--buffers" << QString::number( bufSizeInMb*1024*1024/(75*2352) ); + } + + bool overburn = + k3bcore->globalSettings()->overburn(); + if( overburn ) { + if( m_cdrdaoBinObject->hasFeature("overburn") ) + *m_process << "--overburn"; + else + emit infoMessage( i18n("Cdrdao %1 does not support overburning.").arg(m_cdrdaoBinObject->version), WARNING ); + } + +} + +void K3bCdrdaoWriter::setReadArguments() +{ + // readRaw + if ( m_readRaw ) + *m_process << "--read-raw"; + + // subchan + if ( m_readSubchan != None ) + { + *m_process << "--read-subchan"; + switch ( m_readSubchan ) + { + case RW: + *m_process << "rw"; + break; + case RW_RAW: + *m_process << "rw_raw"; + break; + case None: + break; + } + } + + // TAO Source + if ( m_taoSource ) + *m_process << "--tao-source"; + + // TAO Source Adjust + if ( m_taoSourceAdjust != -1 ) + *m_process << "--tao-source-adjust" + << QString("%1").arg(m_taoSourceAdjust); + + // paranoia Mode + if ( m_paranoiaMode != -1 ) + *m_process << "--paranoia-mode" + << QString("%1").arg(m_paranoiaMode); + + // session + if ( m_session != -1 ) + *m_process << "--session" + << QString("%1").arg(m_session); + + // fast TOC + if ( m_fastToc ) + *m_process << "--fast-toc"; + +} + +void K3bCdrdaoWriter::setCopyArguments() +{ + // source device and source driver + *m_process << "--source-device" << K3b::externalBinDeviceParameter(m_sourceDevice, m_cdrdaoBinObject); + if ( m_sourceDevice->cdrdaoDriver() != "auto" ) + *m_process << "--source-driver" << m_sourceDevice->cdrdaoDriver(); + else if( defaultToGenericMMC( m_sourceDevice, false ) ) { + kdDebug() << "(K3bCdrdaoWriter) defaulting to generic-mmc driver for " << m_sourceDevice->blockDeviceName() << endl; + *m_process << "--source-driver" << "generic-mmc"; + } + + // on-the-fly + if ( m_onTheFly ) + *m_process << "--on-the-fly"; +} + +void K3bCdrdaoWriter::setBlankArguments() +{ + // device and driver + *m_process << "--device" + << K3b::externalBinDeviceParameter(burnDevice(), m_cdrdaoBinObject); + + if( burnDevice()->cdrdaoDriver() != "auto" ) + { + *m_process << "--driver"; + if( burnDevice()->cdTextCapable() == 1 ) + *m_process << QString("%1:0x00000010").arg( burnDevice()->cdrdaoDriver() ); + else + *m_process << burnDevice()->cdrdaoDriver(); + } + else if( defaultToGenericMMC( burnDevice(), true ) ) { + kdDebug() << "(K3bCdrdaoWriter) defaulting to generic-mmc driver for " << burnDevice()->blockDeviceName() << endl; + *m_process << "--driver" << "generic-mmc"; + } + + // burn speed + if( d->usedSpeed != 0 ) + *m_process << "--speed" << QString("%1").arg(d->usedSpeed); + + // blank-mode + *m_process << "--blank-mode"; + switch (m_blankMode) + { + case FULL: + *m_process << "full"; + break; + case MINIMAL: + *m_process << "minimal"; + break; + } +} + +void K3bCdrdaoWriter::setCommonArguments() +{ + + // additional user parameters from config + const QStringList& params = m_cdrdaoBinObject->userParameters(); + for( QStringList::const_iterator it = params.begin(); it != params.end(); ++it ) + *m_process << *it; + + + // display debug info + *m_process << "-n" << "-v" << "2"; + + // we have the power to do what ever we want. ;) + *m_process << "--force"; + + // eject + if( m_eject && !m_forceNoEject ) + *m_process << "--eject"; + + // remote + *m_process << "--remote" << QString("%1").arg(m_cdrdaoComm[0]); + + // data File + if ( ! m_dataFile.isEmpty() ) + *m_process << "--datafile" << m_dataFile; + + // BIN/CUE + if ( ! m_cueFileLnk.isEmpty() ) + *m_process << m_cueFileLnk; + // TOC File + else if ( ! m_tocFile.isEmpty() ) + *m_process << m_tocFile; +} + +K3bCdrdaoWriter* K3bCdrdaoWriter::addArgument( const QString& arg ) +{ + *m_process << arg; + return this; +} + + +void K3bCdrdaoWriter::start() +{ + jobStarted(); + + d->speedEst->reset(); + + delete m_process; // kdelibs want this! + m_process = new K3bProcess(); + m_process->setRunPrivileged(true); + m_process->setSplitStdout(false); + m_process->setRawStdin(true); + connect( m_process, SIGNAL(stderrLine(const QString&)), + this, SLOT(slotStdLine(const QString&)) ); + connect( m_process, SIGNAL(processExited(KProcess*)), + this, SLOT(slotProcessExited(KProcess*)) ); + + m_canceled = false; + m_knownError = false; + + m_cdrdaoBinObject = k3bcore->externalBinManager()->binObject("cdrdao"); + + if( !m_cdrdaoBinObject ) { + emit infoMessage( i18n("Could not find %1 executable.").arg("cdrdao"), ERROR ); + jobFinished(false); + return; + } + + emit debuggingOutput( "Used versions", "cdrdao: " + m_cdrdaoBinObject->version ); + + if( !m_cdrdaoBinObject->copyright.isEmpty() ) + emit infoMessage( i18n("Using %1 %2 - Copyright (C) %3").arg(m_cdrdaoBinObject->name()).arg(m_cdrdaoBinObject->version).arg(m_cdrdaoBinObject->copyright), INFO ); + + + // the message size changed in cdrdao 1.1.8) + if( m_cdrdaoBinObject->version >= K3bVersion( 1, 1, 8 ) ) + d->progressMsgSize = sizeof(ProgressMsg2); + else + d->progressMsgSize = sizeof(ProgressMsg); + + // since the --speed parameter is used several times in this code we + // determine the speed in auto once at the beginning + d->usedSpeed = burnSpeed(); + if( d->usedSpeed == 0 ) { + // try to determine the writeSpeed + // if it fails determineMaximalWriteSpeed() will return 0 and + // the choice is left to cdrdao + d->usedSpeed = burnDevice()->determineMaximalWriteSpeed(); + } + d->usedSpeed /= 175; + + switch ( m_command ) + { + case WRITE: + case COPY: + if (!m_tocFile.isEmpty()) + { + + // if tocfile is a cuesheet than create symlinks to *.cue and the binary listed inside the cuesheet. + // now works without the .bin extension too. + if ( !cueSheet() ) { + m_backupTocFile = m_tocFile + ".k3bbak"; + + // workaround, cdrdao deletes the tocfile when --remote parameter is set + if ( !KIO::NetAccess::copy(KURL(m_tocFile),KURL(m_backupTocFile), (QWidget*) 0) ) + { + kdDebug() << "(K3bCdrdaoWriter) could not backup " << m_tocFile << " to " << m_backupTocFile << endl; + emit infoMessage( i18n("Could not backup tocfile."), ERROR ); + jobFinished(false); + return; + } + } + } + break; + case BLANK: + case READ: + break; + } + prepareArgumentList(); + // set working dir to dir part of toc file (to allow rel names in toc-file) + m_process->setWorkingDirectory(QUrl(m_tocFile).dirPath()); + + kdDebug() << "***** cdrdao parameters:\n"; + const QValueList<QCString>& args = m_process->args(); + QString s; + for( QValueList<QCString>::const_iterator it = args.begin(); it != args.end(); ++it ) + { + s += *it + " "; + } + kdDebug() << s << flush << endl; + emit debuggingOutput("cdrdao command:", s); + + m_currentTrack = 0; + reinitParser(); + + switch ( m_command ) + { + case READ: + emit newSubTask( i18n("Preparing read process...") ); + break; + case WRITE: + emit newSubTask( i18n("Preparing write process...") ); + break; + case COPY: + emit newSubTask( i18n("Preparing copy process...") ); + break; + case BLANK: + emit newSubTask( i18n("Preparing blanking process...") ); + break; + } + + // FIXME: check the return value + if( K3b::isMounted( burnDevice() ) ) { + emit infoMessage( i18n("Unmounting medium"), INFO ); + K3b::unmount( burnDevice() ); + } + + // block the device (including certain checks) + k3bcore->blockDevice( burnDevice() ); + + // lock the device for good in this process since it will + // be opened in the growisofs process + burnDevice()->close(); + burnDevice()->usageLock(); + + if( !m_process->start( KProcess::NotifyOnExit, KProcess::AllOutput ) ) + { + // something went wrong when starting the program + // it "should" be the executable + kdDebug() << "(K3bCdrdaoWriter) could not start cdrdao" << endl; + emit infoMessage( i18n("Could not start %1.").arg("cdrdao"), K3bJob::ERROR ); + jobFinished(false); + } + else + { + switch ( m_command ) + { + case WRITE: + if( simulate() ) + { + emit infoMessage(i18n("Starting DAO simulation at %1x speed...").arg(d->usedSpeed), + K3bJob::INFO ); + emit newTask( i18n("Simulating") ); + } + else + { + emit infoMessage( i18n("Starting DAO writing at %1x speed...").arg(d->usedSpeed), K3bJob::INFO ); + emit newTask( i18n("Writing") ); + } + break; + case READ: + emit infoMessage(i18n("Starting reading..."), K3bJob::INFO ); + emit newTask( i18n("Reading") ); + break; + case COPY: + if( simulate() ) + { + emit infoMessage(i18n("Starting simulation copy at %1x speed...").arg(d->usedSpeed), K3bJob::INFO ); + emit newTask( i18n("Simulating") ); + } + else + { + emit infoMessage( i18n("Starting copy at %1x speed...").arg(d->usedSpeed), K3bJob::INFO ); + emit newTask( i18n("Copying") ); + } + break; + case BLANK: + emit infoMessage(i18n("Starting blanking..."), K3bJob::INFO ); + emit newTask( i18n("Blanking") ); + } + } +} + + +void K3bCdrdaoWriter::cancel() +{ + m_canceled = true; + + if( m_process ) { + if( m_process->isRunning() ) { + m_process->disconnect(); + m_process->kill(); + + // we need to unlock the device because cdrdao locked it while writing + // + // FIXME: try to determine wheater we are writing or reading and choose + // the device to unblock based on that result. + // + if( m_command == READ ) { + // FIXME: this is a hack + setBurnDevice( m_sourceDevice ); + } + + // this will unblock and eject the drive and emit the finished/canceled signals + K3bAbstractWriter::cancel(); + } + } +} + + +bool K3bCdrdaoWriter::cueSheet() +{ + + // TODO: do this in the K3bCueFileParser + + if ( m_tocFile.lower().endsWith( ".cue" ) ) { + QFile f( m_tocFile ); + if ( f.open( IO_ReadOnly ) ) { + QTextStream ts( &f ); + if ( !ts.eof() ) { + QString line = ts.readLine(); + f.close(); + int pos = line.find( "FILE \"" ); + if( pos < 0 ) + return false; + + pos += 6; + int endPos = line.find( "\" BINARY", pos+1 ); + if( endPos < 0 ) + return false; + + line = line.mid( pos, endPos-pos ); + QFileInfo fi( QFileInfo( m_tocFile ).dirPath() + "/" + QFileInfo( line ).fileName() ); + QString binpath = fi.filePath(); + kdDebug() << QString("K3bCdrdaoWriter::cueSheet() BinFilePath from CueFile: %1").arg( line ) << endl; + kdDebug() << QString("K3bCdrdaoWriter::cueSheet() absolute BinFilePath: %1").arg( binpath ) << endl; + + if ( !fi.exists() ) + return false; + + KTempFile tempF; + QString tempFile = tempF.name(); + tempF.unlink(); + + if ( symlink(QFile::encodeName( binpath ), QFile::encodeName( tempFile + ".bin") ) == -1 ) + return false; + if ( symlink(QFile::encodeName( m_tocFile ), QFile::encodeName( tempFile + ".cue") ) == -1 ) + return false; + + kdDebug() << QString("K3bCdrdaoWriter::cueSheet() symlink BinFileName: %1.bin").arg( tempFile ) << endl; + kdDebug() << QString("K3bCdrdaoWriter::cueSheet() symlink CueFileName: %1.cue").arg( tempFile ) << endl; + m_binFileLnk = tempFile + ".bin"; + m_cueFileLnk = tempFile + ".cue"; + return true; + } + } + } + + return false; +} + +void K3bCdrdaoWriter::slotStdLine( const QString& line ) +{ + parseCdrdaoLine(line); +} + + +void K3bCdrdaoWriter::slotProcessExited( KProcess* p ) +{ + // release the device within this process + burnDevice()->usageUnlock(); + + // unblock the device + k3bcore->unblockDevice( burnDevice() ); + + switch ( m_command ) + { + case WRITE: + case COPY: + if ( !m_binFileLnk.isEmpty() ) { + KIO::NetAccess::del(KURL::fromPathOrURL(m_cueFileLnk), (QWidget*) 0); + KIO::NetAccess::del(KURL::fromPathOrURL(m_binFileLnk), (QWidget*) 0); + } + else if( (!QFile::exists( m_tocFile ) || K3b::filesize( KURL::fromPathOrURL(m_tocFile) ) == 0 ) && !m_onTheFly ) + { + // cdrdao removed the tocfile :( + // we need to recover it + if ( !KIO::NetAccess::copy(KURL::fromPathOrURL(m_backupTocFile), KURL::fromPathOrURL(m_tocFile), (QWidget*) 0) ) + { + kdDebug() << "(K3bCdrdaoWriter) restoring tocfile " << m_tocFile << " failed." << endl; + emit infoMessage( i18n("Due to a bug in cdrdao the toc/cue file %1 has been deleted. " + "K3b was unable to restore it from the backup %2.").arg(m_tocFile).arg(m_backupTocFile), ERROR ); + } + else if ( !KIO::NetAccess::del(KURL::fromPathOrURL(m_backupTocFile), (QWidget*) 0) ) + { + kdDebug() << "(K3bCdrdaoWriter) delete tocfile backkup " << m_backupTocFile << " failed." << endl; + } + } + break; + case BLANK: + case READ: + break; + } + + if( m_canceled ) + return; + + if( p->normalExit() ) + { + switch( p->exitStatus() ) + { + case 0: + if( simulate() ) + emit infoMessage( i18n("Simulation successfully completed"), K3bJob::SUCCESS ); + else + switch ( m_command ) + { + case READ: + emit infoMessage( i18n("Reading successfully completed"), K3bJob::SUCCESS ); + break; + case WRITE: + emit infoMessage( i18n("Writing successfully completed"), K3bJob::SUCCESS ); + break; + case COPY: + emit infoMessage( i18n("Copying successfully completed"), K3bJob::SUCCESS ); + break; + case BLANK: + emit infoMessage( i18n("Blanking successfully completed"), K3bJob::SUCCESS ); + break; + } + + if( m_command == WRITE || m_command == COPY ) { + int s = d->speedEst->average(); + emit infoMessage( i18n("Average overall write speed: %1 KB/s (%2x)").arg(s).arg(KGlobal::locale()->formatNumber((double)s/150.0), 2), INFO ); + } + + jobFinished( true ); + break; + + default: + if( !m_knownError && !wasSourceUnreadable() ) { + emit infoMessage( i18n("%1 returned an unknown error (code %2).").arg(m_cdrdaoBinObject->name()).arg(p->exitStatus()), + K3bJob::ERROR ); + emit infoMessage( i18n("Please include the debugging output in your problem report."), K3bJob::ERROR ); + } + + jobFinished( false ); + break; + } + } + else + { + emit infoMessage( i18n("%1 did not exit cleanly.").arg("cdrdao"), K3bJob::ERROR ); + jobFinished( false ); + } +} + + +void K3bCdrdaoWriter::unknownCdrdaoLine( const QString& line ) +{ + if( line.contains( "at speed" ) ) + { + // parse the speed and inform the user if cdrdao switched it down + int pos = line.find( "at speed" ); + int po2 = line.find( QRegExp("\\D"), pos + 9 ); + int speed = line.mid( pos+9, po2-pos-9 ).toInt(); + if( speed < d->usedSpeed ) + { + emit infoMessage( i18n("Medium or burner do not support writing at %1x speed").arg(d->usedSpeed), K3bJob::WARNING ); + emit infoMessage( i18n("Switching down burn speed to %1x").arg(speed), K3bJob::WARNING ); + } + } +} + + +void K3bCdrdaoWriter::reinitParser() +{ + ::memset( &d->oldMsg, 0, sizeof(ProgressMsg2) ); + ::memset( &d->newMsg, 0, sizeof(ProgressMsg2) ); + + m_currentTrack=0; +} + +void K3bCdrdaoWriter::parseCdrdaoLine( const QString& str ) +{ + emit debuggingOutput( "cdrdao", str ); + // kdDebug() << "(cdrdaoparse)" << str << endl; + // find some messages from cdrdao + // ----------------------------------------------------------------------------------------- + if( (str).startsWith( "Warning" ) || (str).startsWith( "WARNING" ) || (str).startsWith( "ERROR" ) ) + { + parseCdrdaoError( str ); + } + else if( (str).startsWith( "Wrote" ) && !str.contains("blocks") ) + { + parseCdrdaoWrote( str ); + } + else if( (str).startsWith( "Executing power" ) ) + { + emit newSubTask( i18n("Executing Power calibration") ); + } + else if( (str).startsWith( "Power calibration successful" ) ) + { + emit infoMessage( i18n("Power calibration successful"), K3bJob::INFO ); + emit newSubTask( i18n("Preparing burn process...") ); + } + else if( (str).startsWith( "Flushing cache" ) ) + { + emit newSubTask( i18n("Flushing cache") ); + } + else if( (str).startsWith( "Writing CD-TEXT lead" ) ) + { + emit newSubTask( i18n("Writing CD-Text lead-in...") ); + } + else if( (str).startsWith( "Turning BURN-Proof on" ) ) + { + emit infoMessage( i18n("Turning BURN-Proof on"), K3bJob::INFO ); + } + else if( str.startsWith( "Copying" ) ) + { + emit infoMessage( str, K3bJob::INFO ); + } + else if( str.startsWith( "Found ISRC" ) ) + { + emit infoMessage( i18n("Found ISRC code"), K3bJob::INFO ); + } + else if( str.startsWith( "Found pre-gap" ) ) + { + emit infoMessage( i18n("Found pregap: %1").arg( str.mid(str.find(":")+1) ), K3bJob::INFO ); + } + else + unknownCdrdaoLine(str); +} + +void K3bCdrdaoWriter::parseCdrdaoError( const QString& line ) +{ + int pos = -1; + + if( line.contains( "No driver found" ) || + line.contains( "use option --driver" ) ) + { + emit infoMessage( i18n("No cdrdao driver found."), K3bJob::ERROR ); + emit infoMessage( i18n("Please select one manually in the device settings."), K3bJob::ERROR ); + emit infoMessage( i18n("For most current drives this would be 'generic-mmc'."), K3bJob::ERROR ); + m_knownError = true; + } + else if( line.contains( "Cannot setup device" ) ) + { + // no nothing... + } + else if( line.contains( "not ready") ) + { + emit infoMessage( i18n("Device not ready, waiting."),K3bJob::WARNING ); + } + else if( line.contains("Drive does not accept any cue sheet") ) + { + emit infoMessage( i18n("Cue sheet not accepted."), K3bJob::ERROR ); + m_knownError = true; + } + else if( (pos = line.find( "Illegal option" )) > 0 ) { + // ERROR: Illegal option: -wurst + emit infoMessage( i18n("No valid %1 option: %2").arg(m_cdrdaoBinObject->name()).arg(line.mid(pos+16)), + ERROR ); + m_knownError = true; + } + else if( line.contains( "exceeds capacity" ) ) { + emit infoMessage( i18n("Data does not fit on disk."), ERROR ); + if( m_cdrdaoBinObject->hasFeature("overburn") ) + emit infoMessage( i18n("Enable overburning in the advanced K3b settings to burn anyway."), INFO ); + m_knownError = true; + } + // else if( !line.contains( "remote progress message" ) ) +// emit infoMessage( line, K3bJob::ERROR ); +} + +void K3bCdrdaoWriter::parseCdrdaoWrote( const QString& line ) +{ + int pos, po2; + pos = line.find( "Wrote" ); + po2 = line.find( " ", pos + 6 ); + int processed = line.mid( pos+6, po2-pos-6 ).toInt(); + + pos = line.find( "of" ); + po2 = line.find( " ", pos + 3 ); + m_size = line.mid( pos+3, po2-pos-3 ).toInt(); + + d->speedEst->dataWritten( processed*1024 ); + + emit processedSize( processed, m_size ); +} + + +void K3bCdrdaoWriter::parseCdrdaoMessage() +{ + static const char msgSync[] = { 0xff, 0x00, 0xff, 0x00 }; + unsigned int avail = m_comSock->bytesAvailable(); + unsigned int msgs = avail / ( sizeof(msgSync)+d->progressMsgSize ); + unsigned int count = 0; + + if ( msgs < 1 ) + return; + else if ( msgs > 1) { + // move the read-index forward to the beginnig of the most recent message + count = ( msgs-1 ) * ( sizeof(msgSync)+d->progressMsgSize ); + m_comSock->at(count); + kdDebug() << "(K3bCdrdaoParser) " << msgs-1 << " message(s) skipped" << endl; + } + + while( count < avail ) { + + // search for msg sync + int state = 0; + char buf; + while( state < 4 ) { + buf = m_comSock->getch(); + ++count; + if( count == avail ) { + // kdDebug() << "(K3bCdrdaoParser) remote message sync not found (" << count << ")" << endl; + return; + } + + if( buf == msgSync[state] ) + ++state; + else + state = 0; + } + + if( (avail - count) < d->progressMsgSize ) { + kdDebug() << "(K3bCdrdaoParser) could not read complete remote message." << endl; + return; + } + + // read one message (the message size changed in cdrdao 1.1.8) + ::memset( &d->newMsg, 0, d->progressMsgSize ); + int size = m_comSock->readBlock( (char*)&d->newMsg, d->progressMsgSize); + if( size == -1 ) { + kdDebug() << "(K3bCdrdaoParser) read error" << endl; + return; + } + count += size; + + // sometimes the progress takes one step back (on my system when using paranoia-level 3) + // so we just use messages that are greater than the previous or first messages + if( d->oldMsg < d->newMsg + || ( d->newMsg.track == 1 && + d->newMsg.trackProgress <= 10 )) { + + if( d->newMsg.track != m_currentTrack ) { + switch( d->newMsg.status ) { + case PGSMSG_RCD_EXTRACTING: + emit nextTrack( d->newMsg.track, d->newMsg.totalTracks ); + break; + case PGSMSG_WCD_LEADIN: + emit newSubTask( i18n("Writing leadin ") ); + break; + case PGSMSG_WCD_DATA: + emit nextTrack( d->newMsg.track, d->newMsg.totalTracks ); + break; + case PGSMSG_WCD_LEADOUT: + emit newSubTask( i18n("Writing leadout ") ); + break; + } + + m_currentTrack = d->newMsg.track; + } + + if( d->newMsg.status == PGSMSG_WCD_LEADIN || d->newMsg.status == PGSMSG_WCD_LEADOUT ) { + // cdrdao >= 1.1.8 emits progress data when writing the lead-in and lead-out :) + emit subPercent( d->newMsg.totalProgress/10 ); + } + else { + emit subPercent( d->newMsg.trackProgress/10 ); + emit percent( d->newMsg.totalProgress/10 ); + } + + emit buffer(d->newMsg.bufferFillRate); + + if( d->progressMsgSize == (unsigned int)sizeof(ProgressMsg2) ) + emit deviceBuffer( d->newMsg.writerFillRate ); + + ::memcpy( &d->oldMsg, &d->newMsg, d->progressMsgSize ); + } + } +} + + +void K3bCdrdaoWriter::slotThroughput( int t ) +{ + // FIXME: determine sector size + emit writeSpeed( t, 150 ); +} + + +QString K3bCdrdaoWriter::findDriverFile( const K3bExternalBin* bin ) +{ + if( !bin ) + return QString::null; + + // cdrdao normally in (prefix)/bin and driver table in (prefix)/share/cdrdao + QString path = bin->path; + path.truncate( path.findRev("/") ); + path.truncate( path.findRev("/") ); + path += "/share/cdrdao/drivers"; + if( QFile::exists(path) ) + return path; + else { + kdDebug() << "(K3bCdrdaoWriter) could not find cdrdao driver table." << endl; + return QString::null; + } +} + + +// returns true if the driver file could be opened and no driver could be found +// TODO: cache the drivers +bool K3bCdrdaoWriter::defaultToGenericMMC( K3bDevice::Device* dev, bool writer ) +{ + QString driverTable = findDriverFile( m_cdrdaoBinObject ); + if( !driverTable.isEmpty() ) { + QFile f( driverTable ); + if( f.open( IO_ReadOnly ) ) { + // read all drivers + QStringList drivers; + QTextStream fStr( &f ); + while( !fStr.atEnd() ) { + QString line = fStr.readLine(); + if( line.isEmpty() ) + continue; + if( line[0] == '#' ) + continue; + if( line[0] == 'R' && writer ) + continue; + if( line[0] == 'W' && !writer ) + continue; + drivers.append(line); + } + + // search for the driver + for( QStringList::const_iterator it = drivers.begin(); it != drivers.end(); ++it ) { + if( (*it).section( '|', 1, 1 ) == dev->vendor() && + (*it).section( '|', 2, 2 ) == dev->description() ) + return false; + } + + // no driver found + return true; + } + else { + kdDebug() << "(K3bCdrdaoWriter) could not open driver table " << driverTable << endl; + return false; + } + } + else + return false; +} + + +#include "k3bcdrdaowriter.moc" |