File indexing completed on 2024-05-05 04:50:57

0001 /*
0002     SPDX-FileCopyrightText: 2009 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-FileCopyrightText: 1998-2009 Sebastian Trueg <trueg@k3b.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "k3bmetawriter.h"
0009 #include "k3bcdrecordwriter.h"
0010 #include "k3bcdrskinwriter.h"
0011 #include "k3bcdrdaowriter.h"
0012 #include "k3bgrowisofswriter.h"
0013 #include "k3btocfilewriter.h"
0014 #include "k3binffilewriter.h"
0015 
0016 #include "k3bexternalbinmanager.h"
0017 #include "k3bglobals.h"
0018 #include "k3btrack.h"
0019 #include "k3btoc.h"
0020 #include "k3bmedium.h"
0021 #include "k3bcore.h"
0022 #include "k3bmediacache.h"
0023 #include "k3bdevicetypes.h"
0024 #include "k3bdeviceglobals.h"
0025 #include "k3b_i18n.h"
0026 
0027 #include <QFile>
0028 
0029 
0030 class K3b::MetaWriter::Private
0031 {
0032 public:
0033     Private()
0034         : writingApp(WritingAppAuto),
0035           writingMode(WritingModeAuto),
0036           clone(false),
0037           multiSession(false),
0038           layerBreak(0),
0039           hideFirstTrack(false),
0040           supportedWritingMedia(Device::MEDIA_WRITABLE),
0041           writingJob(0) {
0042     }
0043 
0044     // member vars set via setXXX methods
0045     // ----------------------------------
0046     WritingApp writingApp;
0047     WritingMode writingMode;
0048     QString cueFile;
0049     bool clone;
0050     bool multiSession;
0051     Device::CdText cdText;
0052     qint64 layerBreak;
0053     bool hideFirstTrack;
0054     Device::Toc toc;
0055     Device::MediaTypes supportedWritingMedia;
0056     QStringList images;
0057     // ----------------------------------
0058 
0059 
0060     // status vars
0061     // ----------------------------------
0062     WritingApp usedWritingApp;
0063     WritingMode usedWritingMode;
0064 
0065     AbstractWriter* writingJob;
0066 
0067     QVector<QString> infFiles;
0068     QString tocFile;
0069     // ----------------------------------
0070 
0071 
0072     void prepareTempFileNames( const QString& path = QString() )
0073     {
0074         infFiles.clear();
0075 
0076         QString prefix = K3b::findUniqueFilePrefix( "k3b_tmp_", path ) + '_';
0077 
0078         for( int i = 0; i < toc.count(); ++i ) {
0079             infFiles.append( prefix + QString::number( i+1 ).rightJustified( 2, '0' ) + ".inf" );
0080         }
0081 
0082         tocFile = prefix + ".toc";
0083     }
0084 
0085     QString tocFileName()
0086     {
0087         if( tocFile.isEmpty() )
0088             prepareTempFileNames();
0089         return tocFile;
0090     }
0091 
0092     QString infFileName( int track )
0093     {
0094         if( infFiles.count() < track )
0095             prepareTempFileNames();
0096         return infFiles.at( track - 1 );
0097     }
0098 
0099     void cleanupTempFiles()
0100     {
0101         for( int i = 0; i < infFiles.count(); ++i ) {
0102             if( QFile::exists( infFiles[i] ) )
0103                 QFile::remove( infFiles[i] );
0104         }
0105 
0106         if( QFile::exists( tocFile ) )
0107             QFile::remove( tocFile );
0108 
0109         tocFile.truncate(0);
0110     }
0111 };
0112 
0113 
0114 K3b::MetaWriter::MetaWriter( Device::Device* dev, JobHandler* hdl, QObject* parent )
0115     : AbstractWriter( dev, hdl,  parent ),
0116       d(new Private())
0117 {
0118 }
0119 
0120 
0121 K3b::MetaWriter::~MetaWriter()
0122 {
0123     delete d->writingJob;
0124     delete d;
0125 }
0126 
0127 
0128 QIODevice* K3b::MetaWriter::ioDevice() const
0129 {
0130     if( d->writingJob )
0131         return d->writingJob->ioDevice();
0132     else
0133         return 0;
0134 }
0135 
0136 
0137 void K3b::MetaWriter::start()
0138 {
0139     jobStarted();
0140 
0141     // step 1: see if we are set up correctly
0142     if( !ensureSettingsIntegrity() ) {
0143         jobFinished( false );
0144         return;
0145     }
0146 
0147     if( !determineUsedAppAndMode() ) {
0148         jobFinished( false );
0149         return;
0150     }
0151 
0152     delete d->writingJob;
0153     d->writingJob = 0;
0154 
0155     bool success = true;
0156     switch( d->usedWritingApp ) {
0157     case K3b::WritingAppCdrecord:
0158         success = setupCdrecordJob();
0159         break;
0160     case K3b::WritingAppCdrdao:
0161         success = setupCdrdaoJob();
0162         break;
0163     case K3b::WritingAppGrowisofs:
0164         success = setupGrowisofsob();
0165         break;
0166     case K3b::WritingAppCdrskin:
0167         success = setupCdrskinJob();
0168         break;
0169     default:
0170         Q_ASSERT(false);
0171         break;
0172     }
0173 
0174     if( !success ) {
0175         jobFinished( false );
0176         return;
0177     }
0178 
0179     informUser();
0180 
0181     connectJob( d->writingJob, SLOT(slotWritingJobFinished(bool)) );
0182     connect( d->writingJob, SIGNAL(buffer(int)),
0183              this, SIGNAL(buffer(int)) );
0184     connect( d->writingJob, SIGNAL(deviceBuffer(int)),
0185              this, SIGNAL(deviceBuffer(int)) );
0186     connect( d->writingJob, SIGNAL(writeSpeed(int,K3b::Device::SpeedMultiplicator)),
0187              this, SIGNAL(writeSpeed(int,K3b::Device::SpeedMultiplicator)) );
0188     connect( d->writingJob, SIGNAL(nextTrack(int,int)),
0189              this, SIGNAL(nextTrack(int,int)) );
0190 
0191     d->writingJob->start();
0192 }
0193 
0194 
0195 bool K3b::MetaWriter::ensureSettingsIntegrity()
0196 {
0197     if( d->toc.isEmpty() && d->cueFile.isEmpty() ) {
0198         emit infoMessage( QLatin1String("Internal error: job not setup properly: cue file and toc set! "
0199                                         "The application needs fixing!"), MessageError );
0200         return false;
0201     }
0202     else if( !d->images.isEmpty() && d->images.count() != d->toc.count() ) {
0203         emit infoMessage( QLatin1String("Internal error: job not setup properly: image count != track count! "
0204                                         "The application needs fixing!"), MessageError );
0205         return false;
0206     }
0207     else if( d->toc.contentType() == Device::MIXED ) {
0208         int dtc = 0;
0209         for( int i = 0; i < d->toc.count(); ++i ) {
0210             Device::Track track = d->toc[i];
0211             if( track.type() == Device::Track::TYPE_DATA ) {
0212                 if( i > 0 && i+1 == d->toc.count() ) {
0213                     emit infoMessage( QLatin1String("Internal error: job not setup properly: can only handle data tracks at the beginning or end of toc! "
0214                                                     "The application needs fixing!"), MessageError );
0215                     return false;
0216                 }
0217                 ++dtc;
0218             }
0219         }
0220         if( dtc > 1 ) {
0221             emit infoMessage( QLatin1String("Internal error: job not setup properly: cannot handle more than one data track in a session! "
0222                                             "The application needs fixing!"), MessageError );
0223             return false;
0224         }
0225     }
0226 
0227     return true;
0228 }
0229 
0230 
0231 bool K3b::MetaWriter::determineUsedAppAndMode()
0232 {
0233     // =============================================
0234     // Get the burn medium
0235     // =============================================
0236 
0237     Device::MediaTypes mt = d->supportedWritingMedia;
0238     // a little bit of restricting
0239     if( d->writingMode == K3b::WritingModeRestrictedOverwrite ) // we treat DVD+R(W) as restricted overwrite media
0240         mt = K3b::Device::MEDIA_DVD_RW_OVWR|K3b::Device::MEDIA_DVD_PLUS_RW|K3b::Device::MEDIA_DVD_PLUS_R;
0241     else if( d->writingMode == K3b::WritingModeIncrementalSequential )
0242         mt ^= Device::MEDIA_CD_ALL;
0243     else if( d->writingMode == K3b::WritingModeRaw )
0244         mt ^= (Device::MEDIA_DVD_ALL|Device::MEDIA_BD_ALL);
0245 
0246     Device::MediaType mediaType = waitForMedium( burnDevice(), Device::STATE_EMPTY, mt );
0247     if( mediaType == Device::MEDIA_UNKNOWN )
0248         return false;
0249 
0250     Medium medium = k3bcore->mediaCache()->medium( burnDevice() );
0251 
0252 
0253     // =============================================
0254     // Some values we need later on
0255     // =============================================
0256 
0257     bool onTheFly = d->cueFile.isEmpty() && d->images.isEmpty();
0258     bool cdrecordOnTheFly = false;
0259     bool cdrecordCdText = false;
0260     bool cdrecordBluRay = false;
0261     bool cdrecordWodim = false;
0262     bool growisofsBluRay = false;
0263     const K3b::ExternalBin* cdrecordBin = k3bcore->externalBinManager()->binObject("cdrecord");
0264     if( cdrecordBin ) {
0265         cdrecordOnTheFly = cdrecordBin->hasFeature( "audio-stdin" );
0266         cdrecordCdText = cdrecordBin->hasFeature( "cdtext" );
0267         cdrecordBluRay = cdrecordBin->hasFeature( "blu-ray" );
0268         cdrecordWodim = cdrecordBin->hasFeature( "wodim" );
0269     }
0270     if( k3bcore->externalBinManager()->binObject("growisofs") ) {
0271         growisofsBluRay = k3bcore->externalBinManager()->binObject("growisofs")->hasFeature( "blu-ray" );
0272     }
0273 
0274 
0275     // =============================================
0276     // Determine writing app
0277     // =============================================
0278 
0279     d->usedWritingApp = d->writingApp;
0280     if( d->writingApp == K3b::WritingAppAuto ) {
0281         if( mediaType & Device::MEDIA_CD_ALL ) {
0282             if( d->usedWritingMode == K3b::WritingModeSao ) {
0283                 // there are none-DAO writers that are supported by cdrdao
0284                 if( !burnDevice()->dao() ||
0285                     ( !cdrecordOnTheFly && onTheFly ) ||
0286                     ( !d->cdText.isEmpty() && !cdrecordCdText ) ||
0287                     d->hideFirstTrack ) {
0288                     d->usedWritingApp = K3b::WritingAppCdrdao;
0289                 }
0290                 // cdrecord seems to have problems writing xa 1 disks in dao mode? At least on my system!
0291                 else if( d->cueFile.isEmpty() &&
0292                          d->toc.first().mode() != Device::Track::MODE1 ) {
0293                     d->usedWritingApp = K3b::WritingAppCdrdao;
0294                 }
0295                 else {
0296                     d->usedWritingApp = K3b::WritingAppCdrecord;
0297                 }
0298             }
0299             else
0300                 d->usedWritingApp = K3b::WritingAppCdrecord;
0301         }
0302         else {
0303             if ( d->writingApp == K3b::WritingAppCdrdao ) {
0304                 emit infoMessage( i18n( "Cannot write %1 media using %2. Falling back to default application.",
0305                                         K3b::Device::mediaTypeString( mediaType, true ), QLatin1String("cdrdao") ), MessageWarning );
0306                 d->writingApp = K3b::WritingAppAuto;
0307             }
0308 
0309             if( d->toc.count() != 1 || d->toc.first().mode() != Device::Track::MODE1 ) {
0310                 emit infoMessage( i18n("DVD and Blu-ray tracks can only be written in MODE1."), MessageWarning );
0311             }
0312 
0313             if( mediaType & (K3b::Device::MEDIA_DVD_PLUS_RW|K3b::Device::MEDIA_DVD_RW_OVWR) &&
0314                 d->multiSession ) {
0315                 // we can only do this with growisofs
0316                 d->usedWritingApp = WritingAppGrowisofs;
0317             }
0318             else if( mediaType & Device::MEDIA_DVD_ALL ) {
0319                 // wodim (at least on fedora) doesn't do DVDs all that well, use growisofs instead
0320                 if ( cdrecordWodim ) {
0321                     d->usedWritingApp = WritingAppGrowisofs;
0322                 }
0323                 else {
0324                     d->usedWritingApp = WritingAppCdrecord;
0325                 }
0326             }
0327             else if( mediaType & Device::MEDIA_BD_ALL ) {
0328                 if( cdrecordBluRay && ! cdrecordWodim ) {
0329                     d->usedWritingApp = WritingAppCdrecord;
0330                 }
0331                 else if( growisofsBluRay ) {
0332                     d->usedWritingApp = WritingAppGrowisofs;
0333                 }
0334                 else {
0335                     emit infoMessage( i18n("Missing Blu-ray support in cdrecord and growisofs. Please update the system."),  MessageError );
0336                     return false;
0337                 }
0338             }
0339         }
0340     }
0341     else
0342         d->usedWritingApp = d->writingApp;
0343 
0344 
0345     // =============================================
0346     // Determine writing mode
0347     // =============================================
0348 
0349     if( d->writingMode == K3b::WritingModeAuto ) {
0350         if( mediaType & (Device::MEDIA_DVD_PLUS_RW|K3b::Device::MEDIA_DVD_RW_OVWR) ) {
0351             d->usedWritingMode = K3b::WritingModeRestrictedOverwrite;
0352         }
0353         else if( mediaType & Device::MEDIA_DVD_PLUS_ALL ) {
0354             d->usedWritingMode = K3b::WritingModeSao;
0355         }
0356         else if( mediaType & (K3b::Device::MEDIA_DVD_RW_SEQ|
0357                               K3b::Device::MEDIA_DVD_RW) ) {
0358             d->usedWritingMode = K3b::WritingModeIncrementalSequential;
0359         }
0360         else if( mediaType & K3b::Device::MEDIA_DVD_MINUS_ALL ) {
0361             if( d->multiSession )
0362                 d->usedWritingMode = K3b::WritingModeIncrementalSequential;
0363             else
0364                 d->usedWritingMode = K3b::WritingModeSao;
0365         }
0366         else if( mediaType & Device::MEDIA_CD_ALL ) {
0367             if( !d->cueFile.isEmpty() ) {
0368                 d->usedWritingMode = WritingModeSao;
0369             }
0370             else {
0371                 if( d->toc.contentType() == Device::MIXED ) {
0372                     // when writing mode in one session, TAO is the only choice
0373                     // otherwise we need to see what kind of session we write
0374                     d->usedWritingMode = WritingModeTao;
0375                 }
0376 
0377                 else if( d->toc.contentType() == Device::DATA ) {
0378                     //
0379                     // Data sessions are simple: always SAO except if there is multisession involved
0380                     // However, if we add a session, even if d->multiSession is false, use TAO!
0381                     //
0382                     if( burnDevice()->dao() &&
0383                         d->toc.first().mode() == K3b::Device::Track::MODE1 &&
0384                         !d->multiSession &&
0385                         medium.diskInfo().empty() )
0386                         d->usedWritingMode = K3b::WritingModeSao;
0387                     else
0388                         d->usedWritingMode = K3b::WritingModeTao;
0389                 }
0390                 else {
0391                     //
0392                     // there are a lot of writers out there which produce coasters
0393                     // in dao mode if the CD contains pregaps of length 0 (or maybe already != 2 secs?)
0394                     //
0395                     // Also most writers do not accept cuesheets with tracks smaller than 4 seconds (a violation
0396                     // of the red book standard) in DAO mode.
0397                     //
0398                     bool zeroPregap = false;
0399                     bool less4Sec = false;
0400                     for( int i = 0; i < d->toc.count(); ++i ) {
0401                         Device::Track track = d->toc[i];
0402 
0403                         if( track.type() != Device::Track::TYPE_AUDIO )
0404                             continue;
0405 
0406                         if( track.index0() == 0 && i+1 < d->toc.count() ) // the last track's postgap is always 0
0407                             zeroPregap = true;
0408 
0409                         if( track.length() < K3b::Msf( 0, 4, 0 ) )
0410                             less4Sec = true;
0411                     }
0412 
0413                     //
0414                     // DAO is always the first choice
0415                     // RAW second and TAO last
0416                     // there are none-DAO writers that are supported by cdrdao
0417                     //
0418                     // older cdrecord versions do not support the -shorttrack option in RAW writing mode
0419                     //
0420                     if( !burnDevice()->dao() && d->usedWritingApp == K3b::WritingAppCdrecord ) {
0421                         if( !burnDevice()->supportsRawWriting() &&
0422                             ( !less4Sec || (cdrecordBin && cdrecordBin->hasFeature( "short-track-raw" ) ) ) )
0423                             d->usedWritingMode = K3b::WritingModeRaw;
0424                         else
0425                             d->usedWritingMode = K3b::WritingModeTao;
0426                     }
0427                     else {
0428                         if( (zeroPregap || less4Sec) && burnDevice()->supportsRawWriting() ) {
0429                             d->usedWritingMode = K3b::WritingModeRaw;
0430                             if( less4Sec )
0431                                 emit infoMessage( i18n("Track lengths below 4 seconds violate the Red Book standard."), MessageWarning );
0432                         }
0433                         else
0434                             d->usedWritingMode = K3b::WritingModeSao;
0435                     }
0436                 }
0437             }
0438         }
0439         else {
0440             // FIXME: what to use for BD?
0441             d->usedWritingMode = K3b::WritingModeSao;
0442         }
0443     }
0444     else {
0445         d->usedWritingMode = d->writingMode;
0446 
0447         if( !(mediaType & (K3b::Device::MEDIA_DVD_RW|K3b::Device::MEDIA_DVD_RW_OVWR|K3b::Device::MEDIA_DVD_RW_SEQ)) &&
0448             d->writingMode == K3b::WritingModeRestrictedOverwrite ) {
0449             emit infoMessage( i18n("Restricted Overwrite is not possible with DVD-R media."), MessageInfo );
0450             return false;
0451         }
0452     }
0453 
0454 
0455     // =============================================
0456     // Some final checks and safety nets
0457     // =============================================
0458 
0459     // on-the-fly writing with cdrecord >= 2.01a13
0460     if( d->usedWritingApp == K3b::WritingAppCdrecord &&
0461         onTheFly &&
0462         !cdrecordOnTheFly ) {
0463         emit infoMessage( i18n("On-the-fly writing with cdrecord < 2.01a13 not supported."), MessageError );
0464         return false;
0465     }
0466 
0467     if( d->usedWritingApp == K3b::WritingAppCdrecord &&
0468         !d->cdText.isEmpty() ) {
0469         if( !cdrecordCdText ) {
0470             emit infoMessage( i18n("Cdrecord %1 does not support CD-Text writing.",
0471                                    k3bcore->externalBinManager()->binObject("cdrecord")->version()), MessageError );
0472             return false;
0473         }
0474         else if( d->usedWritingMode == K3b::WritingModeTao ) {
0475             emit infoMessage( i18n("It is not possible to write CD-Text in TAO mode."), MessageWarning );
0476             return false;
0477         }
0478     }
0479 
0480     if( d->usedWritingMode == K3b::WritingModeIncrementalSequential ) {
0481         if( burnDevice()->featureCurrent( K3b::Device::FEATURE_INCREMENTAL_STREAMING_WRITABLE ) == 0 ) {
0482             if( !questionYesNo( i18n("Your writer (%1 %2) does not support Incremental Streaming with %3 "
0483                                      "media. Multisession will not be possible. Continue anyway?",
0484                                      burnDevice()->vendor(),
0485                                      burnDevice()->description(),
0486                                      K3b::Device::mediaTypeString(mediaType, true) ),
0487                                 i18n("No Incremental Streaming") ) ) {
0488                 return false;
0489             }
0490             else {
0491                 d->usedWritingMode = K3b::WritingModeSao;
0492             }
0493         }
0494     }
0495 
0496     if( !(mediaType & (K3b::Device::MEDIA_DVD_RW|K3b::Device::MEDIA_DVD_RW_OVWR|K3b::Device::MEDIA_DVD_RW_SEQ)) &&
0497         d->usedWritingMode == K3b::WritingModeRestrictedOverwrite ) {
0498         emit infoMessage( i18n("Restricted Overwrite is not possible with DVD-R media."), MessageInfo );
0499     }
0500 
0501     if( simulate() ) {
0502         if( mediaType & K3b::Device::MEDIA_DVD_PLUS_ALL ) {
0503             if( !questionYesNo( i18n("DVD+R(W) media do not support write simulation. "
0504                                      "Do you really want to continue? The media will actually be "
0505                                      "written to."),
0506                                 i18n("No Simulation with DVD+R(W)") ) ) {
0507                 return false;
0508             }
0509         }
0510         else if( mediaType & Device::MEDIA_DVD_MINUS_ALL &&
0511                  !burnDevice()->dvdMinusTestwrite() ) {
0512             if( !questionYesNo( i18n("Your writer (%1 %2) does not support simulation with DVD-R(W) media. "
0513                                      "Do you really want to continue? The media will actually be "
0514                                      "written to.",
0515                                      burnDevice()->vendor(),
0516                                      burnDevice()->description()),
0517                                 i18n("No Simulation with DVD-R(W)") ) ) {
0518                 return false;
0519             }
0520         }
0521     }
0522 
0523     // cdrecord manpage says that "not all" writers are able to write
0524     // multisession disks in dao mode. That means there are writers that can.
0525 
0526     // Does it really make sence to write Data ms cds in DAO mode since writing the
0527     // first session of a cd-extra in DAO mode is no problem with my writer while
0528     // writing the second data session is only possible in TAO mode.
0529     if( mediaType & Device::MEDIA_CD_ALL &&
0530         d->usedWritingMode == K3b::WritingModeSao &&
0531         d->multiSession )
0532         emit infoMessage( i18n("Most writers do not support writing "
0533                                "multisession CDs in DAO mode."), MessageWarning );
0534 
0535     qDebug() << "Writing mode:     " << d->writingMode;
0536     qDebug() << "Used Writing mode:" << d->usedWritingMode;
0537     qDebug() << "Writing app:     " << d->writingApp;
0538     qDebug() << "Used Writing app:" << d->usedWritingApp;
0539 
0540     return true;
0541 }
0542 
0543 
0544 bool K3b::MetaWriter::setupCdrecordJob()
0545 {
0546     K3b::CdrecordWriter* writer = new K3b::CdrecordWriter( burnDevice(), this, this );
0547     d->writingJob = writer;
0548 
0549     writer->setWritingMode( d->usedWritingMode );
0550     writer->setSimulate( simulate() );
0551     writer->setBurnSpeed( burnSpeed() );
0552     writer->setMulti( d->multiSession );
0553 
0554     if( d->multiSession &&
0555         !d->toc.isEmpty() &&
0556         d->images.isEmpty() ) {
0557         writer->addArgument("-waiti");
0558     }
0559 
0560     if( d->cueFile.isEmpty() ) {
0561         bool firstAudioTrack = true;
0562         int audioTrackCnt = 0;
0563 
0564         for( int i = 0; i < d->toc.count(); ++i ) {
0565             Device::Track track = d->toc[i];
0566             QString image;
0567             if( d->images.count() )
0568                 image = d->images[i];
0569 
0570             //
0571             // Add a data track
0572             //
0573             if( track.type() == Device::Track::TYPE_DATA ) {
0574                 if( track.mode() == Device::Track::MODE1 ) {
0575                     writer->addArgument( "-data" );
0576                 }
0577                 else {
0578                     const K3b::ExternalBin* cdrecordBin = k3bcore->externalBinManager()->binObject("cdrecord");
0579                     if( cdrecordBin && cdrecordBin->hasFeature( "xamix" ) )
0580                         writer->addArgument( "-xa" );
0581                     else
0582                         writer->addArgument( "-xa1" );
0583                 }
0584 
0585                 if( image.isEmpty() )
0586                     writer->addArgument( QString("-tsize=%1s").arg(track.length().lba()) )->addArgument("-");
0587                 else
0588                     writer->addArgument( image );
0589             }
0590 
0591             //
0592             // Add an audio track
0593             //
0594             else {
0595                 if( firstAudioTrack ) {
0596                     firstAudioTrack = false;
0597                     writer->addArgument( "-useinfo" );
0598 
0599                     // add raw cdtext data
0600                     if( !d->cdText.isEmpty() ) {
0601                         writer->setRawCdText( d->cdText.rawPackData() );
0602                     }
0603 
0604                     writer->addArgument( "-audio" );
0605 
0606                     // we always pad because although K3b makes sure all tracks' length are multiples of 2352
0607                     // it seems that normalize sometimes corrupts these lengths
0608                     // FIXME: see K3b::AudioJob for the whole less4secs and zeroPregap handling
0609                     writer->addArgument( "-pad" );
0610 
0611                     // Allow tracks shorter than 4 seconds
0612                     writer->addArgument( "-shorttrack" );
0613                 }
0614 
0615                 K3b::InfFileWriter infFileWriter;
0616                 infFileWriter.setTrack( track );
0617                 infFileWriter.setTrackNumber( ++audioTrackCnt );
0618                 if( image.isEmpty() )
0619                     infFileWriter.setBigEndian( false );
0620                 if( !infFileWriter.save( d->infFileName( audioTrackCnt ) ) )
0621                     return false;
0622 
0623                 if( image.isEmpty() ) {
0624                     // this is only supported by cdrecord versions >= 2.01a13
0625                     writer->addArgument( QFile::encodeName( d->infFileName( audioTrackCnt ) ) );
0626                 }
0627                 else {
0628                     writer->addArgument( QFile::encodeName( image ) );
0629                 }
0630             }
0631         }
0632     }
0633     else {
0634         writer->setCueFile( d->cueFile );
0635     }
0636 
0637     return true;
0638 }
0639 
0640 
0641 bool K3b::MetaWriter::setupCdrdaoJob()
0642 {
0643     QString tocFile = d->cueFile;
0644 
0645     if( !d->cueFile.isEmpty() ) {
0646         K3b::TocFileWriter tocFileWriter;
0647 
0648         //
0649         // TOC
0650         //
0651         tocFileWriter.setData( d->toc );
0652         tocFileWriter.setHideFirstTrack( d->hideFirstTrack );
0653 
0654         //
0655         // CD-Text
0656         //
0657         if( !d->cdText.isEmpty() ) {
0658             Device::CdText text = d->cdText;
0659             // if data in first track we need to add a dummy cdtext
0660             if( d->toc.first().type() == Device::Track::TYPE_DATA )
0661                 text.insert( 0, K3b::Device::TrackCdText() );
0662 
0663             tocFileWriter.setCdText( text );
0664         }
0665 
0666         //
0667         // image filenames
0668         //
0669         tocFileWriter.setFilenames( d->images );
0670 
0671         if( !tocFileWriter.save( d->tocFile ))
0672             return false;
0673 
0674         tocFile = d->tocFile;
0675     }
0676 
0677 
0678     K3b::CdrdaoWriter* writer = new K3b::CdrdaoWriter( burnDevice(), this, this );
0679     writer->setSimulate( simulate() );
0680     writer->setBurnSpeed( burnSpeed() );
0681 
0682     // multisession only for the first session
0683     writer->setMulti( d->multiSession );
0684 
0685     writer->setTocFile( tocFile );
0686 
0687     d->writingJob = writer;
0688 
0689     return true;
0690 }
0691 
0692 
0693 bool K3b::MetaWriter::setupGrowisofsob()
0694 {
0695     K3b::GrowisofsWriter* job = new K3b::GrowisofsWriter( burnDevice(), this, this );
0696 
0697     // these do only make sense with DVD-R(W)
0698     job->setSimulate( simulate() );
0699     job->setBurnSpeed( burnSpeed() );
0700     job->setWritingMode( d->usedWritingMode );
0701     job->setCloseDvd( !d->multiSession );
0702 
0703     //
0704     // In case the first layer size is not known let the
0705     // split be determined by growisofs
0706     //
0707     if( d->layerBreak > 0 ) {
0708         job->setLayerBreak( d->layerBreak );
0709     }
0710     else {
0711         // this is only used in DAO mode with growisofs >= 5.15
0712         job->setTrackSize( d->toc.first().length().lba() );
0713     }
0714 
0715     if( d->images.isEmpty() )
0716         job->setImageToWrite( QString() ); // read from stdin
0717     else
0718         job->setImageToWrite( d->images.first() );
0719 
0720     d->writingJob = job;
0721 
0722     return true;
0723 }
0724 
0725 
0726 bool K3b::MetaWriter::setupCdrskinJob()
0727 {
0728     K3b::CdrskinWriter* writer = new K3b::CdrskinWriter( burnDevice(), this, this );
0729     d->writingJob = writer;
0730 
0731     writer->setWritingMode( d->usedWritingMode );
0732     writer->setSimulate( simulate() );
0733     writer->setBurnSpeed( burnSpeed() );
0734     writer->setMulti( d->multiSession );
0735 
0736     if( d->multiSession &&
0737         !d->toc.isEmpty() &&
0738         d->images.isEmpty() ) {
0739         writer->addArgument("-waiti");
0740     }
0741 
0742     if( d->cueFile.isEmpty() ) {
0743         int audioTrackCnt = 0;
0744 
0745         for( int i = 0; i < d->toc.count(); ++i ) {
0746             Device::Track track = d->toc[i];
0747             QString image;
0748             if( d->images.count() )
0749                 image = d->images[i];
0750 
0751             //
0752             // Add a data track
0753             //
0754             if( track.type() == Device::Track::TYPE_DATA ) {
0755                 if( track.mode() == Device::Track::MODE1 ) {
0756                     writer->addArgument( "-data" );
0757                 }
0758                 else {
0759                     if( k3bcore->externalBinManager()->binObject("cdrskin") &&
0760                         k3bcore->externalBinManager()->binObject("cdrskin")->hasFeature( "xamix" ) )
0761                         writer->addArgument( "-xa" );
0762                     else
0763                         writer->addArgument( "-xa1" );
0764                 }
0765 
0766                 if( image.isEmpty() )
0767                     writer->addArgument( QString("-tsize=%1s").arg(track.length().lba()) )->addArgument("-");
0768                 else
0769                     writer->addArgument( image );
0770             }
0771 
0772             //
0773             // Add an audio track
0774             //
0775             else {
0776                 K3b::InfFileWriter infFileWriter;
0777                 infFileWriter.setTrack( track );
0778                 infFileWriter.setTrackNumber( ++audioTrackCnt );
0779                 if( image.isEmpty() )
0780                     infFileWriter.setBigEndian( false );
0781                 if( !infFileWriter.save( d->infFileName( audioTrackCnt ) ) )
0782                     return false;
0783 
0784                 if (image.isEmpty()) {
0785                     emit infoMessage(i18n("No version of cdrskin can do this yet."), MessageError);
0786                 } else {
0787                     writer->addArgument(QFile::encodeName(image));
0788                 }
0789             }
0790         }
0791     }
0792     else {
0793         writer->setCueFile( d->cueFile );
0794     }
0795 
0796     return true;
0797 }
0798 
0799 
0800 void K3b::MetaWriter::cancel()
0801 {
0802     if( active() ) {
0803         if( d->writingJob && d->writingJob->active() ) {
0804             d->writingJob->cancel();
0805         }
0806         else {
0807             // can this really happen?
0808             emit canceled();
0809             jobFinished(false);
0810         }
0811     }
0812 }
0813 
0814 
0815 void K3b::MetaWriter::slotWritingJobFinished( bool success )
0816 {
0817     d->cleanupTempFiles();
0818     jobFinished( success );
0819 }
0820 
0821 
0822 void K3b::MetaWriter::setSessionToWrite( const Device::Toc& toc, const QStringList& images )
0823 {
0824     d->toc = toc;
0825     d->images = images;
0826 }
0827 
0828 
0829 void K3b::MetaWriter::setSupportedWritingMedia( Device::MediaTypes types )
0830 {
0831     d->supportedWritingMedia = types;
0832 }
0833 
0834 
0835 void K3b::MetaWriter::setWritingApp( WritingApp app )
0836 {
0837     d->writingApp = app;
0838 }
0839 
0840 
0841 void K3b::MetaWriter::setWritingMode( WritingMode mode )
0842 {
0843     d->writingMode = mode;
0844 }
0845 
0846 
0847 void K3b::MetaWriter::setCueFile( const QString& s)
0848 {
0849     d->cueFile = s;
0850 }
0851 
0852 
0853 void K3b::MetaWriter::setClone( bool b )
0854 {
0855     d->clone = b;
0856 }
0857 
0858 
0859 void K3b::MetaWriter::setMultiSession( bool b )
0860 {
0861     d->multiSession = b;
0862 }
0863 
0864 
0865 void K3b::MetaWriter::setCdText( const Device::CdText& cdtext )
0866 {
0867     d->cdText = cdtext;
0868 }
0869 
0870 
0871 void K3b::MetaWriter::setLayerBreak( qint64 lb )
0872 {
0873     d->layerBreak = lb;
0874 }
0875 
0876 
0877 void K3b::MetaWriter::setHideFirstTrack( bool b )
0878 {
0879     d->hideFirstTrack = b;
0880 }
0881 
0882 
0883 void K3b::MetaWriter::informUser()
0884 {
0885     Medium medium = k3bcore->mediaCache()->medium( burnDevice() );
0886 
0887     if( medium.diskInfo().mediaType() == Device::MEDIA_CD_R ) {
0888         if( medium.diskInfo().empty() ) {
0889             if( d->usedWritingMode == WritingModeSao )
0890                 emit infoMessage( i18n("Writing CD in Session At Once mode."), MessageInfo );
0891             else if( d->usedWritingMode == WritingModeTao )
0892                 emit infoMessage( i18n("Writing CD in Track At Once mode."), MessageInfo );
0893             else if( d->usedWritingMode == WritingModeRaw )
0894                 emit infoMessage( i18n("Writing CD in Raw mode."), MessageInfo );
0895         }
0896         else {
0897             emit infoMessage( i18n("Appending session to CD"), MessageInfo );
0898         }
0899     }
0900 
0901     else if( medium.diskInfo().mediaType() == Device::MEDIA_CD_RW ) {
0902         if( medium.diskInfo().empty() ) {
0903             if( d->usedWritingMode == WritingModeSao )
0904                 emit infoMessage( i18n("Writing rewritable CD in Session At Once mode."), MessageInfo );
0905             else if( d->usedWritingMode == WritingModeTao )
0906                 emit infoMessage( i18n("Writing rewritable CD in Track At Once mode."), MessageInfo );
0907             else if( d->usedWritingMode == WritingModeRaw )
0908                 emit infoMessage( i18n("Writing rewritable CD in Raw mode."), MessageInfo );
0909         }
0910         else {
0911             emit infoMessage( i18n("Appending session to rewritable CD."), MessageInfo );
0912         }
0913     }
0914 
0915     else if( Device::isDvdMedia( medium.diskInfo().mediaType() ) ) {
0916         if( medium.diskInfo().appendable() ) {
0917             if( medium.diskInfo().mediaType() & (Device::MEDIA_DVD_PLUS_RW|Device::MEDIA_DVD_PLUS_RW_DL) )
0918                 emit infoMessage( i18n("Growing ISO 9660 filesystem on DVD+RW."), MessageInfo );
0919             else if( medium.diskInfo().mediaType() == Device::MEDIA_DVD_RW_OVWR )
0920                 emit infoMessage( i18n("Growing ISO 9660 filesystem on DVD-RW in restricted overwrite mode."), MessageInfo );
0921             else if( medium.diskInfo().mediaType() == Device::MEDIA_DVD_PLUS_R )
0922                 emit infoMessage( i18n("Appending session to DVD+R."), MessageInfo );
0923             else if( medium.diskInfo().mediaType() == Device::MEDIA_DVD_PLUS_R_DL )
0924                 emit infoMessage( i18n("Appending session to Double Layer DVD+R."), MessageInfo );
0925             else
0926                 emit infoMessage( i18n("Appending session to %1.", K3b::Device::mediaTypeString(medium.diskInfo().mediaType(), true) ), MessageInfo );
0927         }
0928         else {
0929             if( medium.diskInfo().mediaType() == Device::MEDIA_DVD_RW_OVWR )
0930                 emit infoMessage( i18n("Writing DVD-RW in restricted overwrite mode."), MessageInfo );
0931             else if( medium.diskInfo().mediaType() == Device::MEDIA_DVD_PLUS_R_DL )
0932                 emit infoMessage( i18n("Writing Double Layer DVD+R."), MessageInfo );
0933             else if( medium.diskInfo().mediaType() & Device::MEDIA_DVD_PLUS_ALL )
0934                 emit infoMessage( i18n("Writing %1.", K3b::Device::mediaTypeString(medium.diskInfo().mediaType(), true)), MessageInfo );
0935             else if( d->usedWritingMode == WritingModeSao )
0936                 emit infoMessage( i18n("Writing %1 in DAO mode.", K3b::Device::mediaTypeString(medium.diskInfo().mediaType(), true) ), MessageInfo );
0937             else if( d->usedWritingMode == WritingModeIncrementalSequential )
0938                 emit infoMessage( i18n("Writing %1 in incremental mode.", K3b::Device::mediaTypeString(medium.diskInfo().mediaType(), true) ), MessageInfo );
0939         }
0940     }
0941 
0942     else {
0943         emit infoMessage( i18n("Writing %1.", K3b::Device::mediaTypeString(medium.diskInfo().mediaType(), true) ), MessageInfo );
0944     }
0945 }
0946 
0947 
0948 K3b::WritingApp K3b::MetaWriter::usedWritingApp() const
0949 {
0950     return d->usedWritingApp;
0951 }
0952 
0953 
0954 K3b::WritingMode K3b::MetaWriter::usedWritingMode() const
0955 {
0956     return d->usedWritingMode;
0957 }
0958 
0959 #include "moc_k3bmetawriter.cpp"