/* * * $Id: k3bcdrdaowriter.cpp 654649 2007-04-16 17:55:50Z trueg $ * Copyright (C) 2003 Sebastian Trueg * Klaus-Dieter Krannich * * This file is part of the K3b project. * Copyright (C) 1998-2007 Sebastian Trueg * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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, TQObject* 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, TQT_SIGNAL(throughput(int)), this, TQT_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 TQSocket(); m_comSock->setSocket(m_cdrdaoComm[1]); m_comSock->socketDevice()->setReceiveBufferSize(49152); // magic number from TQt documentation m_comSock->socketDevice()->setBlocking(false); connect( m_comSock, TQT_SIGNAL(readyRead()), this, TQT_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 << TQString("%1:0x00000010").tqarg( 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" << TQString("%1").tqarg(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.").tqarg(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" << TQString::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.").tqarg(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" << TQString("%1").tqarg(m_taoSourceAdjust); // paranoia Mode if ( m_paranoiaMode != -1 ) *m_process << "--paranoia-mode" << TQString("%1").tqarg(m_paranoiaMode); // session if ( m_session != -1 ) *m_process << "--session" << TQString("%1").tqarg(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 << TQString("%1:0x00000010").tqarg( 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" << TQString("%1").tqarg(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 TQStringList& params = m_cdrdaoBinObject->userParameters(); for( TQStringList::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" << TQString("%1").tqarg(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 TQString& arg ) { *m_process << arg; return this; } void K3bCdrdaoWriter::start() { jobStarted(); d->speedEst->reset(); delete m_process; // tdelibs want this! m_process = new K3bProcess(); m_process->setRunPrivileged(true); m_process->setSplitStdout(false); m_process->setRawStdin(true); connect( m_process, TQT_SIGNAL(stderrLine(const TQString&)), this, TQT_SLOT(slotStdLine(const TQString&)) ); connect( m_process, TQT_SIGNAL(processExited(KProcess*)), this, TQT_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.").tqarg("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").tqarg(m_cdrdaoBinObject->name()).tqarg(m_cdrdaoBinObject->version).tqarg(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), (TQWidget*) 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(TQUrl(m_tocFile).dirPath()); kdDebug() << "***** cdrdao parameters:\n"; const TQValueList& args = m_process->args(); TQString s; for( TQValueList::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.").tqarg("cdrdao"), K3bJob::ERROR ); jobFinished(false); } else { switch ( m_command ) { case WRITE: if( simulate() ) { emit infoMessage(i18n("Starting DAO simulation at %1x speed...").tqarg(d->usedSpeed), K3bJob::INFO ); emit newTask( i18n("Simulating") ); } else { emit infoMessage( i18n("Starting DAO writing at %1x speed...").tqarg(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...").tqarg(d->usedSpeed), K3bJob::INFO ); emit newTask( i18n("Simulating") ); } else { emit infoMessage( i18n("Starting copy at %1x speed...").tqarg(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" ) ) { TQFile f( m_tocFile ); if ( f.open( IO_ReadOnly ) ) { TQTextStream ts( &f ); if ( !ts.eof() ) { TQString 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 ); TQFileInfo fi( TQFileInfo( m_tocFile ).dirPath() + "/" + TQFileInfo( line ).fileName() ); TQString binpath = fi.filePath(); kdDebug() << TQString("K3bCdrdaoWriter::cueSheet() BinFilePath from CueFile: %1").tqarg( line ) << endl; kdDebug() << TQString("K3bCdrdaoWriter::cueSheet() absolute BinFilePath: %1").tqarg( binpath ) << endl; if ( !fi.exists() ) return false; KTempFile tempF; TQString tempFile = tempF.name(); tempF.unlink(); if ( symlink(TQFile::encodeName( binpath ), TQFile::encodeName( tempFile + ".bin") ) == -1 ) return false; if ( symlink(TQFile::encodeName( m_tocFile ), TQFile::encodeName( tempFile + ".cue") ) == -1 ) return false; kdDebug() << TQString("K3bCdrdaoWriter::cueSheet() symlink BinFileName: %1.bin").tqarg( tempFile ) << endl; kdDebug() << TQString("K3bCdrdaoWriter::cueSheet() symlink CueFileName: %1.cue").tqarg( tempFile ) << endl; m_binFileLnk = tempFile + ".bin"; m_cueFileLnk = tempFile + ".cue"; return true; } } } return false; } void K3bCdrdaoWriter::slotStdLine( const TQString& 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), (TQWidget*) 0); KIO::NetAccess::del(KURL::fromPathOrURL(m_binFileLnk), (TQWidget*) 0); } else if( (!TQFile::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), (TQWidget*) 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.").tqarg(m_tocFile).tqarg(m_backupTocFile), ERROR ); } else if ( !KIO::NetAccess::del(KURL::fromPathOrURL(m_backupTocFile), (TQWidget*) 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)").tqarg(s).tqarg(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).").tqarg(m_cdrdaoBinObject->name()).tqarg(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.").tqarg("cdrdao"), K3bJob::ERROR ); jobFinished( false ); } } void K3bCdrdaoWriter::unknownCdrdaoLine( const TQString& 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( TQRegExp("\\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").tqarg(d->usedSpeed), K3bJob::WARNING ); emit infoMessage( i18n("Switching down burn speed to %1x").tqarg(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 TQString& 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").tqarg( str.mid(str.find(":")+1) ), K3bJob::INFO ); } else unknownCdrdaoLine(str); } void K3bCdrdaoWriter::parseCdrdaoError( const TQString& 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").tqarg(m_cdrdaoBinObject->name()).tqarg(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 TQString& 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 ); } TQString K3bCdrdaoWriter::findDriverFile( const K3bExternalBin* bin ) { if( !bin ) return TQString(); // cdrdao normally in (prefix)/bin and driver table in (prefix)/share/cdrdao TQString path = bin->path; path.truncate( path.findRev("/") ); path.truncate( path.findRev("/") ); path += "/share/cdrdao/drivers"; if( TQFile::exists(path) ) return path; else { kdDebug() << "(K3bCdrdaoWriter) could not find cdrdao driver table." << endl; return TQString(); } } // 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 ) { TQString driverTable = findDriverFile( m_cdrdaoBinObject ); if( !driverTable.isEmpty() ) { TQFile f( driverTable ); if( f.open( IO_ReadOnly ) ) { // read all drivers TQStringList drivers; TQTextStream fStr( &f ); while( !fStr.atEnd() ) { TQString 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( TQStringList::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"