File indexing completed on 2024-06-16 07:42:05

0001 /*
0002     SPDX-FileCopyrightText: 2011 Michal Malek <michalm@jabster.pl>
0003     SPDX-FileCopyrightText: 1998-2010 Sebastian Trueg <trueg@k3b.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 
0009 #include "k3biso9660imagewritingjob.h"
0010 #include "k3bverificationjob.h"
0011 #include "k3bmetawriter.h"
0012 
0013 #include "k3bdevice.h"
0014 #include "k3bdiskinfo.h"
0015 #include "k3bdevicehandler.h"
0016 #include "k3bglobals.h"
0017 #include "k3bcore.h"
0018 #include "k3bversion.h"
0019 #include "k3bexternalbinmanager.h"
0020 #include "k3bchecksumpipe.h"
0021 #include "k3bfilesplitter.h"
0022 #include "k3bglobalsettings.h"
0023 #include "k3b_i18n.h"
0024 
0025 #include <KIO/Global>
0026 
0027 #include <QDebug>
0028 #include <QString>
0029 #include <QFile>
0030 
0031 
0032 class K3b::Iso9660ImageWritingJob::Private
0033 {
0034 public:
0035     K3b::ChecksumPipe checksumPipe;
0036     K3b::FileSplitter imageFile;
0037 
0038     bool isDvdImage;
0039     int currentCopy;
0040     bool canceled;
0041     bool finished;
0042 
0043     VerificationJob* verifyJob;
0044     MetaWriter* writer;
0045 };
0046 
0047 
0048 K3b::Iso9660ImageWritingJob::Iso9660ImageWritingJob( K3b::JobHandler* hdl )
0049     : K3b::BurnJob( hdl ),
0050       m_writingMode(K3b::WritingModeAuto),
0051       m_simulate(false),
0052       m_device(0),
0053       m_noFix(false),
0054       m_speed(2),
0055       m_dataMode(K3b::DataModeAuto),
0056       m_copies(1)
0057 {
0058     d = new Private;
0059     d->verifyJob = 0;
0060     d->writer = 0;
0061 }
0062 
0063 
0064 K3b::Iso9660ImageWritingJob::~Iso9660ImageWritingJob()
0065 {
0066     delete d->writer;
0067     delete d;
0068 }
0069 
0070 
0071 void K3b::Iso9660ImageWritingJob::start()
0072 {
0073     d->canceled = d->finished = false;
0074     d->currentCopy = 1;
0075 
0076     jobStarted();
0077 
0078     if( m_simulate )
0079         m_verifyData = false;
0080 
0081     emit newTask( i18n("Preparing data") );
0082 
0083     if( !QFile::exists( m_imagePath ) ) {
0084         emit infoMessage( i18n("Could not find image %1", m_imagePath), K3b::Job::MessageError );
0085         jobFinished( false );
0086         return;
0087     }
0088 
0089     KIO::filesize_t mb = K3b::imageFilesize( QUrl::fromLocalFile(m_imagePath) )/1024ULL/1024ULL;
0090 
0091     // very rough test but since most dvd images are 4,x or 8,x GB it should be enough
0092     d->isDvdImage = ( mb > 900ULL );
0093 
0094     startWriting();
0095 }
0096 
0097 
0098 void K3b::Iso9660ImageWritingJob::slotWriterJobFinished( bool success )
0099 {
0100     if( d->canceled ) {
0101         d->finished = true;
0102         emit canceled();
0103         jobFinished(false);
0104         return;
0105     }
0106 
0107     d->checksumPipe.close();
0108 
0109     if( success ) {
0110         if( !m_simulate && m_verifyData ) {
0111             emit burning(false);
0112 
0113             // alright
0114             // the writerJob should have emitted the "simulation/writing successful" signal
0115 
0116             if( !d->verifyJob ) {
0117                 d->verifyJob = new K3b::VerificationJob( this );
0118                 connectSubJob( d->verifyJob,
0119                                SLOT(slotVerificationFinished(bool)),
0120                                K3b::Job::DEFAULT_SIGNAL_CONNECTION,
0121                                K3b::Job::DEFAULT_SIGNAL_CONNECTION,
0122                                SLOT(slotVerificationProgress(int)),
0123                                SIGNAL(subPercent(int)) );
0124             }
0125             d->verifyJob->setDevice( m_device );
0126             d->verifyJob->clear();
0127             d->verifyJob->addTrack( 1, d->checksumPipe.checksum(), K3b::imageFilesize( QUrl::fromLocalFile(m_imagePath) )/2048 );
0128 
0129             if( m_copies == 1 )
0130                 emit newTask( i18n("Verifying written data") );
0131             else
0132                 emit newTask( i18n("Verifying written copy %1 of %2", d->currentCopy, m_copies) );
0133 
0134             d->verifyJob->start();
0135         }
0136         else if( d->currentCopy >= m_copies ) {
0137             if ( k3bcore->globalSettings()->ejectMedia() ) {
0138                 K3b::Device::eject( m_device );
0139             }
0140             d->finished = true;
0141             jobFinished(true);
0142         }
0143         else {
0144             d->currentCopy++;
0145             if( !K3b::eject( m_device ) ) {
0146                 blockingInformation( i18n("K3b was unable to eject the written medium. Please do so manually.") );
0147             }
0148             startWriting();
0149         }
0150     }
0151     else {
0152         if ( k3bcore->globalSettings()->ejectMedia() ) {
0153             K3b::Device::eject( m_device );
0154         }
0155         d->finished = true;
0156         jobFinished(false);
0157     }
0158 }
0159 
0160 
0161 void K3b::Iso9660ImageWritingJob::slotVerificationFinished( bool success )
0162 {
0163     if( d->canceled ) {
0164         d->finished = true;
0165         emit canceled();
0166         jobFinished(false);
0167         return;
0168     }
0169 
0170     if( success && d->currentCopy < m_copies ) {
0171         d->currentCopy++;
0172         connect( K3b::Device::eject( m_device ), SIGNAL(finished(bool)),
0173                  this, SLOT(startWriting()) );
0174         return;
0175     }
0176 
0177     if( k3bcore->globalSettings()->ejectMedia() )
0178         K3b::Device::eject( m_device );
0179 
0180     d->finished = true;
0181     jobFinished( success );
0182 }
0183 
0184 
0185 void K3b::Iso9660ImageWritingJob::slotVerificationProgress( int p )
0186 {
0187     emit percent( (int)(100.0 / (double)m_copies * ( (double)(d->currentCopy-1) + 0.5 + (double)p/200.0 )) );
0188 }
0189 
0190 
0191 void K3b::Iso9660ImageWritingJob::slotWriterPercent( int p )
0192 {
0193     emit subPercent( p );
0194 
0195     if( m_verifyData )
0196         emit percent( (int)(100.0 / (double)m_copies * ( (double)(d->currentCopy-1) + ((double)p/200.0) )) );
0197     else
0198         emit percent( (int)(100.0 / (double)m_copies * ( (double)(d->currentCopy-1) + ((double)p/100.0) )) );
0199 }
0200 
0201 
0202 void K3b::Iso9660ImageWritingJob::slotNextTrack( int, int )
0203 {
0204     if( m_copies == 1 )
0205         emit newSubTask( i18n("Writing image") );
0206     else
0207         emit newSubTask( i18n("Writing copy %1 of %2", d->currentCopy, m_copies) );
0208 }
0209 
0210 
0211 void K3b::Iso9660ImageWritingJob::cancel()
0212 {
0213     if( !d->finished ) {
0214         d->canceled = true;
0215 
0216         if( d->writer )
0217             d->writer->cancel();
0218         if( m_verifyData && d->verifyJob )
0219             d->verifyJob->cancel();
0220     }
0221 }
0222 
0223 
0224 void K3b::Iso9660ImageWritingJob::startWriting()
0225 {
0226     emit newSubTask( i18n("Waiting for medium") );
0227 
0228     // we wait for the following:
0229     // 1. If special CD features are requested: CD types only Special are:
0230     // K3b::WritingAppCdrdao with K3b::WritingModeAuto or K3b::WritingModeSao,
0231     // any WritingApp with K3b::WritingModeTao,
0232     // any WritingApp with K3b::WritingModeRaw
0233     // 2. If formatted DVD-RW is requested: formatted DVD-RW only Request is:
0234     // K3b::WritingModeRestrictedOverwrite
0235     // 3. If image is larger than 900 MiB (d->isDvdImage == true): DVD or BD
0236     // types See K3b::Iso9660ImageWritingJob::start()
0237     // 4. If image not larger than 900 MiB: All media types
0238     // 5. If not decided yet: DVD and BD media types.
0239 
0240     Device::MediaTypes mt = Device::MediaTypes();
0241     if (m_writingMode == K3b::WritingModeAuto ||
0242         m_writingMode == K3b::WritingModeSao) {
0243         if (writingApp() == K3b::WritingAppCdrdao)
0244             mt = K3b::Device::MEDIA_WRITABLE_CD;
0245         else if (d->isDvdImage)
0246             mt = K3b::Device::MEDIA_WRITABLE_DVD | K3b::Device::MEDIA_WRITABLE_BD;
0247         else
0248             mt = K3b::Device::MEDIA_WRITABLE;
0249     } else if (m_writingMode == K3b::WritingModeTao ||
0250                m_writingMode == K3b::WritingModeRaw) {
0251         mt = K3b::Device::MEDIA_WRITABLE_CD;
0252     } else if (m_writingMode == K3b::WritingModeRestrictedOverwrite) {
0253         mt = /*K3b::Device::MEDIA_DVD_PLUS_R | K3b::Device::MEDIA_DVD_PLUS_R_DL |*/
0254              K3b::Device::MEDIA_DVD_PLUS_RW | K3b::Device::MEDIA_DVD_RW_OVWR;
0255     } else {
0256         mt = K3b::Device::MEDIA_WRITABLE_DVD | K3b::Device::MEDIA_WRITABLE_BD;
0257     }
0258 
0259     // wait for the media
0260     Device::MediaType media = waitForMedium( m_device, K3b::Device::STATE_EMPTY, mt, K3b::imageFilesize( QUrl::fromLocalFile(m_imagePath) )/2048 );
0261     if( media == Device::MEDIA_UNKNOWN ) {
0262         d->finished = true;
0263         emit canceled();
0264         jobFinished(false);
0265         return;
0266     }
0267 
0268     // we simply always calculate the checksum, thus making the code simpler
0269     d->imageFile.close();
0270     d->imageFile.setName( m_imagePath );
0271     d->imageFile.open( QIODevice::ReadOnly );
0272     d->checksumPipe.close();
0273     d->checksumPipe.readFrom( &d->imageFile, true );
0274 
0275     if( prepareWriter() ) {
0276         emit burning(true);
0277         d->writer->start();
0278 #ifdef __GNUC__
0279 #warning Growisofs needs stdin to be closed in order to exit gracefully. Cdrecord does not. However,  if closed with cdrecord we loose parts of stderr. Why?
0280 #endif
0281         d->checksumPipe.writeTo( d->writer->ioDevice(), d->writer->usedWritingApp() == K3b::WritingAppGrowisofs );
0282         d->checksumPipe.open( K3b::ChecksumPipe::MD5, true );
0283     }
0284     else {
0285         d->finished = true;
0286         jobFinished(false);
0287     }
0288 }
0289 
0290 
0291 bool K3b::Iso9660ImageWritingJob::prepareWriter()
0292 {
0293     delete d->writer;
0294 
0295     d->writer = new MetaWriter( m_device, this );
0296 
0297     d->writer->setWritingMode( m_writingMode );
0298     qDebug() << "DEBUG:" << __PRETTY_FUNCTION__ << writingApp();
0299     d->writer->setWritingApp( writingApp() );
0300     d->writer->setSimulate( m_simulate );
0301     d->writer->setBurnSpeed( m_speed );
0302     d->writer->setMultiSession( m_noFix );
0303 
0304     Device::Toc toc;
0305     toc << Device::Track( 0, Msf(K3b::imageFilesize( QUrl::fromLocalFile(m_imagePath) )/2048)-1,
0306                           Device::Track::TYPE_DATA,
0307                           ( m_dataMode == K3b::DataModeAuto && m_noFix ) ||
0308                           m_dataMode == K3b::DataMode2
0309                           ? Device::Track::XA_FORM2
0310                           : Device::Track::MODE1 );
0311     d->writer->setSessionToWrite( toc );
0312 
0313     connect( d->writer, SIGNAL(infoMessage(QString,int)), this, SIGNAL(infoMessage(QString,int)) );
0314     connect( d->writer, SIGNAL(nextTrack(int,int)), this, SLOT(slotNextTrack(int,int)) );
0315     connect( d->writer, SIGNAL(percent(int)), this, SLOT(slotWriterPercent(int)) );
0316     connect( d->writer, SIGNAL(processedSize(int,int)), this, SIGNAL(processedSize(int,int)) );
0317     connect( d->writer, SIGNAL(buffer(int)), this, SIGNAL(bufferStatus(int)) );
0318     connect( d->writer, SIGNAL(deviceBuffer(int)), this, SIGNAL(deviceBuffer(int)) );
0319     connect( d->writer, SIGNAL(writeSpeed(int,K3b::Device::SpeedMultiplicator)), this, SIGNAL(writeSpeed(int,K3b::Device::SpeedMultiplicator)) );
0320     connect( d->writer, SIGNAL(finished(bool)), this, SLOT(slotWriterJobFinished(bool)) );
0321     connect( d->writer, SIGNAL(newTask(QString)), this, SIGNAL(newTask(QString)) );
0322     connect( d->writer, SIGNAL(newSubTask(QString)), this, SIGNAL(newSubTask(QString)) );
0323     connect( d->writer, SIGNAL(debuggingOutput(QString,QString)),
0324              this, SIGNAL(debuggingOutput(QString,QString)) );
0325 
0326     return true;
0327 }
0328 
0329 
0330 QString K3b::Iso9660ImageWritingJob::jobDescription() const
0331 {
0332     if( m_simulate )
0333         return i18n("Simulating ISO 9660 Image");
0334     else
0335         return ( i18n("Burning ISO 9660 Image")
0336                  + ( m_copies > 1
0337                      ? i18np(" - %1 Copy", " - %1 Copies", m_copies)
0338                      : QString() ) );
0339 }
0340 
0341 
0342 QString K3b::Iso9660ImageWritingJob::jobDetails() const
0343 {
0344     return m_imagePath.section('/', -1) + QString( " (%1)" ).arg(KIO::convertSize(K3b::filesize(QUrl::fromLocalFile(m_imagePath))));
0345 }
0346 
0347 
0348 QString K3b::Iso9660ImageWritingJob::jobSource() const
0349 {
0350     return m_imagePath;
0351 }
0352 
0353 
0354 QString K3b::Iso9660ImageWritingJob::jobTarget() const
0355 {
0356     if( m_device )
0357         return m_device->vendor() + ' ' + m_device->description();
0358     else
0359         return QString ();
0360 }
0361 
0362 #include "moc_k3biso9660imagewritingjob.cpp"