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.
1102 lines
31 KiB
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"
|