You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
k3b/libk3b/projects/k3bcdrdaowriter.cpp

1102 lines
31 KiB

/*
*
* $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 <tqstring.h>
#include <tqstringlist.h>
#include <tqvaluelist.h>
#include <tqregexp.h>
#include <tqfile.h>
#include <tqfileinfo.h>
#include <tqdir.h>
#include <tqurl.h>
#include <tqsocket.h>
#include <tqsocketdevice.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,
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; // kdelibs 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<TQCString>& args = m_process->args();
TQString s;
for( TQValueList<TQCString>::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"