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"