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

0001 /*
0002     SPDX-FileCopyrightText: 2003-2009 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-FileCopyrightText: 2011 Michal Malek <michalm@jabster.pl>
0004     SPDX-FileCopyrightText: 1998-2009 Sebastian Trueg <trueg@k3b.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "k3bclonejob.h"
0010 
0011 #include "k3breadcdreader.h"
0012 #include "k3bcdrecordwriter.h"
0013 #include "k3bexternalbinmanager.h"
0014 #include "k3bdevice.h"
0015 #include "k3bdevicehandler.h"
0016 #include "k3bglobals.h"
0017 #include "k3bcore.h"
0018 #include "k3bclonetocreader.h"
0019 #include "k3bglobalsettings.h"
0020 #include "k3b_i18n.h"
0021 
0022 #include <QDebug>
0023 #include <QFile>
0024 #include <QFileInfo>
0025 
0026 
0027 
0028 class K3b::CloneJob::Private
0029 {
0030 public:
0031     Private()
0032         : doneCopies(0) {
0033     }
0034 
0035     int doneCopies;
0036 };
0037 
0038 
0039 K3b::CloneJob::CloneJob( K3b::JobHandler* hdl, QObject* parent )
0040     : K3b::BurnJob( hdl, parent ),
0041       m_writerDevice(0),
0042       m_readerDevice(0),
0043       m_writerJob(0),
0044       m_readcdReader(0),
0045       m_removeImageFiles(false),
0046       m_canceled(false),
0047       m_running(false),
0048       m_simulate(false),
0049       m_speed(1),
0050       m_copies(1),
0051       m_onlyCreateImage(false),
0052       m_onlyBurnExistingImage(false),
0053       m_readRetries(128)
0054 {
0055     d = new Private;
0056 }
0057 
0058 
0059 K3b::CloneJob::~CloneJob()
0060 {
0061     delete d;
0062 }
0063 
0064 
0065 void K3b::CloneJob::start()
0066 {
0067     jobStarted();
0068 
0069     m_canceled = false;
0070     m_running = true;
0071 
0072 
0073     // TODO: check the cd size and warn the user if not enough space
0074 
0075     //
0076     // We first check if cdrecord has clone support
0077     // The readcdReader will check the same for readcd
0078     //
0079     const K3b::ExternalBin* cdrecordBin = k3bcore->externalBinManager()->binObject( "cdrecord" );
0080     if( !cdrecordBin ) {
0081         emit infoMessage( i18n("Could not find %1 executable.",QString("cdrecord")), MessageError );
0082         jobFinished(false);
0083         m_running = false;
0084         return;
0085     }
0086     else if( cdrecordBin && !cdrecordBin->hasFeature( "clone" ) ) {
0087         emit infoMessage( i18n("Cdrecord version %1 does not have cloning support.",cdrecordBin->version()), MessageError );
0088         jobFinished(false);
0089         m_running = false;
0090         return;
0091     }
0092 
0093     if( (!m_onlyCreateImage && !writer()) ||
0094         (!m_onlyBurnExistingImage && !readingDevice()) ) {
0095         emit infoMessage( i18n("No device set."), MessageError );
0096         jobFinished(false);
0097         m_running = false;
0098         return;
0099     }
0100 
0101     if( !m_onlyCreateImage ) {
0102         if( !writer()->supportsWritingMode( K3b::Device::WRITINGMODE_RAW_R96R ) &&
0103             !writer()->supportsWritingMode( K3b::Device::WRITINGMODE_RAW_R16 ) ) {
0104             emit infoMessage( i18n("CD writer %1 (%2) does not support cloning.",
0105                                    writer()->vendor(),
0106                                    writer()->description()), MessageError );
0107             m_running = false;
0108             jobFinished(false);
0109             return;
0110         }
0111     }
0112 
0113     if( m_imagePath.isEmpty() ) {
0114         m_imagePath = K3b::findTempFile( "img" );
0115     }
0116     else if( QFileInfo(m_imagePath).isDir() ) {
0117         m_imagePath = K3b::findTempFile( "img", m_imagePath );
0118     }
0119 
0120     if( m_onlyBurnExistingImage ) {
0121         startWriting();
0122     }
0123     else {
0124         emit burning( false );
0125 
0126         prepareReader();
0127 
0128         if( waitForMedium( readingDevice(),
0129                           K3b::Device::STATE_COMPLETE,
0130                           K3b::Device::MEDIA_WRITABLE_CD|K3b::Device::MEDIA_CD_ROM ) == Device::MEDIA_UNKNOWN ) {
0131             m_running = false;
0132             emit canceled();
0133             jobFinished(false);
0134             return;
0135         }
0136 
0137         emit newTask( i18n("Reading clone image") );
0138 
0139         m_readcdReader->start();
0140     }
0141 }
0142 
0143 
0144 void K3b::CloneJob::prepareReader()
0145 {
0146     if( !m_readcdReader ) {
0147         m_readcdReader = new K3b::ReadcdReader( this, this );
0148         connect( m_readcdReader, SIGNAL(percent(int)), this, SLOT(slotReadingPercent(int)) );
0149         connect( m_readcdReader, SIGNAL(percent(int)), this, SIGNAL(subPercent(int)) );
0150         connect( m_readcdReader, SIGNAL(processedSize(int,int)), this, SIGNAL(processedSubSize(int,int)) );
0151         connect( m_readcdReader, SIGNAL(finished(bool)), this, SLOT(slotReadingFinished(bool)) );
0152         connect( m_readcdReader, SIGNAL(infoMessage(QString,int)), this, SIGNAL(infoMessage(QString,int)) );
0153         connect( m_readcdReader, SIGNAL(newTask(QString)), this, SIGNAL(newSubTask(QString)) );
0154         connect( m_readcdReader, SIGNAL(debuggingOutput(QString,QString)),
0155                  this, SIGNAL(debuggingOutput(QString,QString)) );
0156     }
0157 
0158     m_readcdReader->setReadDevice( readingDevice() );
0159     m_readcdReader->setReadSpeed( 0 ); // MAX
0160     m_readcdReader->setDisableCorrection( m_noCorrection );
0161     m_readcdReader->setImagePath( m_imagePath );
0162     m_readcdReader->setClone( true );
0163     m_readcdReader->setRetries( m_readRetries );
0164 }
0165 
0166 
0167 void K3b::CloneJob::prepareWriter()
0168 {
0169     if( !m_writerJob ) {
0170         m_writerJob = new K3b::CdrecordWriter( writer(), this, this );
0171         connect( m_writerJob, SIGNAL(infoMessage(QString,int)), this, SIGNAL(infoMessage(QString,int)) );
0172         connect( m_writerJob, SIGNAL(percent(int)), this, SLOT(slotWriterPercent(int)) );
0173         connect( m_writerJob, SIGNAL(percent(int)), this, SIGNAL(subPercent(int)) );
0174         connect( m_writerJob, SIGNAL(nextTrack(int,int)), this, SLOT(slotWriterNextTrack(int,int)) );
0175         connect( m_writerJob, SIGNAL(processedSize(int,int)), this, SIGNAL(processedSubSize(int,int)) );
0176         connect( m_writerJob, SIGNAL(buffer(int)), this, SIGNAL(bufferStatus(int)) );
0177         connect( m_writerJob, SIGNAL(deviceBuffer(int)), this, SIGNAL(deviceBuffer(int)) );
0178         connect( m_writerJob, SIGNAL(writeSpeed(int,K3b::Device::SpeedMultiplicator)), this, SIGNAL(writeSpeed(int,K3b::Device::SpeedMultiplicator)) );
0179         connect( m_writerJob, SIGNAL(finished(bool)), this, SLOT(slotWriterFinished(bool)) );
0180         //    connect( m_writerJob, SIGNAL(newTask(QString)), this, SIGNAL(newTask(QString)) );
0181         connect( m_writerJob, SIGNAL(newSubTask(QString)), this, SIGNAL(newSubTask(QString)) );
0182         connect( m_writerJob, SIGNAL(debuggingOutput(QString,QString)),
0183                  this, SIGNAL(debuggingOutput(QString,QString)) );
0184     }
0185 
0186     m_writerJob->clearArguments();
0187     m_writerJob->setWritingMode( K3b::WritingModeRaw );
0188     m_writerJob->setClone( true );
0189     m_writerJob->setSimulate( m_simulate );
0190     m_writerJob->setBurnSpeed( m_speed );
0191     m_writerJob->addArgument( m_imagePath );
0192 }
0193 
0194 
0195 void K3b::CloneJob::cancel()
0196 {
0197     if( m_running ) {
0198         m_canceled = true;
0199         if( m_readcdReader )
0200             m_readcdReader->cancel();
0201         if( m_writerJob )
0202             m_writerJob->cancel();
0203     }
0204 }
0205 
0206 
0207 void K3b::CloneJob::slotWriterPercent( int p )
0208 {
0209     if( m_onlyBurnExistingImage )
0210         emit percent( (int)((double)(d->doneCopies)*100.0/(double)(m_copies) + (double)p/(double)(m_copies)) );
0211     else
0212         emit percent( (int)((double)(1+d->doneCopies)*100.0/(double)(1+m_copies) + (double)p/(double)(1+m_copies)) );
0213 }
0214 
0215 
0216 void K3b::CloneJob::slotWriterNextTrack( int t, int tt )
0217 {
0218     emit newSubTask( i18n("Writing Track %1 of %2",t,tt) );
0219 }
0220 
0221 
0222 void K3b::CloneJob::slotWriterFinished( bool success )
0223 {
0224     if( m_canceled ) {
0225         removeImageFiles();
0226         m_running = false;
0227         emit canceled();
0228         jobFinished(false);
0229         return;
0230     }
0231 
0232     if( success ) {
0233         d->doneCopies++;
0234 
0235         emit infoMessage( i18n("Successfully written clone copy %1.",d->doneCopies), MessageInfo );
0236 
0237         if( d->doneCopies < m_copies ) {
0238             K3b::Device::eject( writer() );
0239             startWriting();
0240         }
0241         else {
0242             if ( k3bcore->globalSettings()->ejectMedia() ) {
0243                 K3b::Device::eject( writer() );
0244             }
0245 
0246             if( m_removeImageFiles )
0247                 removeImageFiles();
0248             m_running = false;
0249             jobFinished(true);
0250         }
0251     }
0252     else {
0253         removeImageFiles();
0254         m_running = false;
0255         jobFinished(false);
0256     }
0257 }
0258 
0259 
0260 void K3b::CloneJob::slotReadingPercent( int p )
0261 {
0262     emit percent( m_onlyCreateImage ? p : (int)((double)p/(double)(1+m_copies)) );
0263 }
0264 
0265 
0266 void K3b::CloneJob::slotReadingFinished( bool success )
0267 {
0268     if( m_canceled ) {
0269         removeImageFiles();
0270         m_running = false;
0271         emit canceled();
0272         jobFinished(false);
0273         return;
0274     }
0275 
0276     if( success ) {
0277         //
0278         // Make a quick test if the image is really valid.
0279         // Readcd does not seem to have proper exit codes
0280         //
0281         K3b::CloneTocReader ctr( m_imagePath );
0282         if( ctr.isValid() ) {
0283             emit infoMessage( i18n("Successfully read disk."), MessageInfo );
0284             if( m_onlyCreateImage ) {
0285                 m_running = false;
0286                 jobFinished(true);
0287             }
0288             else {
0289                 if( writer() == readingDevice() && k3bcore->globalSettings()->ejectMedia() )
0290                     K3b::Device::eject( writer() );
0291                 startWriting();
0292             }
0293         }
0294         else {
0295             emit infoMessage( i18n("Failed to read disk completely in clone mode."), MessageError );
0296             removeImageFiles();
0297             m_running = false;
0298             jobFinished(false);
0299         }
0300     }
0301     else {
0302         emit infoMessage( i18n("Error while reading disk."), MessageError );
0303         removeImageFiles();
0304         m_running = false;
0305         jobFinished(false);
0306     }
0307 }
0308 
0309 
0310 void K3b::CloneJob::startWriting()
0311 {
0312     emit burning( true );
0313 
0314     // start writing
0315     prepareWriter();
0316 
0317     if( waitForMedium( writer(),
0318                       K3b::Device::STATE_EMPTY,
0319                       K3b::Device::MEDIA_WRITABLE_CD ) == Device::MEDIA_UNKNOWN ) {
0320         removeImageFiles();
0321         m_running = false;
0322         emit canceled();
0323         jobFinished(false);
0324         return;
0325     }
0326 
0327     if( m_simulate )
0328         emit newTask( i18n("Simulating clone copy") );
0329     else
0330         emit newTask( i18n("Writing clone copy %1",d->doneCopies+1) );
0331 
0332     m_writerJob->start();
0333 }
0334 
0335 
0336 void K3b::CloneJob::removeImageFiles()
0337 {
0338     if( !m_onlyBurnExistingImage ) {
0339         emit infoMessage( i18n("Removing image files."), MessageInfo );
0340         if( QFile::exists( m_imagePath ) )
0341             QFile::remove( m_imagePath );
0342         if( QFile::exists( m_imagePath + ".toc" ) )
0343             QFile::remove( m_imagePath + ".toc"  );
0344     }
0345 }
0346 
0347 
0348 QString K3b::CloneJob::jobDescription() const
0349 {
0350     if( m_onlyCreateImage )
0351         return i18n("Creating Clone Image");
0352     else if( m_onlyBurnExistingImage ) {
0353         if( m_simulate )
0354             return i18n("Simulating Clone Image");
0355         else
0356             return i18n("Burning Clone Image");
0357     }
0358     else if( m_simulate )
0359         return i18n("Simulating CD Cloning");
0360     else
0361         return i18n("Cloning CD");
0362 }
0363 
0364 
0365 QString K3b::CloneJob::jobDetails() const
0366 {
0367     return i18np("Creating 1 clone copy",
0368                  "Creating %1 clone copies",
0369                  (m_simulate||m_onlyCreateImage) ? 1 : m_copies );
0370 }
0371 
0372 
0373 QString K3b::CloneJob::jobSource() const
0374 {
0375     if( m_readerDevice )
0376         return m_readerDevice->vendor() + ' ' + m_readerDevice->description();
0377     else
0378         return QString();
0379 }
0380 
0381 
0382 QString K3b::CloneJob::jobTarget() const
0383 {
0384     if( m_writerDevice )
0385         return m_writerDevice->vendor() + ' ' + m_writerDevice->description();
0386     else
0387         return m_imagePath;
0388 }
0389 
0390 #include "moc_k3bclonejob.cpp"