File indexing completed on 2024-05-12 04:51:04

0001 /*
0002     SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "k3bdatamultisessionparameterjob.h"
0007 
0008 #include "k3bthread.h"
0009 #include "k3biso9660.h"
0010 #include "k3bdevice.h"
0011 #include "k3bdiskinfo.h"
0012 #include "k3bdevicetypes.h"
0013 #include "k3bglobals.h"
0014 #include "k3btoc.h"
0015 #include "k3btrack.h"
0016 #include "k3bdatadoc.h"
0017 #include "k3b_i18n.h"
0018 
0019 
0020 class K3b::DataMultiSessionParameterJob::Private
0021 {
0022 public:
0023     K3b::DataDoc* doc;
0024 
0025     K3b::DataDoc::MultiSessionMode usedMultiSessionMode;
0026     unsigned int previousSessionStart;
0027     unsigned int nextSessionStart;
0028     // true if the last session should be imported into the filesystem or not
0029     bool importSession;
0030 };
0031 
0032 
0033 
0034 K3b::DataMultiSessionParameterJob::DataMultiSessionParameterJob( K3b::DataDoc* doc, K3b::JobHandler* hdl, QObject* parent )
0035     : K3b::ThreadJob( hdl, parent ),
0036       d( new Private() )
0037 {
0038     d->doc = doc;
0039 }
0040 
0041 
0042 K3b::DataMultiSessionParameterJob::~DataMultiSessionParameterJob()
0043 {
0044     delete d;
0045 }
0046 
0047 
0048 K3b::DataDoc::MultiSessionMode K3b::DataMultiSessionParameterJob::usedMultiSessionMode() const
0049 {
0050     return d->usedMultiSessionMode;
0051 }
0052 
0053 
0054 unsigned int K3b::DataMultiSessionParameterJob::previousSessionStart() const
0055 {
0056     return d->previousSessionStart;
0057 }
0058 
0059 
0060 unsigned int K3b::DataMultiSessionParameterJob::nextSessionStart() const
0061 {
0062     return d->nextSessionStart;
0063 }
0064 
0065 
0066 bool K3b::DataMultiSessionParameterJob::importPreviousSession() const
0067 {
0068     return d->importSession;
0069 }
0070 
0071 
0072 bool K3b::DataMultiSessionParameterJob::run()
0073 {
0074     d->usedMultiSessionMode = d->doc->multiSessionMode();
0075 
0076     if( !d->doc->onlyCreateImages() ) {
0077         if ( d->usedMultiSessionMode == K3b::DataDoc::AUTO ) {
0078             if( d->doc->writingMode() == K3b::WritingModeAuto ||
0079                 !( d->doc->writingMode() & (K3b::Device::WRITINGMODE_SAO|K3b::Device::WRITINGMODE_RAW) ) ) {
0080                 emit newSubTask( i18n("Searching for old session") );
0081 
0082                 //
0083                 // Wait for the medium.
0084                 // In case an old session was imported we always want to continue or finish a multisession CD/DVD.
0085                 // Otherwise we wait for everything we could handle and decide what to do in
0086                 // determineMultiSessionMode( K3b::Device::DeviceHandler* ) below.
0087                 //
0088 
0089                 Device::MediaStates wantedMediaState = K3b::Device::STATE_INCOMPLETE|K3b::Device::STATE_EMPTY;
0090                 if( d->doc->importedSession() >= 0 )
0091                     wantedMediaState = K3b::Device::STATE_INCOMPLETE;
0092 
0093                 Device::MediaType m = waitForMedium( d->doc->burner(),
0094                                                      wantedMediaState,
0095                                                      K3b::Device::MEDIA_WRITABLE );
0096 
0097                 if( m == Device::MEDIA_UNKNOWN ) {
0098                     cancel();
0099                     return false;
0100                 }
0101                 else {
0102                     d->usedMultiSessionMode = determineMultiSessionModeFromMedium();
0103                 }
0104             }
0105             else {
0106                 d->usedMultiSessionMode = K3b::DataDoc::NONE;
0107             }
0108         }
0109 
0110         // FIXME: not sure if it is good to always wait for a medium this early
0111         if( d->usedMultiSessionMode == K3b::DataDoc::CONTINUE ||
0112             d->usedMultiSessionMode == K3b::DataDoc::FINISH ) {
0113             int m = waitForMedium( d->doc->burner(),
0114                                   K3b::Device::STATE_INCOMPLETE,
0115                                   K3b::Device::MEDIA_WRITABLE );
0116 
0117             if( m < 0 ) {
0118                 cancel();
0119                 return false;
0120             }
0121 
0122             if ( !setupMultiSessionParameters() ) {
0123                 cancel();
0124                 return false;
0125             }
0126         }
0127     }
0128 
0129     return true;
0130 }
0131 
0132 
0133 K3b::DataDoc::MultiSessionMode K3b::DataMultiSessionParameterJob::determineMultiSessionModeFromMedium()
0134 {
0135     K3b::Device::DiskInfo info = d->doc->burner()->diskInfo();
0136 
0137     // FIXME: Does BD-RE really behave like DVD+RW here?
0138     if( info.mediaType() & (K3b::Device::MEDIA_DVD_PLUS_RW|
0139                             K3b::Device::MEDIA_DVD_PLUS_RW_DL|
0140                             K3b::Device::MEDIA_DVD_RW_OVWR|
0141                             K3b::Device::MEDIA_BD_RE|
0142                             K3b::Device::MEDIA_DVD_RAM|
0143                             K3b::Device::MEDIA_BD_R_RRM) ) {
0144         //
0145         // we need to handle DVD+RW and DVD-RW overwrite media differently since remainingSize() is not valid
0146         // in both cases
0147         // Since one never closes a DVD+RW we only differ between CONTINUE and START
0148         //
0149 
0150         qDebug() << "(K3b::DataMultiSessionParameterJob) found overwrite medium.";
0151 
0152         // try to check the filesystem size
0153         K3b::Iso9660 iso( d->doc->burner() );
0154         if( iso.open() && info.capacity() - iso.primaryDescriptor().volumeSpaceSize >= d->doc->burningLength() ) {
0155             return K3b::DataDoc::CONTINUE;
0156         }
0157         else {
0158             return K3b::DataDoc::START;
0159         }
0160     }
0161 
0162     else if( info.appendable() ) {
0163         //
0164         //  1. the project does not fit -> no multisession (resulting in asking for another media above)
0165         //     Exception: a session was imported.
0166         //  2. the project does fit and fills up the medium to some arbitrary percentage -> finish multisession
0167         //  3. Special case for the 4GB boundary which seems to be enforced by a linux kernel issue
0168         //  4. the project does fit and does not fill up the CD -> continue multisession
0169         //
0170 
0171         qDebug() << "(K3b::DataMultiSessionParameterJob) found appendable medium.";
0172 
0173         if( d->doc->size() > info.remainingSize().mode1Bytes() &&
0174             d->doc->importedSession() < 0 ) {
0175             return K3b::DataDoc::NONE;
0176         }
0177         else if( d->doc->size() >= info.capacity().mode1Bytes()*9/10 ) {
0178             return K3b::DataDoc::FINISH;
0179         }
0180         else if( info.capacity() < 2621440 /* ~ 5 GB */ &&
0181                     info.size() + d->doc->burningLength() + 11400 /* used size + project size + session gap */ > 2097152 /* 4 GB */ ) {
0182             return K3b::DataDoc::FINISH;
0183         }
0184         else {
0185             return K3b::DataDoc::CONTINUE;
0186         }
0187     }
0188 
0189     else { // empty and complete rewritable media
0190         //
0191         // 1. the project does fit and fills up the medium to some arbitrary percentage -> finish multisession
0192         // 2. Special case for the 4GB boundary which seems to be enforced by a linux kernel issue
0193         //
0194 
0195         if( d->doc->size() >= info.capacity().mode1Bytes()*9/10 ||
0196             d->doc->writingMode() == K3b::WritingModeSao ) {
0197             return K3b::DataDoc::NONE;
0198         }
0199         else if( Device::isDvdMedia( info.mediaType() ) &&
0200                  info.capacity() < 2621440 /* ~ 5 GB */ &&
0201                  d->doc->length() + 11400 /* used size + project size + session gap */ > 2097152 /* 4 GB */ ) {
0202             return K3b::DataDoc::NONE;
0203         }
0204         else {
0205             return K3b::DataDoc::START;
0206         }
0207     }
0208 }
0209 
0210 
0211 bool K3b::DataMultiSessionParameterJob::setupMultiSessionParameters()
0212 {
0213     K3b::Device::DiskInfo info = d->doc->burner()->diskInfo();
0214     K3b::Device::Toc toc = d->doc->burner()->readToc();
0215 
0216     if( toc.isEmpty() )
0217     {
0218         emit infoMessage( i18n("No medium inserted or an empty medium in %1. Cannot continue multisession disk.",
0219                                d->doc->burner()->vendor() + ' ' + d->doc->burner()->description() ), K3b::Job::MessageError );
0220         return false;
0221     }
0222 
0223     //
0224     // determine the multisession import info
0225     //
0226     unsigned long lastSessionStart = 0;
0227     unsigned long nextSessionStart = 0;
0228     d->importSession = true;
0229 
0230     // FIXME: Does BD-RE really behave like DVD+RW here?
0231     if( info.mediaType() & (K3b::Device::MEDIA_DVD_PLUS_RW|
0232                             K3b::Device::MEDIA_DVD_PLUS_RW_DL|
0233                             K3b::Device::MEDIA_DVD_RW_OVWR|
0234                             K3b::Device::MEDIA_BD_RE|
0235                             K3b::Device::MEDIA_DVD_RAM|
0236                             K3b::Device::MEDIA_BD_R_RRM) ) {
0237         lastSessionStart = 0;
0238 
0239         // get info from iso filesystem
0240         K3b::Iso9660 iso( d->doc->burner(), toc.last().firstSector().lba() );
0241         if( iso.open() ) {
0242             nextSessionStart = iso.primaryDescriptor().volumeSpaceSize;
0243         }
0244         else {
0245             emit infoMessage( i18n("Could not open ISO 9660 filesystem in %1.",
0246                                    d->doc->burner()->vendor() + ' ' + d->doc->burner()->description() ), K3b::Job::MessageError );
0247             return false;
0248         }
0249     }
0250     else {
0251         nextSessionStart = d->doc->burner()->nextWritableAddress();
0252         lastSessionStart = toc.last().firstSector().lba();
0253         if ( d->doc->importedSession() > 0 ) {
0254             for ( K3b::Device::Toc::const_iterator it = toc.constBegin(); it != toc.constEnd(); ++it ) {
0255                 if ( ( *it ).session() == d->doc->importedSession() ) {
0256                     lastSessionStart = ( *it ).firstSector().lba();
0257                     if ( ( *it ).type() == K3b::Device::Track::TYPE_AUDIO )
0258                         d->importSession = false;
0259                     break;
0260                 }
0261             }
0262         }
0263     }
0264 
0265     if ( info.mediaType() & K3b::Device::MEDIA_DVD_ALL ) {
0266         // pad to closest 32K boundary
0267         nextSessionStart += 15;
0268         nextSessionStart /= 16;
0269         nextSessionStart *= 16;
0270 
0271         // growisofs does it. I actually do not know yet why.... :(
0272         lastSessionStart += 16;
0273     }
0274 
0275     d->previousSessionStart = lastSessionStart;
0276     if (nextSessionStart == 0) {
0277         emit infoMessage(i18n("Medium is not of multi-session type and does not contain ISO 9660. Cannot emulate multi-session on it."), MessageError);
0278         return false;
0279     }
0280     d->nextSessionStart = nextSessionStart;
0281 
0282     return true;
0283 }
0284 
0285 #include "moc_k3bdatamultisessionparameterjob.cpp"